summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mem/SimpleMemory.py4
-rw-r--r--src/mem/simple_mem.cc89
-rw-r--r--src/mem/simple_mem.hh56
3 files changed, 130 insertions, 19 deletions
diff --git a/src/mem/SimpleMemory.py b/src/mem/SimpleMemory.py
index c47d04222..9361b45d8 100644
--- a/src/mem/SimpleMemory.py
+++ b/src/mem/SimpleMemory.py
@@ -47,3 +47,7 @@ class SimpleMemory(AbstractMemory):
port = SlavePort("Slave ports")
latency = Param.Latency('30ns', "Request to response latency")
latency_var = Param.Latency('0ns', "Request to response latency variance")
+ # The memory bandwidth limit default is set to 12.8GB/s which is
+ # representative of a x64 DDR3-1600 channel.
+ bandwidth = Param.MemoryBandwidth('12.8GB/s',
+ "Combined read and write bandwidth")
diff --git a/src/mem/simple_mem.cc b/src/mem/simple_mem.cc
index b9db64276..a67b4825f 100644
--- a/src/mem/simple_mem.cc
+++ b/src/mem/simple_mem.cc
@@ -47,9 +47,11 @@
using namespace std;
-SimpleMemory::SimpleMemory(const Params* p) :
+SimpleMemory::SimpleMemory(const SimpleMemoryParams* p) :
AbstractMemory(p),
- port(name() + ".port", *this), lat(p->latency), lat_var(p->latency_var)
+ port(name() + ".port", *this), lat(p->latency),
+ lat_var(p->latency_var), bandwidth(p->bandwidth),
+ isBusy(false), retryReq(false), releaseEvent(this)
{
}
@@ -89,6 +91,80 @@ SimpleMemory::doFunctionalAccess(PacketPtr pkt)
functionalAccess(pkt);
}
+bool
+SimpleMemory::recvTimingReq(PacketPtr pkt)
+{
+ if (pkt->memInhibitAsserted()) {
+ // snooper will supply based on copy of packet
+ // still target's responsibility to delete packet
+ delete pkt;
+ return true;
+ }
+
+ // we should never get a new request after committing to retry the
+ // current one, the bus violates the rule as it simply sends a
+ // retry to the next one waiting on the retry list, so simply
+ // ignore it
+ if (retryReq)
+ return false;
+
+ // if we are busy with a read or write, remember that we have to
+ // retry
+ if (isBusy) {
+ retryReq = true;
+ return false;
+ }
+
+ // update the release time according to the bandwidth limit, and
+ // do so with respect to the time it takes to finish this request
+ // rather than long term as it is the short term data rate that is
+ // limited for any real memory
+
+ // only look at reads and writes when determining if we are busy,
+ // and for how long, as it is not clear what to regulate for the
+ // other types of commands
+ if (pkt->isRead() || pkt->isWrite()) {
+ // calculate an appropriate tick to release to not exceed
+ // the bandwidth limit
+ Tick duration = pkt->getSize() * bandwidth;
+
+ // only consider ourselves busy if there is any need to wait
+ // to avoid extra events being scheduled for (infinitely) fast
+ // memories
+ if (duration != 0) {
+ schedule(releaseEvent, curTick() + duration);
+ isBusy = true;
+ }
+ }
+
+ // go ahead and deal with the packet and put the response in the
+ // queue if there is one
+ bool needsResponse = pkt->needsResponse();
+ Tick latency = doAtomicAccess(pkt);
+ // turn packet around to go back to requester if response expected
+ if (needsResponse) {
+ // doAtomicAccess() should already have turned packet into
+ // atomic response
+ assert(pkt->isResponse());
+ port.schedTimingResp(pkt, curTick() + latency);
+ } else {
+ delete pkt;
+ }
+
+ return true;
+}
+
+void
+SimpleMemory::release()
+{
+ assert(isBusy);
+ isBusy = false;
+ if (retryReq) {
+ retryReq = false;
+ port.sendRetry();
+ }
+}
+
SlavePort &
SimpleMemory::getSlavePort(const std::string &if_name, int idx)
{
@@ -113,7 +189,8 @@ SimpleMemory::drain(Event *de)
SimpleMemory::MemoryPort::MemoryPort(const std::string& _name,
SimpleMemory& _memory)
- : SimpleTimingPort(_name, &_memory), memory(_memory)
+ : QueuedSlavePort(_name, &_memory, queueImpl),
+ queueImpl(_memory, *this), memory(_memory)
{ }
AddrRangeList
@@ -145,6 +222,12 @@ SimpleMemory::MemoryPort::recvFunctional(PacketPtr pkt)
pkt->popLabel();
}
+bool
+SimpleMemory::MemoryPort::recvTimingReq(PacketPtr pkt)
+{
+ return memory.recvTimingReq(pkt);
+}
+
SimpleMemory*
SimpleMemoryParams::create()
{
diff --git a/src/mem/simple_mem.hh b/src/mem/simple_mem.hh
index 95bfa57ad..4f7af864b 100644
--- a/src/mem/simple_mem.hh
+++ b/src/mem/simple_mem.hh
@@ -55,17 +55,22 @@
/**
* The simple memory is a basic single-ported memory controller with
- * an infinite throughput and a fixed latency, potentially with a
- * variance added to it. It uses a SimpleTimingPort to implement the
- * timing accesses.
+ * an configurable throughput and latency, potentially with a variance
+ * added to the latter. It uses a QueueSlavePort to avoid dealing with
+ * the flow control of sending responses.
*/
class SimpleMemory : public AbstractMemory
{
private:
- class MemoryPort : public SimpleTimingPort
+ class MemoryPort : public QueuedSlavePort
{
+
+ private:
+
+ /// Queue holding the response packets
+ SlavePacketQueue queueImpl;
SimpleMemory& memory;
public:
@@ -74,11 +79,13 @@ class SimpleMemory : public AbstractMemory
protected:
- virtual Tick recvAtomic(PacketPtr pkt);
+ Tick recvAtomic(PacketPtr pkt);
+
+ void recvFunctional(PacketPtr pkt);
- virtual void recvFunctional(PacketPtr pkt);
+ bool recvTimingReq(PacketPtr pkt);
- virtual AddrRangeList getAddrRanges() const;
+ AddrRangeList getAddrRanges() const;
};
@@ -87,10 +94,32 @@ class SimpleMemory : public AbstractMemory
Tick lat;
Tick lat_var;
+ /// Bandwidth in ticks per byte
+ const double bandwidth;
+
+ /**
+ * Track the state of the memory as either idle or busy, no need
+ * for an enum with only two states.
+ */
+ bool isBusy;
+
+ /**
+ * Remember if we have to retry an outstanding request that
+ * arrived while we were busy.
+ */
+ bool retryReq;
+
+ /**
+ * Release the memory after being busy and send a retry if a
+ * request was rejected in the meanwhile.
+ */
+ void release();
+
+ EventWrapper<SimpleMemory, &SimpleMemory::release> releaseEvent;
+
public:
- typedef SimpleMemoryParams Params;
- SimpleMemory(const Params *p);
+ SimpleMemory(const SimpleMemoryParams *p);
virtual ~SimpleMemory() { }
unsigned int drain(Event* de);
@@ -98,17 +127,12 @@ class SimpleMemory : public AbstractMemory
virtual SlavePort& getSlavePort(const std::string& if_name, int idx = -1);
virtual void init();
- const Params *
- params() const
- {
- return dynamic_cast<const Params *>(_params);
- }
-
protected:
Tick doAtomicAccess(PacketPtr pkt);
void doFunctionalAccess(PacketPtr pkt);
- virtual Tick calculateLatency(PacketPtr pkt);
+ bool recvTimingReq(PacketPtr pkt);
+ Tick calculateLatency(PacketPtr pkt);
};