1/* 2 * Copyright (c) 2015, University of Kaiserslautern 3 * Copyright (c) 2016, Dresden University of Technology (TU Dresden) 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * 1. Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the copyright holder nor the names of its 18 * contributors may be used to endorse or promote products derived from 19 * this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 25 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 * Authors: Matthias Jung 34 * Abdul Mutaal Ahmad 35 * Christian Menard 36 */ 37 38#include "sc_ext.hh" 39#include "sc_mm.hh" 40#include "sc_slave_port.hh" 41#include "slave_transactor.hh" 42 43namespace Gem5SystemC 44{ 45 46/** 47 * Instantiate a tlm memory manager that takes care about all the 48 * tlm transactions in the system 49 */ 50MemoryManager mm; 51 52/** 53 * Convert a gem5 packet to a TLM payload by copying all the relevant 54 * information to a previously allocated tlm payload 55 */ 56void 57packet2payload(PacketPtr packet, tlm::tlm_generic_payload &trans) 58{ 59 trans.set_address(packet->getAddr()); 60 61 /* Check if this transaction was allocated by mm */ 62 sc_assert(trans.has_mm()); 63 64 unsigned int size = packet->getSize(); 65 unsigned char *data = packet->getPtr<unsigned char>(); 66 67 trans.set_data_length(size); 68 trans.set_streaming_width(size); 69 trans.set_data_ptr(data); 70 71 if (packet->isRead()) { 72 trans.set_command(tlm::TLM_READ_COMMAND); 73 } 74 else if (packet->isInvalidate()) { 75 /* Do nothing */ 76 } else if (packet->isWrite()) { 77 trans.set_command(tlm::TLM_WRITE_COMMAND); 78 } else { 79 SC_REPORT_FATAL("SCSlavePort", "No R/W packet"); 80 } 81} 82 83/** 84 * Similar to TLM's blocking transport (LT) 85 */ 86Tick 87SCSlavePort::recvAtomic(PacketPtr packet) 88{ 89 CAUGHT_UP; 90 SC_REPORT_INFO("SCSlavePort", "recvAtomic hasn't been tested much"); 91 92 panic_if(packet->cacheResponding(), "Should not see packets where cache " 93 "is responding"); 94 95 panic_if(!(packet->isRead() || packet->isWrite()), 96 "Should only see read and writes at TLM memory\n"); 97 98 99 sc_core::sc_time delay = sc_core::SC_ZERO_TIME; 100 101 102 /* Prepare the transaction */ 103 tlm::tlm_generic_payload * trans = mm.allocate(); 104 trans->acquire(); 105 packet2payload(packet, *trans); 106 107 /* Attach the packet pointer to the TLM transaction to keep track */ 108 Gem5Extension* extension = new Gem5Extension(packet); 109 trans->set_auto_extension(extension); 110 111 /* Execute b_transport: */ 112 if (packet->cmd == MemCmd::SwapReq) { 113 SC_REPORT_FATAL("SCSlavePort", "SwapReq not supported"); 114 } else if (packet->isRead()) { 115 transactor->socket->b_transport(*trans, delay); 116 } else if (packet->isInvalidate()) { 117 // do nothing 118 } else if (packet->isWrite()) { 119 transactor->socket->b_transport(*trans, delay); 120 } else { 121 SC_REPORT_FATAL("SCSlavePort", "Typo of request not supported"); 122 } 123 124 if (packet->needsResponse()) { 125 packet->makeResponse(); 126 } 127 128 trans->release(); 129 130 return delay.value(); 131} 132 133/** 134 * Similar to TLM's debug transport 135 */ 136void 137SCSlavePort::recvFunctional(PacketPtr packet) 138{ 139 /* Prepare the transaction */ 140 tlm::tlm_generic_payload * trans = mm.allocate(); 141 trans->acquire(); 142 packet2payload(packet, *trans); 143 144 /* Attach the packet pointer to the TLM transaction to keep track */ 145 Gem5Extension* extension = new Gem5Extension(packet); 146 trans->set_auto_extension(extension); 147 148 /* Execute Debug Transport: */ 149 unsigned int bytes = transactor->socket->transport_dbg(*trans); 150 if (bytes != trans->get_data_length()) { 151 SC_REPORT_FATAL("SCSlavePort","debug transport was not completed"); 152 } 153 154 trans->release(); 155} 156 157bool 158SCSlavePort::recvTimingSnoopResp(PacketPtr packet) 159{ 160 /* Snooping should be implemented with tlm_dbg_transport */ 161 SC_REPORT_FATAL("SCSlavePort","unimplemented func.: recvTimingSnoopResp"); 162 return false; 163} 164 165void 166SCSlavePort::recvFunctionalSnoop(PacketPtr packet) 167{ 168 /* Snooping should be implemented with tlm_dbg_transport */ 169 SC_REPORT_FATAL("SCSlavePort","unimplemented func.: recvFunctionalSnoop"); 170} 171 172/** 173 * Similar to TLM's non-blocking transport (AT) 174 */ 175bool 176SCSlavePort::recvTimingReq(PacketPtr packet) 177{ 178 CAUGHT_UP; 179 180 panic_if(packet->cacheResponding(), "Should not see packets where cache " 181 "is responding"); 182 183 panic_if(!(packet->isRead() || packet->isWrite()), 184 "Should only see read and writes at TLM memory\n"); 185 186 187 /* We should never get a second request after noting that a retry is 188 * required */ 189 sc_assert(!needToSendRequestRetry); 190 191 /* Remember if a request comes in while we're blocked so that a retry 192 * can be sent to gem5 */ 193 if (blockingRequest) { 194 needToSendRequestRetry = true; 195 return false; 196 } 197 198 /* NOTE: normal tlm is blocking here. But in our case we return false 199 * and tell gem5 when a retry can be done. This is the main difference 200 * in the protocol: 201 * if (requestInProgress) 202 * { 203 * wait(endRequestEvent); 204 * } 205 * requestInProgress = trans; 206 */ 207 208 /* Prepare the transaction */ 209 tlm::tlm_generic_payload * trans = mm.allocate(); 210 trans->acquire(); 211 packet2payload(packet, *trans); 212 213 /* Attach the packet pointer to the TLM transaction to keep track */ 214 Gem5Extension* extension = new Gem5Extension(packet); 215 trans->set_auto_extension(extension); 216 217 /* 218 * Pay for annotated transport delays. 219 * 220 * The header delay marks the point in time, when the packet first is seen 221 * by the transactor. This is the point int time, when the transactor needs 222 * to send the BEGIN_REQ to the SystemC world. 223 * 224 * NOTE: We drop the payload delay here. Normally, the receiver would be 225 * responsible for handling the payload delay. In this case, however, 226 * the receiver is a SystemC module and has no notion of the gem5 227 * transport protocol and we cannot simply forward the 228 * payload delay to the receiving module. Instead, we expect the 229 * receiving SystemC module to model the payload delay by deferring 230 * the END_REQ. This could lead to incorrect delays, if the XBar 231 * payload delay is longer than the time the receiver needs to accept 232 * the request (time between BEGIN_REQ and END_REQ). 233 * 234 * TODO: We could detect the case described above by remembering the 235 * payload delay and comparing it to the time between BEGIN_REQ and 236 * END_REQ. Then, a warning should be printed. 237 */ 238 auto delay = sc_core::sc_time::from_value(packet->payloadDelay); 239 // reset the delays 240 packet->payloadDelay = 0; 241 packet->headerDelay = 0; 242 243 /* Starting TLM non-blocking sequence (AT) Refer to IEEE1666-2011 SystemC 244 * Standard Page 507 for a visualisation of the procedure */ 245 tlm::tlm_phase phase = tlm::BEGIN_REQ; 246 tlm::tlm_sync_enum status; 247 status = transactor->socket->nb_transport_fw(*trans, phase, delay); 248 /* Check returned value: */ 249 if (status == tlm::TLM_ACCEPTED) { 250 sc_assert(phase == tlm::BEGIN_REQ); 251 /* Accepted but is now blocking until END_REQ (exclusion rule)*/ 252 blockingRequest = trans; 253 } else if (status == tlm::TLM_UPDATED) { 254 /* The Timing annotation must be honored: */ 255 sc_assert(phase == tlm::END_REQ || phase == tlm::BEGIN_RESP); 256 257 PayloadEvent<SCSlavePort> * pe; 258 pe = new PayloadEvent<SCSlavePort>(*this, 259 &SCSlavePort::pec, "PEQ"); 260 pe->notify(*trans, phase, delay); 261 } else if (status == tlm::TLM_COMPLETED) { 262 /* Transaction is over nothing has do be done. */ 263 sc_assert(phase == tlm::END_RESP); 264 trans->release(); 265 } 266 267 return true; 268} 269 270void 271SCSlavePort::pec( 272 PayloadEvent<SCSlavePort> * pe, 273 tlm::tlm_generic_payload& trans, 274 const tlm::tlm_phase& phase) 275{ 276 sc_time delay; 277 278 if (phase == tlm::END_REQ || 279 &trans == blockingRequest && phase == tlm::BEGIN_RESP) { 280 sc_assert(&trans == blockingRequest); 281 blockingRequest = NULL; 282 283 /* Did another request arrive while blocked, schedule a retry */ 284 if (needToSendRequestRetry) { 285 needToSendRequestRetry = false; 286 sendRetryReq(); 287 } 288 } 289 if (phase == tlm::BEGIN_RESP) 290 { 291 CAUGHT_UP; 292 293 auto& extension = Gem5Extension::getExtension(trans); 294 auto packet = extension.getPacket(); 295 296 sc_assert(!blockingResponse); 297 298 bool need_retry = false; 299 300 /* 301 * If the packet was piped through and needs a response, we don't need 302 * to touch the packet and can forward it directly as a response. 303 * Otherwise, we need to make a response and send the transformed 304 * packet. 305 */ 306 if (extension.isPipeThrough()) { 307 if (packet->isResponse()) { 308 need_retry = !sendTimingResp(packet); 309 } 310 } else if (packet->needsResponse()) { 311 packet->makeResponse(); 312 need_retry = !sendTimingResp(packet); 313 } 314 315 if (need_retry) { 316 blockingResponse = &trans; 317 } else { 318 if (phase == tlm::BEGIN_RESP) { 319 /* Send END_RESP and we're finished: */ 320 tlm::tlm_phase fw_phase = tlm::END_RESP; 321 sc_time delay = SC_ZERO_TIME; 322 transactor->socket->nb_transport_fw(trans, fw_phase, delay); 323 /* Release the transaction with all the extensions */ 324 trans.release(); 325 } 326 } 327 } 328 delete pe; 329} 330 331void 332SCSlavePort::recvRespRetry() 333{ 334 CAUGHT_UP; 335 336 /* Retry a response */ 337 sc_assert(blockingResponse); 338 339 tlm::tlm_generic_payload *trans = blockingResponse; 340 blockingResponse = NULL; 341 PacketPtr packet = Gem5Extension::getExtension(trans).getPacket(); 342 343 bool need_retry = !sendTimingResp(packet); 344 345 sc_assert(!need_retry); 346 347 sc_core::sc_time delay = sc_core::SC_ZERO_TIME; 348 tlm::tlm_phase phase = tlm::END_RESP; 349 transactor->socket->nb_transport_fw(*trans, phase, delay); 350 // Release transaction with all the extensions 351 trans->release(); 352} 353 354tlm::tlm_sync_enum 355SCSlavePort::nb_transport_bw(tlm::tlm_generic_payload& trans, 356 tlm::tlm_phase& phase, 357 sc_core::sc_time& delay) 358{ 359 PayloadEvent<SCSlavePort> * pe; 360 pe = new PayloadEvent<SCSlavePort>(*this, &SCSlavePort::pec, "PE"); 361 pe->notify(trans, phase, delay); 362 return tlm::TLM_ACCEPTED; 363} 364 365SCSlavePort::SCSlavePort(const std::string &name_, 366 const std::string &systemc_name, 367 ExternalSlave &owner_) : 368 ExternalSlave::Port(name_, owner_), 369 blockingRequest(NULL), 370 needToSendRequestRetry(false), 371 blockingResponse(NULL), 372 transactor(nullptr) 373{ 374} 375 376void 377SCSlavePort::bindToTransactor(Gem5SlaveTransactor* transactor) 378{ 379 sc_assert(this->transactor == nullptr); 380 381 this->transactor = transactor; 382 383 transactor->socket.register_nb_transport_bw(this, 384 &SCSlavePort::nb_transport_bw); 385} 386 387ExternalSlave::Port* 388SCSlavePortHandler::getExternalPort(const std::string &name, 389 ExternalSlave &owner, 390 const std::string &port_data) 391{ 392 // Create and register a new SystemC slave port 393 auto* port = new SCSlavePort(name, port_data, owner); 394 395 control.registerSlavePort(port_data, port); 396 397 return port; 398} 399 400} 401