Merge yet again with the main repository.
[gem5.git] / src / mem / bus.cc
index cb359734b515d3422ba6f9038649d9be3bc3440d..db71b86b73cdda88dd9323b73ea80a08d93cf417 100644 (file)
 
 #include "base/misc.hh"
 #include "base/trace.hh"
+#include "debug/Bus.hh"
+#include "debug/BusAddrRanges.hh"
+#include "debug/MMU.hh"
 #include "mem/bus.hh"
-#include "params/Bus.hh"
+
+Bus::Bus(const BusParams *p)
+    : MemObject(p), busId(p->bus_id), clock(p->clock),
+      headerCycles(p->header_cycles), width(p->width), tickNextIdle(0),
+      drainEvent(NULL), busIdle(this), inRetry(false), maxId(0),
+      defaultPort(NULL), funcPort(NULL), funcPortId(-4),
+      useDefaultRange(p->use_default_range), defaultBlockSize(p->block_size),
+      cachedBlockSize(0), cachedBlockSizeValid(false)
+{
+    //width, clock period, and header cycles must be positive
+    if (width <= 0)
+        fatal("Bus width must be positive\n");
+    if (clock <= 0)
+        fatal("Bus clock period must be positive\n");
+    if (headerCycles <= 0)
+        fatal("Number of header cycles must be positive\n");
+    clearBusCache();
+    clearPortCache();
+}
 
 Port *
 Bus::getPort(const std::string &if_name, int idx)
@@ -84,6 +105,7 @@ Bus::deletePortRefs(Port *p)
     if (funcPort == bp)
         return;
     interfaces.erase(bp->getId());
+    clearBusCache();
     delete bp;
 }
 
@@ -97,74 +119,72 @@ Bus::init()
         intIter->second->sendStatusChange(Port::RangeChange);
 }
 
-Bus::BusFreeEvent::BusFreeEvent(Bus *_bus) : Event(&mainEventQueue), bus(_bus)
+Bus::BusFreeEvent::BusFreeEvent(Bus *_bus)
+    : bus(_bus)
 {}
 
-void Bus::BusFreeEvent::process()
+void
+Bus::BusFreeEvent::process()
 {
     bus->recvRetry(-1);
 }
 
-const char * Bus::BusFreeEvent::description()
+const char *
+Bus::BusFreeEvent::description() const
 {
     return "bus became available";
 }
 
-void Bus::occupyBus(PacketPtr pkt)
+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;
+    // 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;
+            tickNextIdle = curTick() - (curTick() % clock) + clock;
     }
 
+    Tick headerTime = tickNextIdle + 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.
     int numCycles = 0;
