ruby: memory controllers now inherit from an abstract "MemoryControl" class
authorNuwan Jayasena <Nuwan.Jayasena@amd.com>
Wed, 11 Jul 2012 05:51:53 +0000 (22:51 -0700)
committerNuwan Jayasena <Nuwan.Jayasena@amd.com>
Wed, 11 Jul 2012 05:51:53 +0000 (22:51 -0700)
src/mem/ruby/system/MemoryControl.cc
src/mem/ruby/system/MemoryControl.hh
src/mem/ruby/system/MemoryControl.py
src/mem/ruby/system/RubyMemoryControl.cc [new file with mode: 0644]
src/mem/ruby/system/RubyMemoryControl.hh [new file with mode: 0644]
src/mem/ruby/system/RubyMemoryControl.py [new file with mode: 0644]
src/mem/ruby/system/SConscript
src/mem/slicc/symbols/StateMachine.py

index 4e5ebdbe92142cad892aa80148a6668cea08e747..cf6a618e0b39a7bcf48d8445d3b6b5cb31d794fa 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * Copyright (c) 2012 Advanced Micro Devices, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/*
- * Description:  This module simulates a basic DDR-style memory controller
- * (and can easily be extended to do FB-DIMM as well).
- *
- * This module models a single channel, connected to any number of
- * DIMMs with any number of ranks of DRAMs each.  If you want multiple
- * address/data channels, you need to instantiate multiple copies of
- * this module.
- *
- * Each memory request is placed in a queue associated with a specific
- * memory bank.  This queue is of finite size; if the queue is full
- * the request will back up in an (infinite) common queue and will
- * effectively throttle the whole system.  This sort of behavior is
- * intended to be closer to real system behavior than if we had an
- * infinite queue on each bank.  If you want the latter, just make
- * the bank queues unreasonably large.
- *
- * The head item on a bank queue is issued when all of the
- * following are true:
- *   the bank is available
- *   the address path to the DIMM is available
- *   the data path to or from the DIMM is available
- *
- * Note that we are not concerned about fixed offsets in time.  The bank
- * will not be used at the same moment as the address path, but since
- * there is no queue in the DIMM or the DRAM it will be used at a constant
- * number of cycles later, so it is treated as if it is used at the same
- * time.
- *
- * We are assuming closed bank policy; that is, we automatically close
- * each bank after a single read or write.  Adding an option for open
- * bank policy is for future work.
- *
- * We are assuming "posted CAS"; that is, we send the READ or WRITE
- * immediately after the ACTIVATE.  This makes scheduling the address
- * bus trivial; we always schedule a fixed set of cycles.  For DDR-400,
- * this is a set of two cycles; for some configurations such as
- * DDR-800 the parameter tRRD forces this to be set to three cycles.
- *
- * We assume a four-bit-time transfer on the data wires.  This is
- * the minimum burst length for DDR-2.  This would correspond
- * to (for example) a memory where each DIMM is 72 bits wide
- * and DIMMs are ganged in pairs to deliver 64 bytes at a shot.
- * This gives us the same occupancy on the data wires as on the
- * address wires (for the two-address-cycle case).
- *
- * The only non-trivial scheduling problem is the data wires.
- * A write will use the wires earlier in the operation than a read
- * will; typically one cycle earlier as seen at the DRAM, but earlier
- * by a worst-case round-trip wire delay when seen at the memory controller.
- * So, while reads from one rank can be scheduled back-to-back
- * every two cycles, and writes (to any rank) scheduled every two cycles,
- * when a read is followed by a write we need to insert a bubble.
- * Furthermore, consecutive reads from two different ranks may need
- * to insert a bubble due to skew between when one DRAM stops driving the
- * wires and when the other one starts.  (These bubbles are parameters.)
- *
- * This means that when some number of reads and writes are at the
- * heads of their queues, reads could starve writes, and/or reads
- * to the same rank could starve out other requests, since the others
- * would never see the data bus ready.
- * For this reason, we have implemented an anti-starvation feature.
- * A group of requests is marked "old", and a counter is incremented
- * each cycle as long as any request from that batch has not issued.
- * if the counter reaches twice the bank busy time, we hold off any
- * newer requests until all of the "old" requests have issued.
- *
- * We also model tFAW.  This is an obscure DRAM parameter that says
- * that no more than four activate requests can happen within a window
- * of a certain size.  For most configurations this does not come into play,
- * or has very little effect, but it could be used to throttle the power
- * consumption of the DRAM.  In this implementation (unlike in a DRAM
- * data sheet) TFAW is measured in memory bus cycles; i.e. if TFAW = 16
- * then no more than four activates may happen within any 16 cycle window.
- * Refreshes are included in the activates.
- *
- */
-
 #include "base/cast.hh"
 #include "base/cprintf.hh"
+#include "mem/ruby/common/Address.hh"
 #include "mem/ruby/common/Consumer.hh"
 #include "mem/ruby/common/Global.hh"
 #include "mem/ruby/network/Network.hh"
 #include "mem/ruby/slicc_interface/NetworkMessage.hh"
 #include "mem/ruby/slicc_interface/RubySlicc_ComponentMapping.hh"
 #include "mem/ruby/system/MemoryControl.hh"
+#include "mem/ruby/system/RubyMemoryControl.hh"
+#include "mem/ruby/system/System.hh"
 
 using namespace std;
+MemoryControl::MemoryControl(const Params *p) : SimObject(p), m_event(this) {};
+MemoryControl::~MemoryControl() {};
 
