diff options
-rw-r--r-- | src/mem/SimpleMemory.py | 4 | ||||
-rw-r--r-- | src/mem/simple_mem.cc | 89 | ||||
-rw-r--r-- | src/mem/simple_mem.hh | 56 |
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); }; |