misc: Clean up and complete the gem5<->SystemC-TLM bridge [7/10]
authorChristian Menard <Christian.Menard@tu-dresden.de>
Fri, 10 Feb 2017 00:15:43 +0000 (19:15 -0500)
committerChristian Menard <Christian.Menard@tu-dresden.de>
Fri, 10 Feb 2017 00:15:43 +0000 (19:15 -0500)
The current TLM bridge only provides a Slave Port that allows the gem5
world to send request to the SystemC world. This patch series refractors
and cleans up the existing code, and adds a Master Port that allows the
SystemC world to send requests to the gem5 world.

This patch:
 * Implement 'pipe through' for gem5 Packets (see explanation below)

Basically, this patch ensures that all transactions that originated in the
gem5 world are converted back to the original packet when entering the gem5
world.  So far, this only worked for packets that are responded to by a
SyctemC component (e.g. when a gem5 CPU sends a request to a SystemC
memory). By implementing the 'pipe through' this patch ensures, that
packets that are responded to by a gem5 component (e.g. when a gem5 CPU
sends a request to a gem5 memory via a SystemC interconnect) are handled
properly.

Reviewed at http://reviews.gem5.org/r/3796/

Signed-off-by: Jason Lowe-Power <jason@lowepower.com>
util/tlm/sc_ext.cc
util/tlm/sc_ext.hh
util/tlm/sc_master_port.cc
util/tlm/sc_master_port.hh
util/tlm/sc_slave_port.cc

index db0c36f03f2f39c4c20927adeabaa0b0e15ec457..439e1ca152d3f1e8666d86101604d4fce03c05ff 100644 (file)
@@ -45,6 +45,7 @@ namespace Gem5SystemC
 Gem5Extension::Gem5Extension(PacketPtr packet)
 {
     Packet = packet;
+    pipeThrough = false;
 }
 
 Gem5Extension& Gem5Extension::getExtension(const tlm_generic_payload *payload)
index 79416f5ce013d0edc14de88d37df771b878e0d52..970a1b73d911a1e5343e1b40d7644ecec6eba0be 100644 (file)
@@ -62,8 +62,12 @@ class Gem5Extension: public tlm::tlm_extension<Gem5Extension>
         getExtension(const tlm::tlm_generic_payload &payload);
     PacketPtr getPacket();
 
+    bool isPipeThrough() const { return pipeThrough; }
+    void setPipeThrough() { pipeThrough = true; }
+
   private:
     PacketPtr Packet;
+    bool pipeThrough;
 };
 
 }
index 4ab1f51c6206e1bf76121915fd18a4db67fefd32..5f395026011ae69213dfa49f8467c33e0a85bf3a 100644 (file)
@@ -36,6 +36,7 @@
 
 #include "master_transactor.hh"
 #include "params/ExternalMaster.hh"
+#include "sc_ext.hh"
 #include "sc_master_port.hh"
 #include "sim/system.hh"
 
@@ -87,6 +88,7 @@ SCMasterPort::SCMasterPort(const std::string& name_,
     peq(this, &SCMasterPort::peq_cb),
     waitForRetry(false),
     pendingRequest(nullptr),
+    pendingPacket(nullptr),
     needToSendRetry(false),
     responseInProgress(false),
     transactor(nullptr),
@@ -158,6 +160,7 @@ SCMasterPort::nb_transport_fw(tlm::tlm_generic_payload& trans,
     }
 
     // ... and queue the valid transaction
+    trans.acquire();
     peq.notify(trans, phase, delay);
     return tlm::TLM_ACCEPTED;
 }
@@ -191,18 +194,35 @@ SCMasterPort::handleBeginReq(tlm::tlm_generic_payload& trans)
 {
     sc_assert(!waitForRetry);
     sc_assert(pendingRequest == nullptr);
+    sc_assert(pendingPacket == nullptr);
 
     trans.acquire();
-    auto pkt = generatePacket(trans);
+
+    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;
     }
 }
 
@@ -236,11 +256,25 @@ void
 SCMasterPort::b_transport(tlm::tlm_generic_payload& trans,
                         sc_core::sc_time& t)
 {
-    auto pkt = generatePacket(trans);
+    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);
+    }
 