-class Consumer;
-
-// Value to reset watchdog timer to.
-// If we're idle for this many memory control cycles,
-// shut down our clock (our rescheduling of ourselves).
-// Refresh shuts down as well.
-// When we restart, we'll be in a different phase
-// with respect to ruby cycles, so this introduces
-// a slight inaccuracy.  But it is necessary or the
-// ruby tester never terminates because the event
-// queue is never empty.
-#define IDLECOUNT_MAX_VALUE 1000
-
-// Output operator definition
-
-ostream&
-operator<<(ostream& out, const MemoryControl& obj)
-{
-    obj.print(out);
-    out << flush;
-    return out;
-}
-
-
-// ****************************************************************
-
-// CONSTRUCTOR
-MemoryControl::MemoryControl(const Params *p)
-    : SimObject(p), m_event(this)
-{
-    m_mem_bus_cycle_multiplier = p->mem_bus_cycle_multiplier;
-    m_banks_per_rank = p->banks_per_rank;
-    m_ranks_per_dimm = p->ranks_per_dimm;
-    m_dimms_per_channel = p->dimms_per_channel;
-    m_bank_bit_0 = p->bank_bit_0;
-    m_rank_bit_0 = p->rank_bit_0;
-    m_dimm_bit_0 = p->dimm_bit_0;
-    m_bank_queue_size = p->bank_queue_size;
-    m_bank_busy_time = p->bank_busy_time;
-    m_rank_rank_delay = p->rank_rank_delay;
-    m_read_write_delay = p->read_write_delay;
-    m_basic_bus_busy_time = p->basic_bus_busy_time;
-    m_mem_ctl_latency = p->mem_ctl_latency;
-    m_refresh_period = p->refresh_period;
-    m_tFaw = p->tFaw;
-    m_mem_random_arbitrate = p->mem_random_arbitrate;
-    m_mem_fixed_delay = p->mem_fixed_delay;
-
-    m_profiler_ptr = new MemCntrlProfiler(name(),
-                                          m_banks_per_rank,
-                                          m_ranks_per_dimm,
-                                          m_dimms_per_channel);
-}
-
-void
-MemoryControl::init()
-{
-    m_msg_counter = 0;
-
-    assert(m_tFaw <= 62); // must fit in a uint64 shift register
-
-    m_total_banks = m_banks_per_rank * m_ranks_per_dimm * m_dimms_per_channel;
-    m_total_ranks = m_ranks_per_dimm * m_dimms_per_channel;
-    m_refresh_period_system = m_refresh_period / m_total_banks;
-
-    m_bankQueues = new list<MemoryNode> [m_total_banks];
-    assert(m_bankQueues);
-
-    m_bankBusyCounter = new int [m_total_banks];
-    assert(m_bankBusyCounter);
-
-    m_oldRequest = new int [m_total_banks];
-    assert(m_oldRequest);
-
-    for (int i = 0; i < m_total_banks; i++) {
-        m_bankBusyCounter[i] = 0;
-        m_oldRequest[i] = 0;
-    }
-
-    m_busBusyCounter_Basic = 0;
-    m_busBusyCounter_Write = 0;
-    m_busBusyCounter_ReadNewRank = 0;
-    m_busBusy_WhichRank = 0;
-
-    m_roundRobin = 0;
-    m_refresh_count = 1;
-    m_need_refresh = 0;
-    m_refresh_bank = 0;
-    m_idleCount = 0;
-    m_ageCounter = 0;
-
-    // Each tfaw shift register keeps a moving bit pattern
-    // which shows when recent activates have occurred.
-    // m_tfaw_count keeps track of how many 1 bits are set
-    // in each shift register.  When m_tfaw_count is >= 4,
-    // new activates are not allowed.
-    m_tfaw_shift = new uint64[m_total_ranks];
-    m_tfaw_count = new int[m_total_ranks];
-    for (int i = 0; i < m_total_ranks; i++) {
-        m_tfaw_shift[i] = 0;
-        m_tfaw_count[i] = 0;
-    }
-}
-
-MemoryControl::~MemoryControl()
-{
-    delete [] m_bankQueues;
-    delete [] m_bankBusyCounter;
-    delete [] m_oldRequest;
-    delete m_profiler_ptr;
-}
-
-// enqueue new request from directory
-void
-MemoryControl::enqueue(const MsgPtr& message, int latency)
-{
-    Time current_time = g_eventQueue_ptr->getTime();
-    Time arrival_time = current_time + latency;
-    const MemoryMsg* memMess = safe_cast<const MemoryMsg*>(message.get());
-    physical_address_t addr = memMess->getAddress().getAddress();
-    MemoryRequestType type = memMess->getType();
-    bool is_mem_read = (type == MemoryRequestType_MEMORY_READ);
-    MemoryNode thisReq(arrival_time, message, addr, is_mem_read, !is_mem_read);
-    enqueueMemRef(thisReq);
-}
-
-// Alternate entry point used when we already have a MemoryNode
-// structure built.
-void
-MemoryControl::enqueueMemRef(MemoryNode& memRef)
-{
-    m_msg_counter++;
-    memRef.m_msg_counter = m_msg_counter;
-    physical_address_t addr = memRef.m_addr;
-    int bank = getBank(addr);
-
-    DPRINTF(RubyMemory,
-            "New memory request%7d: %#08x %c arrived at %10d bank = %3x sched %c\n",
-            m_msg_counter, addr, memRef.m_is_mem_read ? 'R':'W',
-            memRef.m_time * g_eventQueue_ptr->getClock(),
-            bank, m_event.scheduled() ? 'Y':'N');
-
-    m_profiler_ptr->profileMemReq(bank);
-    m_input_queue.push_back(memRef);
-
-    if (!m_event.scheduled()) {
-        schedule(m_event, curTick() + 1);
-    }
-}
-
-// dequeue, peek, and isReady are used to transfer completed requests
-// back to the directory
-void
-MemoryControl::dequeue()
-{
-    assert(isReady());
-    m_response_queue.pop_front();
-}
-
-const Message*
-MemoryControl::peek()
-{
-    MemoryNode node = peekNode();
-    Message* msg_ptr = node.m_msgptr.get();
-    assert(msg_ptr != NULL);
-    return msg_ptr;
-}
-
-MemoryNode
-MemoryControl::peekNode()
-{
-    assert(isReady());
-    MemoryNode req = m_response_queue.front();
-    DPRINTF(RubyMemory, "Peek: memory request%7d: %#08x %c sched %c\n",
-            req.m_msg_counter, req.m_addr, req.m_is_mem_read ? 'R':'W',
-            m_event.scheduled() ? 'Y':'N');
-
-    return req;
-}
-
-bool
-MemoryControl::isReady()
-{
-    return ((!m_response_queue.empty()) &&
-            (m_response_queue.front().m_time <= g_eventQueue_ptr->getTime()));
-}
-
-void
-MemoryControl::setConsumer(Consumer* consumer_ptr)
-{
-    m_consumer_ptr = consumer_ptr;
-}
-
-void
-MemoryControl::print(ostream& out) const
-{
-}
-
-void
-MemoryControl::printConfig(ostream& out)
-{
-    out << "Memory Control " << name() << ":" << endl;
-    out << "  Ruby cycles per memory cycle: " << m_mem_bus_cycle_multiplier
-        << endl;
-    out << "  Basic read latency: " << m_mem_ctl_latency << endl;
-    if (m_mem_fixed_delay) {
-        out << "  Fixed Latency mode:  Added cycles = " << m_mem_fixed_delay
-            << endl;
-    } else {
-        out << "  Bank busy time: " << m_bank_busy_time << " memory cycles"
-            << endl;
-        out << "  Memory channel busy time: " << m_basic_bus_busy_time << endl;
-        out << "  Dead cycles between reads to different ranks: "
-            << m_rank_rank_delay << endl;
-        out << "  Dead cycle between a read and a write: "
-            << m_read_write_delay << endl;
-        out << "  tFaw (four-activate) window: " << m_tFaw << endl;
-    }
-    out << "  Banks per rank: " << m_banks_per_rank << endl;
-    out << "  Ranks per DIMM: " << m_ranks_per_dimm << endl;
-    out << "  DIMMs per channel:  " << m_dimms_per_channel << endl;
-    out << "  LSB of bank field in address: " << m_bank_bit_0 << endl;
-    out << "  LSB of rank field in address: " << m_rank_bit_0 << endl;
-    out << "  LSB of DIMM field in address: " << m_dimm_bit_0 << endl;
-    out << "  Max size of each bank queue: " << m_bank_queue_size << endl;
-    out << "  Refresh period (within one bank): " << m_refresh_period << endl;
-    out << "  Arbitration randomness: " << m_mem_random_arbitrate << endl;
-}
-
-void
-MemoryControl::clearStats() const
-{
-    m_profiler_ptr->clearStats();
-}
-
-void
-MemoryControl::printStats(ostream& out) const
-{
-    m_profiler_ptr->printStats(out);
-}
-
-// Queue up a completed request to send back to directory
-void
-MemoryControl::enqueueToDirectory(MemoryNode req, int latency)
-{
-    Time arrival_time = g_eventQueue_ptr->getTime()
-        + (latency * m_mem_bus_cycle_multiplier);
-    req.m_time = arrival_time;
-    m_response_queue.push_back(req);
-
-    DPRINTF(RubyMemory, "Enqueueing msg %#08x %c back to directory at %15d\n",
-            req.m_addr, req.m_is_mem_read ? 'R':'W',
-            arrival_time * g_eventQueue_ptr->getClock());
-
-    // schedule the wake up
-    g_eventQueue_ptr->scheduleEventAbsolute(m_consumer_ptr, arrival_time);
-}
-
-// getBank returns an integer that is unique for each
-// bank across this memory controller.
-int
-MemoryControl::getBank(physical_address_t addr)
-{
-    int dimm = (addr >> m_dimm_bit_0) & (m_dimms_per_channel - 1);
-    int rank = (addr >> m_rank_bit_0) & (m_ranks_per_dimm - 1);
-    int bank = (addr >> m_bank_bit_0) & (m_banks_per_rank - 1);
-    return (dimm * m_ranks_per_dimm * m_banks_per_rank)
-        + (rank * m_banks_per_rank)
-        + bank;
-}
-
-// getRank returns an integer that is unique for each rank
-// and independent of individual bank.
-int
-MemoryControl::getRank(int bank)
-{
-    int rank = (bank / m_banks_per_rank);
-    assert (rank < (m_ranks_per_dimm * m_dimms_per_channel));
-    return rank;
-}
-
-// queueReady determines if the head item in a bank queue
-// can be issued this cycle
-bool
-MemoryControl::queueReady(int bank)
-{
-    if ((m_bankBusyCounter[bank] > 0) && !m_mem_fixed_delay) {
-        m_profiler_ptr->profileMemBankBusy();
-
-        DPRINTF(RubyMemory, "bank %x busy %d\n", bank, m_bankBusyCounter[bank]);
-        return false;
-    }
-
-    if (m_mem_random_arbitrate >= 2) {
-        if ((random() % 100) < m_mem_random_arbitrate) {
-            m_profiler_ptr->profileMemRandBusy();
-            return false;
-        }
-    }
-
-    if (m_mem_fixed_delay)
-        return true;
-
-    if ((m_ageCounter > (2 * m_bank_busy_time)) && !m_oldRequest[bank]) {
-        m_profiler_ptr->profileMemNotOld();
-        return false;
-    }
-
-    if (m_busBusyCounter_Basic == m_basic_bus_busy_time) {
-        // Another bank must have issued this same cycle.  For
-        // profiling, we count this as an arb wait rather than a bus
-        // wait.  This is a little inaccurate since it MIGHT have also
-        // been blocked waiting for a read-write or a read-read
-        // instead, but it's pretty close.
-        m_profiler_ptr->profileMemArbWait(1);
-        return false;
-    }
-
-    if (m_busBusyCounter_Basic > 0) {
-        m_profiler_ptr->profileMemBusBusy();
-        return false;
-    }
-
-    int rank = getRank(bank);
-    if (m_tfaw_count[rank] >= ACTIVATE_PER_TFAW) {
-        m_profiler_ptr->profileMemTfawBusy();
-        return false;
-    }
-
-    bool write = !m_bankQueues[bank].front().m_is_mem_read;
-    if (write && (m_busBusyCounter_Write > 0)) {
-        m_profiler_ptr->profileMemReadWriteBusy();
-        return false;
-    }
-
-    if (!write && (rank != m_busBusy_WhichRank)
-        && (m_busBusyCounter_ReadNewRank > 0)) {
-        m_profiler_ptr->profileMemDataBusBusy();
-        return false;
-    }
-
-    return true;
-}
-
-// issueRefresh checks to see if this bank has a refresh scheduled
-// and, if so, does the refresh and returns true
-bool
-MemoryControl::issueRefresh(int bank)
-{
-    if (!m_need_refresh || (m_refresh_bank != bank))
-        return false;
-    if (m_bankBusyCounter[bank] > 0)
-        return false;
-    // Note that m_busBusyCounter will prevent multiple issues during
-    // the same cycle, as well as on different but close cycles:
-    if (m_busBusyCounter_Basic > 0)
-        return false;
-    int rank = getRank(bank);
-    if (m_tfaw_count[rank] >= ACTIVATE_PER_TFAW)
-        return false;
-
-    // Issue it:
-    DPRINTF(RubyMemory, "Refresh bank %3x\n", bank);
-
-    m_profiler_ptr->profileMemRefresh();
-    m_need_refresh--;
-    m_refresh_bank++;
-    if (m_refresh_bank >= m_total_banks)
-        m_refresh_bank = 0;
-    m_bankBusyCounter[bank] = m_bank_busy_time;
-    m_busBusyCounter_Basic = m_basic_bus_busy_time;
-    m_busBusyCounter_Write = m_basic_bus_busy_time;
-    m_busBusyCounter_ReadNewRank = m_basic_bus_busy_time;
-    markTfaw(rank);
-    return true;
-}
-
-// Mark the activate in the tFaw shift register
-void
-MemoryControl::markTfaw(int rank)
-{
-    if (m_tFaw) {
-        m_tfaw_shift[rank] |= (1 << (m_tFaw-1));
-        m_tfaw_count[rank]++;
-    }
-}
-
-// Issue a memory request: Activate the bank, reserve the address and
-// data buses, and queue the request for return to the requesting
-// processor after a fixed latency.
-void
-MemoryControl::issueRequest(int bank)
-{
-    int rank = getRank(bank);
-    MemoryNode req = m_bankQueues[bank].front();
-    m_bankQueues[bank].pop_front();
-
-    DPRINTF(RubyMemory, "Mem issue request%7d: %#08x %c "
-            "bank=%3x sched %c\n", req.m_msg_counter, req.m_addr,
-            req.m_is_mem_read? 'R':'W',
-            bank, m_event.scheduled() ? 'Y':'N');
-
-    if (req.m_msgptr) {  // don't enqueue L3 writebacks
-        enqueueToDirectory(req, m_mem_ctl_latency + m_mem_fixed_delay);
-    }
-    m_oldRequest[bank] = 0;
-    markTfaw(rank);
-    m_bankBusyCounter[bank] = m_bank_busy_time;
-    m_busBusy_WhichRank = rank;
-    if (req.m_is_mem_read) {
-        m_profiler_ptr->profileMemRead();
-        m_busBusyCounter_Basic = m_basic_bus_busy_time;
-        m_busBusyCounter_Write = m_basic_bus_busy_time + m_read_write_delay;
-        m_busBusyCounter_ReadNewRank =
-            m_basic_bus_busy_time + m_rank_rank_delay;
-    } else {
-        m_profiler_ptr->profileMemWrite();
-        m_busBusyCounter_Basic = m_basic_bus_busy_time;
-        m_busBusyCounter_Write = m_basic_bus_busy_time;
-        m_busBusyCounter_ReadNewRank = m_basic_bus_busy_time;
-    }
-}
-
-// executeCycle:  This function is called once per memory clock cycle
-// to simulate all the periodic hardware.
-void
-MemoryControl::executeCycle()
-{
-    // Keep track of time by counting down the busy counters:
-    for (int bank=0; bank < m_total_banks; bank++) {
-        if (m_bankBusyCounter[bank] > 0) m_bankBusyCounter[bank]--;
-    }
-    if (m_busBusyCounter_Write > 0)
-        m_busBusyCounter_Write--;
-    if (m_busBusyCounter_ReadNewRank > 0)
-        m_busBusyCounter_ReadNewRank--;
-    if (m_busBusyCounter_Basic > 0)
-        m_busBusyCounter_Basic--;
-
-    // Count down the tFAW shift registers:
-    for (int rank=0; rank < m_total_ranks; rank++) {
-        if (m_tfaw_shift[rank] & 1) m_tfaw_count[rank]--;
-        m_tfaw_shift[rank] >>= 1;
-    }
-
-    // After time period expires, latch an indication that we need a refresh.
-    // Disable refresh if in mem_fixed_delay mode.
-    if (!m_mem_fixed_delay) m_refresh_count--;
-    if (m_refresh_count == 0) {
-        m_refresh_count = m_refresh_period_system;
-
-        // Are we overrunning our ability to refresh?
-        assert(m_need_refresh < 10);
-        m_need_refresh++;
-    }
-
-    // If this batch of requests is all done, make a new batch:
-    m_ageCounter++;
-    int anyOld = 0;
-    for (int bank=0; bank < m_total_banks; bank++) {
-        anyOld |= m_oldRequest[bank];
-    }
-    if (!anyOld) {
-        for (int bank=0; bank < m_total_banks; bank++) {
-            if (!m_bankQueues[bank].empty()) m_oldRequest[bank] = 1;
-        }
-        m_ageCounter = 0;
-    }
-
-    // If randomness desired, re-randomize round-robin position each cycle
-    if (m_mem_random_arbitrate) {
-        m_roundRobin = random() % m_total_banks;
-    }
-
-    // For each channel, scan round-robin, and pick an old, ready
-    // request and issue it.  Treat a refresh request as if it were at
-    // the head of its bank queue.  After we issue something, keep
-    // scanning the queues just to gather statistics about how many
-    // are waiting.  If in mem_fixed_delay mode, we can issue more
-    // than one request per cycle.
-    int queueHeads = 0;
-    int banksIssued = 0;
-    for (int i = 0; i < m_total_banks; i++) {
-        m_roundRobin++;
-        if (m_roundRobin >= m_total_banks) m_roundRobin = 0;
-        issueRefresh(m_roundRobin);
-        int qs = m_bankQueues[m_roundRobin].size();
-        if (qs > 1) {
-            m_profiler_ptr->profileMemBankQ(qs-1);
-        }
-        if (qs > 0) {
-            // we're not idle if anything is queued
-            m_idleCount = IDLECOUNT_MAX_VALUE;
-            queueHeads++;
-            if (queueReady(m_roundRobin)) {
-                issueRequest(m_roundRobin);
-                banksIssued++;
-                if (m_mem_fixed_delay) {
-                    m_profiler_ptr->profileMemWaitCycles(m_mem_fixed_delay);
-                }
-            }
-        }
-    }
-
-    // memWaitCycles is a redundant catch-all for the specific
-    // counters in queueReady
-    m_profiler_ptr->profileMemWaitCycles(queueHeads - banksIssued);
-
-    // Check input queue and move anything to bank queues if not full.
-    // Since this is done here at the end of the cycle, there will
-    // always be at least one cycle of latency in the bank queue.  We
-    // deliberately move at most one request per cycle (to simulate
-    // typical hardware).  Note that if one bank queue fills up, other
-    // requests can get stuck behind it here.
-    if (!m_input_queue.empty()) {
-        // we're not idle if anything is pending
-        m_idleCount = IDLECOUNT_MAX_VALUE;
-        MemoryNode req = m_input_queue.front();
-        int bank = getBank(req.m_addr);
-        if (m_bankQueues[bank].size() < m_bank_queue_size) {
-            m_input_queue.pop_front();
-            m_bankQueues[bank].push_back(req);
-        }
-        m_profiler_ptr->profileMemInputQ(m_input_queue.size());
-    }
-}
-
-unsigned int
-MemoryControl::drain(Event *de)
-{
-    DPRINTF(RubyMemory, "MemoryController drain\n");
-    if(m_event.scheduled()) {
-        deschedule(m_event);
-    }
-    return 0;
-}
-
-// wakeup:  This function is called once per memory controller clock cycle.
-void
-MemoryControl::wakeup()
-{
-    DPRINTF(RubyMemory, "MemoryController wakeup\n");
-    // execute everything
-    executeCycle();
-
-    m_idleCount--;
-    if (m_idleCount > 0) {
-        assert(!m_event.scheduled());
-        schedule(m_event, curTick() + m_mem_bus_cycle_multiplier);
-    }
-}
-
-MemoryControl *
+RubyMemoryControl *
 RubyMemoryControlParams::create()
 {
-    return new MemoryControl(this);
+    return new RubyMemoryControl(this);
 }
 
