diff --git a/.hgignore b/.hgignore index 9e4aee18a..9f3ff247c 100644 --- a/.hgignore +++ b/.hgignore @@ -9,3 +9,4 @@ cscope.out .*.swp m5out src/doxygen +ext/dramsim2/DRAMSim2 diff --git a/SConstruct b/SConstruct index cdd44a871..27f44f893 100755 --- a/SConstruct +++ b/SConstruct @@ -1154,6 +1154,10 @@ main.SConscript('ext/libfdt/SConscript', main.SConscript('ext/fputils/SConscript', variant_dir = joinpath(build_root, 'fputils')) +# DRAMSim2 build is shared across all configs in the build root. +main.SConscript('ext/dramsim2/SConscript', + variant_dir = joinpath(build_root, 'dramsim2')) + ################################################### # # This function is used to set up a directory with switching headers diff --git a/configs/common/MemConfig.py b/configs/common/MemConfig.py index 456ff4e1d..b49995369 100644 --- a/configs/common/MemConfig.py +++ b/configs/common/MemConfig.py @@ -54,6 +54,7 @@ _mem_aliases_all = [ ("lpddr2_s4_1066_x32", "LPDDR2_S4_1066_x32"), ("lpddr3_1600_x32", "LPDDR3_1600_x32"), ("wio_200_x128", "WideIO_200_x128"), + ("dramsim2", "DRAMSim2") ] # Filtered list of aliases. Only aliases for existing memory diff --git a/ext/dramsim2/README b/ext/dramsim2/README new file mode 100644 index 000000000..f83e3f0bd --- /dev/null +++ b/ext/dramsim2/README @@ -0,0 +1,11 @@ +Follow these steps to get DRAMSim2 as part of gem5 + +1. Download DRAMSim2 + 1.1 Go to ext/dramsim2 (this directory) + 1.2 Clone DRAMSim2: git clone git://github.com/dramninjasUMD/DRAMSim2.git + +2. Compile gem5 + 2.1 Business as usual + +3. Run gem5 with DRAMSim2 + 3.1 Use --mem-type=dramsim2 and set the device and system configuration diff --git a/ext/dramsim2/SConscript b/ext/dramsim2/SConscript new file mode 100644 index 000000000..23ba55e52 --- /dev/null +++ b/ext/dramsim2/SConscript @@ -0,0 +1,86 @@ +# -*- mode:python -*- + +# 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 + +import os + +Import('main') + +# See if we got a cloned DRAMSim2 repo as a subdirectory and set the +# HAVE_DRAMSIM flag accordingly +if not os.path.exists(Dir('.').srcnode().abspath + '/DRAMSim2'): + main['HAVE_DRAMSIM'] = False + Return() + +# We have got the folder, so add the library and build the wrappers +main['HAVE_DRAMSIM'] = True + +# Add the appropriate files. We leave out the trace driven simulator +dram_files = [] + +def DRAMFile(filename): + dram_files.append(File('DRAMSim2/' + filename)) + +DRAMFile('AddressMapping.cpp') +DRAMFile('Bank.cpp') +DRAMFile('BankState.cpp') +DRAMFile('BusPacket.cpp') +DRAMFile('ClockDomain.cpp') +DRAMFile('CommandQueue.cpp') +DRAMFile('IniReader.cpp') +DRAMFile('MemoryController.cpp') +DRAMFile('MemorySystem.cpp') +DRAMFile('MultiChannelMemorySystem.cpp') +DRAMFile('Rank.cpp') +DRAMFile('SimulatorObject.cpp') +DRAMFile('Transaction.cpp') + +# DRAMSim2 violates some of the warning flags used by gem5, so +# we explicitly disable them here +dramenv = main.Clone() +dramenv.Append(CCFLAGS=['-Wno-unused-value']) + +# Tell DRAMSim2 to not store any data as this is already covered by +# the wrapper +dramenv.Append(CCFLAGS=['-DNO_STORAGE']) + +dramenv.Library('dramsim2', [main.SharedObject(f) for f in dram_files]) + +main.Prepend(CPPPATH=Dir('.')) +main.Append(LIBS=['dramsim2']) +main.Prepend(LIBPATH=[Dir('.')]) diff --git a/src/mem/DRAMSim2.py b/src/mem/DRAMSim2.py new file mode 100644 index 000000000..9352e8b12 --- /dev/null +++ b/src/mem/DRAMSim2.py @@ -0,0 +1,56 @@ +# 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 + +from m5.params import * +from AbstractMemory import * + +# A wrapper for DRAMSim2 multi-channel memory controller +class DRAMSim2(AbstractMemory): + type = 'DRAMSim2' + cxx_header = "mem/dramsim2.hh" + + # A single port for now + port = SlavePort("Slave port") + + deviceConfigFile = Param.String("ini/DDR3_micron_32M_8B_x8_sg15.ini", + "Device configuration file") + systemConfigFile = Param.String("system.ini.example", + "Memory organisation configuration file") + filePath = Param.String("ext/dramsim2/DRAMSim2/", + "Directory to prepend to file names") + traceFile = Param.String("", "Output file for trace generation") + enableDebug = Param.Bool(False, "Enable DRAMSim2 debug output") diff --git a/src/mem/SConscript b/src/mem/SConscript index 90b49067e..9096fad5c 100644 --- a/src/mem/SConscript +++ b/src/mem/SConscript @@ -66,6 +66,11 @@ if env['TARGET_ISA'] != 'null': Source('se_translating_port_proxy.cc') Source('page_table.cc') + if env['HAVE_DRAMSIM']: + SimObject('DRAMSim2.py') + Source('dramsim2_wrapper.cc') + Source('dramsim2.cc') + DebugFlag('BaseBus') DebugFlag('BusAddrRanges') DebugFlag('CoherentBus') @@ -81,6 +86,8 @@ DebugFlag('MMU') DebugFlag('MemoryAccess') DebugFlag('PacketQueue') +DebugFlag("DRAMSim2") + DebugFlag('ProtocolTrace') DebugFlag('RubyCache') DebugFlag('RubyCacheTrace') 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( + this, &DRAMSim2::readComplete); + DRAMSim::TransactionCompleteCB* write_cb = + new DRAMSim::Callback( + 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(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); +} diff --git a/src/mem/dramsim2.hh b/src/mem/dramsim2.hh new file mode 100644 index 000000000..c61b84cbe --- /dev/null +++ b/src/mem/dramsim2.hh @@ -0,0 +1,210 @@ +/* + * 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 + */ + +/** + * @file + * DRAMSim2 + */ +#ifndef __MEM_DRAMSIM2_HH__ +#define __MEM_DRAMSIM2_HH__ + +#include + +#include "base/hashmap.hh" +#include "mem/abstract_mem.hh" +#include "mem/dramsim2_wrapper.hh" +#include "mem/qport.hh" +#include "params/DRAMSim2.hh" + +class DRAMSim2 : public AbstractMemory +{ + private: + + /** + * The memory port has to deal with its own flow control to avoid + * having unbounded storage that is implicitly created in the port + * itself. + */ + class MemoryPort : public SlavePort + { + + private: + + DRAMSim2& memory; + + public: + + MemoryPort(const std::string& _name, DRAMSim2& _memory); + + protected: + + Tick recvAtomic(PacketPtr pkt); + + void recvFunctional(PacketPtr pkt); + + bool recvTimingReq(PacketPtr pkt); + + void recvRetry(); + + AddrRangeList getAddrRanges() const; + + }; + + MemoryPort port; + + /** + * The actual DRAMSim2 wrapper + */ + DRAMSim2Wrapper wrapper; + + /** + * Is the connected port waiting for a retry from us + */ + bool retryReq; + + /** + * Are we waiting for a retry for sending a response. + */ + bool retryResp; + + /** + * Keep track of what packets are outstanding per + * address, and do so separately for reads and writes. This is + * done so that we can return the right packet on completion from + * DRAMSim. + */ + m5::hash_map > outstandingReads; + m5::hash_map > outstandingWrites; + + /** + * Count the number of outstanding transactions so that we can + * block any further requests until there is space in DRAMSim2 and + * the sending queue we need to buffer the response packets. + */ + unsigned int nbrOutstandingReads; + unsigned int nbrOutstandingWrites; + + /** + * Queue to hold response packets until we can send them + * back. This is needed as DRAMSim2 unconditionally passes + * responses back without any flow control. + */ + std::deque responseQueue; + + /** + * If we need to drain, keep the drain manager around until we're + * done here. + */ + DrainManager *drainManager; + + unsigned int nbrOutstanding() const; + + /** + * When a packet is ready, use the "access()" method in + * AbstractMemory to actually create the response packet, and send + * it back to the outside world requestor. + * + * @param pkt The packet from the outside world + */ + void accessAndRespond(PacketPtr pkt); + + void sendResponse(); + + /** + * Event to schedule sending of responses + */ + EventWrapper sendResponseEvent; + + /** + * Progress the controller one clock cycle. + */ + void tick(); + + /** + * Event to schedule clock ticks + */ + EventWrapper tickEvent; + + /** @todo this is a temporary workaround until the 4-phase code is + * committed. upstream caches needs this packet until true is returned, so + * hold onto it for deletion until a subsequent call + */ + std::vector pendingDelete; + + public: + + typedef DRAMSim2Params Params; + DRAMSim2(const Params *p); + + /** + * Read completion callback. + * + * @param id Channel id of the responder + * @param addr Address of the request + * @param cycle Internal cycle count of DRAMSim2 + */ + void readComplete(unsigned id, uint64_t addr, uint64_t cycle); + + /** + * Write completion callback. + * + * @param id Channel id of the responder + * @param addr Address of the request + * @param cycle Internal cycle count of DRAMSim2 + */ + void writeComplete(unsigned id, uint64_t addr, uint64_t cycle); + + unsigned int drain(DrainManager* dm); + + virtual BaseSlavePort& getSlavePort(const std::string& if_name, + PortID idx = InvalidPortID); + + virtual void init(); + virtual void startup(); + + protected: + + Tick recvAtomic(PacketPtr pkt); + void recvFunctional(PacketPtr pkt); + bool recvTimingReq(PacketPtr pkt); + void recvRetry(); + +}; + +#endif // __MEM_DRAMSIM2_HH__ diff --git a/src/mem/dramsim2_wrapper.cc b/src/mem/dramsim2_wrapper.cc new file mode 100644 index 000000000..34a7ad5d5 --- /dev/null +++ b/src/mem/dramsim2_wrapper.cc @@ -0,0 +1,199 @@ +/* + * 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 + +/** + * When building the debug binary, we need to undo the command-line + * definition of DEBUG not to clash with DRAMSim2 print macros that + * are included for no obvious reason. + */ +#ifdef DEBUG +#undef DEBUG +#endif + +#include + +#include "DRAMSim2/MultiChannelMemorySystem.h" +#include "base/compiler.hh" +#include "base/misc.hh" +#include "mem/dramsim2_wrapper.hh" + +/** + * DRAMSim2 requires SHOW_SIM_OUTPUT to be defined (declared extern in + * the DRAMSim2 print macros), otherwise we get linking errors due to + * undefined references + */ +int SHOW_SIM_OUTPUT = 0; + +DRAMSim2Wrapper::DRAMSim2Wrapper(const std::string& config_file, + const std::string& system_file, + const std::string& working_dir, + const std::string& trace_file, + unsigned int memory_size_mb, + bool enable_debug) : + dramsim(new DRAMSim::MultiChannelMemorySystem(config_file, system_file, + working_dir, trace_file, + memory_size_mb, NULL, NULL)), + _clockPeriod(0.0), _queueSize(0), _burstSize(0) +{ + // tell DRAMSim2 to ignore its internal notion of a CPU frequency + dramsim->setCPUClockSpeed(0); + + // switch on debug output if requested + if (enable_debug) + SHOW_SIM_OUTPUT = 1; + + // there is no way of getting DRAMSim2 to tell us what frequency + // it is assuming, so we have to extract it ourselves + _clockPeriod = extractConfig("tCK=", + working_dir + '/' + config_file); + + if (!_clockPeriod) + fatal("DRAMSim2 wrapper failed to get clock\n"); + + // we also need to know what transaction queue size DRAMSim2 is + // using so we can stall when responses are blocked + _queueSize = extractConfig("TRANS_QUEUE_DEPTH=", + working_dir + '/' + system_file); + + if (!_queueSize) + fatal("DRAMSim2 wrapper failed to get queue size\n"); + + + // finally, get the data bus bits and burst length so we can add a + // sanity check for the burst size + unsigned int dataBusBits = + extractConfig("JEDEC_DATA_BUS_BITS=", + working_dir + '/' + system_file); + unsigned int burstLength = + extractConfig("BL=", working_dir + '/' + config_file); + + if (!dataBusBits || !burstLength) + fatal("DRAMSim22 wrapper failed to get burst size\n"); + + _burstSize = dataBusBits * burstLength / 8; +} + +DRAMSim2Wrapper::~DRAMSim2Wrapper() +{ + delete dramsim; +} + +template +T +DRAMSim2Wrapper::extractConfig(const std::string& field_name, + const std::string& file_name) const +{ + std::ifstream file_stream(file_name.c_str(), ios::in); + + if (!file_stream.good()) + fatal("DRAMSim2 wrapper could not open %s for reading\n", file_name); + + bool found = false; + T res; + std::string line; + while (!found && file_stream) { + getline(file_stream, line); + if (line.substr(0, field_name.size()) == field_name) { + found = true; + istringstream iss(line.substr(field_name.size())); + iss >> res; + } + } + + file_stream.close(); + + if (!found) + fatal("DRAMSim2 wrapper could not find %s in %s\n", field_name, + file_name); + + return res; +} + +void +DRAMSim2Wrapper::printStats() +{ + dramsim->printStats(true); +} + +void +DRAMSim2Wrapper::setCallbacks(DRAMSim::TransactionCompleteCB* read_callback, + DRAMSim::TransactionCompleteCB* write_callback) +{ + // simply pass it on, for now we ignore the power callback + dramsim->RegisterCallbacks(read_callback, write_callback, NULL); +} + +bool +DRAMSim2Wrapper::canAccept() const +{ + return dramsim->willAcceptTransaction(); +} + +void +DRAMSim2Wrapper::enqueue(bool is_write, uint64_t addr) +{ + bool success M5_VAR_USED = dramsim->addTransaction(is_write, addr); + assert(success); +} + +double +DRAMSim2Wrapper::clockPeriod() const +{ + return _clockPeriod; +} + +unsigned int +DRAMSim2Wrapper::queueSize() const +{ + return _queueSize; +} + +unsigned int +DRAMSim2Wrapper::burstSize() const +{ + return _burstSize; +} + +void +DRAMSim2Wrapper::tick() +{ + dramsim->update(); +} diff --git a/src/mem/dramsim2_wrapper.hh b/src/mem/dramsim2_wrapper.hh new file mode 100644 index 000000000..94ae25a8b --- /dev/null +++ b/src/mem/dramsim2_wrapper.hh @@ -0,0 +1,163 @@ +/* + * 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 + */ + +/** + * @file + * DRAMSim2Wrapper declaration + */ + +#ifndef __MEM_DRAMSIM2_WRAPPER_HH__ +#define __MEM_DRAMSIM2_WRAPPER_HH__ + +#include + +#include "DRAMSim2/Callback.h" + +/** + * Forward declaration to avoid includes + */ +namespace DRAMSim { + +class MultiChannelMemorySystem; + +} + +/** + * Wrapper class to avoid having DRAMSim2 names like ClockDomain etc + * clashing with the normal gem5 world. Many of the DRAMSim2 headers + * do not make use of namespaces, and quite a few also open up + * std. The only thing that needs to be exposed externally are the + * callbacks. This wrapper effectively avoids clashes by not including + * any of the conventional gem5 headers (e.g. Packet or SimObject). + */ +class DRAMSim2Wrapper +{ + + private: + + DRAMSim::MultiChannelMemorySystem* dramsim; + + double _clockPeriod; + + unsigned int _queueSize; + + unsigned int _burstSize; + + template + T extractConfig(const std::string& field_name, + const std::string& file_name) const; + + public: + + /** + * Create an instance of the DRAMSim2 multi-channel memory + * controller using a specific config and system description. + * + * @param config_file Memory config file + * @param system_file Controller config file + * @param working_dir Path pre-pended to config files + * @param trace_file Output trace file + * @param memory_size_mb Total memory size in MByte + * @param enable_debug Enable debug output + */ + DRAMSim2Wrapper(const std::string& config_file, + const std::string& system_file, + const std::string& working_dir, + const std::string& trace_file, + unsigned int memory_size_mb, + bool enable_debug); + ~DRAMSim2Wrapper(); + + /** + * Print the stats gathered in DRAMsim2. + */ + void printStats(); + + /** + * Set the callbacks to use for read and write completion. + * + * @param read_callback Callback used for read completions + * @param write_callback Callback used for write completions + */ + void setCallbacks(DRAMSim::TransactionCompleteCB* read_callback, + DRAMSim::TransactionCompleteCB* write_callback); + + /** + * Determine if the controller can accept a new packet or not. + * + * @return true if the controller can accept transactions + */ + bool canAccept() const; + + /** + * Enqueue a packet. This assumes that canAccept has returned true. + * + * @param pkt Packet to turn into a DRAMSim2 transaction + */ + void enqueue(bool is_write, uint64_t addr); + + /** + * Get the internal clock period used by DRAMSim2, specified in + * ns. + * + * @return The clock period of the DRAM interface in ns + */ + double clockPeriod() const; + + /** + * Get the transaction queue size used by DRAMSim2. + * + * @return The queue size counted in number of transactions + */ + unsigned int queueSize() const; + + /** + * Get the burst size in bytes used by DRAMSim2. + * + * @return The burst size in bytes (data width * burst length) + */ + unsigned int burstSize() const; + + /** + * Progress the memory controller one cycle + */ + void tick(); +}; + +#endif //__MEM_DRAMSIM2_WRAPPER_HH__