diff options
author | Christian Menard <christian.menard@tu-dresden.de> | 2017-05-23 00:48:52 +0200 |
---|---|---|
committer | Christian Menard <christian.menard@tu-dresden.de> | 2017-05-30 10:47:32 +0000 |
commit | 01921763a47d1ed2238ee5d4435edbf752783a95 (patch) | |
tree | 3c1a8f43e2baeeaec04208f297b9e782e9393eae /util/tlm/src/sc_slave_port.cc | |
parent | 6e8b0f659602602765fcfdb4d32a8aa2548d669e (diff) | |
download | gem5-01921763a47d1ed2238ee5d4435edbf752783a95.tar.xz |
misc: Reorder sources in util/tlm and rewrite build scripts
* Use one SConstruct to build everything instead of one SConstruct for
each example.
* Introduce SConscripts for sub-directories.
* Build in 'build' instead of the source tree.
* Build and link to SystemC from the ext/systemc directory. This
ensures that SystemC does not need to be installed on the host and
avoids possible issues caused by an incompatible SystemC build.
* Update the README and add some minor fixes
Change-Id: I641ed94f542626864fb7af499ad1be8fd4ad929f
Reviewed-on: https://gem5-review.googlesource.com/3480
Reviewed-by: Matthias Jung <jungma@eit.uni-kl.de>
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Maintainer: Jason Lowe-Power <jason@lowepower.com>
Diffstat (limited to 'util/tlm/src/sc_slave_port.cc')
-rw-r--r-- | util/tlm/src/sc_slave_port.cc | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/util/tlm/src/sc_slave_port.cc b/util/tlm/src/sc_slave_port.cc new file mode 100644 index 000000000..dcf7c5d95 --- /dev/null +++ b/util/tlm/src/sc_slave_port.cc @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2015, University of Kaiserslautern + * Copyright (c) 2016, Dresden University of Technology (TU Dresden) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Matthias Jung + * Abdul Mutaal Ahmad + * Christian Menard + */ + +#include "sc_ext.hh" +#include "sc_mm.hh" +#include "sc_slave_port.hh" +#include "slave_transactor.hh" + +namespace Gem5SystemC +{ + +/** + * Instantiate a tlm memory manager that takes care about all the + * tlm transactions in the system + */ +MemoryManager mm; + +/** + * Convert a gem5 packet to a TLM payload by copying all the relevant + * information to a previously allocated tlm payload + */ +void +packet2payload(PacketPtr packet, tlm::tlm_generic_payload &trans) +{ + trans.set_address(packet->getAddr()); + + /* Check if this transaction was allocated by mm */ + sc_assert(trans.has_mm()); + + unsigned int size = packet->getSize(); + unsigned char *data = packet->getPtr<unsigned char>(); + + trans.set_data_length(size); + trans.set_streaming_width(size); + trans.set_data_ptr(data); + + if (packet->isRead()) { + trans.set_command(tlm::TLM_READ_COMMAND); + } + else if (packet->isInvalidate()) { + /* Do nothing */ + } else if (packet->isWrite()) { + trans.set_command(tlm::TLM_WRITE_COMMAND); + } else { + SC_REPORT_FATAL("SCSlavePort", "No R/W packet"); + } +} + +/** + * Similar to TLM's blocking transport (LT) + */ +Tick +SCSlavePort::recvAtomic(PacketPtr packet) +{ + CAUGHT_UP; + SC_REPORT_INFO("SCSlavePort", "recvAtomic hasn't been tested much"); + + panic_if(packet->cacheResponding(), "Should not see packets where cache " + "is responding"); + + panic_if(!(packet->isRead() || packet->isWrite()), + "Should only see read and writes at TLM memory\n"); + + + sc_core::sc_time delay = sc_core::SC_ZERO_TIME; + + + /* Prepare the transaction */ + tlm::tlm_generic_payload * trans = mm.allocate(); + trans->acquire(); + packet2payload(packet, *trans); + + /* Attach the packet pointer to the TLM transaction to keep track */ + Gem5Extension* extension = new Gem5Extension(packet); + trans->set_auto_extension(extension); + + /* Execute b_transport: */ + if (packet->cmd == MemCmd::SwapReq) { + SC_REPORT_FATAL("SCSlavePort", "SwapReq not supported"); + } else if (packet->isRead()) { + transactor->socket->b_transport(*trans, delay); + } else if (packet->isInvalidate()) { + // do nothing + } else if (packet->isWrite()) { + transactor->socket->b_transport(*trans, delay); + } else { + SC_REPORT_FATAL("SCSlavePort", "Typo of request not supported"); + } + + if (packet->needsResponse()) { + packet->makeResponse(); + } + + trans->release(); + + return delay.value(); +} + +/** + * Similar to TLM's debug transport + */ +void +SCSlavePort::recvFunctional(PacketPtr packet) +{ + /* Prepare the transaction */ + tlm::tlm_generic_payload * trans = mm.allocate(); + trans->acquire(); + packet2payload(packet, *trans); + + /* Attach the packet pointer to the TLM transaction to keep track */ + Gem5Extension* extension = new Gem5Extension(packet); + trans->set_auto_extension(extension); + + /* Execute Debug Transport: */ + unsigned int bytes = transactor->socket->transport_dbg(*trans); + if (bytes != trans->get_data_length()) { + SC_REPORT_FATAL("SCSlavePort","debug transport was not completed"); + } + + trans->release(); +} + +bool +SCSlavePort::recvTimingSnoopResp(PacketPtr packet) +{ + /* Snooping should be implemented with tlm_dbg_transport */ + SC_REPORT_FATAL("SCSlavePort","unimplemented func.: recvTimingSnoopResp"); + return false; +} + +void +SCSlavePort::recvFunctionalSnoop(PacketPtr packet) +{ + /* Snooping should be implemented with tlm_dbg_transport */ + SC_REPORT_FATAL("SCSlavePort","unimplemented func.: recvFunctionalSnoop"); +} + +/** + * Similar to TLM's non-blocking transport (AT) + */ +bool +SCSlavePort::recvTimingReq(PacketPtr packet) +{ + CAUGHT_UP; + + panic_if(packet->cacheResponding(), "Should not see packets where cache " + "is responding"); + + panic_if(!(packet->isRead() || packet->isWrite()), + "Should only see read and writes at TLM memory\n"); + + + /* We should never get a second request after noting that a retry is + * required */ + sc_assert(!needToSendRequestRetry); + + /* Remember if a request comes in while we're blocked so that a retry + * can be sent to gem5 */ + if (blockingRequest) { + needToSendRequestRetry = true; + return false; + } + + /* NOTE: normal tlm is blocking here. But in our case we return false + * and tell gem5 when a retry can be done. This is the main difference + * in the protocol: + * if (requestInProgress) + * { + * wait(endRequestEvent); + * } + * requestInProgress = trans; + */ + + /* Prepare the transaction */ + tlm::tlm_generic_payload * trans = mm.allocate(); + trans->acquire(); + packet2payload(packet, *trans); + + /* Attach the packet pointer to the TLM transaction to keep track */ + Gem5Extension* extension = new Gem5Extension(packet); + trans->set_auto_extension(extension); + + /* + * Pay for annotated transport delays. + * + * The header delay marks the point in time, when the packet first is seen + * by the transactor. This is the point int time, when the transactor needs + * to send the BEGIN_REQ to the SystemC world. + * + * NOTE: We drop the payload delay here. Normally, the receiver would be + * responsible for handling the payload delay. In this case, however, + * the receiver is a SystemC module and has no notion of the gem5 + * transport protocol and we cannot simply forward the + * payload delay to the receiving module. Instead, we expect the + * receiving SystemC module to model the payload delay by deferring + * the END_REQ. This could lead to incorrect delays, if the XBar + * payload delay is longer than the time the receiver needs to accept + * the request (time between BEGIN_REQ and END_REQ). + * + * TODO: We could detect the case described above by remembering the + * payload delay and comparing it to the time between BEGIN_REQ and + * END_REQ. Then, a warning should be printed. + */ + auto delay = sc_core::sc_time::from_value(packet->payloadDelay); + // reset the delays + packet->payloadDelay = 0; + packet->headerDelay = 0; + + /* Starting TLM non-blocking sequence (AT) Refer to IEEE1666-2011 SystemC + * Standard Page 507 for a visualisation of the procedure */ + tlm::tlm_phase phase = tlm::BEGIN_REQ; + tlm::tlm_sync_enum status; + status = transactor->socket->nb_transport_fw(*trans, phase, delay); + /* Check returned value: */ + if (status == tlm::TLM_ACCEPTED) { + sc_assert(phase == tlm::BEGIN_REQ); + /* Accepted but is now blocking until END_REQ (exclusion rule)*/ + blockingRequest = trans; + } else if (status == tlm::TLM_UPDATED) { + /* The Timing annotation must be honored: */ + sc_assert(phase == tlm::END_REQ || phase == tlm::BEGIN_RESP); + + PayloadEvent<SCSlavePort> * pe; + pe = new PayloadEvent<SCSlavePort>(*this, + &SCSlavePort::pec, "PEQ"); + pe->notify(*trans, phase, delay); + } else if (status == tlm::TLM_COMPLETED) { + /* Transaction is over nothing has do be done. */ + sc_assert(phase == tlm::END_RESP); + trans->release(); + } + + return true; +} + +void +SCSlavePort::pec( + PayloadEvent<SCSlavePort> * pe, + tlm::tlm_generic_payload& trans, + const tlm::tlm_phase& phase) +{ + sc_time delay; + + if (phase == tlm::END_REQ || + &trans == blockingRequest && phase == tlm::BEGIN_RESP) { + sc_assert(&trans == blockingRequest); + blockingRequest = NULL; + + /* Did another request arrive while blocked, schedule a retry */ + if (needToSendRequestRetry) { + needToSendRequestRetry = false; + sendRetryReq(); + } + } + if (phase == tlm::BEGIN_RESP) + { + CAUGHT_UP; + + auto& extension = Gem5Extension::getExtension(trans); + auto packet = extension.getPacket(); + + sc_assert(!blockingResponse); + + bool need_retry = false; + + /* + * If the packet was piped through and needs a response, we don't need + * to touch the packet and can forward it directly as a response. + * Otherwise, we need to make a response and send the transformed + * packet. + */ + if (extension.isPipeThrough()) { + if (packet->isResponse()) { + need_retry = !sendTimingResp(packet); + } + } else if (packet->needsResponse()) { + packet->makeResponse(); + need_retry = !sendTimingResp(packet); + } + + if (need_retry) { + blockingResponse = &trans; + } else { + if (phase == tlm::BEGIN_RESP) { + /* Send END_RESP and we're finished: */ + tlm::tlm_phase fw_phase = tlm::END_RESP; + sc_time delay = SC_ZERO_TIME; + transactor->socket->nb_transport_fw(trans, fw_phase, delay); + /* Release the transaction with all the extensions */ + trans.release(); + } + } + } + delete pe; +} + +void +SCSlavePort::recvRespRetry() +{ + CAUGHT_UP; + + /* Retry a response */ + sc_assert(blockingResponse); + + tlm::tlm_generic_payload *trans = blockingResponse; + blockingResponse = NULL; + PacketPtr packet = Gem5Extension::getExtension(trans).getPacket(); + + bool need_retry = !sendTimingResp(packet); + + sc_assert(!need_retry); + + sc_core::sc_time delay = sc_core::SC_ZERO_TIME; + tlm::tlm_phase phase = tlm::END_RESP; + transactor->socket->nb_transport_fw(*trans, phase, delay); + // Release transaction with all the extensions + trans->release(); +} + +tlm::tlm_sync_enum +SCSlavePort::nb_transport_bw(tlm::tlm_generic_payload& trans, + tlm::tlm_phase& phase, + sc_core::sc_time& delay) +{ + PayloadEvent<SCSlavePort> * pe; + pe = new PayloadEvent<SCSlavePort>(*this, &SCSlavePort::pec, "PE"); + pe->notify(trans, phase, delay); + return tlm::TLM_ACCEPTED; +} + +SCSlavePort::SCSlavePort(const std::string &name_, + const std::string &systemc_name, + ExternalSlave &owner_) : + ExternalSlave::Port(name_, owner_), + blockingRequest(NULL), + needToSendRequestRetry(false), + blockingResponse(NULL), + transactor(nullptr) +{ +} + +void +SCSlavePort::bindToTransactor(Gem5SlaveTransactor* transactor) +{ + sc_assert(this->transactor == nullptr); + + this->transactor = transactor; + + transactor->socket.register_nb_transport_bw(this, + &SCSlavePort::nb_transport_bw); +} + +ExternalSlave::Port* +SCSlavePortHandler::getExternalPort(const std::string &name, + ExternalSlave &owner, + const std::string &port_data) +{ + // Create and register a new SystemC slave port + auto* port = new SCSlavePort(name, port_data, owner); + + control.registerSlavePort(port_data, port); + + return port; +} + +} |