+
index 48ce8a8e014019cc34163c02a89fb70b7c7dd9da..eb3de8aef6316a987af2246ebba5478c30da0ed3 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * Copyright (c) 2012 Advanced Micro Devices, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,8 +27,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef __MEM_RUBY_SYSTEM_MEMORY_CONTROL_HH__
-#define __MEM_RUBY_SYSTEM_MEMORY_CONTROL_HH__
+#ifndef __MEM_RUBY_SYSTEM_ABSTRACT_MEMORY_CONTROL_HH__
+#define __MEM_RUBY_SYSTEM_ABSTRACT_MEMORY_CONTROL_HH__
 
 #include <iostream>
 #include <list>
 #include "mem/ruby/slicc_interface/Message.hh"
 #include "mem/ruby/system/AbstractMemOrCache.hh"
 #include "mem/ruby/system/MemoryNode.hh"
-#include "params/RubyMemoryControl.hh"
+#include "mem/ruby/system/System.hh"
 #include "sim/sim_object.hh"
 
-// This constant is part of the definition of tFAW; see
-// the comments in header to MemoryControl.cc
-#define ACTIVATE_PER_TFAW 4
-
 //////////////////////////////////////////////////////////////////////////////
 
 class Consumer;
@@ -54,45 +51,50 @@ class MemoryControl :
     public SimObject, public Consumer, public AbstractMemOrCache
 {
   public:
-
-    typedef RubyMemoryControlParams Params;
     MemoryControl(const Params *p);
-    void init();
+    virtual void init() = 0;
 
     ~MemoryControl();
 
-    unsigned int drain(Event *de);
+    unsigned int drain(Event *de) = 0;
 
-    void wakeup();
+    virtual void wakeup() = 0;
 
-    void setConsumer(Consumer* consumer_ptr);
-    Consumer* getConsumer() { return m_consumer_ptr; };
-    void setDescription(const std::string& name) { m_description = name; };
-    std::string getDescription() { return m_description; };
+    virtual void setConsumer(Consumer* consumer_ptr) = 0;
+    virtual Consumer* getConsumer() = 0;
+    virtual void setDescription(const std::string& name) = 0;
+    virtual std::string getDescription() = 0;
 
     // Called from the directory:
-    void enqueue(const MsgPtr& message, int latency );
-    void enqueueMemRef(MemoryNode& memRef);
-    void dequeue();
-    const Message* peek();
-    MemoryNode peekNode();
-    bool isReady();
-    bool areNSlotsAvailable(int n) { return true; };  // infinite queue length
+    virtual void enqueue(const MsgPtr& message, int latency ) = 0;
+    virtual void enqueueMemRef(MemoryNode& memRef) = 0;
+    virtual void dequeue() = 0;
+    virtual const Message* peek() = 0;
+    virtual MemoryNode peekNode() = 0;
+    virtual bool isReady() = 0;
+    virtual bool areNSlotsAvailable(int n) = 0;  // infinite queue length
 
     //// Called from L3 cache:
     //void writeBack(physical_address_t addr);
 
-    void printConfig(std::ostream& out);
-    void print(std::ostream& out) const;
-    void clearStats() const;
-    void printStats(std::ostream& out) const;
+    virtual void printConfig(std::ostream& out) = 0;
+    virtual void print(std::ostream& out) const = 0;
+    virtual void clearStats() const = 0;
+    virtual void printStats(std::ostream& out) const = 0;
+
+    virtual void regStats() {};
+
+    virtual const int getChannel(const physical_address_t addr) const = 0;
+    virtual const int getBank(const physical_address_t addr) const = 0;
+    virtual const int getRank(const physical_address_t addr) const = 0;
+    virtual const int getRow(const physical_address_t addr) const = 0;
 
     //added by SS
-    int getBanksPerRank() { return m_banks_per_rank; };
-    int getRanksPerDimm() { return m_ranks_per_dimm; };
-    int getDimmsPerChannel() { return m_dimms_per_channel; }
+    virtual int getBanksPerRank() = 0;
+    virtual int getRanksPerDimm() = 0;
+    virtual int getDimmsPerChannel() = 0;
 
-  private:
+protected:
     class MemCntrlEvent : public Event
     {
       public:
@@ -106,76 +108,7 @@ class MemoryControl :
         MemoryControl* mem_cntrl;
     };
 
-    void enqueueToDirectory(MemoryNode req, int latency);
-    int getBank(physical_address_t addr);
-    int getRank(int bank);
-    bool queueReady(int bank);
-    void issueRequest(int bank);
-    bool issueRefresh(int bank);
-    void markTfaw(int rank);
-    void executeCycle();
-
-    // Private copy constructor and assignment operator
-    MemoryControl (const MemoryControl& obj);
-    MemoryControl& operator=(const MemoryControl& obj);
-
-    // data members
-    Consumer* m_consumer_ptr;  // Consumer to signal a wakeup()
-    std::string m_description;
-    int m_msg_counter;
-
-    int m_mem_bus_cycle_multiplier;
-    int m_banks_per_rank;
-    int m_ranks_per_dimm;
-    int m_dimms_per_channel;
-    int m_bank_bit_0;
-    int m_rank_bit_0;
-    int m_dimm_bit_0;
-    unsigned int m_bank_queue_size;
-    int m_bank_busy_time;
-    int m_rank_rank_delay;
-    int m_read_write_delay;
-    int m_basic_bus_busy_time;
-    int m_mem_ctl_latency;
-    int m_refresh_period;
-    int m_mem_random_arbitrate;
-    int m_tFaw;
-    int m_mem_fixed_delay;
-
-    int m_total_banks;
-    int m_total_ranks;
-    int m_refresh_period_system;
-
-    // queues where memory requests live
-    std::list<MemoryNode> m_response_queue;
-    std::list<MemoryNode> m_input_queue;
-    std::list<MemoryNode>* m_bankQueues;
-
-    // Each entry indicates number of address-bus cycles until bank
-    // is reschedulable:
-    int* m_bankBusyCounter;
-    int* m_oldRequest;
-
-    uint64* m_tfaw_shift;
-    int* m_tfaw_count;
-
-    // Each of these indicates number of address-bus cycles until
-    // we can issue a new request of the corresponding type:
-    int m_busBusyCounter_Write;
-    int m_busBusyCounter_ReadNewRank;
-    int m_busBusyCounter_Basic;
-
-    int m_busBusy_WhichRank;  // which rank last granted
-    int m_roundRobin;         // which bank queue was last granted
-    int m_refresh_count;      // cycles until next refresh
-    int m_need_refresh;       // set whenever m_refresh_count goes to zero
-    int m_refresh_bank;       // which bank to refresh next
-    int m_ageCounter;         // age of old requests; to detect starvation
-    int m_idleCount;          // watchdog timer for shutting down
-
-    MemCntrlProfiler* m_profiler_ptr;
-
     MemCntrlEvent m_event;
 };
 
