summaryrefslogtreecommitdiff
path: root/src/mem/dramsim2.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/mem/dramsim2.cc')
-rw-r--r--src/mem/dramsim2.cc410
1 files changed, 410 insertions, 0 deletions
diff --git a/src/mem/dramsim2.cc b/src/mem/dramsim2.cc
new file mode 100644
index 000000000..cbba2767e
--- /dev/null
+++ b/src/mem/dramsim2.cc
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2013 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Andreas Hansson
+ */
+
+#include "DRAMSim2/Callback.h"
+#include "base/callback.hh"
+#include "base/trace.hh"
+#include "debug/DRAMSim2.hh"
+#include "debug/Drain.hh"
+#include "mem/dramsim2.hh"
+#include "sim/system.hh"
+
+DRAMSim2::DRAMSim2(const Params* p) :
+ AbstractMemory(p),
+ port(name() + ".port", *this),
+ wrapper(p->deviceConfigFile, p->systemConfigFile, p->filePath,
+ p->traceFile, p->range.size() / 1024 / 1024, p->enableDebug),
+ retryReq(false), retryResp(false),
+ nbrOutstandingReads(0), nbrOutstandingWrites(0),
+ drainManager(NULL),
+ sendResponseEvent(this), tickEvent(this)
+{
+ DPRINTF(DRAMSim2,
+ "Instantiated DRAMSim2 with clock %d ns and queue size %d\n",
+ wrapper.clockPeriod(), wrapper.queueSize());
+
+ DRAMSim::TransactionCompleteCB* read_cb =
+ new DRAMSim::Callback<DRAMSim2, void, unsigned, uint64_t, uint64_t>(
+ this, &DRAMSim2::readComplete);
+ DRAMSim::TransactionCompleteCB* write_cb =
+ new DRAMSim::Callback<DRAMSim2, void, unsigned, uint64_t, uint64_t>(
+ this, &DRAMSim2::writeComplete);
+ wrapper.setCallbacks(read_cb, write_cb);
+
+ // Register a callback to compensate for the destructor not
+ // being called. The callback prints the DRAMSim2 stats.
+ Callback* cb = new MakeCallback<DRAMSim2Wrapper,
+ &DRAMSim2Wrapper::printStats>(wrapper);
+ registerExitCallback(cb);
+}
+
+void
+DRAMSim2::init()
+{
+ if (!port.isConnected()) {
+ fatal("DRAMSim2 %s is unconnected!\n", name());
+ } else {
+ port.sendRangeChange();
+ }
+
+ if (system()->cacheLineSize() != wrapper.burstSize())
+ fatal("DRAMSim2 burst size %d does not match cache line size %d\n",
+ wrapper.burstSize(), system()->cacheLineSize());
+}
+
+void
+DRAMSim2::startup()
+{
+ // kick off the clock ticks
+ schedule(tickEvent, clockEdge());
+}
+
+void
+DRAMSim2::sendResponse()
+{
+ assert(!retryResp);
+ assert(!responseQueue.empty());
+
+ DPRINTF(DRAMSim2, "Attempting to send response\n");
+
+ bool success = port.sendTimingResp(responseQueue.front());
+ if (success) {
+ responseQueue.pop_front();
+
+ DPRINTF(DRAMSim2, "Have %d read, %d write, %d responses outstanding\n",
+ nbrOutstandingReads, nbrOutstandingWrites,
+ responseQueue.size());
+
+ if (!responseQueue.empty() && !sendResponseEvent.scheduled())
+ schedule(sendResponseEvent, curTick());
+
+ // check if we were asked to drain and if we are now done
+ if (drainManager && nbrOutstanding() == 0) {
+ drainManager->signalDrainDone();
+ drainManager = NULL;
+ }
+ } else {
+ retryResp = true;
+
+ DPRINTF(DRAMSim2, "Waiting for response retry\n");
+
+ assert(!sendResponseEvent.scheduled());
+ }
+}
+
+unsigned int
+DRAMSim2::nbrOutstanding() const
+{
+ return nbrOutstandingReads + nbrOutstandingWrites + responseQueue.size();
+}
+
+void
+DRAMSim2::tick()
+{
+ wrapper.tick();
+
+ // is the connected port waiting for a retry, if so check the
+ // state and send a retry if conditions have changed
+ if (retryReq && nbrOutstanding() < wrapper.queueSize()) {
+ retryReq = false;
+ port.sendRetry();
+ }
+
+ schedule(tickEvent, curTick() + wrapper.clockPeriod() * SimClock::Int::ns);
+}
+
+Tick
+DRAMSim2::recvAtomic(PacketPtr pkt)
+{
+ access(pkt);
+
+ // 50 ns is just an arbitrary value at this point
+ return pkt->memInhibitAsserted() ? 0 : 50000;
+}
+
+void
+DRAMSim2::recvFunctional(PacketPtr pkt)
+{
+ pkt->pushLabel(name());
+
+ functionalAccess(pkt);
+
+ // potentially update the packets in our response queue as well
+ for (auto i = responseQueue.begin(); i != responseQueue.end(); ++i)
+ pkt->checkFunctional(*i);
+
+ pkt->popLabel();
+}
+
+bool
+DRAMSim2::recvTimingReq(PacketPtr pkt)
+{
+ // we should never see a new request while in retry
+ assert(!retryReq);
+
+ // @todo temporary hack to deal with memory corruption issues until
+ // 4-phase transactions are complete
+ for (int x = 0; x < pendingDelete.size(); x++)
+ delete pendingDelete[x];
+ pendingDelete.clear();
+
+ if (pkt->memInhibitAsserted()) {
+ // snooper will supply based on copy of packet
+ // still target's responsibility to delete packet
+ pendingDelete.push_back(pkt);
+ return true;
+ }
+
+ // if we cannot accept we need to send a retry once progress can
+ // be made
+ bool can_accept = nbrOutstanding() < wrapper.queueSize();
+
+ // keep track of the transaction
+ if (pkt->isRead()) {
+ if (can_accept) {
+ outstandingReads[pkt->getAddr()].push(pkt);
+
+ // we count a transaction as outstanding until it has left the
+ // queue in the controller, and the response has been sent
+ // back, note that this will differ for reads and writes
+ ++nbrOutstandingReads;
+ }
+ } else if (pkt->isWrite()) {
+ if (can_accept) {
+ outstandingWrites[pkt->getAddr()].push(pkt);
+
+ ++nbrOutstandingWrites;
+
+ // perform the access for writes
+ accessAndRespond(pkt);
+ }
+ } else {
+ // keep it simple and just respond if necessary
+ accessAndRespond(pkt);
+ return true;
+ }
+
+ if (can_accept) {
+ // we should never have a situation when we think there is space,
+ // and there isn't
+ assert(wrapper.canAccept());
+
+ DPRINTF(DRAMSim2, "Enqueueing address %lld\n", pkt->getAddr());
+
+ // @todo what about the granularity here, implicit assumption that
+ // a transaction matches the burst size of the memory (which we
+ // cannot determine without parsing the ini file ourselves)
+ wrapper.enqueue(pkt->isWrite(), pkt->getAddr());
+
+ return true;
+ } else {
+ retryReq = true;
+ return false;
+ }
+}
+
+void
+DRAMSim2::recvRetry()
+{
+ DPRINTF(DRAMSim2, "Retrying\n");
+
+ assert(retryResp);
+ retryResp = false;
+ sendResponse();
+}
+
+void
+DRAMSim2::accessAndRespond(PacketPtr pkt)
+{
+ DPRINTF(DRAMSim2, "Access for address %lld\n", pkt->getAddr());
+
+ bool needsResponse = pkt->needsResponse();
+
+ // do the actual memory access which also turns the packet into a
+ // response
+ access(pkt);
+
+ // turn packet around to go back to requester if response expected
+ if (needsResponse) {
+ // access already turned the packet into a response
+ assert(pkt->isResponse());
+
+ // @todo someone should pay for this
+ pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
+
+ DPRINTF(DRAMSim2, "Queuing response for address %lld\n",
+ pkt->getAddr());
+
+ // queue it to be sent back
+ responseQueue.push_back(pkt);
+
+ // if we are not already waiting for a retry, or are scheduled
+ // to send a response, schedule an event
+ if (!retryResp && !sendResponseEvent.scheduled())
+ schedule(sendResponseEvent, curTick());
+ } else {
+ // @todo the packet is going to be deleted, and the DRAMPacket
+ // is still having a pointer to it
+ pendingDelete.push_back(pkt);
+ }
+}
+
+void DRAMSim2::readComplete(unsigned id, uint64_t addr, uint64_t cycle)
+{
+ assert(cycle == divCeil(curTick(),
+ wrapper.clockPeriod() * SimClock::Int::ns));
+
+ DPRINTF(DRAMSim2, "Read to address %lld complete\n", addr);
+
+ // get the outstanding reads for the address in question
+ auto p = outstandingReads.find(addr);
+ assert(p != outstandingReads.end());
+
+ // first in first out, which is not necessarily true, but it is
+ // the best we can do at this point
+ PacketPtr pkt = p->second.front();
+ p->second.pop();
+
+ if (p->second.empty())
+ outstandingReads.erase(p);
+
+ // no need to check for drain here as the next call will add a
+ // response to the response queue straight away
+ assert(nbrOutstandingReads != 0);
+ --nbrOutstandingReads;
+
+ // perform the actual memory access
+ accessAndRespond(pkt);
+}
+
+void DRAMSim2::writeComplete(unsigned id, uint64_t addr, uint64_t cycle)
+{
+ assert(cycle == divCeil(curTick(),
+ wrapper.clockPeriod() * SimClock::Int::ns));
+
+ DPRINTF(DRAMSim2, "Write to address %lld complete\n", addr);
+
+ // get the outstanding reads for the address in question
+ auto p = outstandingWrites.find(addr);
+ assert(p != outstandingWrites.end());
+
+ // we have already responded, and this is only to keep track of
+ // what is outstanding
+ p->second.pop();
+ if (p->second.empty())
+ outstandingWrites.erase(p);
+
+ assert(nbrOutstandingWrites != 0);
+ --nbrOutstandingWrites;
+
+ // check if we were asked to drain and if we are now done
+ if (drainManager && nbrOutstanding() == 0) {
+ drainManager->signalDrainDone();
+ drainManager = NULL;
+ }
+}
+
+BaseSlavePort&
+DRAMSim2::getSlavePort(const std::string &if_name, PortID idx)
+{
+ if (if_name != "port") {
+ return MemObject::getSlavePort(if_name, idx);
+ } else {
+ return port;
+ }
+}
+
+unsigned int
+DRAMSim2::drain(DrainManager* dm)
+{
+ // check our outstanding reads and writes and if any they need to
+ // drain
+ if (nbrOutstanding() != 0) {
+ setDrainState(Drainable::Draining);
+ drainManager = dm;
+ return 1;
+ } else {
+ setDrainState(Drainable::Drained);
+ return 0;
+ }
+}
+
+DRAMSim2::MemoryPort::MemoryPort(const std::string& _name,
+ DRAMSim2& _memory)
+ : SlavePort(_name, &_memory), memory(_memory)
+{ }
+
+AddrRangeList
+DRAMSim2::MemoryPort::getAddrRanges() const
+{
+ AddrRangeList ranges;
+ ranges.push_back(memory.getAddrRange());
+ return ranges;
+}
+
+Tick
+DRAMSim2::MemoryPort::recvAtomic(PacketPtr pkt)
+{
+ return memory.recvAtomic(pkt);
+}
+
+void
+DRAMSim2::MemoryPort::recvFunctional(PacketPtr pkt)
+{
+ memory.recvFunctional(pkt);
+}
+
+bool
+DRAMSim2::MemoryPort::recvTimingReq(PacketPtr pkt)
+{
+ // pass it to the memory controller
+ return memory.recvTimingReq(pkt);
+}
+
+void
+DRAMSim2::MemoryPort::recvRetry()
+{
+ memory.recvRetry();
+}
+
+DRAMSim2*
+DRAMSim2Params::create()
+{
+ return new DRAMSim2(this);
+}