summaryrefslogtreecommitdiff
path: root/util/tlm/src/sc_slave_port.cc
diff options
context:
space:
mode:
Diffstat (limited to 'util/tlm/src/sc_slave_port.cc')
-rw-r--r--util/tlm/src/sc_slave_port.cc400
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;
+}
+
+}