-#endif // __MEM_RUBY_SYSTEM_MEMORY_CONTROL_HH__
+#endif //   __MEM_RUBY_SYSTEM_ABSTRACT_MEMORY_CONTROL_HH__
index 8144e70fc3b576b3b1a00310adf81d130b096e1c..dafd0a789d4a8f16d4386ebe8cf22e1ecca23d06 100644 (file)
 from m5.params import *
 from m5.SimObject import SimObject
 
-class RubyMemoryControl(SimObject):
-    type = 'RubyMemoryControl'
+class MemoryControl(SimObject):
+    abstract = True
+    type = 'MemoryControl'
     cxx_class = 'MemoryControl'
     version = Param.Int("");
+
     mem_bus_cycle_multiplier = Param.Int(10, "");
-    banks_per_rank = Param.Int(8, "");
-    ranks_per_dimm = Param.Int(2, "");
-    dimms_per_channel = Param.Int(2, "");
-    bank_bit_0 = Param.Int(8, "");
-    rank_bit_0 = Param.Int(11, "");
-    dimm_bit_0 = Param.Int(12, "");
-    bank_queue_size = Param.Int(12, "");
-    bank_busy_time = Param.Int(11, "");
-    rank_rank_delay = Param.Int(1, "");
-    read_write_delay = Param.Int(2, "");
-    basic_bus_busy_time = Param.Int(2, "");
-    mem_ctl_latency = Param.Int(12, "");
-    refresh_period = Param.Int(1560, "");
-    tFaw = Param.Int(0, "");
-    mem_random_arbitrate = Param.Int(0, "");
-    mem_fixed_delay = Param.Int(0, "");
diff --git a/src/mem/ruby/system/RubyMemoryControl.cc b/src/mem/ruby/system/RubyMemoryControl.cc
new file mode 100644 (file)
index 0000000..e777762
--- /dev/null
@@ -0,0 +1,698 @@
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * Copyright (c) 2012 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+/*
+ * Description:  This module simulates a basic DDR-style memory controller
+ * (and can easily be extended to do FB-DIMM as well).
+ *
+ * This module models a single channel, connected to any number of
+ * DIMMs with any number of ranks of DRAMs each.  If you want multiple
+ * address/data channels, you need to instantiate multiple copies of
+ * this module.
+ *
+ * Each memory request is placed in a queue associated with a specific
+ * memory bank.  This queue is of finite size; if the queue is full
+ * the request will back up in an (infinite) common queue and will
+ * effectively throttle the whole system.  This sort of behavior is
+ * intended to be closer to real system behavior than if we had an
+ * infinite queue on each bank.  If you want the latter, just make
+ * the bank queues unreasonably large.
+ *
+ * The head item on a bank queue is issued when all of the
+ * following are true:
+ *   the bank is available
+ *   the address path to the DIMM is available
+ *   the data path to or from the DIMM is available
+ *
+ * Note that we are not concerned about fixed offsets in time.  The bank
+ * will not be used at the same moment as the address path, but since
+ * there is no queue in the DIMM or the DRAM it will be used at a constant
+ * number of cycles later, so it is treated as if it is used at the same
+ * time.
+ *
+ * We are assuming closed bank policy; that is, we automatically close
+ * each bank after a single read or write.  Adding an option for open
+ * bank policy is for future work.
+ *
+ * We are assuming "posted CAS"; that is, we send the READ or WRITE
+ * immediately after the ACTIVATE.  This makes scheduling the address
+ * bus trivial; we always schedule a fixed set of cycles.  For DDR-400,
+ * this is a set of two cycles; for some configurations such as
+ * DDR-800 the parameter tRRD forces this to be set to three cycles.
+ *
+ * We assume a four-bit-time transfer on the data wires.  This is
+ * the minimum burst length for DDR-2.  This would correspond
+ * to (for example) a memory where each DIMM is 72 bits wide
+ * and DIMMs are ganged in pairs to deliver 64 bytes at a shot.
+ * This gives us the same occupancy on the data wires as on the
+ * address wires (for the two-address-cycle case).
+ *
+ * The only non-trivial scheduling problem is the data wires.
+ * A write will use the wires earlier in the operation than a read
+ * will; typically one cycle earlier as seen at the DRAM, but earlier
+ * by a worst-case round-trip wire delay when seen at the memory controller.
+ * So, while reads from one rank can be scheduled back-to-back
+ * every two cycles, and writes (to any rank) scheduled every two cycles,
+ * when a read is followed by a write we need to insert a bubble.
+ * Furthermore, consecutive reads from two different ranks may need
+ * to insert a bubble due to skew between when one DRAM stops driving the
+ * wires and when the other one starts.  (These bubbles are parameters.)
+ *
+ * This means that when some number of reads and writes are at the
+ * heads of their queues, reads could starve writes, and/or reads
+ * to the same rank could starve out other requests, since the others
+ * would never see the data bus ready.
+ * For this reason, we have implemented an anti-starvation feature.
+ * A group of requests is marked "old", and a counter is incremented
+ * each cycle as long as any request from that batch has not issued.
+ * if the counter reaches twice the bank busy time, we hold off any
+ * newer requests until all of the "old" requests have issued.
+ *
+ * We also model tFAW.  This is an obscure DRAM parameter that says
+ * that no more than four activate requests can happen within a window
+ * of a certain size.  For most configurations this does not come into play,
+ * or has very little effect, but it could be used to throttle the power
+ * consumption of the DRAM.  In this implementation (unlike in a DRAM
+ * data sheet) TFAW is measured in memory bus cycles; i.e. if TFAW = 16
+ * then no more than four activates may happen within any 16 cycle window.
+ * Refreshes are included in the activates.
+ *
+ */
+
+#include "base/cast.hh"
+#include "base/cprintf.hh"
+#include "mem/ruby/common/Address.hh"
+#include "mem/ruby/common/Consumer.hh"
+#include "mem/ruby/common/Global.hh"
+#include "mem/ruby/network/Network.hh"
+#include "mem/ruby/profiler/Profiler.hh"
+#include "mem/ruby/slicc_interface/NetworkMessage.hh"
+#include "mem/ruby/slicc_interface/RubySlicc_ComponentMapping.hh"
+#include "mem/ruby/system/RubyMemoryControl.hh"
+#include "mem/ruby/system/System.hh"
+
+using namespace std;
+
+class Consumer;
+
+// Value to reset watchdog timer to.
+// If we're idle for this many memory control cycles,
+// shut down our clock (our rescheduling of ourselves).
+// Refresh shuts down as well.
+// When we restart, we'll be in a different phase
+// with respect to ruby cycles, so this introduces
+// a slight inaccuracy.  But it is necessary or the
+// ruby tester never terminates because the event
+// queue is never empty.
+#define IDLECOUNT_MAX_VALUE 1000
+
+// Output operator definition
+
+ostream&
+operator<<(ostream& out, const RubyMemoryControl& obj)
+{
+    obj.print(out);
+    out << flush;
+    return out;
+}
+
+
+// ****************************************************************
+
+// CONSTRUCTOR
+RubyMemoryControl::RubyMemoryControl(const Params *p)
+    : MemoryControl(p)
+{
+    m_mem_bus_cycle_multiplier = p->mem_bus_cycle_multiplier;
+    m_banks_per_rank = p->banks_per_rank;
+    m_ranks_per_dimm = p->ranks_per_dimm;
+    m_dimms_per_channel = p->dimms_per_channel;
+    m_bank_bit_0 = p->bank_bit_0;
+    m_rank_bit_0 = p->rank_bit_0;
+    m_dimm_bit_0 = p->dimm_bit_0;
+    m_bank_queue_size = p->bank_queue_size;
+    m_bank_busy_time = p->bank_busy_time;
+    m_rank_rank_delay = p->rank_rank_delay;
+    m_read_write_delay = p->read_write_delay;
+    m_basic_bus_busy_time = p->basic_bus_busy_time;
+    m_mem_ctl_latency = p->mem_ctl_latency;
+    m_refresh_period = p->refresh_period;
+    m_tFaw = p->tFaw;
+    m_mem_random_arbitrate = p->mem_random_arbitrate;
+    m_mem_fixed_delay = p->mem_fixed_delay;
+
+    m_profiler_ptr = new MemCntrlProfiler(name(),
+                                          m_banks_per_rank,
+                                          m_ranks_per_dimm,
+                                          m_dimms_per_channel);
+}
+
+void
+RubyMemoryControl::init()
+{
+    m_msg_counter = 0;
+
+    assert(m_tFaw <= 62); // must fit in a uint64 shift register
+
+    m_total_banks = m_banks_per_rank * m_ranks_per_dimm * m_dimms_per_channel;
+    m_total_ranks = m_ranks_per_dimm * m_dimms_per_channel;
+    m_refresh_period_system = m_refresh_period / m_total_banks;
+
+    m_bankQueues = new list<MemoryNode> [m_total_banks];
+    assert(m_bankQueues);
+
+    m_bankBusyCounter = new int [m_total_banks];
+    assert(m_bankBusyCounter);
+
+    m_oldRequest = new int [m_total_banks];
+    assert(m_oldRequest);
+
+    for (int i = 0; i < m_total_banks; i++) {
+        m_bankBusyCounter[i] = 0;
+        m_oldRequest[i] = 0;
+    }
+
+    m_busBusyCounter_Basic = 0;
+    m_busBusyCounter_Write = 0;
+    m_busBusyCounter_ReadNewRank = 0;
+    m_busBusy_WhichRank = 0;
+
+    m_roundRobin = 0;
+    m_refresh_count = 1;
+    m_need_refresh = 0;
+    m_refresh_bank = 0;
+    m_idleCount = 0;
+    m_ageCounter = 0;
+
+    // Each tfaw shift register keeps a moving bit pattern
+    // which shows when recent activates have occurred.
+    // m_tfaw_count keeps track of how many 1 bits are set
+    // in each shift register.  When m_tfaw_count is >= 4,
+    // new activates are not allowed.
+    m_tfaw_shift = new uint64[m_total_ranks];
+    m_tfaw_count = new int[m_total_ranks];
+    for (int i = 0; i < m_total_ranks; i++) {
+        m_tfaw_shift[i] = 0;
+        m_tfaw_count[i] = 0;
+    }
+}
+
+RubyMemoryControl::~RubyMemoryControl()
+{
+    delete [] m_bankQueues;
+    delete [] m_bankBusyCounter;
+    delete [] m_oldRequest;
+    delete m_profiler_ptr;
+}
+
+// enqueue new request from directory
+void
+RubyMemoryControl::enqueue(const MsgPtr& message, int latency)
+{
+    Time current_time = g_eventQueue_ptr->getTime();
+    Time arrival_time = current_time + latency;
+    const MemoryMsg* memMess = safe_cast<const MemoryMsg*>(message.get());
+    physical_address_t addr = memMess->getAddress().getAddress();
+    MemoryRequestType type = memMess->getType();
+    bool is_mem_read = (type == MemoryRequestType_MEMORY_READ);
+    MemoryNode thisReq(arrival_time, message, addr, is_mem_read, !is_mem_read);
+    enqueueMemRef(thisReq);
+}
+
+// Alternate entry point used when we already have a MemoryNode
+// structure built.
+void
+RubyMemoryControl::enqueueMemRef(MemoryNode& memRef)
+{
+    m_msg_counter++;
+    memRef.m_msg_counter = m_msg_counter;
+    physical_address_t addr = memRef.m_addr;
+    int bank = getBank(addr);
+
+    DPRINTF(RubyMemory,
+            "New memory request%7d: %#08x %c arrived at %10d bank = %3x sched %c\n",
+            m_msg_counter, addr, memRef.m_is_mem_read ? 'R':'W',
+            memRef.m_time * g_eventQueue_ptr->getClock(),
+            bank, m_event.scheduled() ? 'Y':'N');
+
+    m_profiler_ptr->profileMemReq(bank);
+    m_input_queue.push_back(memRef);
+
+    if (!m_event.scheduled()) {
+        schedule(m_event, curTick() + 1);
+    }
+}
+
+// dequeue, peek, and isReady are used to transfer completed requests
+// back to the directory
+void
+RubyMemoryControl::dequeue()
+{
+    assert(isReady());
+    m_response_queue.pop_front();
+}
+
+const Message*
+RubyMemoryControl::peek()
+{
+    MemoryNode node = peekNode();
+    Message* msg_ptr = node.m_msgptr.get();
+    assert(msg_ptr != NULL);
+    return msg_ptr;
+}
+
+MemoryNode
+RubyMemoryControl::peekNode()
+{
+    assert(isReady());
+    MemoryNode req = m_response_queue.front();
+    DPRINTF(RubyMemory, "Peek: memory request%7d: %#08x %c sched %c\n",
+            req.m_msg_counter, req.m_addr, req.m_is_mem_read ? 'R':'W',
+            m_event.scheduled() ? 'Y':'N');
+
+    return req;
+}
+
+bool
+RubyMemoryControl::isReady()
+{
+    return ((!m_response_queue.empty()) &&
+            (m_response_queue.front().m_time <= g_eventQueue_ptr->getTime()));
+}
+
+void
+RubyMemoryControl::setConsumer(Consumer* consumer_ptr)
+{
+    m_consumer_ptr = consumer_ptr;
+}
+
+void
+RubyMemoryControl::print(ostream& out) const
+{
+}
+
+void
+RubyMemoryControl::printConfig(ostream& out)
+{
+    out << "Memory Control " << name() << ":" << endl;
+    out << "  Ruby cycles per memory cycle: " << m_mem_bus_cycle_multiplier
+        << endl;
+    out << "  Basic read latency: " << m_mem_ctl_latency << endl;
+    if (m_mem_fixed_delay) {
+        out << "  Fixed Latency mode:  Added cycles = " << m_mem_fixed_delay
+            << endl;
+    } else {
+        out << "  Bank busy time: " << m_bank_busy_time << " memory cycles"
+            << endl;
+        out << "  Memory channel busy time: " << m_basic_bus_busy_time << endl;
+        out << "  Dead cycles between reads to different ranks: "
+            << m_rank_rank_delay << endl;
+        out << "  Dead cycle between a read and a write: "
+            << m_read_write_delay << endl;
+        out << "  tFaw (four-activate) window: " << m_tFaw << endl;
+    }
+    out << "  Banks per rank: " << m_banks_per_rank << endl;
+    out << "  Ranks per DIMM: " << m_ranks_per_dimm << endl;
+    out << "  DIMMs per channel:  " << m_dimms_per_channel << endl;
+    out << "  LSB of bank field in address: " << m_bank_bit_0 << endl;
+    out << "  LSB of rank field in address: " << m_rank_bit_0 << endl;
+    out << "  LSB of DIMM field in address: " << m_dimm_bit_0 << endl;
+    out << "  Max size of each bank queue: " << m_bank_queue_size << endl;
+    out << "  Refresh period (within one bank): " << m_refresh_period << endl;
+    out << "  Arbitration randomness: " << m_mem_random_arbitrate << endl;
+}
+
+void
+RubyMemoryControl::clearStats() const
+{
+    m_profiler_ptr->clearStats();
+}
+
+void
+RubyMemoryControl::printStats(ostream& out) const
+{
+    m_profiler_ptr->printStats(out);
+}
+
+// Queue up a completed request to send back to directory
+void
+RubyMemoryControl::enqueueToDirectory(MemoryNode req, int latency)
+{
+    Time arrival_time = g_eventQueue_ptr->getTime()
+        + (latency * m_mem_bus_cycle_multiplier);
+    req.m_time = arrival_time;
+    m_response_queue.push_back(req);
+
+    DPRINTF(RubyMemory, "Enqueueing msg %#08x %c back to directory at %15d\n",
+            req.m_addr, req.m_is_mem_read ? 'R':'W',
+            arrival_time * g_eventQueue_ptr->getClock());
+
+    // schedule the wake up
+    g_eventQueue_ptr->scheduleEventAbsolute(m_consumer_ptr, arrival_time);
+}
+
+// getBank returns an integer that is unique for each
+// bank across this memory controller.
+const int
+RubyMemoryControl::getBank(const physical_address_t addr) const
+{
+    int dimm = (addr >> m_dimm_bit_0) & (m_dimms_per_channel - 1);
+    int rank = (addr >> m_rank_bit_0) & (m_ranks_per_dimm - 1);
+    int bank = (addr >> m_bank_bit_0) & (m_banks_per_rank - 1);
+    return (dimm * m_ranks_per_dimm * m_banks_per_rank)
+        + (rank * m_banks_per_rank)
+        + bank;
+}
+
+const int
+RubyMemoryControl::getRank(const physical_address_t addr) const
+{
+    int bank = getBank(addr);
+    int rank = (bank / m_banks_per_rank);
+    assert (rank < (m_ranks_per_dimm * m_dimms_per_channel));
+    return rank;
+}
+
+// getRank returns an integer that is unique for each rank
+// and independent of individual bank.
+const int
+RubyMemoryControl::getRank(int bank) const
+{
+    int rank = (bank / m_banks_per_rank);
+    assert (rank < (m_ranks_per_dimm * m_dimms_per_channel));
+    return rank;
+}
+
+// Not used!
+const int
+RubyMemoryControl::getChannel(const physical_address_t addr) const
+{
+    assert(false);
+    return -1;
+}
+
+// Not used!
+const int
+RubyMemoryControl::getRow(const physical_address_t addr) const
+{
+    assert(false);
+    return -1;
+}
+
+// queueReady determines if the head item in a bank queue
+// can be issued this cycle
+bool
+RubyMemoryControl::queueReady(int bank)
+{
+    if ((m_bankBusyCounter[bank] > 0) && !m_mem_fixed_delay) {
+        m_profiler_ptr->profileMemBankBusy();
+
+        DPRINTF(RubyMemory, "bank %x busy %d\n", bank, m_bankBusyCounter[bank]);
+        return false;
+    }
+
+    if (m_mem_random_arbitrate >= 2) {
+        if ((random() % 100) < m_mem_random_arbitrate) {
+            m_profiler_ptr->profileMemRandBusy();
+            return false;
+        }
+    }
+
+    if (m_mem_fixed_delay)
+        return true;
+
+    if ((m_ageCounter > (2 * m_bank_busy_time)) && !m_oldRequest[bank]) {
+        m_profiler_ptr->profileMemNotOld();
+        return false;
+    }
+
+    if (m_busBusyCounter_Basic == m_basic_bus_busy_time) {
+        // Another bank must have issued this same cycle.  For
+        // profiling, we count this as an arb wait rather than a bus
+        // wait.  This is a little inaccurate since it MIGHT have also
+        // been blocked waiting for a read-write or a read-read
+        // instead, but it's pretty close.
+        m_profiler_ptr->profileMemArbWait(1);
+        return false;
+    }
+
+    if (m_busBusyCounter_Basic > 0) {
+        m_profiler_ptr->profileMemBusBusy();
+        return false;
+    }
+
+    int rank = getRank(bank);
+    if (m_tfaw_count[rank] >= ACTIVATE_PER_TFAW) {
+        m_profiler_ptr->profileMemTfawBusy();
+        return false;
+    }
+
+    bool write = !m_bankQueues[bank].front().m_is_mem_read;
+    if (write && (m_busBusyCounter_Write > 0)) {
+        m_profiler_ptr->profileMemReadWriteBusy();
+        return false;
+    }
+
+    if (!write && (rank != m_busBusy_WhichRank)
+        && (m_busBusyCounter_ReadNewRank > 0)) {
+        m_profiler_ptr->profileMemDataBusBusy();
+        return false;
+    }
+
+    return true;
+}
+
+// issueRefresh checks to see if this bank has a refresh scheduled
+// and, if so, does the refresh and returns true
+bool
+RubyMemoryControl::issueRefresh(int bank)
+{
+    if (!m_need_refresh || (m_refresh_bank != bank))
+        return false;
+    if (m_bankBusyCounter[bank] > 0)
+        return false;
+    // Note that m_busBusyCounter will prevent multiple issues during
+    // the same cycle, as well as on different but close cycles:
+    if (m_busBusyCounter_Basic > 0)
+        return false;
+    int rank = getRank(bank);
+    if (m_tfaw_count[rank] >= ACTIVATE_PER_TFAW)
+        return false;
+
+    // Issue it:
+    DPRINTF(RubyMemory, "Refresh bank %3x\n", bank);
+
+    m_profiler_ptr->profileMemRefresh();
+    m_need_refresh--;
+    m_refresh_bank++;
+    if (m_refresh_bank >= m_total_banks)
+        m_refresh_bank = 0;
+    m_bankBusyCounter[bank] = m_bank_busy_time;
+    m_busBusyCounter_Basic = m_basic_bus_busy_time;
+    m_busBusyCounter_Write = m_basic_bus_busy_time;
+    m_busBusyCounter_ReadNewRank = m_basic_bus_busy_time;
+    markTfaw(rank);
+    return true;
+}
+
+// Mark the activate in the tFaw shift register
+void
+RubyMemoryControl::markTfaw(int rank)
+{
+    if (m_tFaw) {
+        m_tfaw_shift[rank] |= (1 << (m_tFaw-1));
+        m_tfaw_count[rank]++;
+    }
+}
+
+// Issue a memory request: Activate the bank, reserve the address and
+// data buses, and queue the request for return to the requesting
+// processor after a fixed latency.
+void
+RubyMemoryControl::issueRequest(int bank)
+{
+    int rank = getRank(bank);
+    MemoryNode req = m_bankQueues[bank].front();
+    m_bankQueues[bank].pop_front();
+
+    DPRINTF(RubyMemory, "Mem issue request%7d: %#08x %c "
+            "bank=%3x sched %c\n", req.m_msg_counter, req.m_addr,
+            req.m_is_mem_read? 'R':'W',
+            bank, m_event.scheduled() ? 'Y':'N');
+
+    if (req.m_msgptr) {  // don't enqueue L3 writebacks
+        enqueueToDirectory(req, m_mem_ctl_latency + m_mem_fixed_delay);
+    }
+    m_oldRequest[bank] = 0;
+    markTfaw(rank);
+    m_bankBusyCounter[bank] = m_bank_busy_time;
+    m_busBusy_WhichRank = rank;
+    if (req.m_is_mem_read) {
+        m_profiler_ptr->profileMemRead();
+        m_busBusyCounter_Basic = m_basic_bus_busy_time;
+        m_busBusyCounter_Write = m_basic_bus_busy_time + m_read_write_delay;
+        m_busBusyCounter_ReadNewRank =
+            m_basic_bus_busy_time + m_rank_rank_delay;
+    } else {
+        m_profiler_ptr->profileMemWrite();
+        m_busBusyCounter_Basic = m_basic_bus_busy_time;
+        m_busBusyCounter_Write = m_basic_bus_busy_time;
+        m_busBusyCounter_ReadNewRank = m_basic_bus_busy_time;
+    }
+}
+
+// executeCycle:  This function is called once per memory clock cycle
+// to simulate all the periodic hardware.
+void
+RubyMemoryControl::executeCycle()
+{
+    // Keep track of time by counting down the busy counters:
+    for (int bank=0; bank < m_total_banks; bank++) {
+        if (m_bankBusyCounter[bank] > 0) m_bankBusyCounter[bank]--;
+    }
+    if (m_busBusyCounter_Write > 0)
+        m_busBusyCounter_Write--;
+    if (m_busBusyCounter_ReadNewRank > 0)
+        m_busBusyCounter_ReadNewRank--;
+    if (m_busBusyCounter_Basic > 0)
+        m_busBusyCounter_Basic--;
+
+    // Count down the tFAW shift registers:
+    for (int rank=0; rank < m_total_ranks; rank++) {
+        if (m_tfaw_shift[rank] & 1) m_tfaw_count[rank]--;
+        m_tfaw_shift[rank] >>= 1;
+    }
+
+    // After time period expires, latch an indication that we need a refresh.
+    // Disable refresh if in mem_fixed_delay mode.
+    if (!m_mem_fixed_delay) m_refresh_count--;
+    if (m_refresh_count == 0) {
+        m_refresh_count = m_refresh_period_system;
+
+        // Are we overrunning our ability to refresh?
+        assert(m_need_refresh < 10);
+        m_need_refresh++;
+    }
+
+    // If this batch of requests is all done, make a new batch:
+    m_ageCounter++;
+    int anyOld = 0;
+    for (int bank=0; bank < m_total_banks; bank++) {
+        anyOld |= m_oldRequest[bank];
+    }
+    if (!anyOld) {
+        for (int bank=0; bank < m_total_banks; bank++) {
+            if (!m_bankQueues[bank].empty()) m_oldRequest[bank] = 1;
+        }
+        m_ageCounter = 0;
+    }
+
+    // If randomness desired, re-randomize round-robin position each cycle
+    if (m_mem_random_arbitrate) {
+        m_roundRobin = random() % m_total_banks;
+    }
+
+    // For each channel, scan round-robin, and pick an old, ready
+    // request and issue it.  Treat a refresh request as if it were at
+    // the head of its bank queue.  After we issue something, keep
+    // scanning the queues just to gather statistics about how many
+    // are waiting.  If in mem_fixed_delay mode, we can issue more
+    // than one request per cycle.
+    int queueHeads = 0;
+    int banksIssued = 0;
+    for (int i = 0; i < m_total_banks; i++) {
+        m_roundRobin++;
+        if (m_roundRobin >= m_total_banks) m_roundRobin = 0;
+        issueRefresh(m_roundRobin);
+        int qs = m_bankQueues[m_roundRobin].size();
+        if (qs > 1) {
+            m_profiler_ptr->profileMemBankQ(qs-1);
+        }
+        if (qs > 0) {
+            // we're not idle if anything is queued
+            m_idleCount = IDLECOUNT_MAX_VALUE;
+            queueHeads++;
+            if (queueReady(m_roundRobin)) {
+                issueRequest(m_roundRobin);
+                banksIssued++;
+                if (m_mem_fixed_delay) {
+                    m_profiler_ptr->profileMemWaitCycles(m_mem_fixed_delay);
+                }
+            }
+        }
+    }
+
+    // memWaitCycles is a redundant catch-all for the specific
+    // counters in queueReady
+    m_profiler_ptr->profileMemWaitCycles(queueHeads - banksIssued);
+
+    // Check input queue and move anything to bank queues if not full.
+    // Since this is done here at the end of the cycle, there will
+    // always be at least one cycle of latency in the bank queue.  We
+    // deliberately move at most one request per cycle (to simulate
+    // typical hardware).  Note that if one bank queue fills up, other
+    // requests can get stuck behind it here.
+    if (!m_input_queue.empty()) {
+        // we're not idle if anything is pending
+        m_idleCount = IDLECOUNT_MAX_VALUE;
+        MemoryNode req = m_input_queue.front();
+        int bank = getBank(req.m_addr);
+        if (m_bankQueues[bank].size() < m_bank_queue_size) {
+            m_input_queue.pop_front();
+            m_bankQueues[bank].push_back(req);
+        }
+        m_profiler_ptr->profileMemInputQ(m_input_queue.size());
+    }
+}
+
+unsigned int
+RubyMemoryControl::drain(Event *de)
+{
+    DPRINTF(RubyMemory, "MemoryController drain\n");
+    if(m_event.scheduled()) {
+        deschedule(m_event);
+    }
+    return 0;
+}
+
+// wakeup:  This function is called once per memory controller clock cycle.
+void
+RubyMemoryControl::wakeup()
+{
+    DPRINTF(RubyMemory, "MemoryController wakeup\n");
+    // execute everything
+    executeCycle();
+
+    m_idleCount--;
+    if (m_idleCount > 0) {
+        assert(!m_event.scheduled());
+        schedule(m_event, curTick() + m_mem_bus_cycle_multiplier);
+    }
+}
+
diff --git a/src/mem/ruby/system/RubyMemoryControl.hh b/src/mem/ruby/system/RubyMemoryControl.hh
new file mode 100644 (file)
index 0000000..2480865
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * Copyright (c) 2012 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef __MEM_RUBY_SYSTEM_MEMORY_CONTROL_HH__
+#define __MEM_RUBY_SYSTEM_MEMORY_CONTROL_HH__
+
+#include <iostream>
+#include <list>
+#include <string>
+
+#include "mem/protocol/MemoryMsg.hh"
+#include "mem/ruby/common/Address.hh"
+#include "mem/ruby/common/Consumer.hh"
+#include "mem/ruby/common/Global.hh"
+#include "mem/ruby/profiler/MemCntrlProfiler.hh"
+#include "mem/ruby/slicc_interface/Message.hh"
+#include "mem/ruby/system/AbstractMemOrCache.hh"
+#include "mem/ruby/system/MemoryControl.hh"
+#include "mem/ruby/system/MemoryNode.hh"
+#include "mem/ruby/system/System.hh"
+#include "params/RubyMemoryControl.hh"
+#include "sim/sim_object.hh"
+
+// This constant is part of the definition of tFAW; see
+// the comments in header to RubyMemoryControl.cc
+#define ACTIVATE_PER_TFAW 4
+
+//////////////////////////////////////////////////////////////////////////////
+
+class RubyMemoryControl : public MemoryControl
+{
+  public:
+    typedef RubyMemoryControlParams Params;
+    RubyMemoryControl(const Params *p);
+    void init();
+
+    ~RubyMemoryControl();
+
+    unsigned int drain(Event *de);
+
+    void wakeup();
+
+    void setConsumer(Consumer* consumer_ptr);
+    Consumer* getConsumer() { return m_consumer_ptr; };
+    void setDescription(const std::string& name) { m_description = name; };
+    std::string getDescription() { return m_description; };
+
+    // Called from the directory:
+    void enqueue(const MsgPtr& message, int latency );
+    void enqueueMemRef(MemoryNode& memRef);
+    void dequeue();
+    const Message* peek();
+    MemoryNode peekNode();
+    bool isReady();
+    bool areNSlotsAvailable(int n) { return true; };  // infinite queue length
+
+    //// Called from L3 cache:
+    //void writeBack(physical_address_t addr);
+
+    void printConfig(std::ostream& out);
+    void print(std::ostream& out) const;
+    void clearStats() const;
+    void printStats(std::ostream& out) const;
+
+    const int getBank(const physical_address_t addr) const;
+    const int getRank(const physical_address_t addr) const;
+
+    // not used in Ruby memory controller
+    const int getChannel(const physical_address_t addr) const;
+    const int getRow(const physical_address_t addr) const;
+
+    //added by SS
+    int getBanksPerRank() { return m_banks_per_rank; };
+    int getRanksPerDimm() { return m_ranks_per_dimm; };
+    int getDimmsPerChannel() { return m_dimms_per_channel; }
+
+
+  private:
+    void enqueueToDirectory(MemoryNode req, int latency);
+    const int getRank(int bank) const;
+    bool queueReady(int bank);
+    void issueRequest(int bank);
+    bool issueRefresh(int bank);
+    void markTfaw(int rank);
+    void executeCycle();
+
+    // Private copy constructor and assignment operator
+    RubyMemoryControl (const RubyMemoryControl& obj);
+    RubyMemoryControl& operator=(const RubyMemoryControl& obj);
+
+    // data members
+    Consumer* m_consumer_ptr;  // Consumer to signal a wakeup()
+    std::string m_description;
+    int m_msg_counter;
+
+    int m_mem_bus_cycle_multiplier;
+    int m_banks_per_rank;
+    int m_ranks_per_dimm;
+    int m_dimms_per_channel;
+    int m_bank_bit_0;
+    int m_rank_bit_0;
+    int m_dimm_bit_0;
+    unsigned int m_bank_queue_size;
+    int m_bank_busy_time;
+    int m_rank_rank_delay;
+    int m_read_write_delay;
+    int m_basic_bus_busy_time;
+    int m_mem_ctl_latency;
+    int m_refresh_period;
+    int m_mem_random_arbitrate;
+    int m_tFaw;
+    int m_mem_fixed_delay;
+
+    int m_total_banks;
+    int m_total_ranks;
+    int m_refresh_period_system;
+
+    // queues where memory requests live
+    std::list<MemoryNode> m_response_queue;
+    std::list<MemoryNode> m_input_queue;
+    std::list<MemoryNode>* m_bankQueues;
+
+    // Each entry indicates number of address-bus cycles until bank
+    // is reschedulable:
+    int* m_bankBusyCounter;
+    int* m_oldRequest;
+
+    uint64* m_tfaw_shift;
+    int* m_tfaw_count;
+
+    // Each of these indicates number of address-bus cycles until
+    // we can issue a new request of the corresponding type:
+    int m_busBusyCounter_Write;
+    int m_busBusyCounter_ReadNewRank;
+    int m_busBusyCounter_Basic;
+
+    int m_busBusy_WhichRank;  // which rank last granted
+    int m_roundRobin;         // which bank queue was last granted
+    int m_refresh_count;      // cycles until next refresh
+    int m_need_refresh;       // set whenever m_refresh_count goes to zero
+    int m_refresh_bank;       // which bank to refresh next
+    int m_ageCounter;         // age of old requests; to detect starvation
+    int m_idleCount;          // watchdog timer for shutting down
+
+    MemCntrlProfiler* m_profiler_ptr;
+};
+
+#endif // __MEM_RUBY_SYSTEM_MEMORY_CONTROL_HH__
diff --git a/src/mem/ruby/system/RubyMemoryControl.py b/src/mem/ruby/system/RubyMemoryControl.py
new file mode 100644 (file)
index 0000000..f79ed9b
--- /dev/null
@@ -0,0 +1,54 @@
+# Copyright (c) 2009 Advanced Micro Devices, Inc.
+# All rights reserved.
+#
+# 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: Steve Reinhardt
+#          Brad Beckmann
+
+from m5.params import *
+from m5.SimObject import SimObject
+from MemoryControl import MemoryControl
+
+class RubyMemoryControl(MemoryControl):
+    type = 'RubyMemoryControl'
+    cxx_class = 'RubyMemoryControl'
+    version = Param.Int("");
+
+    banks_per_rank = Param.Int(8, "");
+    ranks_per_dimm = Param.Int(2, "");
+    dimms_per_channel = Param.Int(2, "");
+    bank_bit_0 = Param.Int(8, "");
+    rank_bit_0 = Param.Int(11, "");
+    dimm_bit_0 = Param.Int(12, "");
+    bank_queue_size = Param.Int(12, "");
+    bank_busy_time = Param.Int(11, "");
+    rank_rank_delay = Param.Int(1, "");
+    read_write_delay = Param.Int(2, "");
+    basic_bus_busy_time = Param.Int(2, "");
+    mem_ctl_latency = Param.Int(12, "");
+    refresh_period = Param.Int(1560, "");
+    tFaw = Param.Int(0, "");
+    mem_random_arbitrate = Param.Int(0, "");
+    mem_fixed_delay = Param.Int(0, "");
index cbb1da3b11acc2a02c178e2a33609e524d09be02..baa877b3948ef46e1cb07fd8fa25f14c330c2ca1 100644 (file)
@@ -39,6 +39,7 @@ SimObject('DirectoryMemory.py')
 SimObject('MemoryControl.py')
 SimObject('WireBuffer.py')
 SimObject('RubySystem.py')
+SimObject('RubyMemoryControl.py')
 
 Source('DMASequencer.cc')
 Source('DirectoryMemory.cc')
@@ -46,6 +47,7 @@ Source('SparseMemory.cc')
 Source('CacheMemory.cc')
 Source('MemoryControl.cc')
 Source('WireBuffer.cc')
+Source('RubyMemoryControl.cc')
 Source('MemoryNode.cc')
 Source('PersistentTable.cc')
 Source('RubyPort.cc')
index 41348ba6dfa61fe11107767c5470d8b25f8c4c5a..8f4676c4221e76c261bf3127f6353b81fe7b3768 100644 (file)
@@ -39,7 +39,7 @@ python_class_map = {"int": "Int",
                     "WireBuffer": "RubyWireBuffer",
                     "Sequencer": "RubySequencer",
                     "DirectoryMemory": "RubyDirectoryMemory",
-                    "MemoryControl": "RubyMemoryControl",
+                    "MemoryControl": "MemoryControl",
                     "DMASequencer": "DMASequencer"
                     }