diff options
Diffstat (limited to 'src/mem/dramsim2.cc')
-rw-r--r-- | src/mem/dramsim2.cc | 410 |
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); +} |