-    // Requests need one cycle to send an address
-    if (pkt->isRequest())
-        numCycles++;
-    else if (pkt->isResponse() || pkt->hasData()) {
+    if (pkt->hasData()) {
         // If a packet has data, it needs ceil(size/width) cycles to send it
-        // We're using the "adding instead of dividing" trick again here
-        if (pkt->hasData()) {
-            int dataSize = pkt->getSize();
-            numCycles += dataSize/width;
-            if (dataSize % width)
-                numCycles++;
-        } else {
-            // If the packet didn't have data, it must have been a response.
-            // Those use the bus for one cycle to send their data.
+        int dataSize = pkt->getSize();
+        numCycles += dataSize/width;
+        if (dataSize % width)
             numCycles++;
-        }
     }
 
     // The first word will be delivered after the current tick, the delivery
     // of the address if any, and one bus cycle to deliver the data
-    pkt->firstWordTime =
-        tickNextIdle +
-        pkt->isRequest() ? clock : 0 +
-        clock;
-
-    //Advance it numCycles bus cycles.
-    //XXX Should this use the repeated addition trick as well?
-    tickNextIdle += (numCycles * clock);
-    if (!busIdle.scheduled()) {
-        busIdle.schedule(tickNextIdle);
-    } else {
-        busIdle.reschedule(tickNextIdle);
+    pkt->firstWordTime = headerTime + clock;
+
+    pkt->finishTime = headerTime + numCycles * clock;
+
+    return headerTime;
+}
+
+void Bus::occupyBus(Tick until)
+{
+    if (until == 0) {
+        // shortcut for express snoop packets
+        return;
     }
-    DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n",
-            curTick, tickNextIdle);
 
-    // The bus will become idle once the current packet is delivered.
-    pkt->finishTime = tickNextIdle;
+    tickNextIdle = until;
+    reschedule(busIdle, tickNextIdle, true);
+
+    DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n",
+            curTick(), tickNextIdle);
 }
 
 /** Function called by the port when the bus is receiving a Timing
@@ -173,25 +193,35 @@ bool
 Bus::recvTiming(PacketPtr pkt)
 {
     short src = pkt->getSrc();
-    DPRINTF(Bus, "recvTiming: packet src %d dest %d addr 0x%x cmd %s\n",
-            src, pkt->getDest(), pkt->getAddr(), pkt->cmdString());
 
-    BusPort *src_port = (src == defaultId) ? defaultPort : interfaces[src];
+    BusPort *src_port;
+    if (src == defaultId)
+        src_port = defaultPort;
+    else {
+        src_port = checkBusCache(src);
+        if (src_port == NULL) {
+            src_port = interfaces[src];
+            updateBusCache(src, src_port);
+        }
+    }
 
     // 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 ||
+        (tickNextIdle > curTick() ||
          (retryList.size() && (!inRetry || src_port != retryList.front()))))
     {
         addToRetryList(src_port);
-        DPRINTF(Bus, "recvTiming: Bus is busy, returning false\n");
+        DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x BUSY\n",
+                src, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
         return false;
     }
 
-    if (!pkt->isExpressSnoop()) {
-        occupyBus(pkt);
-    }
+    DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x\n",
+            src, 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;
@@ -201,25 +231,28 @@ Bus::recvTiming(PacketPtr pkt)
         dest_port_id = findPort(pkt->getAddr());
         dest_port = (dest_port_id == defaultId) ?
             defaultPort : interfaces[dest_port_id];
-        for (SnoopIter s_iter = snoopPorts.begin();
-             s_iter != snoopPorts.end();
-             s_iter++) {
+        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) {
-#ifndef NDEBUG
                 // cache is not allowed to refuse snoop
-                bool success = p->sendTiming(pkt);
+                bool success M5_VAR_USED = p->sendTiming(pkt);
                 assert(success);
-#else
-                // avoid unused variable warning
-                p->sendTiming(pkt);
-#endif
             }
         }
     } else {
-        assert(dest >= 0 && dest < maxId);
+        assert(dest < maxId);
         assert(dest != src); // catch infinite loops
         dest_port_id = dest;
+        if (dest_port_id == defaultId)
+            dest_port = defaultPort;
+        else {
+            dest_port = checkBusCache(dest);
+            if (dest_port == NULL) {
+                dest_port = interfaces[dest_port_id];
+            // updateBusCache(dest_port_id, dest_port);
+            }
+        }
         dest_port = (dest_port_id == defaultId) ?
             defaultPort : interfaces[dest_port_id];
     }
@@ -233,13 +266,23 @@ Bus::recvTiming(PacketPtr pkt)
             // Packet not successfully sent. Leave or put it on the retry list.
             // illegal to block responses... can lead to deadlock
             assert(!pkt->isResponse());
-            DPRINTF(Bus, "Adding2 a retry to RETRY list %d\n", src);
+            // It's also illegal to force a transaction to retry after
+            // 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());
             addToRetryList(src_port);
+            occupyBus(headerFinishTime);
             return false;
         }
-        // send OK, fall through
+        // send OK, fall through... pkt may have been deleted by
+        // target at this point, so it should *not* be referenced
+        // again.  We'll set it to NULL here just to be safe.
+        pkt = NULL;
     }
 
+    occupyBus(packetFinishTime);
+
     // Packet was successfully sent.
     // Also take care of retries
     if (inRetry) {
@@ -255,7 +298,7 @@ void
 Bus::recvRetry(int id)
 {
     // If there's anything waiting, and the bus isn't busy...
-    if (retryList.size() && curTick >= tickNextIdle) {
+    if (retryList.size() && curTick() >= tickNextIdle) {
         //retryingPort = retryList.front();
         inRetry = true;
         DPRINTF(Bus, "Sending a retry to %s\n", retryList.front()->getPeer()->name());
@@ -268,17 +311,17 @@ Bus::recvRetry(int id)
             inRetry = false;
 
             //Bring tickNextIdle up to the present
-            while (tickNextIdle < curTick)
+            while (tickNextIdle < curTick())
                 tickNextIdle += clock;
 
             //Burn a cycle for the missed grant.
             tickNextIdle += clock;
 
-            busIdle.reschedule(tickNextIdle, true);
+            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.size() == 0 && curTick() >= tickNextIdle) {
         drainEvent->process();
         // Clear the drain event once we're done with it.
         drainEvent = NULL;
@@ -289,34 +332,36 @@ int
 Bus::findPort(Addr addr)
 {
     /* An interval tree would be a better way to do this. --ali. */
