summaryrefslogtreecommitdiff
path: root/util/tlm/src/sc_master_port.cc
diff options
context:
space:
mode:
Diffstat (limited to 'util/tlm/src/sc_master_port.cc')
-rw-r--r--util/tlm/src/sc_master_port.cc427
1 files changed, 427 insertions, 0 deletions
diff --git a/util/tlm/src/sc_master_port.cc b/util/tlm/src/sc_master_port.cc
new file mode 100644
index 000000000..630243359
--- /dev/null
+++ b/util/tlm/src/sc_master_port.cc
@@ -0,0 +1,427 @@
+/*
+ * 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: Christian Menard
+ */
+
+#include <sstream>
+
+#include "master_transactor.hh"
+#include "params/ExternalMaster.hh"
+#include "sc_ext.hh"
+#include "sc_master_port.hh"
+#include "sim/system.hh"
+
+namespace Gem5SystemC
+{
+
+PacketPtr
+SCMasterPort::generatePacket(tlm::tlm_generic_payload& trans)
+{
+ Request::Flags flags;
+ auto req = new Request(trans.get_address(), trans.get_data_length(), flags,
+ owner.masterId);
+
+ MemCmd cmd;
+
+ switch (trans.get_command()) {
+ case tlm::TLM_READ_COMMAND:
+ cmd = MemCmd::ReadReq;
+ break;
+ case tlm::TLM_WRITE_COMMAND:
+ cmd = MemCmd::WriteReq;
+ break;
+ default:
+ SC_REPORT_FATAL("SCMasterPort",
+ "received transaction with unsupported command");
+ }
+
+ /*
+ * Allocate a new Packet. The packet will be deleted when it returns from
+ * the gem5 world as a response.
+ */
+ auto pkt = new Packet(req, cmd);
+ pkt->dataStatic(trans.get_data_ptr());
+
+ return pkt;
+}
+
+void
+SCMasterPort::destroyPacket(PacketPtr pkt)
+{
+ delete pkt;
+}
+
+SCMasterPort::SCMasterPort(const std::string& name_,
+ const std::string& systemc_name,
+ ExternalMaster& owner_,
+ Gem5SimControl& simControl)
+ : ExternalMaster::Port(name_, owner_),
+ peq(this, &SCMasterPort::peq_cb),
+ waitForRetry(false),
+ pendingRequest(nullptr),
+ pendingPacket(nullptr),
+ needToSendRetry(false),
+ responseInProgress(false),
+ transactor(nullptr),
+ simControl(simControl)
+{
+ system =
+ dynamic_cast<const ExternalMasterParams*>(owner_.params())->system;
+}
+
+void
+SCMasterPort::bindToTransactor(Gem5MasterTransactor* transactor)
+{
+ sc_assert(this->transactor == nullptr);
+
+ this->transactor = transactor;
+
+ /*
+ * Register the TLM non-blocking interface when using gem5 Timing mode and
+ * the TLM blocking interface when using the gem5 Atomic mode.
+ * Then the magic (TM) in simple_target_socket automatically transforms
+ * non-blocking in blocking transactions and vice versa.
+ *
+ * NOTE: The mode may change during execution.
+ */
+ if (system->isTimingMode()) {
+ SC_REPORT_INFO("SCMasterPort", "register non-blocking interface");
+ transactor->socket.register_nb_transport_fw(this,
+ &SCMasterPort::nb_transport_fw);
+ } else if (system->isAtomicMode()) {
+ SC_REPORT_INFO("SCMasterPort", "register blocking interface");
+ transactor->socket.register_b_transport(this,
+ &SCMasterPort::b_transport);
+ } else {
+ panic("gem5 operates neither in Timing nor in Atomic mode");
+ }
+
+ transactor->socket.register_transport_dbg(this,
+ &SCMasterPort::transport_dbg);
+}
+
+void
+SCMasterPort::checkTransaction(tlm::tlm_generic_payload& trans)
+{
+ if (trans.is_response_error()) {
+ std::stringstream ss;
+ ss << "Transaction returned with error, response status = "
+ << trans.get_response_string();
+ SC_REPORT_ERROR("TLM-2", ss.str().c_str());
+ }
+}
+
+tlm::tlm_sync_enum
+SCMasterPort::nb_transport_fw(tlm::tlm_generic_payload& trans,
+ tlm::tlm_phase& phase, sc_core::sc_time& delay)
+{
+ uint64_t adr = trans.get_address();
+ unsigned len = trans.get_data_length();
+ unsigned char* byteEnable = trans.get_byte_enable_ptr();
+ unsigned width = trans.get_streaming_width();
+
+ // check the transaction attributes for unsupported features ...
+ if (byteEnable != 0) {
+ trans.set_response_status(tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE);
+ return tlm::TLM_COMPLETED;
+ }
+ if (width < len) { // is this a burst request?
+ trans.set_response_status(tlm::TLM_BURST_ERROR_RESPONSE);
+ return tlm::TLM_COMPLETED;
+ }
+
+ // ... and queue the valid transaction
+ trans.acquire();
+ peq.notify(trans, phase, delay);
+ return tlm::TLM_ACCEPTED;
+}
+
+void
+SCMasterPort::peq_cb(tlm::tlm_generic_payload& trans,
+ const tlm::tlm_phase& phase)
+{
+ // catch up with SystemC time
+ simControl.catchup();
+ assert(curTick() == sc_core::sc_time_stamp().value());
+
+ switch (phase) {
+ case tlm::BEGIN_REQ:
+ handleBeginReq(trans);
+ break;
+ case tlm::END_RESP:
+ handleEndResp(trans);
+ break;
+ default:
+ panic("unimplemented phase in callback");
+ }
+
+ // the functions called above may have scheduled gem5 events
+ // -> notify the event loop handler
+ simControl.notify();
+}
+
+void
+SCMasterPort::handleBeginReq(tlm::tlm_generic_payload& trans)
+{
+ sc_assert(!waitForRetry);
+ sc_assert(pendingRequest == nullptr);
+ sc_assert(pendingPacket == nullptr);
+
+ trans.acquire();
+
+ PacketPtr pkt = nullptr;
+
+ Gem5Extension* extension = nullptr;
+ trans.get_extension(extension);
+
+ // If there is an extension, this transaction was initiated by the gem5
+ // world and we can pipe through the original packet. Otherwise, we
+ // generate a new packet based on the transaction.
+ if (extension != nullptr) {
+ extension->setPipeThrough();
+ pkt = extension->getPacket();
+ } else {
+ pkt = generatePacket(trans);
+ }
+
+ auto tlmSenderState = new TlmSenderState(trans);
+ pkt->pushSenderState(tlmSenderState);
+
+ if (sendTimingReq(pkt)) { // port is free -> send END_REQ immediately
+ sendEndReq(trans);
+ trans.release();
+ } else { // port is blocked -> wait for retry before sending END_REQ
+ waitForRetry = true;
+ pendingRequest = &trans;
+ pendingPacket = pkt;
+ }
+}
+
+void
+SCMasterPort::handleEndResp(tlm::tlm_generic_payload& trans)
+{
+ sc_assert(responseInProgress);
+
+ responseInProgress = false;
+
+ checkTransaction(trans);
+
+ if (needToSendRetry) {
+ sendRetryResp();
+ needToSendRetry = false;
+ }
+}
+
+void
+SCMasterPort::sendEndReq(tlm::tlm_generic_payload& trans)
+{
+ tlm::tlm_phase phase = tlm::END_REQ;
+ auto delay = sc_core::SC_ZERO_TIME;
+
+ auto status = transactor->socket->nb_transport_bw(trans, phase, delay);
+ panic_if(status != tlm::TLM_ACCEPTED,
+ "Unexpected status after sending END_REQ");
+}
+
+void
+SCMasterPort::b_transport(tlm::tlm_generic_payload& trans,
+ sc_core::sc_time& t)
+{
+ Gem5Extension* extension = nullptr;
+ trans.get_extension(extension);
+
+ PacketPtr pkt = nullptr;
+
+ // If there is an extension, this transaction was initiated by the gem5
+ // world and we can pipe through the original packet.
+ if (extension != nullptr) {
+ extension->setPipeThrough();
+ pkt = extension->getPacket();
+ } else {
+ pkt = generatePacket(trans);
+ }
+
+ Tick ticks = sendAtomic(pkt);
+
+ // send an atomic request to gem5
+ panic_if(pkt->needsResponse() && !pkt->isResponse(),
+ "Packet sending failed!\n");
+
+ // one tick is a pico second
+ auto delay =
+ sc_core::sc_time((double)(ticks / SimClock::Int::ps), sc_core::SC_PS);
+
+ // update time
+ t += delay;
+
+ if (extension != nullptr)
+ destroyPacket(pkt);
+
+ trans.set_response_status(tlm::TLM_OK_RESPONSE);
+}
+
+unsigned int
+SCMasterPort::transport_dbg(tlm::tlm_generic_payload& trans)
+{
+ Gem5Extension* extension = nullptr;
+ trans.get_extension(extension);
+
+ // If there is an extension, this transaction was initiated by the gem5
+ // world and we can pipe through the original packet.
+ if (extension != nullptr) {
+ extension->setPipeThrough();
+ sendFunctional(extension->getPacket());
+ } else {
+ auto pkt = generatePacket(trans);
+ sendFunctional(pkt);
+ destroyPacket(pkt);
+ }
+
+ return trans.get_data_length();
+}
+
+bool
+SCMasterPort::get_direct_mem_ptr(tlm::tlm_generic_payload& trans,
+ tlm::tlm_dmi& dmi_data)
+{
+ return false;
+}
+
+bool
+SCMasterPort::recvTimingResp(PacketPtr pkt)
+{
+ // exclusion rule
+ // We need to Wait for END_RESP before sending next BEGIN_RESP
+ if (responseInProgress) {
+ sc_assert(!needToSendRetry);
+ needToSendRetry = true;
+ return false;
+ }
+
+ sc_assert(pkt->isResponse());
+
+ /*
+ * Pay for annotated transport delays.
+ *
+ * See recvTimingReq in sc_slave_port.cc for a detailed description.
+ */
+ auto delay = sc_core::sc_time::from_value(pkt->payloadDelay);
+ // reset the delays
+ pkt->payloadDelay = 0;
+ pkt->headerDelay = 0;
+
+ auto tlmSenderState = dynamic_cast<TlmSenderState*>(pkt->popSenderState());
+ sc_assert(tlmSenderState != nullptr);
+
+ auto& trans = tlmSenderState->trans;
+
+ Gem5Extension* extension = nullptr;
+ trans.get_extension(extension);
+
+ // clean up
+ delete tlmSenderState;
+
+ // If there is an extension the packet was piped through and we must not
+ // delete it. The packet travels back with the transaction.
+ if (extension == nullptr)
+ destroyPacket(pkt);
+ else
+ sc_assert(extension->isPipeThrough());
+
+ sendBeginResp(trans, delay);
+ trans.release();
+
+ return true;
+}
+
+void
+SCMasterPort::sendBeginResp(tlm::tlm_generic_payload& trans,
+ sc_core::sc_time& delay)
+{
+ tlm::tlm_phase phase = tlm::BEGIN_RESP;
+
+ trans.set_response_status(tlm::TLM_OK_RESPONSE);
+
+ auto status = transactor->socket->nb_transport_bw(trans, phase, delay);
+
+ if (status == tlm::TLM_COMPLETED ||
+ status == tlm::TLM_UPDATED && phase == tlm::END_RESP) {
+ // transaction completed -> no need to wait for tlm::END_RESP
+ responseInProgress = false;
+ } else if (status == tlm::TLM_ACCEPTED) {
+ // we need to wait for tlm::END_RESP
+ responseInProgress = true;
+ } else {
+ panic("Unexpected status after sending BEGIN_RESP");
+ }
+}
+
+void
+SCMasterPort::recvReqRetry()
+{
+ sc_assert(waitForRetry);
+ sc_assert(pendingRequest != nullptr);
+ sc_assert(pendingPacket != nullptr);
+
+ if (sendTimingReq(pendingPacket)) {
+ waitForRetry = false;
+ pendingPacket = nullptr;
+
+ auto& trans = *pendingRequest;
+ sendEndReq(trans);
+ trans.release();
+
+ pendingRequest = nullptr;
+ }
+}
+
+void
+SCMasterPort::recvRangeChange()
+{
+ SC_REPORT_WARNING("SCMasterPort",
+ "received address range change but ignored it");
+}
+
+ExternalMaster::Port*
+SCMasterPortHandler::getExternalPort(const std::string &name,
+ ExternalMaster &owner,
+ const std::string &port_data)
+{
+ // Create and register a new SystemC master port
+ auto* port = new SCMasterPort(name, port_data, owner, control);
+
+ control.registerMasterPort(port_data, port);
+
+ return port;
+}
+
+} // namespace Gem5SystemC