summaryrefslogtreecommitdiff
path: root/src/mem/ruby/system/MemoryControl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/mem/ruby/system/MemoryControl.cc')
-rw-r--r--src/mem/ruby/system/MemoryControl.cc840
1 files changed, 436 insertions, 404 deletions
diff --git a/src/mem/ruby/system/MemoryControl.cc b/src/mem/ruby/system/MemoryControl.cc
index 0f12efc36..963cc3947 100644
--- a/src/mem/ruby/system/MemoryControl.cc
+++ b/src/mem/ruby/system/MemoryControl.cc
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
* All rights reserved.
@@ -28,8 +27,6 @@
*/
/*
- * MemoryControl.cc
- *
* Description: This module simulates a basic DDR-style memory controller
* (and can easily be extended to do FB-DIMM as well).
*
@@ -105,25 +102,21 @@
* then no more than four activates may happen within any 16 cycle window.
* Refreshes are included in the activates.
*
- *
- * $Id: $
- *
*/
-#include "mem/ruby/common/Global.hh"
+#include <list>
+
+#include "base/cprintf.hh"
#include "mem/gems_common/Map.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/system/System.hh"
-#include "mem/ruby/slicc_interface/RubySlicc_ComponentMapping.hh"
#include "mem/ruby/slicc_interface/NetworkMessage.hh"
-#include "mem/ruby/network/Network.hh"
-
-#include "mem/ruby/common/Consumer.hh"
-
+#include "mem/ruby/slicc_interface/RubySlicc_ComponentMapping.hh"
#include "mem/ruby/system/MemoryControl.hh"
-
-#include <list>
+#include "mem/ruby/system/System.hh"
class Consumer;
@@ -140,11 +133,12 @@ class Consumer;
// Output operator definition
-ostream& operator<<(ostream& out, const MemoryControl& obj)
+ostream&
+operator<<(ostream& out, const MemoryControl& obj)
{
- obj.print(out);
- out << flush;
- return out;
+ obj.print(out);
+ out << flush;
+ return out;
}
@@ -178,471 +172,509 @@ MemoryControl::MemoryControl(const Params *p)
m_dimms_per_channel);
}
-void MemoryControl::init()
+void
+MemoryControl::init()
{
- m_msg_counter = 0;
-
- m_debug = 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_awakened = 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;
- }
-}
+ m_msg_counter = 0;
+ m_debug = 0;
-// DESTRUCTOR
+ assert(m_tFaw <= 62); // must fit in a uint64 shift register
-MemoryControl::~MemoryControl () {
- delete [] m_bankQueues;
- delete [] m_bankBusyCounter;
- delete [] m_oldRequest;
- delete m_profiler_ptr;
-}
+ 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);
-// PUBLIC METHODS
+ m_bankBusyCounter = new int [m_total_banks];
+ assert(m_bankBusyCounter);
-// 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 = dynamic_cast<const MemoryMsg*>(message.ref());
- 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;
- Time arrival_time = memRef.m_time;
- uint64 at = arrival_time;
- bool is_mem_read = memRef.m_is_mem_read;
- physical_address_t addr = memRef.m_addr;
- int bank = getBank(addr);
- if (m_debug) {
- printf("New memory request%7d: 0x%08llx %c arrived at %10lld ", m_msg_counter, addr, is_mem_read? 'R':'W', at);
- printf("bank =%3x\n", bank);
- }
-
- m_profiler_ptr->profileMemReq(bank);
- m_input_queue.push_back(memRef);
- if (!m_awakened) {
- g_eventQueue_ptr->scheduleEvent(this, 1);
- m_awakened = 1;
- }
-}
+ 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;
-// dequeue, peek, and isReady are used to transfer completed requests
-// back to the directory
+ m_roundRobin = 0;
+ m_refresh_count = 1;
+ m_need_refresh = 0;
+ m_refresh_bank = 0;
+ m_awakened = 0;
+ m_idleCount = 0;
+ m_ageCounter = 0;
-void MemoryControl::dequeue () {
- assert(isReady());
- m_response_queue.pop_front();
+ // 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;
+ }
}
-
-const Message* MemoryControl::peek () {
- MemoryNode node = peekNode();
- Message* msg_ptr = node.m_msgptr.ref();
- assert(msg_ptr != NULL);
- return msg_ptr;
+MemoryControl::~MemoryControl()
+{
+ delete [] m_bankQueues;
+ delete [] m_bankBusyCounter;
+ delete [] m_oldRequest;
+ delete m_profiler_ptr;
}
-
-MemoryNode MemoryControl::peekNode () {
- assert(isReady());
- MemoryNode req = m_response_queue.front();
- uint64 returnTime = req.m_time;
- if (m_debug) {
- printf("Old memory request%7d: 0x%08llx %c peeked at %10lld\n",
- req.m_msg_counter, req.m_addr, req.m_is_mem_read? 'R':'W', returnTime);
- }
- return req;
+// 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 = dynamic_cast<const MemoryMsg*>(message.ref());
+ 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;
+ Time arrival_time = memRef.m_time;
+ uint64 at = arrival_time;
+ bool is_mem_read = memRef.m_is_mem_read;
+ physical_address_t addr = memRef.m_addr;
+ int bank = getBank(addr);
+ if (m_debug) {
+ cprintf("New memory request%7d: %#08x %c arrived at %10d bank = %3x\n",
+ m_msg_counter, addr, is_mem_read? 'R':'W', at, bank);
+ }
-bool MemoryControl::isReady () {
- return ((!m_response_queue.empty()) &&
- (m_response_queue.front().m_time <= g_eventQueue_ptr->getTime()));
+ m_profiler_ptr->profileMemReq(bank);
+ m_input_queue.push_back(memRef);
+ if (!m_awakened) {
+ g_eventQueue_ptr->scheduleEvent(this, 1);
+ m_awakened = 1;
+ }
}
-void MemoryControl::setConsumer (Consumer* consumer_ptr) {
- m_consumer_ptr = consumer_ptr;
+// dequeue, peek, and isReady are used to transfer completed requests
+// back to the directory
+void
+MemoryControl::dequeue()
+{
+ assert(isReady());
+ m_response_queue.pop_front();
}
-void MemoryControl::print (ostream& out) const {
+const Message*
+MemoryControl::peek()
+{
+ MemoryNode node = peekNode();
+ Message* msg_ptr = node.m_msgptr.ref();
+ assert(msg_ptr != NULL);
+ return msg_ptr;
}
-
-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;
+MemoryNode
+MemoryControl::peekNode()
+{
+ assert(isReady());
+ MemoryNode req = m_response_queue.front();
+ uint64 returnTime = req.m_time;
+ if (m_debug) {
+ cprintf("Old memory request%7d: %#08x %c peeked at %10d\n",
+ req.m_msg_counter, req.m_addr, req.m_is_mem_read ? 'R':'W',
+ returnTime);
+ }
+ return req;
}
+bool
+MemoryControl::isReady()
+{
+ return ((!m_response_queue.empty()) &&
+ (m_response_queue.front().m_time <= g_eventQueue_ptr->getTime()));
+}
-void MemoryControl::setDebug (int debugFlag) {
- m_debug = debugFlag;
+void
+MemoryControl::setConsumer(Consumer* consumer_ptr)
+{
+ m_consumer_ptr = consumer_ptr;
}
-void MemoryControl::clearStats() const
+void
+MemoryControl::print(ostream& out) const
{
- m_profiler_ptr->clearStats();
}
-void MemoryControl::printStats(ostream& out) const
+void
+MemoryControl::printConfig(ostream& out)
{
- m_profiler_ptr->printStats(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::setDebug(int debugFlag)
+{
+ m_debug = debugFlag;
+}
-// ****************************************************************
+void
+MemoryControl::clearStats() const
+{
+ m_profiler_ptr->clearStats();
+}
-// PRIVATE METHODS
+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);
-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);
-
- // schedule the wake up
- g_eventQueue_ptr->scheduleEventAbsolute(m_consumer_ptr, arrival_time);
+ // 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;
+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;
+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();
+#if 0
+ if (m_debug)
+ printf(" bank %x busy %d\n", bank, m_bankBusyCounter[bank]);
+#endif
+ return false;
+ }
-bool MemoryControl::queueReady (int bank) {
- if ((m_bankBusyCounter[bank] > 0) && !m_mem_fixed_delay) {
- m_profiler_ptr->profileMemBankBusy();
- //if (m_debug) printf(" 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_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;
-}
+ if (m_mem_fixed_delay)
+ return true;
-// issueRefresh checks to see if this bank has a refresh scheduled
-// and, if so, does the refresh and returns true
+ if ((m_ageCounter > (2 * m_bank_busy_time)) && !m_oldRequest[bank]) {
+ m_profiler_ptr->profileMemNotOld();
+ return false;
+ }
-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:
-
- //if (m_debug) {
- //uint64 current_time = g_eventQueue_ptr->getTime();
- //printf(" Refresh bank %3x at %lld\n", bank, current_time);
- //}
- 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;
-}
+ 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;
+ }
-// 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]++;
- }
-}
+ 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;
+ }
-// 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.
+ if (!write && (rank != m_busBusy_WhichRank)
+ && (m_busBusyCounter_ReadNewRank > 0)) {
+ m_profiler_ptr->profileMemDataBusBusy();
+ return false;
+ }
-void MemoryControl::issueRequest (int bank) {
- int rank = getRank(bank);
- MemoryNode req = m_bankQueues[bank].front();
- m_bankQueues[bank].pop_front();
- if (m_debug) {
- uint64 current_time = g_eventQueue_ptr->getTime();
- printf(" Mem issue request%7d: 0x%08llx %c at %10lld bank =%3x\n",
- req.m_msg_counter, req.m_addr, req.m_is_mem_read? 'R':'W', current_time, bank);
- }
- if (req.m_msgptr.ref() != NULL) { // 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();
+ 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:
+#if 0
+ if (m_debug) {
+ uint64 current_time = g_eventQueue_ptr->getTime();
+ printf(" Refresh bank %3x at %lld\n", bank, current_time);
+ }
+#endif
+
+ 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();
+ if (m_debug) {
+ uint64 current_time = g_eventQueue_ptr->getTime();
+ cprintf(" Mem issue request%7d: %#08x %c at %10d "
+ "bank=%3x\n",
+ req.m_msg_counter, req.m_addr, req.m_is_mem_read? 'R':'W',
+ current_time, bank);
+ }
+ if (req.m_msgptr.ref() != NULL) { // 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++;
+ }
-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;
- assert (m_need_refresh < 10); // Are we overrunning our ability to refresh?
- 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) {
+ // 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++) {
- if (!m_bankQueues[bank].empty()) m_oldRequest[bank] = 1;
+ anyOld |= m_oldRequest[bank];
}
- 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 (!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;
}
- if (qs > 0) {
- m_idleCount = IDLECOUNT_MAX_VALUE; // we're not idle if anything is queued
- queueHeads++;
- if (queueReady(m_roundRobin)) {
- issueRequest(m_roundRobin);
- banksIssued++;
- if (m_mem_fixed_delay) {
- m_profiler_ptr->profileMemWaitCycles(m_mem_fixed_delay);
+
+ // 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()) {
- m_idleCount = IDLECOUNT_MAX_VALUE; // we're not idle if anything is pending
- 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);
+
+ // 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());
}
- m_profiler_ptr->profileMemInputQ(m_input_queue.size());
- }
}
-
// wakeup: This function is called once per memory controller clock cycle.
-
-void MemoryControl::wakeup () {
-
- // execute everything
- executeCycle();
-
- m_idleCount--;
- if (m_idleCount <= 0) {
- m_awakened = 0;
- } else {
- // Reschedule ourselves so that we run every memory cycle:
- g_eventQueue_ptr->scheduleEvent(this, m_mem_bus_cycle_multiplier);
- }
+void
+MemoryControl::wakeup()
+{
+ // execute everything
+ executeCycle();
+
+ m_idleCount--;
+ if (m_idleCount <= 0) {
+ m_awakened = 0;
+ } else {
+ // Reschedule ourselves so that we run every memory cycle:
+ g_eventQueue_ptr->scheduleEvent(this, m_mem_bus_cycle_multiplier);
+ }
}
MemoryControl *