-    int dest_id = -1;
+    int dest_id;
+
+    dest_id = checkPortCache(addr);
+    if (dest_id != -1)
+        return dest_id;
 
+    // Check normal port ranges
     PortIter i = portMap.find(RangeSize(addr,1));
-    if (i != portMap.end())
+    if (i != portMap.end()) {
         dest_id = i->second;
+        updatePortCache(dest_id, i->first.start, i->first.end);
+        return dest_id;
+    }
 
     // Check if this matches the default range
-    if (dest_id == -1) {
-        for (AddrRangeIter iter = defaultRange.begin();
-             iter != defaultRange.end(); iter++) {
-            if (*iter == addr) {
+    if (useDefaultRange) {
+        AddrRangeIter a_end = defaultRange.end();
+        for (AddrRangeIter i = defaultRange.begin(); i != a_end; i++) {
+            if (*i == addr) {
                 DPRINTF(Bus, "  found addr %#llx on default\n", addr);
                 return defaultId;
             }
         }
 
-        if (responderSet) {
-            panic("Unable to find destination for addr (user set default "
-                  "responder): %#llx", addr);
-        } else {
-            DPRINTF(Bus, "Unable to find destination for addr: %#llx, will use "
-                    "default port", addr);
-
-            return defaultId;
-        }
+        panic("Unable to find destination for addr %#llx\n", addr);
     }
 
-    return dest_id;
+    DPRINTF(Bus, "Unable to find destination for addr %#llx, "
+            "will use default port\n", addr);
+    return defaultId;
 }
 
 
@@ -340,8 +385,16 @@ Bus::recvAtomic(PacketPtr pkt)
     int orig_src = pkt->getSrc();
 
     int target_port_id = findPort(pkt->getAddr());
-    Port *target_port = (target_port_id == defaultId) ?
-        defaultPort : interfaces[target_port_id];
+    BusPort *target_port;
+    if (target_port_id == defaultId)
+        target_port = defaultPort;
+    else {
+      target_port = checkBusCache(target_port_id);
+      if (target_port == NULL) {
+          target_port = interfaces[target_port_id];
+          updateBusCache(target_port_id, target_port);
+      }
+    }
 
     SnoopIter s_end = snoopPorts.end();
     for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
@@ -385,7 +438,7 @@ Bus::recvAtomic(PacketPtr pkt)
     }
 
     // why do we have this packet field and the return value both???
-    pkt->finishTime = curTick + response_latency;
+    pkt->finishTime = curTick() + response_latency;
     return response_latency;
 }
 
