mem,ext: Integrating DRAMSim3 with gem5
authorMahyar Samani <msamani@ucdavis.edu>
Mon, 27 Jul 2020 15:26:04 +0000 (15:26 +0000)
committerMahyar Samani <msamani@ucdavis.edu>
Wed, 2 Sep 2020 15:51:00 +0000 (15:51 +0000)
Adding DRAMSim3 source code to the gem5 source code, the original
code was taken from umd-memsys github at https://github.com/umd-memsys/

Change-Id: I32c982206f33b0acf2121f322d15baa064c412c4
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/31757
Reviewed-by: Ayaz Akram <yazakram@ucdavis.edu>
Reviewed-by: Jason Lowe-Power <power.jg@gmail.com>
Maintainer: Jason Lowe-Power <power.jg@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
ext/dramsim3/README [new file with mode: 0644]
ext/dramsim3/SConscript [new file with mode: 0644]
src/mem/DRAMsim3.py [new file with mode: 0644]
src/mem/SConscript
src/mem/dramsim3.cc [new file with mode: 0644]
src/mem/dramsim3.hh [new file with mode: 0644]
src/mem/dramsim3_wrapper.cc [new file with mode: 0644]
src/mem/dramsim3_wrapper.hh [new file with mode: 0644]

diff --git a/ext/dramsim3/README b/ext/dramsim3/README
new file mode 100644 (file)
index 0000000..f3ee0a4
--- /dev/null
@@ -0,0 +1,14 @@
+Follow these steps to get DRAMSim3 as part of gem5
+
+1. Download DRAMSim3
+    1.1 Go to ext/dramsim3 (this directory)
+    1.2 Clone DRAMSim3: git clone git@github.com:umd-memsys/DRAMSim3.git DRAMsim3
+    1.3 cd DRAMSim3 && mkdir build
+    1.4 cd build
+    1.5 cmake ..
+    1.6 make
+
+2. Compile gem5
+    2.1 cd gem5
+    2.2 Business as usual
+
diff --git a/ext/dramsim3/SConscript b/ext/dramsim3/SConscript
new file mode 100644 (file)
index 0000000..9e5a3a1
--- /dev/null
@@ -0,0 +1,69 @@
+# -*- 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.
+#
+
+import os
+
+Import('main')
+
+thermal = False
+
+# See if we got a cloned DRAMSim3 repo as a subdirectory and set the
+# HAVE_DRAMSIM flag accordingly
+if not os.path.exists(Dir('.').srcnode().abspath + '/DRAMsim3'):
+    main['HAVE_DRAMSIM3'] = False
+    Return()
+
+# We have got the folder, so add the library and build the wrappers
+main['HAVE_DRAMSIM3'] = True
+
+
+dramsim_path = os.path.join(Dir('#').abspath, 'ext/dramsim3/DRAMsim3/')
+
+if thermal:
+    superlu_path = os.path.join(dramsim_path, 'ext/SuperLU_MT_3.1/lib')
+    main.Prepend(CPPPATH=Dir('.'))
+    main.Append(LIBS=['dramsim3', 'superlu_mt_OPENMP', 'm', 'f77blas',
+                      'atlas', 'gomp'],
+                LIBPATH=[dramsim_path, superlu_path])
+else:
+    main.Prepend(CPPPATH=Dir('.'))
+    # a littel hacky but can get a shared library working
+    main.Append(LIBS=['dramsim3', 'gomp'],
+                LIBPATH=[dramsim_path],  # compile-time lookup
+                RPATH=[dramsim_path],  # runtime lookup
+                CPPPATH=[dramsim_path+'/src/'])
diff --git a/src/mem/DRAMsim3.py b/src/mem/DRAMsim3.py
new file mode 100644 (file)
index 0000000..e710bf4
--- /dev/null
@@ -0,0 +1,54 @@
+# 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 m5.objects.AbstractMemory import *
+
+# A wrapper for DRAMSim3 multi-channel memory controller
+class DRAMsim3(AbstractMemory):
+    type = 'DRAMsim3'
+    cxx_header = "mem/dramsim3.hh"
+
+    # A single port for now
+    port = ResponsePort("port for receiving requests from"
+                        "the CPU or other requestor")
+
+    configFile = Param.String("ext/dramsim3/DRAMsim3/configs/"
+                              "DDR4_8Gb_x8_2400.ini",
+                              "The configuration file to use with DRAMSim3")
+    filePath = Param.String("ext/dramsim3/DRAMsim3/",
+                            "Directory to prepend to file names")
index 91cae37908ad40b032da0ad597a3164e30a80cb3..07e197b9ed767380032e57b5be79885a6e74556e 100644 (file)
@@ -91,6 +91,11 @@ if env['HAVE_DRAMSIM']:
     Source('dramsim2_wrapper.cc')
     Source('dramsim2.cc')
 
