changeset 31648cc2e0d9 in /z/repo/gem5
details: http://repo.gem5.org/gem5?cmd=changeset;node=31648cc2e0d9
description:
        MEM: Unify bus access methods and prepare for master/slave split

        This patch unifies the recvFunctional, recvAtomic and recvTiming to
        all be based on a similar structure: 1) extract information about the
        incoming packet, 2) send it out to the appropriate snoopers, 3)
        determine where it is going, and 4) forward it to the right
        destination. The naming of variables across the different access
        functions is now consistent as well.

        Additionally, the patch introduces the member functions releaseBus and
        retryWaiting to better distinguish between the two cases when we
        should tell a sender to retry. The first case is when the bus goes
        from busy to idle, and the second case is when it receives a retry
        from a destination that did not immediatelly accept a packet.

        As a very minor change, the MMU debug flag is no longer used in the bus.

diffstat:

 src/mem/bus.cc |  313 +++++++++++++++++++++++++++++++++-----------------------
 src/mem/bus.hh |   30 ++--
 2 files changed, 197 insertions(+), 146 deletions(-)

diffs (truncated from 570 to 300 lines):

diff -r 8c3bd7bea667 -r 31648cc2e0d9 src/mem/bus.cc
--- a/src/mem/bus.cc    Thu Mar 22 06:36:27 2012 -0400
+++ b/src/mem/bus.cc    Thu Mar 22 06:37:21 2012 -0400
@@ -50,13 +50,12 @@
 #include "base/trace.hh"
 #include "debug/Bus.hh"
 #include "debug/BusAddrRanges.hh"
-#include "debug/MMU.hh"
 #include "mem/bus.hh"
 
 Bus::Bus(const BusParams *p)
-    : MemObject(p), busId(p->bus_id), clock(p->clock),
+    : MemObject(p), clock(p->clock),
       headerCycles(p->header_cycles), width(p->width), tickNextIdle(0),
-      drainEvent(NULL), busIdle(this), inRetry(false),
+      drainEvent(NULL), busIdleEvent(this), inRetry(false),
       nbrMasterPorts(p->port_master_connection_count),
       defaultPortId(INVALID_PORT_ID), useDefaultRange(p->use_default_range),
       defaultBlockSize(p->block_size),
@@ -82,6 +81,17 @@
         ++id;
     }
 
+    // see if we have a default master connected and if so add the
+    // port
+    if (p->port_default_connection_count) {
+        defaultPortId = id;
+        std::string portName = csprintf("%s-default", name());
+        interfaces.push_back(new BusPort(portName, this, id));
+        ++id;
+        // this is an additional master port
+        ++nbrMasterPorts;
+    }
+
     // note that the first slave port is now stored on index
     // nbrMasterPorts in the vector
     for (int i = 0; i < p->port_slave_connection_count; ++i) {
@@ -90,15 +100,6 @@
         ++id;
     }
 
-    // see if we have a default master connected and if so add the
-    // port at the end
-    if (p->port_default_connection_count) {
-        defaultPortId = id;
-        std::string portName = csprintf("%s-default", name());
-        interfaces.push_back(new BusPort(portName, this, id));
-        ++id;
-    }
-
     clearPortCache();
 }
 
@@ -137,39 +138,17 @@
     }
 }
 
