diff options
-rw-r--r-- | src/mem/bus.cc | 122 | ||||
-rw-r--r-- | src/mem/bus.hh | 50 | ||||
-rw-r--r-- | src/mem/cache/base_cache.cc | 3 | ||||
-rw-r--r-- | src/mem/packet.hh | 33 | ||||
-rw-r--r-- | src/python/m5/objects/Bus.py | 2 |
5 files changed, 169 insertions, 41 deletions
diff --git a/src/mem/bus.cc b/src/mem/bus.cc index 1646cbd57..7584ffffd 100644 --- a/src/mem/bus.cc +++ b/src/mem/bus.cc @@ -67,6 +67,47 @@ Bus::init() (*intIter)->sendStatusChange(Port::RangeChange); } +Bus::BusFreeEvent::BusFreeEvent(Bus *_bus) : Event(&mainEventQueue), bus(_bus) +{ + assert(!scheduled()); +} + +void Bus::BusFreeEvent::process() +{ + bus->recvRetry(0); +} + +const char * Bus::BusFreeEvent::description() +{ + return "bus became available"; +} + +void +Bus::occupyBus(int numCycles) +{ + //Move up when the bus will next be free + //We avoid the use of divide by adding repeatedly + //This should be faster if the value is updated frequently, but should + //be may be slower otherwise. + + //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. Anthing that happens later will have to "wait" for the + //end of that cycle, and then start using the bus after that. + while (tickNextIdle < curTick) + tickNextIdle += clock; + //Advance it numCycles bus cycles. + //XXX Should this use the repeating add trick as well? + tickNextIdle += (numCycles * clock); + if (!busIdle.scheduled()) { + busIdle.schedule(tickNextIdle); + } else { + busIdle.reschedule(tickNextIdle); + } + 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 * transaction.*/ @@ -77,23 +118,26 @@ Bus::recvTiming(Packet *pkt) DPRINTF(Bus, "recvTiming: packet src %d dest %d addr 0x%x cmd %s\n", pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); + Port *pktPort = interfaces[pkt->getSrc()]; + short dest = pkt->getDest(); if (dest == Packet::Broadcast) { - if (timingSnoop(pkt)) - { + if (timingSnoop(pkt)) { pkt->flags |= SNOOP_COMMIT; bool success = timingSnoop(pkt); assert(success); if (pkt->flags & SATISFIED) { //Cache-Cache transfer occuring + if (retryingPort) { + retryList.pop_front(); + retryingPort = NULL; + } return true; } port = findPort(pkt->getAddr(), pkt->getSrc()); - } - else - { + } else { //Snoop didn't succeed - retryList.push_back(interfaces[pkt->getSrc()]); + addToRetryList(pktPort); return false; } } else { @@ -101,35 +145,61 @@ Bus::recvTiming(Packet *pkt) assert(dest != pkt->getSrc()); // catch infinite loops port = interfaces[dest]; } + + // The packet will be sent. Figure out how long it occupies the bus. + int numCycles = 0; + // Requests need one cycle to send an address + if (pkt->isRequest()) + numCycles++; + else if (pkt->isResponse() || 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(); + for (int transmitted = 0; transmitted < dataSize; + transmitted += 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. + numCycles++; + } + } + + occupyBus(numCycles); + if (port->sendTiming(pkt)) { - // packet was successfully sent, just return true. + // Packet was successfully sent. Return true. + // Also take care of retries + if (retryingPort) { + retryList.pop_front(); + retryingPort = NULL; + } return true; } - // packet not successfully sent - retryList.push_back(interfaces[pkt->getSrc()]); + // Packet not successfully sent. Leave or put it on the retry list. + addToRetryList(pktPort); return false; } void Bus::recvRetry(int id) { - // Go through all the elements on the list calling sendRetry on each - // This is not very efficient at all but it works. Ultimately we should end - // up with something that is more intelligent. - int initialSize = retryList.size(); - int i; - Port *p; - - for (i = 0; i < initialSize; i++) { - assert(retryList.size() > 0); - p = retryList.front(); - retryList.pop_front(); - p->sendRetry(); + // If there's anything waiting... + if (retryList.size()) { + retryingPort = retryList.front(); + retryingPort->sendRetry(); + // If the retryingPort pointer isn't null, sendTiming wasn't called + if (retryingPort) { + warn("sendRetry didn't call sendTiming\n"); + retryList.pop_front(); + retryingPort = NULL; + } } } - Port * Bus::findPort(Addr addr, int id) { @@ -377,16 +447,20 @@ Bus::addressRanges(AddrRangeList &resp, AddrRangeList &snoop, int id) BEGIN_DECLARE_SIM_OBJECT_PARAMS(Bus) Param<int> bus_id; + Param<int> clock; + Param<int> width; END_DECLARE_SIM_OBJECT_PARAMS(Bus) BEGIN_INIT_SIM_OBJECT_PARAMS(Bus) - INIT_PARAM(bus_id, "a globally unique bus id") + INIT_PARAM(bus_id, "a globally unique bus id"), + INIT_PARAM(clock, "bus clock speed"), + INIT_PARAM(width, "width of the bus (bits)") END_INIT_SIM_OBJECT_PARAMS(Bus) CREATE_SIM_OBJECT(Bus) { - return new Bus(getInstanceName(), bus_id); + return new Bus(getInstanceName(), bus_id, clock, width); } REGISTER_SIM_OBJECT("Bus", Bus) diff --git a/src/mem/bus.hh b/src/mem/bus.hh index ff4ec9c8c..a89738775 100644 --- a/src/mem/bus.hh +++ b/src/mem/bus.hh @@ -46,11 +46,18 @@ #include "mem/packet.hh" #include "mem/port.hh" #include "mem/request.hh" +#include "sim/eventq.hh" class Bus : public MemObject { /** a globally unique id for this bus. */ int busId; + /** the clock speed for the bus */ + int clock; + /** the width of the bus in bytes */ + int width; + /** the next tick at which the bus will be idle */ + Tick tickNextIdle; static const int defaultId = -1; @@ -176,6 +183,22 @@ class Bus : public MemObject }; + class BusFreeEvent : public Event + { + Bus * bus; + + public: + BusFreeEvent(Bus * _bus); + void process(); + const char *description(); + }; + + BusFreeEvent busIdle; + + void occupyBus(int numCycles); + + Port * retryingPort; + /** An array of pointers to the peer port interfaces connected to this bus.*/ std::vector<Port*> interfaces; @@ -184,6 +207,23 @@ class Bus : public MemObject * original send failed for whatever reason.*/ std::list<Port*> retryList; + void addToRetryList(Port * port) + { + if (!retryingPort) { + // The device wasn't retrying a packet, or wasn't at an appropriate + // time. + retryList.push_back(port); + } else { + // The device was retrying a packet. It didn't work, so we'll leave + // it at the head of the retry list. + retryingPort = NULL; + + // We shouldn't be receiving a packet from one port when a different + // one is retrying. + assert(port == retryingPort); + } + } + /** Port that handles requests that don't match any of the interfaces.*/ Port *defaultPort; @@ -194,8 +234,14 @@ class Bus : public MemObject virtual void init(); - Bus(const std::string &n, int bus_id) - : MemObject(n), busId(bus_id), defaultPort(NULL) {} + Bus(const std::string &n, int bus_id, int _clock, int _width) + : MemObject(n), busId(bus_id), clock(_clock), width(_width), + tickNextIdle(0), busIdle(this), retryingPort(NULL), defaultPort(NULL) + { + //Both the width and clock period must be positive + assert(width); + assert(clock); + } }; diff --git a/src/mem/cache/base_cache.cc b/src/mem/cache/base_cache.cc index 1a0f63d17..b2c3a0439 100644 --- a/src/mem/cache/base_cache.cc +++ b/src/mem/cache/base_cache.cc @@ -105,8 +105,7 @@ BaseCache::CachePort::recvRetry() drainList.pop_front(); } } - - if (!isCpuSide) + else if (!isCpuSide) { pkt = cache->getPacket(); MSHR* mshr = (MSHR*)pkt->senderState; diff --git a/src/mem/packet.hh b/src/mem/packet.hh index be9bf5f57..5c513ae7a 100644 --- a/src/mem/packet.hh +++ b/src/mem/packet.hh @@ -58,10 +58,6 @@ typedef std::list<PacketPtr> PacketList; #define NO_ALLOCATE 1 << 5 #define SNOOP_COMMIT 1 << 6 -//For statistics we need max number of commands, hard code it at -//20 for now. @todo fix later -#define NUM_MEM_CMDS 1 << 9 - /** * A Packet is used to encapsulate a transfer between two objects in * the memory system (e.g., the L1 and L2 cache). (In contrast, a @@ -164,6 +160,8 @@ class Packet private: /** List of command attributes. */ + // If you add a new CommandAttribute, make sure to increase NUM_MEM_CMDS + // as well. enum CommandAttribute { IsRead = 1 << 0, @@ -174,30 +172,38 @@ class Packet IsResponse = 1 << 5, NeedsResponse = 1 << 6, IsSWPrefetch = 1 << 7, - IsHWPrefetch = 1 << 8 + IsHWPrefetch = 1 << 8, + HasData = 1 << 9 }; +//For statistics we need max number of commands, hard code it at +//20 for now. @todo fix later +#define NUM_MEM_CMDS 1 << 10 + public: /** List of all commands associated with a packet. */ enum Command { InvalidCmd = 0, ReadReq = IsRead | IsRequest | NeedsResponse, - WriteReq = IsWrite | IsRequest | NeedsResponse, - WriteReqNoAck = IsWrite | IsRequest, - ReadResp = IsRead | IsResponse | NeedsResponse, + WriteReq = IsWrite | IsRequest | NeedsResponse | HasData, + WriteReqNoAck = IsWrite | IsRequest | HasData, + ReadResp = IsRead | IsResponse | NeedsResponse | HasData, WriteResp = IsWrite | IsResponse | NeedsResponse, - Writeback = IsWrite | IsRequest, + Writeback = IsWrite | IsRequest | HasData, SoftPFReq = IsRead | IsRequest | IsSWPrefetch | NeedsResponse, HardPFReq = IsRead | IsRequest | IsHWPrefetch | NeedsResponse, - SoftPFResp = IsRead | IsResponse | IsSWPrefetch | NeedsResponse, - HardPFResp = IsRead | IsResponse | IsHWPrefetch | NeedsResponse, + SoftPFResp = IsRead | IsResponse | IsSWPrefetch + | NeedsResponse | HasData, + HardPFResp = IsRead | IsResponse | IsHWPrefetch + | NeedsResponse | HasData, InvalidateReq = IsInvalidate | IsRequest, - WriteInvalidateReq = IsWrite | IsInvalidate | IsRequest, + WriteInvalidateReq = IsWrite | IsInvalidate | IsRequest | HasData, UpgradeReq = IsInvalidate | IsRequest | NeedsResponse, UpgradeResp = IsInvalidate | IsResponse | NeedsResponse, ReadExReq = IsRead | IsInvalidate | IsRequest | NeedsResponse, - ReadExResp = IsRead | IsInvalidate | IsResponse | NeedsResponse + ReadExResp = IsRead | IsInvalidate | IsResponse + | NeedsResponse | HasData }; /** Return the string name of the cmd field (for debugging and @@ -219,6 +225,7 @@ class Packet bool isResponse() { return (cmd & IsResponse) != 0; } bool needsResponse() { return (cmd & NeedsResponse) != 0; } bool isInvalidate() { return (cmd & IsInvalidate) != 0; } + bool hasData() { return (cmd & HasData) != 0; } bool isCacheFill() { return (flags & CACHE_LINE_FILL) != 0; } bool isNoAllocate() { return (flags & NO_ALLOCATE) != 0; } diff --git a/src/python/m5/objects/Bus.py b/src/python/m5/objects/Bus.py index f6828a0d5..6710111e5 100644 --- a/src/python/m5/objects/Bus.py +++ b/src/python/m5/objects/Bus.py @@ -6,3 +6,5 @@ class Bus(MemObject): port = VectorPort("vector port for connecting devices") default = Port("Default port for requests that aren't handeled by a device.") bus_id = Param.Int(0, "blah") + clock = Param.Clock("1GHz", "bus clock speed") + width = Param.Int(64, "bus width (bytes)") |