summaryrefslogtreecommitdiff
path: root/src/mem
diff options
context:
space:
mode:
Diffstat (limited to 'src/mem')
-rw-r--r--src/mem/bus.cc120
-rw-r--r--src/mem/bus.hh26
-rw-r--r--src/mem/cache/cache_impl.hh14
-rw-r--r--src/mem/packet.hh1
-rw-r--r--src/mem/physical.cc21
-rw-r--r--src/mem/physical.hh2
-rw-r--r--src/mem/port.hh5
-rw-r--r--src/mem/tport.cc61
-rw-r--r--src/mem/tport.hh107
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__