-Bus::BusFreeEvent::BusFreeEvent(Bus *_bus)
-    : bus(_bus)
-{}
-
-void
-Bus::BusFreeEvent::process()
-{
-    bus->recvRetry(-1);
-}
-
-const char *
-Bus::BusFreeEvent::description() const
-{
-    return "bus became available";
-}
-
 Tick
 Bus::calcPacketTiming(PacketPtr pkt)
 {
-    // Bring tickNextIdle up to the present tick.
-    // There is some potential ambiguity where a cycle starts, which
-    // might make a difference when devices are acting right around a
-    // cycle boundary. Using a < allows things which happen exactly on
-    // a cycle boundary to take up only the following cycle. Anything
-    // that happens later will have to "wait" for the end of that
-    // cycle, and then start using the bus after that.
-    if (tickNextIdle < curTick()) {
-        tickNextIdle = curTick();
-        if (tickNextIdle % clock != 0)
-            tickNextIdle = curTick() - (curTick() % clock) + clock;
+    // determine the current time rounded to the closest following
+    // clock edge
+    Tick now = curTick();
+    if (now % clock != 0) {
+        now = ((now / clock) + 1) * clock;
     }
 
-    Tick headerTime = tickNextIdle + headerCycles * clock;
+    Tick headerTime = now + headerCycles * clock;
 
     // The packet will be sent. Figure out how long it occupies the bus, and
     // how much of that time is for the first "word", aka bus width.
@@ -199,7 +178,7 @@
     }
 
     tickNextIdle = until;
-    reschedule(busIdle, tickNextIdle, true);
+    reschedule(busIdleEvent, tickNextIdle, true);
 
     DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n",
             curTick(), tickNextIdle);
@@ -210,55 +189,72 @@
 bool
 Bus::recvTiming(PacketPtr pkt)
 {
-    short src = pkt->getSrc();
+    // called for both requests and responses
 
-    BusPort *src_port = interfaces[src];
+    // get the source id and port
+    Packet::NodeID src_id = pkt->getSrc();
+
+    BusPort *src_port = interfaces[src_id];
 
     // If the bus is busy, or other devices are in line ahead of the current
     // one, put this device on the retry list.
     if (!pkt->isExpressSnoop() &&
         (tickNextIdle > curTick() ||
-         (retryList.size() && (!inRetry || src_port != retryList.front()))))
+         (!retryList.empty() && (!inRetry || src_port != retryList.front()))))
     {
         addToRetryList(src_port);
         DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x BUSY\n",
-                src, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
+                src_id, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
         return false;
     }
 
     DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x\n",
-            src, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
+            src_id, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
 
     Tick headerFinishTime = pkt->isExpressSnoop() ? 0 : calcPacketTiming(pkt);
     Tick packetFinishTime = pkt->isExpressSnoop() ? 0 : pkt->finishTime;
 
-    short dest = pkt->getDest();
-    int dest_port_id;
+    Packet::NodeID dest = pkt->getDest();
+    int dest_id;
     Port *dest_port;
 
     if (dest == Packet::Broadcast) {
-        dest_port_id = findPort(pkt->getAddr());
-        dest_port = interfaces[dest_port_id];
+        // the packet is a memory-mapped request and should be broadcasted to
+        // our snoopers
+        assert(pkt->isRequest());
+
         SnoopIter s_end = snoopPorts.end();
         for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) 
{
             BusPort *p = *s_iter;
-            if (p != dest_port && p != src_port) {
+            // we got this request from a snooping master
+            // (corresponding to our own slave port that is also in
+            // snoopPorts) and should not send it back to where it
+            // came from
+            if (p->getId() != src_id) {
                 // cache is not allowed to refuse snoop
                 bool success M5_VAR_USED = p->sendTiming(pkt);
                 assert(success);
             }
         }
+
+        // since it is a request, similar to functional and atomic,
+        // determine the destination based on the address and forward
+        // through the corresponding master port
+        dest_id = findPort(pkt->getAddr());
+        dest_port = interfaces[dest_id];
     } else {
-        assert(dest < interfaces.size());
-        assert(dest != src); // catch infinite loops
-        dest_port_id = dest;
-        dest_port = interfaces[dest_port_id];
+        // the packet is a response, and it should always go back to
+        // the port determined by the destination field
+        dest_id = dest;
+        assert(dest_id != src_id); // catch infinite loops
+        assert(dest_id < interfaces.size());
+        dest_port = interfaces[dest_id];
     }
 