@@ -394,8 +447,6 @@ Bus::recvAtomic(PacketPtr pkt)
 void
 Bus::recvFunctional(PacketPtr pkt)
 {
-    DPRINTF(Bus, "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n",
-            pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
     assert(pkt->getDest() == Packet::Broadcast);
 
     int port_id = findPort(pkt->getAddr());
@@ -404,11 +455,18 @@ Bus::recvFunctional(PacketPtr pkt)
     // id after each
     int src_id = pkt->getSrc();
 
+    if (!pkt->isPrint()) {
+        // don't do DPRINTFs on PrintReq as it clutters up the output
+        DPRINTF(Bus,
+                "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n",
+                src_id, port_id, pkt->getAddr(),
+                pkt->cmdString());
+    }
+
     assert(pkt->isRequest()); // hasn't already been satisfied
 
-    for (SnoopIter s_iter = snoopPorts.begin();
-         s_iter != snoopPorts.end();
-         s_iter++) {
+    SnoopIter s_end = snoopPorts.end();
+    for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
         BusPort *p = *s_iter;
         if (p != port && p->getId() != src_id) {
             p->sendFunctional(pkt);
@@ -433,15 +491,20 @@ Bus::recvStatusChange(Port::Status status, int id)
     bool snoops;
     AddrRangeIter iter;
 
+    if (inRecvStatusChange.count(id))
+        return;
+    inRecvStatusChange.insert(id);
+
     assert(status == Port::RangeChange &&
            "The other statuses need to be implemented.");
 
     DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id);
 
+    clearPortCache();
     if (id == defaultId) {
         defaultRange.clear();
         // Only try to update these ranges if the user set a default responder.
-        if (responderSet) {
+        if (useDefaultRange) {
             defaultPort->getPeerAddressRanges(ranges, snoops);
             assert(snoops == false);
             for(iter = ranges.begin(); iter != ranges.end(); iter++) {
@@ -482,9 +545,12 @@ Bus::recvStatusChange(Port::Status status, int id)
         for (iter = ranges.begin(); iter != ranges.end(); iter++) {
             DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n",
                     iter->start, iter->end, id);
-            if (portMap.insert(*iter, id) == portMap.end())
-                panic("Two devices with same range\n");
-
+            if (portMap.insert(*iter, id) == portMap.end()) {
+                int conflict_id = portMap.find(*iter)->second;
+                fatal("%s has two ports with same range:\n\t%s\n\t%s\n",
+                      name(), interfaces[id]->getPeer()->name(),
+                      interfaces[conflict_id]->getPeer()->name());
+            }
         }
     }
     DPRINTF(MMU, "port list has %d entries\n", portMap.size());
@@ -499,6 +565,7 @@ Bus::recvStatusChange(Port::Status status, int id)
 
     if (id != defaultId && defaultPort)
         defaultPort->sendStatusChange(Port::RangeChange);
+    inRecvStatusChange.erase(id);
 }
 
 void
@@ -549,27 +616,27 @@ Bus::addressRanges(AddrRangeList &resp, bool &snoop, int id)
     }
 }
 
-int
+unsigned
 Bus::findBlockSize(int id)
 {
     if (cachedBlockSizeValid)
         return cachedBlockSize;
 
-    int max_bs = -1;
+    unsigned max_bs = 0;
 
-    for (PortIter portIter = portMap.begin();
-         portIter != portMap.end(); portIter++) {
-        int tmp_bs = interfaces[portIter->second]->peerBlockSize();
+    PortIter p_end = portMap.end();
+    for (PortIter p_iter = portMap.begin(); p_iter != p_end; p_iter++) {
+        unsigned tmp_bs = interfaces[p_iter->second]->peerBlockSize();
         if (tmp_bs > max_bs)
             max_bs = tmp_bs;
     }
-    for (SnoopIter s_iter = snoopPorts.begin();
-         s_iter != snoopPorts.end(); s_iter++) {
-        int tmp_bs = (*s_iter)->peerBlockSize();
+    SnoopIter s_end = snoopPorts.end();
+    for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
+        unsigned tmp_bs = (*s_iter)->peerBlockSize();
         if (tmp_bs > max_bs)
             max_bs = tmp_bs;
     }
-    if (max_bs <= 0)
+    if (max_bs == 0)
         max_bs = defaultBlockSize;
 
     if (max_bs != 64)
@@ -586,23 +653,22 @@ Bus::drain(Event * de)
     //We should check that we're not "doing" anything, and that noone is
     //waiting. We might be idle but have someone waiting if the device we
     //contacted for a retry didn't actually retry.
-    if (curTick >= tickNextIdle && retryList.size() == 0) {
-        return 0;
-    } else {
+    if (retryList.size() || (curTick() < tickNextIdle && busIdle.scheduled())) {
         drainEvent = de;
         return 1;
     }
+    return 0;
 }
 
 void
 Bus::startup()
 {
-    if (tickNextIdle < curTick)
-        tickNextIdle = (curTick / clock) * clock + clock;
+    if (tickNextIdle < curTick())
+        tickNextIdle = (curTick() / clock) * clock + clock;
 }
 
 Bus *
 BusParams::create()
 {
-    return new Bus(name, bus_id, clock, width, responder_set, block_size);
+    return new Bus(this);
 }