+if env['HAVE_DRAMSIM3']:
+    SimObject('DRAMsim3.py')
+    Source('dramsim3_wrapper.cc')
+    Source('dramsim3.cc')
+
 SimObject('MemChecker.py')
 Source('mem_checker.cc')
 Source('mem_checker_monitor.cc')
@@ -115,6 +120,7 @@ DebugFlag('MemoryAccess')
 DebugFlag('PacketQueue')
 DebugFlag('StackDist')
 DebugFlag("DRAMSim2")
+DebugFlag("DRAMsim3")
 DebugFlag('HMCController')
 DebugFlag('SerialLink')
 DebugFlag('TokenPort')
diff --git a/src/mem/dramsim3.cc b/src/mem/dramsim3.cc
new file mode 100644 (file)
index 0000000..fc2eaca
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * 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 "mem/dramsim3.hh"
+
+#include "base/callback.hh"
+#include "base/trace.hh"
+#include "debug/DRAMsim3.hh"
+#include "debug/Drain.hh"
+#include "sim/system.hh"
+
+DRAMsim3::DRAMsim3(const Params* p) :
+    AbstractMemory(p),
+    port(name() + ".port", *this),
+    read_cb(std::bind(&DRAMsim3::readComplete,
+                      this, 0, std::placeholders::_1)),
+    write_cb(std::bind(&DRAMsim3::writeComplete,
+                       this, 0, std::placeholders::_1)),
+    wrapper(p->configFile, p->filePath, read_cb, write_cb),
+    retryReq(false), retryResp(false), startTick(0),
+    nbrOutstandingReads(0), nbrOutstandingWrites(0),
+    sendResponseEvent([this]{ sendResponse(); }, name()),
+    tickEvent([this]{ tick(); }, name())
+{
+    DPRINTF(DRAMsim3,
+            "Instantiated DRAMsim3 with clock %d ns and queue size %d\n",
+            wrapper.clockPeriod(), wrapper.queueSize());
+
+    // Register a callback to compensate for the destructor not
+    // being called. The callback prints the DRAMsim3 stats.
+    registerExitCallback([this]() { wrapper.printStats(); });
+}
+
+void
+DRAMsim3::init()
+{
+    AbstractMemory::init();
+
+    if (!port.isConnected()) {
+        fatal("DRAMsim3 %s is unconnected!\n", name());
+    } else {
+        port.sendRangeChange();
+    }
+
+    if (system()->cacheLineSize() != wrapper.burstSize())
+        fatal("DRAMsim3 burst size %d does not match cache line size %d\n",
+              wrapper.burstSize(), system()->cacheLineSize());
+}
+
+void
+DRAMsim3::startup()
+{
+    startTick = curTick();
+
+    // kick off the clock ticks
+    schedule(tickEvent, clockEdge());
+}
+
+void
+DRAMsim3::resetStats() {
+    wrapper.resetStats();
+}
+
+void
+DRAMsim3::sendResponse()
+{
+    assert(!retryResp);
+    assert(!responseQueue.empty());
+
+    DPRINTF(DRAMsim3, "Attempting to send response\n");
+
+    bool success = port.sendTimingResp(responseQueue.front());
+    if (success) {
+        responseQueue.pop_front();
+
+        DPRINTF(DRAMsim3, "Have %d read, %d write, %d responses outstanding\n",
+                nbrOutstandingReads, nbrOutstandingWrites,
+                responseQueue.size());
+
+        if (!responseQueue.empty() && !sendResponseEvent.scheduled())
+            schedule(sendResponseEvent, curTick());
+
+        if (nbrOutstanding() == 0)
+            signalDrainDone();
+    } else {
+        retryResp = true;
+
+        DPRINTF(DRAMsim3, "Waiting for response retry\n");
+
+        assert(!sendResponseEvent.scheduled());
+    }
+}
+
+unsigned int
+DRAMsim3::nbrOutstanding() const
+{
+    return nbrOutstandingReads + nbrOutstandingWrites + responseQueue.size();
+}
+
+void
+DRAMsim3::tick()
+{
+    // Only tick when it's timing mode
+    if (system()->isTimingMode()) {
+        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.sendRetryReq();
+        }
+    }
+
+    schedule(tickEvent, curTick() + wrapper.clockPeriod() * SimClock::Int::ns);
+}
+
+Tick
+DRAMsim3::recvAtomic(PacketPtr pkt)
+{
+    access(pkt);
+
+    // 50 ns is just an arbitrary value at this point
+    return pkt->cacheResponding() ? 0 : 50000;
+}
+
+void
+DRAMsim3::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->trySatisfyFunctional(*i);
+
+    pkt->popLabel();
+}
+
+bool
+DRAMsim3::recvTimingReq(PacketPtr pkt)
+{
+    // if a cache is responding, sink the packet without further action
+    if (pkt->cacheResponding()) {
+        pendingDelete.reset(pkt);
+        return true;
+    }
+
+    // we should not get a new request after committing to retry the
+    // current one, but unfortunately the CPU violates this rule, so
+    // simply ignore it for now
+    if (retryReq)
+        return false;
+
+    // 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(pkt->getAddr(), pkt->isWrite()));
+
+        DPRINTF(DRAMsim3, "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->getAddr(), pkt->isWrite());
+
+        return true;
+    } else {
+        retryReq = true;
+        return false;
+    }
+}
+
+void
+DRAMsim3::recvRespRetry()
+{
+    DPRINTF(DRAMsim3, "Retrying\n");
+
+    assert(retryResp);
+    retryResp = false;
+    sendResponse();
+}
+
+void
+DRAMsim3::accessAndRespond(PacketPtr pkt)
+{
+    DPRINTF(DRAMsim3, "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());
+        // Here we pay for xbar additional delay and to process the payload
+        // of the packet.
+        Tick time = curTick() + pkt->headerDelay + pkt->payloadDelay;
+        // Reset the timings of the packet
+        pkt->headerDelay = pkt->payloadDelay = 0;
+
+        DPRINTF(DRAMsim3, "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, time);
+    } else {
+        // queue the packet for deletion
+        pendingDelete.reset(pkt);
+    }
+}
+
+void DRAMsim3::readComplete(unsigned id, uint64_t addr)
+{
+
+    DPRINTF(DRAMsim3, "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 DRAMsim3::writeComplete(unsigned id, uint64_t addr)
+{
+
+    DPRINTF(DRAMsim3, "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;
+
+    if (nbrOutstanding() == 0)
+        signalDrainDone();
+}
+
+Port&
+DRAMsim3::getPort(const std::string &if_name, PortID idx)
+{
+    if (if_name != "port") {
+        return ClockedObject::getPort(if_name, idx);
+    } else {
+        return port;
+    }
+}
+
+DrainState
+DRAMsim3::drain()
+{
+    // check our outstanding reads and writes and if any they need to
+    // drain
+    return nbrOutstanding() != 0 ? DrainState::Draining : DrainState::Drained;
+}
+
+DRAMsim3::MemoryPort::MemoryPort(const std::string& _name,
+                                 DRAMsim3& _memory)
+    : SlavePort(_name, &_memory), memory(_memory)
+{ }
+
+AddrRangeList
+DRAMsim3::MemoryPort::getAddrRanges() const
+{
+    AddrRangeList ranges;
+    ranges.push_back(memory.getAddrRange());
+    return ranges;
+}
+
+Tick
+DRAMsim3::MemoryPort::recvAtomic(PacketPtr pkt)
+{
+    return memory.recvAtomic(pkt);
+}
+
+void
+DRAMsim3::MemoryPort::recvFunctional(PacketPtr pkt)
+{
+    memory.recvFunctional(pkt);
+}
+
+bool
+DRAMsim3::MemoryPort::recvTimingReq(PacketPtr pkt)
+{
+    // pass it to the memory controller
+    return memory.recvTimingReq(pkt);
+}
+
+void
+DRAMsim3::MemoryPort::recvRespRetry()
+{
+    memory.recvRespRetry();
+}
+
+DRAMsim3*
+DRAMsim3Params::create()
+{
+    return new DRAMsim3(this);
+}
diff --git a/src/mem/dramsim3.hh b/src/mem/dramsim3.hh
new file mode 100644 (file)
index 0000000..1b4a8a6
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ *
+ */
+
+/**
+ * @file
+ * DRAMsim3
+ */
+#ifndef __MEM_DRAMSIM3_HH__
+#define __MEM_DRAMSIM3_HH__
+
+#include <functional>
+#include <queue>
+#include <unordered_map>
+
+#include "mem/abstract_mem.hh"
+#include "mem/dramsim3_wrapper.hh"
+#include "mem/qport.hh"
+#include "params/DRAMsim3.hh"
+
+class DRAMsim3 : 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:
+
+        DRAMsim3& memory;
+
+      public:
+
+        MemoryPort(const std::string& _name, DRAMsim3& _memory);
+
+      protected:
+
+        Tick recvAtomic(PacketPtr pkt);
+
+        void recvFunctional(PacketPtr pkt);
+
+        bool recvTimingReq(PacketPtr pkt);
+
+        void recvRespRetry();
+
+        AddrRangeList getAddrRanges() const;
+
+    };
+
+    MemoryPort port;
+
+    /**
+     * Callback functions
+     */
+    std::function<void(uint64_t)> read_cb;
+    std::function<void(uint64_t)> write_cb;
+
+    /**
+     * The actual DRAMsim3 wrapper
+     */
+    DRAMsim3Wrapper 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 when the wrapper is started.
+     */
+    Tick startTick;
+
+    /**
+     * 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.
+     */
+    std::unordered_map<Addr, std::queue<PacketPtr> > outstandingReads;
+    std::unordered_map<Addr, std::queue<PacketPtr> > outstandingWrites;
+
+    /**
+     * Count the number of outstanding transactions so that we can
+     * block any further requests until there is space in DRAMsim3 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 DRAMsim3 unconditionally passes
+     * responses back without any flow control.
+     */
+    std::deque<PacketPtr> responseQueue;
+
+
+    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
+     */
+    EventFunctionWrapper sendResponseEvent;
+
+    /**
+     * Progress the controller one clock cycle.
+     */
+    void tick();
+
+    /**
+     * Event to schedule clock ticks
+     */
+    EventFunctionWrapper tickEvent;
+
+    /**
+     * Upstream caches need this packet until true is returned, so
+     * hold it for deletion until a subsequent call
+     */
+    std::unique_ptr<Packet> pendingDelete;
+
+  public:
+
+    typedef DRAMsim3Params Params;
+    DRAMsim3(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 DRAMsim3
+     */
+    void readComplete(unsigned id, uint64_t addr);
+
+    /**
+     * Write completion callback.
+     *
+     * @param id Channel id of the responder
+     * @param addr Address of the request
+     * @param cycle Internal cycle count of DRAMsim3
+     */
+    void writeComplete(unsigned id, uint64_t addr);
+
+    DrainState drain() override;
+
+    virtual Port& getPort(const std::string& if_name,
+                          PortID idx = InvalidPortID) override;
+
+    void init() override;
+    void startup() override;
+
+    void resetStats() override;
+
+  protected:
+
+    Tick recvAtomic(PacketPtr pkt);
+    void recvFunctional(PacketPtr pkt);
+    bool recvTimingReq(PacketPtr pkt);
+    void recvRespRetry();
+
+};
+
+#endif // __MEM_DRAMSIM3_HH__
diff --git a/src/mem/dramsim3_wrapper.cc b/src/mem/dramsim3_wrapper.cc
new file mode 100644 (file)
index 0000000..07754bc
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ *
+ */
+
+#include <cassert>
+
+/**
+ * When building the debug binary, we need to undo the command-line
+ * definition of DEBUG not to clash with DRAMsim3 print macros that
+ * are included for no obvious reason.
+ */
+#ifdef DEBUG
+#undef DEBUG
+#endif
+
+#include "mem/dramsim3_wrapper.hh"
+
+#include <fstream>
+
+#include "DRAMsim3/src/dramsim3.h"
+#include "base/compiler.hh"
+#include "base/logging.hh"
+
+DRAMsim3Wrapper::DRAMsim3Wrapper(const std::string& config_file,
+                                 const std::string& working_dir,
+                                 std::function<void(uint64_t)> read_cb,
+                                 std::function<void(uint64_t)> write_cb) :
+    dramsim(dramsim3::GetMemorySystem(config_file, working_dir,
+                                       read_cb, write_cb)),
+    _clockPeriod(0.0), _queueSize(0), _burstSize(0)
+{
+    // there is no way of getting DRAMsim3 to tell us what frequency
+    // it is assuming, so we have to extract it ourselves
+    _clockPeriod = dramsim->GetTCK();
+
+    if (!_clockPeriod)
+        fatal("DRAMsim3 wrapper failed to get clock\n");
+
+    // we also need to know what transaction queue size DRAMsim3 is
+    // using so we can stall when responses are blocked
+    _queueSize = dramsim->GetQueueSize();
+
+    if (!_queueSize)
+        fatal("DRAMsim3 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 = dramsim->GetBusBits();
+   unsigned int burstLength = dramsim->GetBurstLength();
+
+   if (!dataBusBits || !burstLength)
+       fatal("DRAMsim3 wrapper failed to get burst size\n");
+
+   _burstSize = dataBusBits * burstLength / 8;
+}
+
+DRAMsim3Wrapper::~DRAMsim3Wrapper()
+{
+    delete dramsim;
+}
+
+
+void
+DRAMsim3Wrapper::printStats()
+{
+    dramsim->PrintStats();
+}
+
+void
+DRAMsim3Wrapper::resetStats()
+{
+    dramsim->ResetStats();
+}
+
+void
+DRAMsim3Wrapper::setCallbacks(std::function<void(uint64_t)> read_complete,
+                              std::function<void(uint64_t)> write_complete)
+{
+    dramsim->RegisterCallbacks(read_complete, write_complete);
+}
+
+bool
+DRAMsim3Wrapper::canAccept(uint64_t addr, bool is_write) const
+{
+    return dramsim->WillAcceptTransaction(addr, is_write);
+}
+
+void
+DRAMsim3Wrapper::enqueue(uint64_t addr, bool is_write)
+{
+    bool success M5_VAR_USED = dramsim->AddTransaction(addr, is_write);
+    assert(success);
+}
+
+double
+DRAMsim3Wrapper::clockPeriod() const
+{
+    return _clockPeriod;
+}
+
+unsigned int
+DRAMsim3Wrapper::queueSize() const
+{
+    return _queueSize;
+}
+
+unsigned int
+DRAMsim3Wrapper::burstSize() const
+{
+    return _burstSize;
+}
+
+void
+DRAMsim3Wrapper::tick()
+{
+    dramsim->ClockTick();
+}
+
diff --git a/src/mem/dramsim3_wrapper.hh b/src/mem/dramsim3_wrapper.hh
new file mode 100644 (file)
index 0000000..0c4f6d0
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ *
+ */
+
+/**
+ * @file
+ * DRAMsim3Wrapper declaration
+ */
+
+#ifndef __MEM_DRAMSIM3_WRAPPER_HH__
+#define __MEM_DRAMSIM3_WRAPPER_HH__
+
+#include <functional>
+#include <string>
+
+/**
+ * Forward declaration to avoid includes
+ */
+namespace dramsim3 {
+
+class MemorySystem;
+
+}
+
+/**
+ * Wrapper class to avoid having DRAMsim3 names like ClockDomain etc
+ * clashing with the normal gem5 world. Many of the DRAMsim3 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 DRAMsim3Wrapper
+{
+
+  private:
+
+    dramsim3::MemorySystem* dramsim;
+
+    double _clockPeriod;
+
+    unsigned int _queueSize;
+
+    unsigned int _burstSize;
+
+    template <typename T>
+    T extractConfig(const std::string& field_name,
+                    const std::string& file_name) const;
+
+  public:
+
+    /**
+     * Create an instance of the DRAMsim3 multi-channel memory
+     * controller using a specific config and system description.
+     *
+     * @param config_file Memory config file
+     * @param working_dir Path pre-pended to config files
+     */
+    DRAMsim3Wrapper(const std::string& config_file,
+                    const std::string& working_dir,
+                    std::function<void(uint64_t)> read_cb,
+                    std::function<void(uint64_t)> write_cb);
+    ~DRAMsim3Wrapper();
+
+    /**
+     * Print the stats gathered in DRAMsim3.
+     */
+    void printStats();
+
+    /**
+     * Reset stats (useful for fastforwarding switch)
+     */
+    void resetStats();
+
+    /**
+     * 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(std::function<void(uint64_t)> read_complete,
+                      std::function<void(uint64_t)> write_complete);
+
+    /**
+     * Determine if the controller can accept a new packet or not.
+     *
+     * @return true if the controller can accept transactions
+     */
+    bool canAccept(uint64_t addr, bool is_write) const;
+
+    /**
+     * Enqueue a packet. This assumes that canAccept has returned true.
+     *
+     * @param pkt Packet to turn into a DRAMsim3 transaction
+     */
+    void enqueue(uint64_t addr, bool is_write);
+
+    /**
+     * Get the internal clock period used by DRAMsim3, specified in
+     * ns.
+     *
+     * @return The clock period of the DRAM interface in ns
+     */
+    double clockPeriod() const;
+
+    /**
+     * Get the transaction queue size used by DRAMsim3.
+     *
+     * @return The queue size counted in number of transactions
+     */
+    unsigned int queueSize() const;
+
+    /**
+     * Get the burst size in bytes used by DRAMsim3.
+     *
+     * @return The burst size in bytes (data width * burst length)
+     */
+    unsigned int burstSize() const;
+
+    /**
+     * Progress the memory controller one cycle
+     */
+    void tick();
+};
+
+#endif //__MEM_DRAMSIM3_WRAPPER_HH__