-    if (dest_port_id == src) {
-        // Must be forwarded snoop up from below...
-        assert(dest == Packet::Broadcast);
-    } else {
+    // if this is a snoop from a slave (corresponding to our own
+    // master), i.e. the memory side of the bus, then do not send it
+    // back to where it came from
+    if (dest_id != src_id) {
         // send to actual target
         if (!dest_port->sendTiming(pkt))  {
             // Packet not successfully sent. Leave or put it on the retry list.
@@ -268,7 +264,7 @@
             // someone else has committed to respond.
             assert(!pkt->memInhibitAsserted());
             DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x TGT RETRY\n",
-                    src, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
+                    src_id, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
             addToRetryList(src_port);
             occupyBus(headerFinishTime);
             return false;
@@ -284,7 +280,7 @@
     // Packet was successfully sent.
     // Also take care of retries
     if (inRetry) {
-        DPRINTF(Bus, "Remove retry from list %d\n", src);
+        DPRINTF(Bus, "Remove retry from list %d\n", src_id);
         retryList.pop_front();
         inRetry = false;
     }
@@ -292,38 +288,81 @@
 }
 
 void
-Bus::recvRetry(int id)
+Bus::releaseBus()
 {
-    // If there's anything waiting, and the bus isn't busy...
-    if (retryList.size() && curTick() >= tickNextIdle) {
-        //retryingPort = retryList.front();
-        inRetry = true;
-        DPRINTF(Bus, "Sending a retry to %s\n", 
retryList.front()->getPeer()->name());
-        retryList.front()->sendRetry();
-        // If inRetry is still true, sendTiming wasn't called
-        if (inRetry)
-        {
-            retryList.pop_front();
-            inRetry = false;
+    // releasing the bus means we should now be idle
+    assert(curTick() >= tickNextIdle);
 
-            //Bring tickNextIdle up to the present
-            while (tickNextIdle < curTick())
-                tickNextIdle += clock;
+    // bus is now idle, so if someone is waiting we can retry
+    if (!retryList.empty()) {
+        // note that we block (return false on recvTiming) both
+        // because the bus is busy and because the destination is
+        // busy, and in the latter case the bus may be released before
+        // we see a retry from the destination
+        retryWaiting();
+    }
 
-            //Burn a cycle for the missed grant.
-            tickNextIdle += clock;
-
-            reschedule(busIdle, tickNextIdle, true);
-        }
-    }
     //If we weren't able to drain before, we might be able to now.
-    if (drainEvent && retryList.size() == 0 && curTick() >= tickNextIdle) {
+    if (drainEvent && retryList.empty() && curTick() >= tickNextIdle) {
         drainEvent->process();
         // Clear the drain event once we're done with it.
         drainEvent = NULL;
     }
 }
 
+void
+Bus::retryWaiting()
+{
+    // this should never be called with an empty retry list
+    assert(!retryList.empty());
+
+    // send a retry to the port at the head of the retry list
+    inRetry = true;
+    DPRINTF(Bus, "Sending a retry to %s\n",
+            retryList.front()->getPeer()->name());
+
+    // note that we might have blocked on the receiving port being
+    // busy (rather than the bus itself) and now call retry before the
+    // destination called retry on the bus
+    retryList.front()->sendRetry();
+
+    // If inRetry is still true, sendTiming wasn't called in zero time
+    // (e.g. the cache does this)
+    if (inRetry) {
+        retryList.pop_front();
+        inRetry = false;
+
+        //Bring tickNextIdle up to the present
+        while (tickNextIdle < curTick())
+            tickNextIdle += clock;
+
+        //Burn a cycle for the missed grant.
+        tickNextIdle += clock;
+
+        reschedule(busIdleEvent, tickNextIdle, true);
+    }
+}
+
+void
+Bus::recvRetry(int id)
_______________________________________________
gem5-dev mailing list
[email protected]
http://m5sim.org/mailman/listinfo/gem5-dev

Reply via email to