diff --git a/src/mem/ruby/SConscript b/src/mem/ruby/SConscript index 82a16c9b0..88edb44a4 100644 --- a/src/mem/ruby/SConscript +++ b/src/mem/ruby/SConscript @@ -42,7 +42,6 @@ DebugFlag('RubyCache') DebugFlag('RubyCacheTrace') DebugFlag('RubyDma') DebugFlag('RubyGenerated') -DebugFlag('RubyMemory') DebugFlag('RubyNetwork') DebugFlag('RubyPort') DebugFlag('RubyPrefetcher') @@ -56,7 +55,7 @@ DebugFlag('RubyResourceStalls') CompoundFlag('Ruby', [ 'RubyQueue', 'RubyNetwork', 'RubyTester', 'RubyGenerated', 'RubySlicc', 'RubySystem', 'RubyCache', - 'RubyMemory', 'RubyDma', 'RubyPort', 'RubySequencer', 'RubyCacheTrace', + 'RubyDma', 'RubyPort', 'RubySequencer', 'RubyCacheTrace', 'RubyPrefetcher']) if env['PROTOCOL'] == 'None': diff --git a/src/mem/ruby/profiler/MemCntrlProfiler.cc b/src/mem/ruby/profiler/MemCntrlProfiler.cc deleted file mode 100644 index 0cfc1e629..000000000 --- a/src/mem/ruby/profiler/MemCntrlProfiler.cc +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood - * 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. - */ - -#include "mem/ruby/profiler/MemCntrlProfiler.hh" - -using namespace std; - -MemCntrlProfiler::MemCntrlProfiler(const string& description, - int banks_per_rank, int ranks_per_dimm, int dimms_per_channel) -{ - m_description = description; - m_banks_per_rank = banks_per_rank; - m_ranks_per_dimm = ranks_per_dimm; - m_dimms_per_channel = dimms_per_channel; -} - -MemCntrlProfiler::~MemCntrlProfiler() -{ -} - -void -MemCntrlProfiler::profileMemReq(int bank) -{ - m_memReq++; - m_memBankCount[bank]++; -} - -void -MemCntrlProfiler::profileMemBankBusy() -{ - m_memBankBusy++; -} - -void -MemCntrlProfiler::profileMemBusBusy() -{ - m_memBusBusy++; -} - -void -MemCntrlProfiler::profileMemReadWriteBusy() -{ - m_memReadWriteBusy++; -} - -void -MemCntrlProfiler::profileMemDataBusBusy() -{ - m_memDataBusBusy++; -} - -void -MemCntrlProfiler::profileMemTfawBusy() -{ - m_memTfawBusy++; -} - -void -MemCntrlProfiler::profileMemRefresh() -{ - m_memRefresh++; -} - -void -MemCntrlProfiler::profileMemRead() -{ - m_memRead++; -} - -void -MemCntrlProfiler::profileMemWrite() -{ - m_memWrite++; -} - -void -MemCntrlProfiler::profileMemWaitCycles(int cycles) -{ - m_memWaitCycles += cycles; -} - -void -MemCntrlProfiler::profileMemInputQ(int cycles) -{ - m_memInputQ += cycles; -} - -void -MemCntrlProfiler::profileMemBankQ(int cycles) -{ - m_memBankQ += cycles; -} - -void -MemCntrlProfiler::profileMemArbWait(int cycles) -{ - m_memArbWait += cycles; -} - -void -MemCntrlProfiler::profileMemRandBusy() -{ - m_memRandBusy++; -} - -void -MemCntrlProfiler::profileMemNotOld() -{ - m_memNotOld++; -} - -void -MemCntrlProfiler::regStats() -{ - m_memReq - .name(m_description + ".memReq") - .desc("Total number of memory requests") - .flags(Stats::nozero) - ; - - m_memRead - .name(m_description + ".memRead") - .desc("Number of memory reads") - .flags(Stats::nozero) - ; - - m_memWrite - .name(m_description + ".memWrite") - .desc("Number of memory writes") - .flags(Stats::nozero) - ; - - m_memRefresh - .name(m_description + ".memRefresh") - .desc("Number of memory refreshes") - .flags(Stats::nozero) - ; - - m_memInputQ - .name(m_description + ".memInputQ") - .desc("Delay in the input queue") - .flags(Stats::nozero) - ; - - m_memBankQ - .name(m_description + ".memBankQ") - .desc("Delay behind the head of the bank queue") - .flags(Stats::nozero) - ; - - m_memWaitCycles - .name(m_description + ".memWaitCycles") - .desc("Delay stalled at the head of the bank queue") - .flags(Stats::nozero) - ; - - m_totalStalls - .name(m_description + ".totalStalls") - .desc("Total number of stall cycles") - .flags(Stats::nozero) - ; - - m_totalStalls = m_memInputQ + m_memBankQ + m_memWaitCycles; - - m_stallsPerReq - .name(m_description + ".stallsPerReq") - .desc("Expected number of stall cycles per request") - .flags(Stats::nozero) - ; - - m_stallsPerReq = m_totalStalls / m_memReq; - - // Note: The following "memory stalls" entries are a breakdown of - // the cycles which already showed up in m_memWaitCycles. The - // order is significant; it is the priority of attributing the - // cycles. For example, bank_busy is before arbitration because - // if the bank was busy, we didn't even check arbitration. - // Note: "not old enough" means that since we grouped waiting - // heads-of-queues into batches to avoid starvation, a request in - // a newer batch didn't try to arbitrate yet because there are - // older requests waiting. - m_memBankBusy - .name(m_description + ".memBankBusy") - .desc("memory stalls due to busy bank") - .flags(Stats::nozero) - ; - - m_memRandBusy - .name(m_description + ".memRandBusy") - .desc("memory stalls due to busy random") - .flags(Stats::nozero) - ; - - m_memNotOld - .name(m_description + ".memNotOld") - .desc("memory stalls due to anti starvation") - .flags(Stats::nozero) - ; - - m_memArbWait - .name(m_description + ".memArbWait") - .desc("memory stalls due to arbitration") - .flags(Stats::nozero) - ; - - m_memBusBusy - .name(m_description + ".memBusBusy") - .desc("memory stalls due to busy bus") - .flags(Stats::nozero) - ; - - m_memTfawBusy - .name(m_description + ".memTfawBusy") - .desc("memory stalls for Tfaw") - .flags(Stats::nozero) - ; - - m_memReadWriteBusy - .name(m_description + ".memReadWriteBusy") - .desc("memory stalls due to read write turnaround") - .flags(Stats::nozero) - ; - - m_memDataBusBusy - .name(m_description + ".memDataBusBusy") - .desc("memory stalls due to read read turnaround") - .flags(Stats::nozero) - ; - - int totalBanks = m_banks_per_rank * m_ranks_per_dimm * m_dimms_per_channel; - m_memBankCount - .init(totalBanks) - .name(m_description + ".memBankCount") - .desc("Number of accesses per bank") - .flags(Stats::pdf | Stats::total|Stats::oneline) - ; - for (int i = 0; i < totalBanks; i++) { - m_memBankCount.subname(i, ""); - } -} - diff --git a/src/mem/ruby/profiler/MemCntrlProfiler.hh b/src/mem/ruby/profiler/MemCntrlProfiler.hh deleted file mode 100644 index 56e283f50..000000000 --- a/src/mem/ruby/profiler/MemCntrlProfiler.hh +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood - * 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_PROFILER_MEMCNTRLPROFILER_HH__ -#define __MEM_RUBY_PROFILER_MEMCNTRLPROFILER_HH__ - -#include -#include -#include - -#include "base/statistics.hh" -#include "mem/ruby/common/TypeDefines.hh" - -class MemCntrlProfiler -{ - public: - MemCntrlProfiler(const std::string& description, int banks_per_rank, - int ranks_per_dimm, int dimms_per_channel); - ~MemCntrlProfiler(); - - void regStats(); - - void profileMemReq(int bank); - void profileMemBankBusy(); - void profileMemBusBusy(); - void profileMemTfawBusy(); - void profileMemReadWriteBusy(); - void profileMemDataBusBusy(); - void profileMemRefresh(); - void profileMemRead(); - void profileMemWrite(); - void profileMemWaitCycles(int cycles); - void profileMemInputQ(int cycles); - void profileMemBankQ(int cycles); - void profileMemArbWait(int cycles); - void profileMemRandBusy(); - void profileMemNotOld(); - - void print(std::ostream& out) const; - -private: - // Private copy constructor and assignment operator - MemCntrlProfiler(const MemCntrlProfiler& obj); - MemCntrlProfiler& operator=(const MemCntrlProfiler& obj); - - std::string m_description; - Stats::Scalar m_memReq; - Stats::Scalar m_memRead; - Stats::Scalar m_memWrite; - Stats::Scalar m_memRefresh; - - Stats::Scalar m_memWaitCycles; - Stats::Scalar m_memInputQ; - Stats::Scalar m_memBankQ; - Stats::Formula m_totalStalls; - Stats::Formula m_stallsPerReq; - - Stats::Scalar m_memBankBusy; - Stats::Scalar m_memBusBusy; - Stats::Scalar m_memTfawBusy; - Stats::Scalar m_memReadWriteBusy; - Stats::Scalar m_memDataBusBusy; - Stats::Scalar m_memArbWait; - Stats::Scalar m_memRandBusy; - Stats::Scalar m_memNotOld; - Stats::Vector m_memBankCount; - - int m_banks_per_rank; - int m_ranks_per_dimm; - int m_dimms_per_channel; -}; - -inline std::ostream& -operator<<(std::ostream& out, const MemCntrlProfiler& obj) -{ - obj.print(out); - out << std::flush; - return out; -} - -#endif // __MEM_RUBY_PROFILER_MEMCNTRLPROFILER_HH__ diff --git a/src/mem/ruby/profiler/SConscript b/src/mem/ruby/profiler/SConscript index d1e9972e4..783dc78db 100644 --- a/src/mem/ruby/profiler/SConscript +++ b/src/mem/ruby/profiler/SConscript @@ -35,6 +35,5 @@ if env['PROTOCOL'] == 'None': Source('AccessTraceForAddress.cc') Source('AddressProfiler.cc') -Source('MemCntrlProfiler.cc') Source('Profiler.cc') Source('StoreTrace.cc') diff --git a/src/mem/ruby/structures/MemoryNode.cc b/src/mem/ruby/structures/MemoryNode.cc deleted file mode 100644 index 1e68cc459..000000000 --- a/src/mem/ruby/structures/MemoryNode.cc +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 1999 Mark D. Hill and David A. Wood - * 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. - */ - -#include "mem/ruby/structures/MemoryNode.hh" - -using namespace std; - -void -MemoryNode::print(ostream& out) const -{ - out << "["; - out << m_time << ", "; - out << m_msg_counter << ", "; - out << pkt << "; "; - out << "]"; -} diff --git a/src/mem/ruby/structures/MemoryNode.hh b/src/mem/ruby/structures/MemoryNode.hh deleted file mode 100644 index 558457e23..000000000 --- a/src/mem/ruby/structures/MemoryNode.hh +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2008 Mark D. Hill and David A. Wood - * 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 structure records everything known about a single - * memory request that is queued in the memory controller. - * It is created when the memory request first arrives - * at a memory controller and is deleted when the underlying - * message is enqueued to be sent back to the directory. - */ - -#ifndef __MEM_RUBY_STRUCTURES_MEMORYNODE_HH__ -#define __MEM_RUBY_STRUCTURES_MEMORYNODE_HH__ - -#include - -#include "mem/ruby/common/TypeDefines.hh" -#include "mem/ruby/slicc_interface/Message.hh" - -class MemoryNode -{ - public: - // old constructor - MemoryNode(const Cycles& time, int counter, const PacketPtr p, - Addr addr, const bool is_mem_read) - : m_time(time), pkt(p) - { - m_msg_counter = counter; - m_addr = addr; - m_is_mem_read = is_mem_read; - m_is_dirty_wb = !is_mem_read; - } - - // new constructor - MemoryNode(const Cycles& time, const PacketPtr p, - Addr addr, const bool is_mem_read, - const bool is_dirty_wb) - : m_time(time), pkt(p) - { - m_msg_counter = 0; - m_addr = addr; - m_is_mem_read = is_mem_read; - m_is_dirty_wb = is_dirty_wb; - } - - void print(std::ostream& out) const; - - Cycles m_time; - int m_msg_counter; - PacketPtr pkt; - Addr m_addr; - bool m_is_mem_read; - bool m_is_dirty_wb; -}; - -inline std::ostream& -operator<<(std::ostream& out, const MemoryNode& obj) -{ - obj.print(out); - out << std::flush; - return out; -} - -#endif // __MEM_RUBY_STRUCTURES_MEMORYNODE_HH__ diff --git a/src/mem/ruby/structures/RubyMemoryControl.cc b/src/mem/ruby/structures/RubyMemoryControl.cc deleted file mode 100644 index 095b2e306..000000000 --- a/src/mem/ruby/structures/RubyMemoryControl.cc +++ /dev/null @@ -1,802 +0,0 @@ -/* - * 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 "base/random.hh" -#include "debug/RubyMemory.hh" -#include "mem/ruby/common/Address.hh" -#include "mem/ruby/profiler/Profiler.hh" -#include "mem/ruby/slicc_interface/Message.hh" -#include "mem/ruby/slicc_interface/RubySlicc_ComponentMapping.hh" -#include "mem/ruby/structures/RubyMemoryControl.hh" -#include "mem/ruby/system/RubySystem.hh" - -using namespace std; - -// 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) - : AbstractMemory(p), Consumer(this), port(name() + ".port", *this), - m_event(this) -{ - 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); - - warn("RubyMemoryControl is deprecated, use a DRAMCtrl subclass instead\n"); -} - -void -RubyMemoryControl::init() -{ - m_msg_counter = 0; - assert(m_tFaw <= 62); // must fit in a uint64_t 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 [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_t[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; - } -} - -BaseSlavePort& -RubyMemoryControl::getSlavePort(const string &if_name, PortID idx) -{ - if (if_name != "port") { - return MemObject::getSlavePort(if_name, idx); - } else { - return port; - } -} - -void -RubyMemoryControl::reset() -{ - m_msg_counter = 0; - - assert(m_tFaw <= 62); // must fit in a uint64_t 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; - - assert(m_bankQueues); - - assert(m_bankBusyCounter); - - 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. - 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 -bool -RubyMemoryControl::recvTimingReq(PacketPtr pkt) -{ - Cycles arrival_time = curCycle(); - Addr addr = pkt->getAddr(); - bool is_mem_read = pkt->isRead(); - - access(pkt); - MemoryNode *thisReq = new MemoryNode(arrival_time, pkt, addr, - is_mem_read, !is_mem_read); - enqueueMemRef(thisReq); - return true; -} - -// 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; - Addr addr = memRef->m_addr; - int bank = getBank(addr); - - m_profiler_ptr->profileMemReq(bank); - m_input_queue.push_back(memRef); - - if (!m_event.scheduled()) { - schedule(m_event, clockEdge()); - } -} - -void -RubyMemoryControl::print(ostream& out) const -{ -} - -// Queue up a completed request to send back to directory -void -RubyMemoryControl::enqueueToDirectory(MemoryNode *req, Cycles latency) -{ - Tick arrival_time = clockEdge(latency); - PacketPtr pkt = req->pkt; - - // access already turned the packet into a response - assert(pkt->isResponse()); - - // queue the packet in the response queue to be sent out after - // the static latency has passed - port.schedTimingResp(pkt, arrival_time); - - DPRINTF(RubyMemory, "Enqueueing msg %#08x %c back to directory at %15d\n", - req->m_addr, req->m_is_mem_read ? 'R':'W', arrival_time); -} - -// getBank returns an integer that is unique for each -// bank across this memory controller. -int -RubyMemoryControl::getBank(const Addr 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; -} - -int -RubyMemoryControl::getRank(const Addr 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. -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! -int -RubyMemoryControl::getChannel(const Addr addr) const -{ - assert(false); - return -1; -} - -// Not used! -int -RubyMemoryControl::getRow(const Addr 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_mt.random(0, 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'); - - enqueueToDirectory(req, Cycles(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; - } - - delete req; -} - -// 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_mt.random(0, m_total_banks - 1); - } - - // 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()); - } -} - -DrainState -RubyMemoryControl::drain() -{ - DPRINTF(RubyMemory, "MemoryController drain\n"); - if (m_event.scheduled()) { - deschedule(m_event); - } - return DrainState::Drained; -} - -// 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, clockEdge(Cycles(1))); - } -} - -/** - * This function reads the different buffers that exist in the Ruby Memory - * Controller, and figures out if any of the buffers hold a message that - * contains the data for the address provided in the packet. True is returned - * if any of the messages was read, otherwise false is returned. - * - * I think we should move these buffers to being message buffers, instead of - * being lists. - */ -bool -RubyMemoryControl::functionalRead(Packet *pkt) -{ - for (std::list::iterator it = m_input_queue.begin(); - it != m_input_queue.end(); ++it) { - PacketPtr msg = (*it)->pkt; - if (pkt->checkFunctional(msg)) { - return true; - } - } - - for (std::list::iterator it = m_response_queue.begin(); - it != m_response_queue.end(); ++it) { - PacketPtr msg = (*it)->pkt; - if (pkt->checkFunctional(msg)) { - return true; - } - } - - for (uint32_t bank = 0; bank < m_total_banks; ++bank) { - for (std::list::iterator it = m_bankQueues[bank].begin(); - it != m_bankQueues[bank].end(); ++it) { - PacketPtr msg = (*it)->pkt; - if (pkt->checkFunctional(msg)) { - return true; - } - } - } - - functionalAccess(pkt); - return true; -} - -/** - * This function reads the different buffers that exist in the Ruby Memory - * Controller, and figures out if any of the buffers hold a message that - * needs to functionally written with the data in the packet. - * - * The number of messages written is returned at the end. This is required - * for debugging purposes. - */ -uint32_t -RubyMemoryControl::functionalWrite(Packet *pkt) -{ - uint32_t num_functional_writes = 0; - - for (std::list::iterator it = m_input_queue.begin(); - it != m_input_queue.end(); ++it) { - PacketPtr msg = (*it)->pkt; - if (pkt->checkFunctional(msg)) { - num_functional_writes++; - } - } - - for (std::list::iterator it = m_response_queue.begin(); - it != m_response_queue.end(); ++it) { - PacketPtr msg = (*it)->pkt; - if (pkt->checkFunctional(msg)) { - num_functional_writes++; - } - } - - for (uint32_t bank = 0; bank < m_total_banks; ++bank) { - for (std::list::iterator it = m_bankQueues[bank].begin(); - it != m_bankQueues[bank].end(); ++it) { - PacketPtr msg = (*it)->pkt; - if (pkt->checkFunctional(msg)) { - num_functional_writes++; - } - } - } - - functionalAccess(pkt); - num_functional_writes++; - return num_functional_writes; -} - -void -RubyMemoryControl::regStats() -{ - m_profiler_ptr->regStats(); - AbstractMemory::regStats(); -} - -RubyMemoryControl * -RubyMemoryControlParams::create() -{ - return new RubyMemoryControl(this); -} - -RubyMemoryControl::MemoryPort::MemoryPort(const std::string& name, - RubyMemoryControl& _memory) - : QueuedSlavePort(name, &_memory, queue), queue(_memory, *this), - memory(_memory) -{ } - -AddrRangeList -RubyMemoryControl::MemoryPort::getAddrRanges() const -{ - AddrRangeList ranges; - ranges.push_back(memory.getAddrRange()); - return ranges; -} - -void -RubyMemoryControl::MemoryPort::recvFunctional(PacketPtr pkt) -{ - pkt->pushLabel(memory.name()); - - if (!queue.checkFunctional(pkt)) { - // Default implementation of SimpleTimingPort::recvFunctional() - // calls recvAtomic() and throws away the latency; we can save a - // little here by just not calculating the latency. - memory.functionalWrite(pkt); - } - - pkt->popLabel(); -} - -Tick -RubyMemoryControl::MemoryPort::recvAtomic(PacketPtr pkt) -{ - panic("This controller does not support recv atomic!\n"); -} - -bool -RubyMemoryControl::MemoryPort::recvTimingReq(PacketPtr pkt) -{ - // pass it to the memory controller - return memory.recvTimingReq(pkt); -} diff --git a/src/mem/ruby/structures/RubyMemoryControl.hh b/src/mem/ruby/structures/RubyMemoryControl.hh deleted file mode 100644 index 601c4d1a2..000000000 --- a/src/mem/ruby/structures/RubyMemoryControl.hh +++ /dev/null @@ -1,205 +0,0 @@ -/* - * 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_STRUCTURES_MEMORY_CONTROL_HH__ -#define __MEM_RUBY_STRUCTURES_MEMORY_CONTROL_HH__ - -#include -#include -#include - -#include "mem/abstract_mem.hh" -#include "mem/protocol/MemoryMsg.hh" -#include "mem/ruby/common/Address.hh" -#include "mem/ruby/profiler/MemCntrlProfiler.hh" -#include "mem/ruby/structures/MemoryNode.hh" -#include "mem/ruby/system/RubySystem.hh" -#include "params/RubyMemoryControl.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 AbstractMemory, public Consumer -{ - public: - typedef RubyMemoryControlParams Params; - RubyMemoryControl(const Params *p); - void init() override; - void reset(); - - ~RubyMemoryControl(); - - virtual BaseSlavePort& getSlavePort(const std::string& if_name, - PortID idx = InvalidPortID) override; - DrainState drain() override; - void wakeup() override; - - void setDescription(const std::string& name) { m_description = name; }; - std::string getDescription() { return m_description; }; - - // Called from the directory: - bool recvTimingReq(PacketPtr pkt); - void recvFunctional(PacketPtr pkt); - void enqueueMemRef(MemoryNode *memRef); - bool areNSlotsAvailable(int n) { return true; }; // infinite queue length - - void print(std::ostream& out) const override; - void regStats() override; - - int getBank(const Addr addr) const; - int getRank(const Addr addr) const; - - // not used in Ruby memory controller - int getChannel(const Addr addr) const; - int getRow(const Addr 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; } - - bool functionalRead(Packet *pkt); - uint32_t functionalWrite(Packet *pkt); - - private: - void enqueueToDirectory(MemoryNode *req, Cycles latency); - 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); - - private: - // For now, make use of a queued slave port to avoid dealing with - // flow control for the responses being sent back - class MemoryPort : public QueuedSlavePort - { - RespPacketQueue queue; - RubyMemoryControl& memory; - - public: - MemoryPort(const std::string& name, RubyMemoryControl& _memory); - - protected: - Tick recvAtomic(PacketPtr pkt); - - void recvFunctional(PacketPtr pkt); - - bool recvTimingReq(PacketPtr); - - virtual AddrRangeList getAddrRanges() const; - }; - - /** - * Our incoming port, for a multi-ported controller add a crossbar - * in front of it - */ - MemoryPort port; - - // data members - std::string m_description; - int m_msg_counter; - - 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; - Cycles m_mem_ctl_latency; - int m_refresh_period; - int m_mem_random_arbitrate; - int m_tFaw; - Cycles m_mem_fixed_delay; - - int m_total_banks; - int m_total_ranks; - int m_refresh_period_system; - - // queues where memory requests live - std::list m_response_queue; - std::list m_input_queue; - std::list* m_bankQueues; - - // Each entry indicates number of address-bus cycles until bank - // is reschedulable: - int *m_bankBusyCounter; - int *m_oldRequest; - - uint64_t *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; - - class MemCntrlEvent : public Event - { - public: - MemCntrlEvent(RubyMemoryControl *_mem_cntrl) - { - mem_cntrl = _mem_cntrl; - } - private: - void process() { mem_cntrl->wakeup(); } - - RubyMemoryControl* mem_cntrl; - }; - - MemCntrlEvent m_event; -}; - -std::ostream& operator<<(std::ostream& out, const RubyMemoryControl& obj); - -#endif // __MEM_RUBY_STRUCTURES_MEMORY_CONTROL_HH__ diff --git a/src/mem/ruby/structures/RubyMemoryControl.py b/src/mem/ruby/structures/RubyMemoryControl.py deleted file mode 100644 index 78f2d8dcb..000000000 --- a/src/mem/ruby/structures/RubyMemoryControl.py +++ /dev/null @@ -1,57 +0,0 @@ -# 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 AbstractMemory import AbstractMemory - -class RubyMemoryControl(AbstractMemory): - type = 'RubyMemoryControl' - cxx_class = 'RubyMemoryControl' - cxx_header = "mem/ruby/structures/RubyMemoryControl.hh" - - 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.Cycles(12, ""); - refresh_period = Param.Cycles(1560, ""); - tFaw = Param.Int(0, ""); - mem_random_arbitrate = Param.Int(0, ""); - mem_fixed_delay = Param.Cycles(0, ""); - - # single-ported on the system interface side, instantiate with a - # crossbar in front of the controller for multiple ports - port = SlavePort("Slave port") diff --git a/src/mem/ruby/structures/SConscript b/src/mem/ruby/structures/SConscript index 75fc6370e..c39cd0e51 100644 --- a/src/mem/ruby/structures/SConscript +++ b/src/mem/ruby/structures/SConscript @@ -38,7 +38,6 @@ SimObject('DirectoryMemory.py') SimObject('LRUReplacementPolicy.py') SimObject('PseudoLRUReplacementPolicy.py') SimObject('ReplacementPolicy.py') -SimObject('RubyMemoryControl.py') SimObject('RubyPrefetcher.py') SimObject('WireBuffer.py') @@ -48,8 +47,6 @@ Source('CacheMemory.cc') Source('LRUPolicy.cc') Source('PseudoLRUPolicy.cc') Source('WireBuffer.cc') -Source('RubyMemoryControl.cc') -Source('MemoryNode.cc') Source('PersistentTable.cc') Source('Prefetcher.cc') Source('TimerTable.cc')