From d9172c8f462511cde474040581063180be18540a Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Thu, 5 Oct 2006 16:26:16 -0400 Subject: [PATCH 1/3] Partial reimplementation of the bus. The "clock" and "width" parameters have been added, and the HasData flag has been partially added to packets. --HG-- extra : convert_revision : abb2a259fcf843457abbc0bd36f9504fbe6d7d39 --- src/mem/bus.cc | 41 +++++++++++++++++++++++++++++++++--- src/mem/bus.hh | 16 ++++++++++++-- src/mem/packet.hh | 23 ++++++++++++-------- src/python/m5/objects/Bus.py | 2 ++ 4 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/mem/bus.cc b/src/mem/bus.cc index cf9e54e62..e3b395afc 100644 --- a/src/mem/bus.cc +++ b/src/mem/bus.cc @@ -78,6 +78,16 @@ Bus::recvTiming(Packet *pkt) pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); short dest = pkt->getDest(); + //if (pkt->isRequest() && curTick < tickAddrLastUsed || + // (pkt->isResponse() || pkt->hasData()) && curTick < tickDataLastUsed) { + //We're going to need resources that have already been committed + //Send this guy to the back of the line + //We don't need to worry about scheduling an event to deal with when the + //bus is freed because that's handled when tick*LastUsed is incremented. + // retryList.push_back(interfaces[pkt->getSrc()]); + // return false; + //} + if (dest == Packet::Broadcast) { if ( timingSnoopPhase1(pkt) ) { @@ -95,8 +105,29 @@ Bus::recvTiming(Packet *pkt) assert(dest != pkt->getSrc()); // catch infinite loops port = interfaces[dest]; } + + if (port->sendTiming(pkt)) { - // packet was successfully sent, just return true. + // Packet was successfully sent. + // Figure out what resources were used, and then return true. + //if (pkt->isRequest()) { + // The address bus will be used for one cycle + // while (tickAddrLastUsed <= curTick) + // tickAddrLastUsed += clock; + //} + //if (pkt->isResponse() || pkt->hasData()) { + // Use up the data bus for at least one bus cycle + // while (tickDataLastUsed <= curTick) + // tickDataLastUsed += clock; + // Use up the data bus for however many cycles remain + // if (pkt->hasData()) { + // int dataSize = pkt->getSize(); + // for (int transmitted = width; transmitted < dataSize; + // transmitted += width) { + // tickDataLastUsed += clock; + // } + // } + //} return true; } @@ -380,16 +411,20 @@ Bus::addressRanges(AddrRangeList &resp, AddrRangeList &snoop, int id) BEGIN_DECLARE_SIM_OBJECT_PARAMS(Bus) Param bus_id; + Param clock; + Param 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 941389296..9dd666304 100644 --- a/src/mem/bus.hh +++ b/src/mem/bus.hh @@ -51,6 +51,14 @@ 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 bits */ + int width; + /** the last tick the address bus was used */ + Tick tickAddrLastUsed; + /** the last tick the data bus was used */ + Tick tickDataLastUsed; static const int defaultId = -1; @@ -199,8 +207,12 @@ 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), + tickAddrLastUsed(0), tickDataLastUsed(0), defaultPort(NULL) + { + assert(width); + } }; diff --git a/src/mem/packet.hh b/src/mem/packet.hh index c7d28010c..b14343b47 100644 --- a/src/mem/packet.hh +++ b/src/mem/packet.hh @@ -174,7 +174,8 @@ class Packet IsResponse = 1 << 5, NeedsResponse = 1 << 6, IsSWPrefetch = 1 << 7, - IsHWPrefetch = 1 << 8 + IsHWPrefetch = 1 << 8, + HasData = 1 << 9 }; public: @@ -183,21 +184,24 @@ class Packet { 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 +223,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..b7c55990c 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 (bits)") From 34b697cd04e7d645f8bf95d5c1a2dfeb623181f1 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Sun, 8 Oct 2006 14:04:49 -0400 Subject: [PATCH 2/3] Add in HasData, and move the define of NUM_MEM_CMDS to a more visible location. --HG-- extra : convert_revision : 4379efe892ca0a39363ee04009e1bbb8c8f77afa --- src/mem/packet.hh | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/mem/packet.hh b/src/mem/packet.hh index b14343b47..e7047b0cd 100644 --- a/src/mem/packet.hh +++ b/src/mem/packet.hh @@ -58,10 +58,6 @@ typedef std::list 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, @@ -178,30 +176,34 @@ class Packet 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,// | HasData, - WriteReqNoAck = IsWrite | IsRequest,// | HasData, - ReadResp = IsRead | IsResponse | NeedsResponse,// | HasData, + WriteReq = IsWrite | IsRequest | NeedsResponse | HasData, + WriteReqNoAck = IsWrite | IsRequest | HasData, + ReadResp = IsRead | IsResponse | NeedsResponse | HasData, WriteResp = IsWrite | IsResponse | NeedsResponse, - Writeback = IsWrite | IsRequest,// | HasData, + Writeback = IsWrite | IsRequest | HasData, SoftPFReq = IsRead | IsRequest | IsSWPrefetch | NeedsResponse, HardPFReq = IsRead | IsRequest | IsHWPrefetch | NeedsResponse, SoftPFResp = IsRead | IsResponse | IsSWPrefetch - | NeedsResponse,// | HasData, + | NeedsResponse | HasData, HardPFResp = IsRead | IsResponse | IsHWPrefetch - | NeedsResponse,// | HasData, + | NeedsResponse | HasData, InvalidateReq = IsInvalidate | IsRequest, - WriteInvalidateReq = IsWrite | IsInvalidate | IsRequest,// | HasData, + WriteInvalidateReq = IsWrite | IsInvalidate | IsRequest | HasData, UpgradeReq = IsInvalidate | IsRequest | NeedsResponse, UpgradeResp = IsInvalidate | IsResponse | NeedsResponse, ReadExReq = IsRead | IsInvalidate | IsRequest | NeedsResponse, ReadExResp = IsRead | IsInvalidate | IsResponse - | NeedsResponse,// | HasData + | NeedsResponse | HasData }; /** Return the string name of the cmd field (for debugging and From 00481d1f192c832d654379c2296d5b6020c12b1a Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Sun, 8 Oct 2006 14:08:58 -0400 Subject: [PATCH 3/3] A possible implementation of a multiplexed bus. --HG-- extra : convert_revision : 3c560eda12ffd8ca539c91024baf2770b963ede8 --- src/mem/bus.cc | 164 +++++++++++++++++++++++++++++++------------------ src/mem/bus.hh | 53 ++++++++++++++-- 2 files changed, 152 insertions(+), 65 deletions(-) diff --git a/src/mem/bus.cc b/src/mem/bus.cc index e3b395afc..fff3dfed6 100644 --- a/src/mem/bus.cc +++ b/src/mem/bus.cc @@ -67,6 +67,44 @@ Bus::init() (*intIter)->sendStatusChange(Port::RangeChange); } +Bus::BusFreeEvent::BusFreeEvent(Bus *_bus) : Event(&mainEventQueue), bus(_bus) +{} + +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); + } +} /** Function called by the port when the bus is receiving a Timing * transaction.*/ @@ -77,83 +115,89 @@ 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()); - short dest = pkt->getDest(); - //if (pkt->isRequest() && curTick < tickAddrLastUsed || - // (pkt->isResponse() || pkt->hasData()) && curTick < tickDataLastUsed) { - //We're going to need resources that have already been committed - //Send this guy to the back of the line - //We don't need to worry about scheduling an event to deal with when the - //bus is freed because that's handled when tick*LastUsed is incremented. - // retryList.push_back(interfaces[pkt->getSrc()]); - // return false; - //} + Port *pktPort = interfaces[pkt->getSrc()]; - if (dest == Packet::Broadcast) { - if ( timingSnoopPhase1(pkt) ) - { - timingSnoopPhase2(pkt); - port = findPort(pkt->getAddr(), pkt->getSrc()); - } - else - { - //Snoop didn't succeed - retryList.push_back(interfaces[pkt->getSrc()]); - return false; - } - } else { - assert(dest >= 0 && dest < interfaces.size()); - assert(dest != pkt->getSrc()); // catch infinite loops - port = interfaces[dest]; + // If the bus is busy, or other devices are in line ahead of the current one, + // put this device on the retry list. + if (tickNextIdle > curTick || (retryList.size() && pktPort != retryingPort)) { + addToRetryList(pktPort); + return false; } + // If the bus is blocked, make the device wait. + if (!(port = findDestPort(pkt, pkt->getSrc()))) { + addToRetryList(pktPort); + return false; + } + + // 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. - // Figure out what resources were used, and then return true. - //if (pkt->isRequest()) { - // The address bus will be used for one cycle - // while (tickAddrLastUsed <= curTick) - // tickAddrLastUsed += clock; - //} - //if (pkt->isResponse() || pkt->hasData()) { - // Use up the data bus for at least one bus cycle - // while (tickDataLastUsed <= curTick) - // tickDataLastUsed += clock; - // Use up the data bus for however many cycles remain - // if (pkt->hasData()) { - // int dataSize = pkt->getSize(); - // for (int transmitted = width; transmitted < dataSize; - // transmitted += width) { - // tickDataLastUsed += clock; - // } - // } - //} + // Packet was successfully sent. Return true. 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, either sendTiming wasn't + //called, or it was and the packet was successfully sent. + if (retryingPort) { + retryList.pop_front(); + retryingPort = 0; + } } } +Port * +Bus::findDestPort(PacketPtr pkt, int id) +{ + Port * port = NULL; + short dest = pkt->getDest(); + + if (dest == Packet::Broadcast) { + if (timingSnoopPhase1(pkt)) { + timingSnoopPhase2(pkt); + port = findPort(pkt->getAddr(), pkt->getSrc()); + } + //else, port stays NULL + } else { + assert(dest >= 0 && dest < interfaces.size()); + assert(dest != pkt->getSrc()); // catch infinite loops + port = interfaces[dest]; + } + return port; +} Port * Bus::findPort(Addr addr, int id) diff --git a/src/mem/bus.hh b/src/mem/bus.hh index 9dd666304..96f1152a6 100644 --- a/src/mem/bus.hh +++ b/src/mem/bus.hh @@ -46,6 +46,7 @@ #include "mem/packet.hh" #include "mem/port.hh" #include "mem/request.hh" +#include "sim/eventq.hh" class Bus : public MemObject { @@ -55,10 +56,8 @@ class Bus : public MemObject int clock; /** the width of the bus in bits */ int width; - /** the last tick the address bus was used */ - Tick tickAddrLastUsed; - /** the last tick the data bus was used */ - Tick tickDataLastUsed; + /** the next tick at which the bus will be idle */ + Tick tickNextIdle; static const int defaultId = -1; @@ -101,6 +100,15 @@ class Bus : public MemObject */ Port *findPort(Addr addr, int id); + /** Finds the port a packet should be sent to. If the bus is blocked, no port + * is returned. + * @param pkt Packet to find a destination port for. + * @param id Id of the port this packet was received from + * (to prevent loops) + */ + + Port *findDestPort(PacketPtr pkt, int id); + /** Find all ports with a matching snoop range, except src port. Keep in mind * that the ranges shouldn't overlap or you will get a double snoop to the same * interface.and the cache will assert out. @@ -189,6 +197,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 interfaces; @@ -197,6 +221,23 @@ class Bus : public MemObject * original send failed for whatever reason.*/ std::list 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 = 0; + + // 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; @@ -209,9 +250,11 @@ class Bus : public MemObject Bus(const std::string &n, int bus_id, int _clock, int _width) : MemObject(n), busId(bus_id), clock(_clock), width(_width), - tickAddrLastUsed(0), tickDataLastUsed(0), defaultPort(NULL) + tickNextIdle(0), busIdle(this), retryingPort(0), defaultPort(NULL) { + //Both the width and clock period must be positive assert(width); + assert(clock); } };