diff options
Diffstat (limited to 'src/mem')
-rw-r--r-- | src/mem/bus.cc | 120 | ||||
-rw-r--r-- | src/mem/bus.hh | 26 | ||||
-rw-r--r-- | src/mem/cache/cache_impl.hh | 14 | ||||
-rw-r--r-- | src/mem/packet.hh | 1 | ||||
-rw-r--r-- | src/mem/physical.cc | 21 | ||||
-rw-r--r-- | src/mem/physical.hh | 2 | ||||
-rw-r--r-- | src/mem/port.hh | 5 | ||||
-rw-r--r-- | src/mem/tport.cc | 61 | ||||
-rw-r--r-- | src/mem/tport.hh | 107 |
9 files changed, 255 insertions, 102 deletions
diff --git a/src/mem/bus.cc b/src/mem/bus.cc index b945f93b3..cf9e54e62 100644 --- a/src/mem/bus.cc +++ b/src/mem/bus.cc @@ -79,7 +79,17 @@ Bus::recvTiming(Packet *pkt) short dest = pkt->getDest(); if (dest == Packet::Broadcast) { - port = findPort(pkt->getAddr(), pkt->getSrc()); + 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 @@ -128,7 +138,7 @@ Bus::findPort(Addr addr, int id) if (portList[i].range == addr) { dest_id = portList[i].portId; found = true; - DPRINTF(Bus, " found addr 0x%llx on device %d\n", addr, dest_id); + DPRINTF(Bus, " found addr %#llx on device %d\n", addr, dest_id); } i++; } @@ -137,11 +147,11 @@ Bus::findPort(Addr addr, int id) if (dest_id == -1) { for (iter = defaultRange.begin(); iter != defaultRange.end(); iter++) { if (*iter == addr) { - DPRINTF(Bus, " found addr 0x%llx on default\n", addr); + DPRINTF(Bus, " found addr %#llx on default\n", addr); return defaultPort; } } - panic("Unable to find destination for addr: %llx", addr); + panic("Unable to find destination for addr: %#llx", addr); } @@ -151,6 +161,77 @@ Bus::findPort(Addr addr, int id) return interfaces[dest_id]; } +std::vector<int> +Bus::findSnoopPorts(Addr addr, int id) +{ + int i = 0; + AddrRangeIter iter; + std::vector<int> ports; + + while (i < portSnoopList.size()) + { + if (portSnoopList[i].range == addr && portSnoopList[i].portId != id) { + //Careful to not overlap ranges + //or snoop will be called more than once on the port + ports.push_back(portSnoopList[i].portId); + DPRINTF(Bus, " found snoop addr %#llx on device%d\n", addr, + portSnoopList[i].portId); + } + i++; + } + return ports; +} + +void +Bus::atomicSnoop(Packet *pkt) +{ + std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc()); + + while (!ports.empty()) + { + interfaces[ports.back()]->sendAtomic(pkt); + ports.pop_back(); + } +} + +bool +Bus::timingSnoopPhase1(Packet *pkt) +{ + std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc()); + bool success = true; + + while (!ports.empty() && success) + { + snoopCallbacks.push_back(ports.back()); + success = interfaces[ports.back()]->sendTiming(pkt); + ports.pop_back(); + } + if (!success) + { + while (!snoopCallbacks.empty()) + { + interfaces[snoopCallbacks.back()]->sendStatusChange(Port::SnoopSquash); + snoopCallbacks.pop_back(); + } + return false; + } + return true; +} + +void +Bus::timingSnoopPhase2(Packet *pkt) +{ + bool success; + pkt->flags |= SNOOP_COMMIT; + while (!snoopCallbacks.empty()) + { + success = interfaces[snoopCallbacks.back()]->sendTiming(pkt); + //We should not fail on snoop callbacks + assert(success); + snoopCallbacks.pop_back(); + } +} + /** Function called by the port when the bus is receiving a Atomic * transaction.*/ Tick @@ -159,6 +240,7 @@ Bus::recvAtomic(Packet *pkt) DPRINTF(Bus, "recvAtomic: packet src %d dest %d addr 0x%x cmd %s\n", pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); assert(pkt->getDest() == Packet::Broadcast); + atomicSnoop(pkt); return findPort(pkt->getAddr(), pkt->getSrc())->sendAtomic(pkt); } @@ -193,7 +275,7 @@ Bus::recvStatusChange(Port::Status status, int id) assert(snoops.size() == 0); for(iter = ranges.begin(); iter != ranges.end(); iter++) { defaultRange.push_back(*iter); - DPRINTF(BusAddrRanges, "Adding range %llx - %llx for default\n", + DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n", iter->start, iter->end); } } else { @@ -201,6 +283,7 @@ Bus::recvStatusChange(Port::Status status, int id) assert((id < interfaces.size() && id >= 0) || id == -1); Port *port = interfaces[id]; std::vector<DevMap>::iterator portIter; + std::vector<DevMap>::iterator snoopIter; // Clean out any previously existent ids for (portIter = portList.begin(); portIter != portList.end(); ) { @@ -210,16 +293,31 @@ Bus::recvStatusChange(Port::Status status, int id) portIter++; } + for (snoopIter = portSnoopList.begin(); snoopIter != portSnoopList.end(); ) { + if (snoopIter->portId == id) + snoopIter = portSnoopList.erase(snoopIter); + else + snoopIter++; + } + port->getPeerAddressRanges(ranges, snoops); - // not dealing with snooping yet either - assert(snoops.size() == 0); + for(iter = snoops.begin(); iter != snoops.end(); iter++) { + DevMap dm; + dm.portId = id; + dm.range = *iter; + + DPRINTF(BusAddrRanges, "Adding snoop range %#llx - %#llx for id %d\n", + dm.range.start, dm.range.end, id); + portSnoopList.push_back(dm); + } + for(iter = ranges.begin(); iter != ranges.end(); iter++) { DevMap dm; dm.portId = id; dm.range = *iter; - DPRINTF(BusAddrRanges, "Adding range %llx - %llx for id %d\n", + DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n", dm.range.start, dm.range.end, id); portList.push_back(dm); } @@ -251,7 +349,7 @@ Bus::addressRanges(AddrRangeList &resp, AddrRangeList &snoop, int id) for (dflt_iter = defaultRange.begin(); dflt_iter != defaultRange.end(); dflt_iter++) { resp.push_back(*dflt_iter); - DPRINTF(BusAddrRanges, " -- %#llX : %#llX\n",dflt_iter->start, + DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n",dflt_iter->start, dflt_iter->end); } for (portIter = portList.begin(); portIter != portList.end(); portIter++) { @@ -267,13 +365,13 @@ Bus::addressRanges(AddrRangeList &resp, AddrRangeList &snoop, int id) if (portIter->range.start >= dflt_iter->start && portIter->range.end <= dflt_iter->end) { subset = true; - DPRINTF(BusAddrRanges, " -- %#llX : %#llX is a SUBSET\n", + DPRINTF(BusAddrRanges, " -- %#llx : %#llx is a SUBSET\n", portIter->range.start, portIter->range.end); } } if (portIter->portId != id && !subset) { resp.push_back(portIter->range); - DPRINTF(BusAddrRanges, " -- %#llX : %#llX\n", + DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n", portIter->range.start, portIter->range.end); } } diff --git a/src/mem/bus.hh b/src/mem/bus.hh index cd25fab2c..941389296 100644 --- a/src/mem/bus.hh +++ b/src/mem/bus.hh @@ -60,6 +60,9 @@ class Bus : public MemObject }; std::vector<DevMap> portList; AddrRangeList defaultRange; + std::vector<DevMap> portSnoopList; + + std::vector<int> snoopCallbacks; /** Function called by the port when the bus is recieving a Timing @@ -90,6 +93,29 @@ class Bus : public MemObject */ Port *findPort(Addr addr, 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. + * @param addr Address to find snoop prts for. + * @param id Id of the src port of the request to avoid calling snoop on src + * @return vector of IDs to snoop on + */ + std::vector<int> findSnoopPorts(Addr addr, int id); + + /** Snoop all relevant ports atomicly. */ + void atomicSnoop(Packet *pkt); + + /** Snoop for NACK and Blocked in phase 1 + * @return True if succeds. + */ + bool timingSnoopPhase1(Packet *pkt); + + /** @todo Don't need to commit all snoops just those that need it + *(register somehow). */ + /** Commit all snoops now that we know if any of them would have blocked. + */ + void timingSnoopPhase2(Packet *pkt); + /** Process address range request. * @param resp addresses that we can respond to * @param snoop addresses that we would like to snoop diff --git a/src/mem/cache/cache_impl.hh b/src/mem/cache/cache_impl.hh index 2f9415852..11cd84e88 100644 --- a/src/mem/cache/cache_impl.hh +++ b/src/mem/cache/cache_impl.hh @@ -60,6 +60,9 @@ doTimingAccess(Packet *pkt, CachePort *cachePort, bool isCpuSide) { if (isCpuSide) { + if (pkt->isWrite() && (pkt->req->getFlags() & LOCKED)) { + pkt->req->setScResult(1); + } access(pkt); } else @@ -79,6 +82,11 @@ doAtomicAccess(Packet *pkt, bool isCpuSide) { if (isCpuSide) { + //Temporary solution to LL/SC + if (pkt->isWrite() && (pkt->req->getFlags() & LOCKED)) { + pkt->req->setScResult(1); + } + probe(pkt, true); //TEMP ALWAYS SUCCES FOR NOW pkt->result = Packet::Success; @@ -103,6 +111,12 @@ doFunctionalAccess(Packet *pkt, bool isCpuSide) { //TEMP USE CPU?THREAD 0 0 pkt->req->setThreadContext(0,0); + + //Temporary solution to LL/SC + if (pkt->isWrite() && (pkt->req->getFlags() & LOCKED)) { + assert("Can't handle LL/SC on functional path\n"); + } + probe(pkt, true); //TEMP ALWAYS SUCCESFUL FOR NOW pkt->result = Packet::Success; diff --git a/src/mem/packet.hh b/src/mem/packet.hh index 5d8308df7..c7d28010c 100644 --- a/src/mem/packet.hh +++ b/src/mem/packet.hh @@ -56,6 +56,7 @@ typedef std::list<PacketPtr> PacketList; #define CACHE_LINE_FILL 1 << 3 #define COMPRESSED 1 << 4 #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 diff --git a/src/mem/physical.cc b/src/mem/physical.cc index f4fbd2fb1..8fea733ec 100644 --- a/src/mem/physical.cc +++ b/src/mem/physical.cc @@ -182,7 +182,8 @@ PhysicalMemory::getAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) { snoop.clear(); resp.clear(); - resp.push_back(RangeSize(params()->addrRange.start, params()->addrRange.size())); + resp.push_back(RangeSize(params()->addrRange.start, + params()->addrRange.size())); } int @@ -191,21 +192,6 @@ PhysicalMemory::MemoryPort::deviceBlockSize() return memory->deviceBlockSize(); } -bool -PhysicalMemory::MemoryPort::recvTiming(Packet *pkt) -{ - assert(pkt->result != Packet::Nacked); - - Tick latency = memory->calculateLatency(pkt); - - memory->doFunctionalAccess(pkt); - - pkt->makeTimingResponse(); - sendTiming(pkt, latency); - - return true; -} - Tick PhysicalMemory::MemoryPort::recvAtomic(Packet *pkt) { @@ -216,6 +202,9 @@ PhysicalMemory::MemoryPort::recvAtomic(Packet *pkt) void PhysicalMemory::MemoryPort::recvFunctional(Packet *pkt) { + // Default implementation of SimpleTimingPort::recvFunctional() + // calls recvAtomic() and throws away the latency; we can save a + // little here by just not calculating the latency. memory->doFunctionalAccess(pkt); } diff --git a/src/mem/physical.hh b/src/mem/physical.hh index 1489e6700..02308b2ef 100644 --- a/src/mem/physical.hh +++ b/src/mem/physical.hh @@ -57,8 +57,6 @@ class PhysicalMemory : public MemObject protected: - virtual bool recvTiming(Packet *pkt); - virtual Tick recvAtomic(Packet *pkt); virtual void recvFunctional(Packet *pkt); diff --git a/src/mem/port.hh b/src/mem/port.hh index 42e369205..6b4184043 100644 --- a/src/mem/port.hh +++ b/src/mem/port.hh @@ -106,7 +106,8 @@ class Port /** Holds the ports status. Currently just that a range recomputation needs * to be done. */ enum Status { - RangeChange + RangeChange, + SnoopSquash }; void setName(const std::string &name) @@ -251,11 +252,13 @@ class FunctionalPort : public Port : Port(_name) {} + protected: virtual bool recvTiming(Packet *pkt) { panic("FuncPort is UniDir"); } virtual Tick recvAtomic(Packet *pkt) { panic("FuncPort is UniDir"); } virtual void recvFunctional(Packet *pkt) { panic("FuncPort is UniDir"); } virtual void recvStatusChange(Status status) {} + public: /** a write function that also does an endian conversion. */ template <typename T> inline void writeHtoG(Addr addr, T d); diff --git a/src/mem/tport.cc b/src/mem/tport.cc index 90cf68f02..55c301c87 100644 --- a/src/mem/tport.cc +++ b/src/mem/tport.cc @@ -31,18 +31,41 @@ #include "mem/tport.hh" void +SimpleTimingPort::recvFunctional(Packet *pkt) +{ + // just do an atomic access and throw away the returned latency + recvAtomic(pkt); +} + +bool +SimpleTimingPort::recvTiming(Packet *pkt) +{ + // If the device is only a slave, it should only be sending + // responses, which should never get nacked. There used to be + // code to hanldle nacks here, but I'm pretty sure it didn't work + // correctly with the drain code, so that would need to be fixed + // if we ever added it back. + assert(pkt->result != Packet::Nacked); + Tick latency = recvAtomic(pkt); + // turn packet around to go back to requester + pkt->makeTimingResponse(); + sendTimingLater(pkt, latency); + return true; +} + +void SimpleTimingPort::recvRetry() { bool result = true; while (result && transmitList.size()) { - result = Port::sendTiming(transmitList.front()); + result = sendTiming(transmitList.front()); if (result) transmitList.pop_front(); } - if (transmitList.size() == 0 && drainEvent) { - drainEvent->process(); - drainEvent = NULL; - } + if (transmitList.size() == 0 && drainEvent) { + drainEvent->process(); + drainEvent = NULL; + } } void @@ -50,26 +73,18 @@ SimpleTimingPort::SendEvent::process() { port->outTiming--; assert(port->outTiming >= 0); - if (port->Port::sendTiming(packet)) - if (port->transmitList.size() == 0 && port->drainEvent) { - port->drainEvent->process(); - port->drainEvent = NULL; - } - return; - - port->transmitList.push_back(packet); -} - -void -SimpleTimingPort::resendNacked(Packet *pkt) { - pkt->reinitNacked(); - if (transmitList.size()) { - transmitList.push_front(pkt); + if (port->sendTiming(packet)) { + // send successfule + if (port->transmitList.size() == 0 && port->drainEvent) { + port->drainEvent->process(); + port->drainEvent = NULL; + } } else { - if (!Port::sendTiming(pkt)) - transmitList.push_front(pkt); + // send unsuccessful (due to flow control). Will get retry + // callback later; save for then. + port->transmitList.push_back(packet); } -}; +} unsigned int diff --git a/src/mem/tport.hh b/src/mem/tport.hh index 5473e945e..df6d48196 100644 --- a/src/mem/tport.hh +++ b/src/mem/tport.hh @@ -28,67 +28,52 @@ * Authors: Ali Saidi */ +#ifndef __MEM_TPORT_HH__ +#define __MEM_TPORT_HH__ + /** * @file - * Implement a port which adds simple support of a sendTiming() function that - * takes a delay. In this way the * device can immediatly call - * sendTiming(pkt, time) after processing a request and the request will be - * handled by the port even if the port bus the device connects to is blocked. + * + * Declaration of SimpleTimingPort. */ -/** recvTiming and drain should be implemented something like this when this - * class is used. - -bool -PioPort::recvTiming(Packet *pkt) -{ - if (pkt->result == Packet::Nacked) { - resendNacked(pkt); - } else { - Tick latency = device->recvAtomic(pkt); - // turn packet around to go back to requester - pkt->makeTimingResponse(); - sendTiming(pkt, latency); - } - return true; -} - -PioDevice::drain(Event *de) -{ - unsigned int count; - count = SimpleTimingPort->drain(de); - if (count) - changeState(Draining); - else - changeState(Drained); - return count; -} -*/ - -#ifndef __MEM_TPORT_HH__ -#define __MEM_TPORT_HH__ - #include "mem/port.hh" #include "sim/eventq.hh" #include <list> #include <string> +/** + * A simple port for interfacing objects that basically have only + * functional memory behavior (e.g. I/O devices) to the memory system. + * Both timing and functional accesses are implemented in terms of + * atomic accesses. A derived port class thus only needs to provide + * recvAtomic() to support all memory access modes. + * + * The tricky part is handling recvTiming(), where the response must + * be scheduled separately via a later call to sendTiming(). This + * feature is handled by scheduling an internal event that calls + * sendTiming() after a delay, and optionally rescheduling the + * response if it is nacked. + */ class SimpleTimingPort : public Port { protected: - /** A list of outgoing timing response packets that haven't been serviced - * yet. */ + /** A list of outgoing timing response packets that haven't been + * serviced yet. */ std::list<Packet*> transmitList; + /** - * This class is used to implemented sendTiming() with a delay. When a delay - * is requested a new event is created. When the event time expires it - * attempts to send the packet. If it cannot, the packet is pushed onto the - * transmit list to be sent when recvRetry() is called. */ + * This class is used to implemented sendTiming() with a delay. When + * a delay is requested a new event is created. When the event time + * expires it attempts to send the packet. If it cannot, the packet + * is pushed onto the transmit list to be sent when recvRetry() is + * called. */ class SendEvent : public Event { SimpleTimingPort *port; Packet *packet; + public: SendEvent(SimpleTimingPort *p, Packet *pkt, Tick t) : Event(&mainEventQueue), port(p), packet(pkt) { setFlags(AutoDelete); schedule(curTick + t); } @@ -97,8 +82,6 @@ class SimpleTimingPort : public Port virtual const char *description() { return "Future scheduled sendTiming event"; } - - friend class SimpleTimingPort; }; @@ -112,23 +95,49 @@ class SimpleTimingPort : public Port Event *drainEvent; /** Schedule a sendTiming() event to be called in the future. */ - void sendTiming(Packet *pkt, Tick time) - { outTiming++; new SimpleTimingPort::SendEvent(this, pkt, time); } + void sendTimingLater(Packet *pkt, Tick time) + { outTiming++; new SendEvent(this, pkt, time); } /** This function is notification that the device should attempt to send a * packet again. */ virtual void recvRetry(); - void resendNacked(Packet *pkt); + /** Implemented using recvAtomic(). */ + void recvFunctional(Packet *pkt); + + /** Implemented using recvAtomic(). */ + bool recvTiming(Packet *pkt); + + /** + * Simple ports generally don't care about any status + * changes... can always override this in cases where that's not + * true. */ + virtual void recvStatusChange(Status status) { } + + public: SimpleTimingPort(std::string pname) : Port(pname), outTiming(0), drainEvent(NULL) {} + /** Hook for draining timing accesses from the system. The + * associated SimObject's drain() functions should be implemented + * something like this when this class is used: + \code + PioDevice::drain(Event *de) + { + unsigned int count; + count = SimpleTimingPort->drain(de); + if (count) + changeState(Draining); + else + changeState(Drained); + return count; + } + \endcode + */ unsigned int drain(Event *de); - - friend class SimpleTimingPort::SendEvent; }; #endif // __MEM_TPORT_HH__ |