-    // send an atomic request to gem5
     Tick ticks = sendAtomic(pkt);
-    panic_if(!pkt->isResponse(), "Packet sending failed!\n");
+
+    // send an atomic request to gem5
+    panic_if(pkt->needsResponse() && !pkt->isResponse(),
+             "Packet sending failed!\n");
 
     // one tick is a pico second
     auto delay =
@@ -249,7 +283,8 @@ SCMasterPort::b_transport(tlm::tlm_generic_payload& trans,
     // update time
     t += delay;
 
-    destroyPacket(pkt);
+    if (extension != nullptr)
+        destroyPacket(pkt);
 
     trans.set_response_status(tlm::TLM_OK_RESPONSE);
 }
@@ -257,11 +292,19 @@ SCMasterPort::b_transport(tlm::tlm_generic_payload& trans,
 unsigned int
 SCMasterPort::transport_dbg(tlm::tlm_generic_payload& trans)
 {
-    auto pkt = generatePacket(trans);
-
-    sendFunctional(pkt);
-
-    destroyPacket(pkt);
+    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();
 }
@@ -291,11 +334,22 @@ SCMasterPort::recvTimingResp(PacketPtr pkt)
       sc_core::sc_time::from_value(pkt->payloadDelay + pkt->headerDelay);
 
     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;
-    destroyPacket(pkt);
+
+    // 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();
@@ -330,14 +384,18 @@ SCMasterPort::recvReqRetry()
 {
     sc_assert(waitForRetry);
     sc_assert(pendingRequest != nullptr);
+    sc_assert(pendingPacket != nullptr);
 
-    auto& trans = *pendingRequest;
+    if (sendTimingReq(pendingPacket)) {
+        waitForRetry = false;
+        pendingPacket = nullptr;
 
-    waitForRetry = false;
-    pendingRequest = nullptr;
+        auto& trans = *pendingRequest;
+        sendEndReq(trans);
+        trans.release();
 
-    // retry
-    handleBeginReq(trans);
+        pendingRequest = nullptr;
+    }
 }
 
 void
index 5fae9b6b4eab9a588a8180b8c752acaa17f4c844..a1ab3a8f2917ab89846bc32fd42f6d1dd3fe8a99 100644 (file)
@@ -59,6 +59,13 @@ class Gem5MasterTransactor;
  * added as a sender state to the gem5 packet. This way the payload can be
  * restored when the response packet arrives at the port.
  *
+ * Special care is required, when the TLM transaction originates from a
+ * SCSlavePort (i.e. it is a gem5 packet that enters back into the gem5 world).
+ * This is a common scenario, when multiple gem5 CPUs communicate via a SystemC
+ * interconnect. In this case, the master port restores the original packet
+ * from the payload extension (added by the SCSlavePort) and forwards it to the
+ * gem5 world. Throughout the code, this mechanism is called 'pipe through'.
+ *
  * If gem5 operates in atomic mode, the master port registers the TLM blocking
  * interface and automatically translates non-blocking requests to blocking.
  * If gem5 operates in timing mode, the transactor registers the non-blocking
@@ -82,6 +89,7 @@ class SCMasterPort : public ExternalMaster::Port
 
     bool waitForRetry;
     tlm::tlm_generic_payload* pendingRequest;
+    PacketPtr pendingPacket;
 
     bool needToSendRetry;
 
index cc4218fc0720055eefed8753d05d740de8b79a11..b62c64724258ff9c0ad849a2c9be11ac457d60b4 100644 (file)
@@ -265,16 +265,26 @@ SCSlavePort::pec(
     {
         CAUGHT_UP;
 
-        PacketPtr packet = Gem5Extension::getExtension(trans).getPacket();
+        auto& extension = Gem5Extension::getExtension(trans);
+        auto packet = extension.getPacket();
 
         sc_assert(!blockingResponse);
 
-        bool need_retry;
-        if (packet->needsResponse()) {
+        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);
-        } else {
-            need_retry = false;
         }
 
         if (need_retry) {
@@ -289,8 +299,6 @@ SCSlavePort::pec(
                 trans.release();
             }
         }
-    } else {
-        SC_REPORT_FATAL("SCSlavePort", "Invalid protocol phase in pec");
     }
     delete pe;
 }