summaryrefslogtreecommitdiff
path: root/src/mem/dram_ctrl.cc
diff options
context:
space:
mode:
authorOmar Naji <Omar.Naji@arm.com>2014-12-23 09:31:18 -0500
committerOmar Naji <Omar.Naji@arm.com>2014-12-23 09:31:18 -0500
commit381d1da79147fbe8ae62ab4886446bdd7b3c478f (patch)
tree4a6ca73e27cdedf4c53753fbdce03e8d71306cd3 /src/mem/dram_ctrl.cc
parent152c02354ee53f4f4f10ac01911eb92386ef6fd2 (diff)
downloadgem5-381d1da79147fbe8ae62ab4886446bdd7b3c478f.tar.xz
mem: Add rank-wise refresh to the DRAM controller
This patch adds rank-wise refresh to the controller, as opposed to the channel-wide refresh currently in place. In essence each rank can be refreshed independently, and for this to be possible the controller is extended with a state machine per rank. Without this patch the data bus is always idle during a refresh, as all the ranks are refreshing at the same time. With the rank-wise refresh it is possible to use one rank while another one is refreshing, and thus the data bus can be kept busy. The patch introduces a Rank class to encapsulate the state per rank, and also shifts all the relevant banks, activation tracking etc to the rank. The arbitration is also updated to consider the state of the rank.
Diffstat (limited to 'src/mem/dram_ctrl.cc')
-rw-r--r--src/mem/dram_ctrl.cc725
1 files changed, 408 insertions, 317 deletions
diff --git a/src/mem/dram_ctrl.cc b/src/mem/dram_ctrl.cc
index 62ae9e984..eec4706a6 100644
--- a/src/mem/dram_ctrl.cc
+++ b/src/mem/dram_ctrl.cc
@@ -40,6 +40,7 @@
* Authors: Andreas Hansson
* Ani Udipi
* Neha Agarwal
+ * Omar Naji
*/
#include "base/bitfield.hh"
@@ -59,8 +60,7 @@ DRAMCtrl::DRAMCtrl(const DRAMCtrlParams* p) :
port(name() + ".port", *this),
retryRdReq(false), retryWrReq(false),
busState(READ),
- nextReqEvent(this), respondEvent(this), activateEvent(this),
- prechargeEvent(this), refreshEvent(this), powerEvent(this),
+ nextReqEvent(this), respondEvent(this),
drainManager(NULL),
deviceSize(p->device_size),
deviceBusWidth(p->device_bus_width), burstLength(p->burst_length),
@@ -89,32 +89,19 @@ DRAMCtrl::DRAMCtrl(const DRAMCtrlParams* p) :
maxAccessesPerRow(p->max_accesses_per_row),
frontendLatency(p->static_frontend_latency),
backendLatency(p->static_backend_latency),
- busBusyUntil(0), refreshDueAt(0), refreshState(REF_IDLE),
- pwrStateTrans(PWR_IDLE), pwrState(PWR_IDLE), prevArrival(0),
- nextReqTime(0), pwrStateTick(0), numBanksActive(0),
- activeRank(0), timeStampOffset(0)
+ busBusyUntil(0), prevArrival(0),
+ nextReqTime(0), activeRank(0), timeStampOffset(0)
{
- // create the bank states based on the dimensions of the ranks and
- // banks
- banks.resize(ranksPerChannel);
-
- //create list of drampower objects. For each rank 1 drampower instance.
for (int i = 0; i < ranksPerChannel; i++) {
- DRAMPower drampower = DRAMPower(p, false);
- rankPower.emplace_back(drampower);
- }
+ Rank* rank = new Rank(*this, p);
+ ranks.push_back(rank);
- actTicks.resize(ranksPerChannel);
- for (size_t c = 0; c < ranksPerChannel; ++c) {
- banks[c].resize(banksPerRank);
- actTicks[c].resize(activationLimit, 0);
- }
+ rank->actTicks.resize(activationLimit, 0);
+ rank->banks.resize(banksPerRank);
+ rank->rank = i;
- // set the bank indices
- for (int r = 0; r < ranksPerChannel; r++) {
for (int b = 0; b < banksPerRank; b++) {
- banks[r][b].rank = r;
- banks[r][b].bank = b;
+ rank->banks[b].bank = b;
// GDDR addressing of banks to BG is linear.
// Here we assume that all DRAM generations address bank groups as
// follows:
@@ -126,10 +113,10 @@ DRAMCtrl::DRAMCtrl(const DRAMCtrlParams* p) :
// banks 1,5,9,13 are in bank group 1
// banks 2,6,10,14 are in bank group 2
// banks 3,7,11,15 are in bank group 3
- banks[r][b].bankgr = b % bankGroupsPerRank;
+ rank->banks[b].bankgr = b % bankGroupsPerRank;
} else {
// No bank groups; simply assign to bank number
- banks[r][b].bankgr = b;
+ rank->banks[b].bankgr = b;
}
}
}
@@ -254,19 +241,18 @@ DRAMCtrl::startup()
{
// timestamp offset should be in clock cycles for DRAMPower
timeStampOffset = divCeil(curTick(), tCK);
+
// update the start tick for the precharge accounting to the
// current tick
- pwrStateTick = curTick();
+ for (auto r : ranks) {
+ r->startup(curTick() + tREFI - tRP);
+ }
// shift the bus busy time sufficiently far ahead that we never
// have to worry about negative values when computing the time for
// the next request, this will add an insignificant bubble at the
// start of simulation
busBusyUntil = curTick() + tRP + tRCD + tCL;
-
- // kick off the refresh, and give ourselves enough time to
- // precharge
- schedule(refreshEvent, curTick() + tREFI - tRP);
}
Tick
@@ -411,7 +397,7 @@ DRAMCtrl::decodeAddr(PacketPtr pkt, Addr dramPktAddr, unsigned size,
// later
uint16_t bank_id = banksPerRank * rank + bank;
return new DRAMPacket(pkt, isRead, rank, bank, row, bank_id, dramPktAddr,
- size, banks[rank][bank]);
+ size, ranks[rank]->banks[bank], *ranks[rank]);
}
void
@@ -755,7 +741,7 @@ DRAMCtrl::processRespondEvent()
}
}
-void
+bool
DRAMCtrl::chooseNext(std::deque<DRAMPacket*>& queue, bool switched_cmd_type)
{
// This method does the arbitration between requests. The chosen
@@ -764,20 +750,39 @@ DRAMCtrl::chooseNext(std::deque<DRAMPacket*>& queue, bool switched_cmd_type)
// FCFS, this method does nothing
assert(!queue.empty());
+ // bool to indicate if a packet to an available rank is found
+ bool found_packet = false;
if (queue.size() == 1) {
- DPRINTF(DRAM, "Single request, nothing to do\n");
- return;
+ DRAMPacket* dram_pkt = queue.front();
+ // available rank corresponds to state refresh idle
+ if (ranks[dram_pkt->rank]->isAvailable()) {
+ found_packet = true;
+ DPRINTF(DRAM, "Single request, going to a free rank\n");
+ } else {
+ DPRINTF(DRAM, "Single request, going to a busy rank\n");
+ }
+ return found_packet;
}
if (memSchedPolicy == Enums::fcfs) {
- // Do nothing, since the correct request is already head
+ // check if there is a packet going to a free rank
+ for(auto i = queue.begin(); i != queue.end() ; ++i) {
+ DRAMPacket* dram_pkt = *i;
+ if (ranks[dram_pkt->rank]->isAvailable()) {
+ queue.erase(i);
+ queue.push_front(dram_pkt);
+ found_packet = true;
+ break;
+ }
+ }
} else if (memSchedPolicy == Enums::frfcfs) {
- reorderQueue(queue, switched_cmd_type);
+ found_packet = reorderQueue(queue, switched_cmd_type);
} else
panic("No scheduling policy chosen\n");
+ return found_packet;
}
-void
+bool
DRAMCtrl::reorderQueue(std::deque<DRAMPacket*>& queue, bool switched_cmd_type)
{
// Only determine this when needed
@@ -785,50 +790,66 @@ DRAMCtrl::reorderQueue(std::deque<DRAMPacket*>& queue, bool switched_cmd_type)
// Search for row hits first, if no row hit is found then schedule the
// packet to one of the earliest banks available
+ bool found_packet = false;
bool found_earliest_pkt = false;
bool found_prepped_diff_rank_pkt = false;
- auto selected_pkt_it = queue.begin();
+ auto selected_pkt_it = queue.end();
for (auto i = queue.begin(); i != queue.end() ; ++i) {
DRAMPacket* dram_pkt = *i;
const Bank& bank = dram_pkt->bankRef;
+ // check if rank is busy. If this is the case jump to the next packet
// Check if it is a row hit
- if (bank.openRow == dram_pkt->row) {
- if (dram_pkt->rank == activeRank || switched_cmd_type) {
- // FCFS within the hits, giving priority to commands
- // that access the same rank as the previous burst
- // to minimize bus turnaround delays
- // Only give rank prioity when command type is not changing
- DPRINTF(DRAM, "Row buffer hit\n");
- selected_pkt_it = i;
- break;
- } else if (!found_prepped_diff_rank_pkt) {
- // found row hit for command on different rank than prev burst
- selected_pkt_it = i;
- found_prepped_diff_rank_pkt = true;
- }
- } else if (!found_earliest_pkt & !found_prepped_diff_rank_pkt) {
- // No row hit and
- // haven't found an entry with a row hit to a new rank
- if (earliest_banks == 0)
- // Determine entries with earliest bank prep delay
- // Function will give priority to commands that access the
- // same rank as previous burst and can prep the bank seamlessly
- earliest_banks = minBankPrep(queue, switched_cmd_type);
-
- // FCFS - Bank is first available bank
- if (bits(earliest_banks, dram_pkt->bankId, dram_pkt->bankId)) {
- // Remember the packet to be scheduled to one of the earliest
- // banks available, FCFS amongst the earliest banks
- selected_pkt_it = i;
- found_earliest_pkt = true;
+ if (dram_pkt->rankRef.isAvailable()) {
+ if (bank.openRow == dram_pkt->row) {
+ if (dram_pkt->rank == activeRank || switched_cmd_type) {
+ // FCFS within the hits, giving priority to commands
+ // that access the same rank as the previous burst
+ // to minimize bus turnaround delays
+ // Only give rank prioity when command type is
+ // not changing
+ DPRINTF(DRAM, "Row buffer hit\n");
+ selected_pkt_it = i;
+ break;
+ } else if (!found_prepped_diff_rank_pkt) {
+ // found row hit for command on different rank
+ // than prev burst
+ selected_pkt_it = i;
+ found_prepped_diff_rank_pkt = true;
+ }
+ } else if (!found_earliest_pkt & !found_prepped_diff_rank_pkt) {
+ // packet going to a rank which is currently not waiting for a
+ // refresh, No row hit and
+ // haven't found an entry with a row hit to a new rank
+ if (earliest_banks == 0)
+ // Determine entries with earliest bank prep delay
+ // Function will give priority to commands that access the
+ // same rank as previous burst and can prep
+ // the bank seamlessly
+ earliest_banks = minBankPrep(queue, switched_cmd_type);
+
+ // FCFS - Bank is first available bank
+ if (bits(earliest_banks, dram_pkt->bankId,
+ dram_pkt->bankId)) {
+ // Remember the packet to be scheduled to one of
+ // the earliest banks available, FCFS amongst the
+ // earliest banks
+ selected_pkt_it = i;
+ //if the packet found is going to a rank that is currently
+ //not busy then update the found_packet to true
+ found_earliest_pkt = true;
+ }
}
}
}
- DRAMPacket* selected_pkt = *selected_pkt_it;
- queue.erase(selected_pkt_it);
- queue.push_front(selected_pkt);
+ if (selected_pkt_it != queue.end()) {
+ DRAMPacket* selected_pkt = *selected_pkt_it;
+ queue.erase(selected_pkt_it);
+ queue.push_front(selected_pkt);
+ found_packet = true;
+ }
+ return found_packet;
}
void
@@ -864,119 +885,108 @@ DRAMCtrl::accessAndRespond(PacketPtr pkt, Tick static_latency)
}
void
-DRAMCtrl::activateBank(Bank& bank, Tick act_tick, uint32_t row)
+DRAMCtrl::activateBank(Rank& rank_ref, Bank& bank_ref,
+ Tick act_tick, uint32_t row)
{
- // get the rank index from the bank
- uint8_t rank = bank.rank;
-
- assert(actTicks[rank].size() == activationLimit);
+ assert(rank_ref.actTicks.size() == activationLimit);
DPRINTF(DRAM, "Activate at tick %d\n", act_tick);
// update the open row
- assert(bank.openRow == Bank::NO_ROW);
- bank.openRow = row;
+ assert(bank_ref.openRow == Bank::NO_ROW);
+ bank_ref.openRow = row;
// start counting anew, this covers both the case when we
// auto-precharged, and when this access is forced to
// precharge
- bank.bytesAccessed = 0;
- bank.rowAccesses = 0;
+ bank_ref.bytesAccessed = 0;
+ bank_ref.rowAccesses = 0;
- ++numBanksActive;
- assert(numBanksActive <= banksPerRank * ranksPerChannel);
+ ++rank_ref.numBanksActive;
+ assert(rank_ref.numBanksActive <= banksPerRank);
DPRINTF(DRAM, "Activate bank %d, rank %d at tick %lld, now got %d active\n",
- bank.bank, bank.rank, act_tick, numBanksActive);
+ bank_ref.bank, rank_ref.rank, act_tick,
+ ranks[rank_ref.rank]->numBanksActive);
- rankPower[bank.rank].powerlib.doCommand(MemCommand::ACT, bank.bank,
- divCeil(act_tick, tCK) -
- timeStampOffset);
+ rank_ref.power.powerlib.doCommand(MemCommand::ACT, bank_ref.bank,
+ divCeil(act_tick, tCK) -
+ timeStampOffset);
DPRINTF(DRAMPower, "%llu,ACT,%d,%d\n", divCeil(act_tick, tCK) -
- timeStampOffset, bank.bank, bank.rank);
+ timeStampOffset, bank_ref.bank, rank_ref.rank);
// The next access has to respect tRAS for this bank
- bank.preAllowedAt = act_tick + tRAS;
+ bank_ref.preAllowedAt = act_tick + tRAS;
// Respect the row-to-column command delay
- bank.colAllowedAt = std::max(act_tick + tRCD, bank.colAllowedAt);
+ bank_ref.colAllowedAt = std::max(act_tick + tRCD, bank_ref.colAllowedAt);
// start by enforcing tRRD
for(int i = 0; i < banksPerRank; i++) {
// next activate to any bank in this rank must not happen
// before tRRD
- if (bankGroupArch && (bank.bankgr == banks[rank][i].bankgr)) {
+ if (bankGroupArch && (bank_ref.bankgr == rank_ref.banks[i].bankgr)) {
// bank group architecture requires longer delays between
// ACT commands within the same bank group. Use tRRD_L
// in this case
- banks[rank][i].actAllowedAt = std::max(act_tick + tRRD_L,
- banks[rank][i].actAllowedAt);
+ rank_ref.banks[i].actAllowedAt = std::max(act_tick + tRRD_L,
+ rank_ref.banks[i].actAllowedAt);
} else {
// use shorter tRRD value when either
// 1) bank group architecture is not supportted
// 2) bank is in a different bank group
- banks[rank][i].actAllowedAt = std::max(act_tick + tRRD,
- banks[rank][i].actAllowedAt);
+ rank_ref.banks[i].actAllowedAt = std::max(act_tick + tRRD,
+ rank_ref.banks[i].actAllowedAt);
}
}
// next, we deal with tXAW, if the activation limit is disabled
// then we directly schedule an activate power event
- if (!actTicks[rank].empty()) {
+ if (!rank_ref.actTicks.empty()) {
// sanity check
- if (actTicks[rank].back() &&
- (act_tick - actTicks[rank].back()) < tXAW) {
+ if (rank_ref.actTicks.back() &&
+ (act_tick - rank_ref.actTicks.back()) < tXAW) {
panic("Got %d activates in window %d (%llu - %llu) which "
"is smaller than %llu\n", activationLimit, act_tick -
- actTicks[rank].back(), act_tick, actTicks[rank].back(),
- tXAW);
+ rank_ref.actTicks.back(), act_tick,
+ rank_ref.actTicks.back(), tXAW);
}
// shift the times used for the book keeping, the last element
// (highest index) is the oldest one and hence the lowest value
- actTicks[rank].pop_back();
+ rank_ref.actTicks.pop_back();
// record an new activation (in the future)
- actTicks[rank].push_front(act_tick);
+ rank_ref.actTicks.push_front(act_tick);
// cannot activate more than X times in time window tXAW, push the
// next one (the X + 1'st activate) to be tXAW away from the
// oldest in our window of X
- if (actTicks[rank].back() &&
- (act_tick - actTicks[rank].back()) < tXAW) {
+ if (rank_ref.actTicks.back() &&
+ (act_tick - rank_ref.actTicks.back()) < tXAW) {
DPRINTF(DRAM, "Enforcing tXAW with X = %d, next activate "
"no earlier than %llu\n", activationLimit,
- actTicks[rank].back() + tXAW);
+ rank_ref.actTicks.back() + tXAW);
for(int j = 0; j < banksPerRank; j++)
// next activate must not happen before end of window
- banks[rank][j].actAllowedAt =
- std::max(actTicks[rank].back() + tXAW,
- banks[rank][j].actAllowedAt);
+ rank_ref.banks[j].actAllowedAt =
+ std::max(rank_ref.actTicks.back() + tXAW,
+ rank_ref.banks[j].actAllowedAt);
}
}
// at the point when this activate takes place, make sure we
// transition to the active power state
- if (!activateEvent.scheduled())
- schedule(activateEvent, act_tick);
- else if (activateEvent.when() > act_tick)
+ if (!rank_ref.activateEvent.scheduled())
+ schedule(rank_ref.activateEvent, act_tick);
+ else if (rank_ref.activateEvent.when() > act_tick)
// move it sooner in time
- reschedule(activateEvent, act_tick);
+ reschedule(rank_ref.activateEvent, act_tick);
}
void
-DRAMCtrl::processActivateEvent()
-{
- // we should transition to the active state as soon as any bank is active
- if (pwrState != PWR_ACT)
- // note that at this point numBanksActive could be back at
- // zero again due to a precharge scheduled in the future
- schedulePowerEvent(PWR_ACT, curTick());
-}
-
-void
-DRAMCtrl::prechargeBank(Bank& bank, Tick pre_at, bool trace)
+DRAMCtrl::prechargeBank(Rank& rank_ref, Bank& bank, Tick pre_at, bool trace)
{
// make sure the bank has an open row
assert(bank.openRow != Bank::NO_ROW);
@@ -994,19 +1004,20 @@ DRAMCtrl::prechargeBank(Bank& bank, Tick pre_at, bool trace)
bank.actAllowedAt = std::max(bank.actAllowedAt, pre_done_at);
- assert(numBanksActive != 0);
- --numBanksActive;
+ assert(rank_ref.numBanksActive != 0);
+ --rank_ref.numBanksActive;
DPRINTF(DRAM, "Precharging bank %d, rank %d at tick %lld, now got "
- "%d active\n", bank.bank, bank.rank, pre_at, numBanksActive);
+ "%d active\n", bank.bank, rank_ref.rank, pre_at,
+ rank_ref.numBanksActive);
if (trace) {
- rankPower[bank.rank].powerlib.doCommand(MemCommand::PRE, bank.bank,
+ rank_ref.power.powerlib.doCommand(MemCommand::PRE, bank.bank,
divCeil(pre_at, tCK) -
timeStampOffset);
DPRINTF(DRAMPower, "%llu,PRE,%d,%d\n", divCeil(pre_at, tCK) -
- timeStampOffset, bank.bank, bank.rank);
+ timeStampOffset, bank.bank, rank_ref.rank);
}
// if we look at the current number of active banks we might be
// tempted to think the DRAM is now idle, however this can be
@@ -1014,22 +1025,10 @@ DRAMCtrl::prechargeBank(Bank& bank, Tick pre_at, bool trace)
// would have reached the idle state, so schedule an event and
// rather check once we actually make it to the point in time when
// the (last) precharge takes place
- if (!prechargeEvent.scheduled())
- schedule(prechargeEvent, pre_done_at);
- else if (prechargeEvent.when() < pre_done_at)
- reschedule(prechargeEvent, pre_done_at);
-}
-
-void
-DRAMCtrl::processPrechargeEvent()
-{
- // if we reached zero, then special conditions apply as we track
- // if all banks are precharged for the power models
- if (numBanksActive == 0) {
- // we should transition to the idle state when the last bank
- // is precharged
- schedulePowerEvent(PWR_IDLE, curTick());
- }
+ if (!rank_ref.prechargeEvent.scheduled())
+ schedule(rank_ref.prechargeEvent, pre_done_at);
+ else if (rank_ref.prechargeEvent.when() < pre_done_at)
+ reschedule(rank_ref.prechargeEvent, pre_done_at);
}
void
@@ -1038,6 +1037,9 @@ DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
DPRINTF(DRAM, "Timing access to addr %lld, rank/bank/row %d %d %d\n",
dram_pkt->addr, dram_pkt->rank, dram_pkt->bank, dram_pkt->row);
+ // get the rank
+ Rank& rank = dram_pkt->rankRef;
+
// get the bank
Bank& bank = dram_pkt->bankRef;
@@ -1055,7 +1057,7 @@ DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
// If there is a page open, precharge it.
if (bank.openRow != Bank::NO_ROW) {
- prechargeBank(bank, std::max(bank.preAllowedAt, curTick()));
+ prechargeBank(rank, bank, std::max(bank.preAllowedAt, curTick()));
}
// next we need to account for the delay in activating the
@@ -1064,7 +1066,7 @@ DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
// Record the activation and deal with all the global timing
// constraints caused be a new activation (tRRD and tXAW)
- activateBank(bank, act_tick, dram_pkt->row);
+ activateBank(rank, bank, act_tick, dram_pkt->row);
// issue the command as early as possible
cmd_at = bank.colAllowedAt;
@@ -1089,7 +1091,8 @@ DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
// before tCCD_L. Different bank group timing requirement is
// tBURST; Add tCS for different ranks
if (dram_pkt->rank == j) {
- if (bankGroupArch && (bank.bankgr == banks[j][i].bankgr)) {
+ if (bankGroupArch &&
+ (bank.bankgr == ranks[j]->banks[i].bankgr)) {
// bank group architecture requires longer delays between
// RD/WR burst commands to the same bank group.
// Use tCCD_L in this case
@@ -1108,8 +1111,8 @@ DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
// Add tCS to account for rank-to-rank bus delay requirements
cmd_dly = tBURST + tCS;
}
- banks[j][i].colAllowedAt = std::max(cmd_at + cmd_dly,
- banks[j][i].colAllowedAt);
+ ranks[j]->banks[i].colAllowedAt = std::max(cmd_at + cmd_dly,
+ ranks[j]->banks[i].colAllowedAt);
}
}
@@ -1188,7 +1191,7 @@ DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
if (auto_precharge) {
// if auto-precharge push a PRE command at the correct tick to the
// list used by DRAMPower library to calculate power
- prechargeBank(bank, std::max(curTick(), bank.preAllowedAt));
+ prechargeBank(rank, bank, std::max(curTick(), bank.preAllowedAt));
DPRINTF(DRAM, "Auto-precharged bank: %d\n", dram_pkt->bankId);
}
@@ -1199,7 +1202,7 @@ DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
DPRINTF(DRAM, "Access to %lld, ready at %lld bus busy until %lld.\n",
dram_pkt->addr, dram_pkt->readyTime, busBusyUntil);
- rankPower[dram_pkt->rank].powerlib.doCommand(command, dram_pkt->bank,
+ dram_pkt->rankRef.power.powerlib.doCommand(command, dram_pkt->bank,
divCeil(cmd_at, tCK) -
timeStampOffset);
@@ -1236,6 +1239,25 @@ DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
void
DRAMCtrl::processNextReqEvent()
{
+ int busyRanks = 0;
+ for (auto r : ranks) {
+ if (!r->isAvailable()) {
+ // rank is busy refreshing
+ busyRanks++;
+
+ // let the rank know that if it was waiting to drain, it
+ // is now done and ready to proceed
+ r->checkDrainDone();
+ }
+ }
+
+ if (busyRanks == ranksPerChannel) {
+ // if all ranks are refreshing wait for them to finish
+ // and stall this state machine without taking any further
+ // action, and do not schedule a new nextReqEvent
+ return;
+ }
+
// pre-emptively set to false. Overwrite if in READ_TO_WRITE
// or WRITE_TO_READ state
bool switched_cmd_type = false;
@@ -1262,22 +1284,6 @@ DRAMCtrl::processNextReqEvent()
switched_cmd_type = true;
}
- if (refreshState != REF_IDLE) {
- // if a refresh waiting for this event loop to finish, then hand
- // over now, and do not schedule a new nextReqEvent
- if (refreshState == REF_DRAIN) {
- DPRINTF(DRAM, "Refresh drain done, now precharging\n");
-
- refreshState = REF_PRE;
-
- // hand control back to the refresh event loop
- schedule(refreshEvent, curTick());
- }
-
- // let the refresh finish before issuing any further requests
- return;
- }
-
// when we get here it is either a read or a write
if (busState == READ) {
@@ -1305,12 +1311,23 @@ DRAMCtrl::processNextReqEvent()
return;
}
} else {
+ // bool to check if there is a read to a free rank
+ bool found_read = false;
+
// Figure out which read request goes next, and move it to the
// front of the read queue
- chooseNext(readQueue, switched_cmd_type);
+ found_read = chooseNext(readQueue, switched_cmd_type);
+
+ // if no read to an available rank is found then return
+ // at this point. There could be writes to the available ranks
+ // which are above the required threshold. However, to
+ // avoid adding more complexity to the code, return and wait
+ // for a refresh event to kick things into action again.
+ if (!found_read)
+ return;
DRAMPacket* dram_pkt = readQueue.front();
-
+ assert(dram_pkt->rankRef.isAvailable());
// here we get a bit creative and shift the bus busy time not
// just the tWTR, but also a CAS latency to capture the fact
// that we are allowed to prepare a new bank, but not issue a
@@ -1355,8 +1372,20 @@ DRAMCtrl::processNextReqEvent()
busState = READ_TO_WRITE;
}
} else {
- chooseNext(writeQueue, switched_cmd_type);
+ // bool to check if write to free rank is found
+ bool found_write = false;
+
+ found_write = chooseNext(writeQueue, switched_cmd_type);
+
+ // if no writes to an available rank are found then return.
+ // There could be reads to the available ranks. However, to avoid
+ // adding more complexity to the code, return at this point and wait
+ // for a refresh event to kick things into action again.
+ if (!found_write)
+ return;
+
DRAMPacket* dram_pkt = writeQueue.front();
+ assert(dram_pkt->rankRef.isAvailable());
// sanity check
assert(dram_pkt->size <= burstSize);
@@ -1390,8 +1419,10 @@ DRAMCtrl::processNextReqEvent()
// nothing to do
}
}
-
- schedule(nextReqEvent, std::max(nextReqTime, curTick()));
+ // It is possible that a refresh to another rank kicks things back into
+ // action before reaching this point.
+ if (!nextReqEvent.scheduled())
+ schedule(nextReqEvent, std::max(nextReqTime, curTick()));
// If there is space available and we have writes waiting then let
// them retry. This is done here to ensure that the retry does not
@@ -1419,23 +1450,26 @@ DRAMCtrl::minBankPrep(const deque<DRAMPacket*>& queue,
// determine if we have queued transactions targetting the
// bank in question
vector<bool> got_waiting(ranksPerChannel * banksPerRank, false);
- for (auto p = queue.begin(); p != queue.end(); ++p) {
- got_waiting[(*p)->bankId] = true;
+ for (const auto& p : queue) {
+ if(p->rankRef.isAvailable())
+ got_waiting[p->bankId] = true;
}
for (int i = 0; i < ranksPerChannel; i++) {
for (int j = 0; j < banksPerRank; j++) {
- uint8_t bank_id = i * banksPerRank + j;
+ uint16_t bank_id = i * banksPerRank + j;
// if we have waiting requests for the bank, and it is
// amongst the first available, update the mask
if (got_waiting[bank_id]) {
+ // make sure this rank is not currently refreshing.
+ assert(ranks[i]->isAvailable());
// simplistic approximation of when the bank can issue
// an activate, ignoring any rank-to-rank switching
// cost in this calculation
- Tick act_at = banks[i][j].openRow == Bank::NO_ROW ?
- banks[i][j].actAllowedAt :
- std::max(banks[i][j].preAllowedAt, curTick()) + tRP;
+ Tick act_at = ranks[i]->banks[j].openRow == Bank::NO_ROW ?
+ ranks[i]->banks[j].actAllowedAt :
+ std::max(ranks[i]->banks[j].preAllowedAt, curTick()) + tRP;
// prioritize commands that access the
// same rank as previous burst
@@ -1499,8 +1533,66 @@ DRAMCtrl::minBankPrep(const deque<DRAMPacket*>& queue,
return bank_mask;
}
+DRAMCtrl::Rank::Rank(DRAMCtrl& _memory, const DRAMCtrlParams* _p)
+ : EventManager(&_memory), memory(_memory),
+ pwrStateTrans(PWR_IDLE), pwrState(PWR_IDLE), pwrStateTick(0),
+ refreshState(REF_IDLE), refreshDueAt(0),
+ power(_p, false), numBanksActive(0),
+ activateEvent(*this), prechargeEvent(*this),
+ refreshEvent(*this), powerEvent(*this)
+{ }
+
+void
+DRAMCtrl::Rank::startup(Tick ref_tick)
+{
+ assert(ref_tick > curTick());
+
+ pwrStateTick = curTick();
+
+ // kick off the refresh, and give ourselves enough time to
+ // precharge
+ schedule(refreshEvent, ref_tick);
+}
+
+void
+DRAMCtrl::Rank::checkDrainDone()
+{
+ // if this rank was waiting to drain it is now able to proceed to
+ // precharge
+ if (refreshState == REF_DRAIN) {
+ DPRINTF(DRAM, "Refresh drain done, now precharging\n");
+
+ refreshState = REF_PRE;
+
+ // hand control back to the refresh event loop
+ schedule(refreshEvent, curTick());
+ }
+}
+
+void
+DRAMCtrl::Rank::processActivateEvent()
+{
+ // we should transition to the active state as soon as any bank is active
+ if (pwrState != PWR_ACT)
+ // note that at this point numBanksActive could be back at
+ // zero again due to a precharge scheduled in the future
+ schedulePowerEvent(PWR_ACT, curTick());
+}
+
+void
+DRAMCtrl::Rank::processPrechargeEvent()
+{
+ // if we reached zero, then special conditions apply as we track
+ // if all banks are precharged for the power models
+ if (numBanksActive == 0) {
+ // we should transition to the idle state when the last bank
+ // is precharged
+ schedulePowerEvent(PWR_IDLE, curTick());
+ }
+}
+
void
-DRAMCtrl::processRefreshEvent()
+DRAMCtrl::Rank::processRefreshEvent()
{
// when first preparing the refresh, remember when it was due
if (refreshState == REF_IDLE) {
@@ -1513,10 +1605,14 @@ DRAMCtrl::processRefreshEvent()
DPRINTF(DRAM, "Refresh due\n");
}
- // let any scheduled read or write go ahead, after which it will
+ // let any scheduled read or write to the same rank go ahead,
+ // after which it will
// hand control back to this event loop
if (refreshState == REF_DRAIN) {
- if (nextReqEvent.scheduled()) {
+ // if a request is at the moment being handled and this request is
+ // accessing the current rank then wait for it to finish
+ if ((rank == memory.activeRank)
+ && (memory.nextReqEvent.scheduled())) {
// hand control over to the request loop until it is
// evaluated next
DPRINTF(DRAM, "Refresh awaiting draining\n");
@@ -1538,39 +1634,35 @@ DRAMCtrl::processRefreshEvent()
// first determine when we can precharge
Tick pre_at = curTick();
- for (int i = 0; i < ranksPerChannel; i++) {
- for (int j = 0; j < banksPerRank; j++) {
- // respect both causality and any existing bank
- // constraints, some banks could already have a
- // (auto) precharge scheduled
- pre_at = std::max(banks[i][j].preAllowedAt, pre_at);
- }
+
+ for (auto &b : banks) {
+ // respect both causality and any existing bank
+ // constraints, some banks could already have a
+ // (auto) precharge scheduled
+ pre_at = std::max(b.preAllowedAt, pre_at);
}
- // make sure all banks are precharged, and for those that
+ // make sure all banks per rank are precharged, and for those that
// already are, update their availability
- Tick act_allowed_at = pre_at + tRP;
-
- for (int i = 0; i < ranksPerChannel; i++) {
- for (int j = 0; j < banksPerRank; j++) {
- if (banks[i][j].openRow != Bank::NO_ROW) {
- prechargeBank(banks[i][j], pre_at, false);
- } else {
- banks[i][j].actAllowedAt =
- std::max(banks[i][j].actAllowedAt, act_allowed_at);
- banks[i][j].preAllowedAt =
- std::max(banks[i][j].preAllowedAt, pre_at);
- }
+ Tick act_allowed_at = pre_at + memory.tRP;
+
+ for (auto &b : banks) {
+ if (b.openRow != Bank::NO_ROW) {
+ memory.prechargeBank(*this, b, pre_at, false);
+ } else {
+ b.actAllowedAt = std::max(b.actAllowedAt, act_allowed_at);
+ b.preAllowedAt = std::max(b.preAllowedAt, pre_at);
}
+ }
- // at the moment this affects all ranks
- rankPower[i].powerlib.doCommand(MemCommand::PREA, 0,
- divCeil(pre_at, tCK) -
- timeStampOffset);
+ // precharge all banks in rank
+ power.powerlib.doCommand(MemCommand::PREA, 0,
+ divCeil(pre_at, memory.tCK) -
+ memory.timeStampOffset);
- DPRINTF(DRAMPower, "%llu,PREA,0,%d\n", divCeil(pre_at, tCK) -
- timeStampOffset, i);
- }
+ DPRINTF(DRAMPower, "%llu,PREA,0,%d\n",
+ divCeil(pre_at, memory.tCK) -
+ memory.timeStampOffset, rank);
} else {
DPRINTF(DRAM, "All banks already precharged, starting refresh\n");
@@ -1595,52 +1687,50 @@ DRAMCtrl::processRefreshEvent()
assert(numBanksActive == 0);
assert(pwrState == PWR_REF);
- Tick ref_done_at = curTick() + tRFC;
+ Tick ref_done_at = curTick() + memory.tRFC;
- for (int i = 0; i < ranksPerChannel; i++) {
- for (int j = 0; j < banksPerRank; j++) {
- banks[i][j].actAllowedAt = ref_done_at;
- }
-
- // at the moment this affects all ranks
- rankPower[i].powerlib.doCommand(MemCommand::REF, 0,
- divCeil(curTick(), tCK) -
- timeStampOffset);
-
- // at the moment sort the list of commands and update the counters
- // for DRAMPower libray when doing a refresh
- sort(rankPower[i].powerlib.cmdList.begin(),
- rankPower[i].powerlib.cmdList.end(), DRAMCtrl::sortTime);
-
- // update the counters for DRAMPower, passing false to
- // indicate that this is not the last command in the
- // list. DRAMPower requires this information for the
- // correct calculation of the background energy at the end
- // of the simulation. Ideally we would want to call this
- // function with true once at the end of the
- // simulation. However, the discarded energy is extremly
- // small and does not effect the final results.
- rankPower[i].powerlib.updateCounters(false);
-
- // call the energy function
- rankPower[i].powerlib.calcEnergy();
-
- // Update the stats
- updatePowerStats(i);
-
- DPRINTF(DRAMPower, "%llu,REF,0,%d\n", divCeil(curTick(), tCK) -
- timeStampOffset, i);
+ for (auto &b : banks) {
+ b.actAllowedAt = ref_done_at;
}
+ // at the moment this affects all ranks
+ power.powerlib.doCommand(MemCommand::REF, 0,
+ divCeil(curTick(), memory.tCK) -
+ memory.timeStampOffset);
+
+ // at the moment sort the list of commands and update the counters
+ // for DRAMPower libray when doing a refresh
+ sort(power.powerlib.cmdList.begin(),
+ power.powerlib.cmdList.end(), DRAMCtrl::sortTime);
+
+ // update the counters for DRAMPower, passing false to
+ // indicate that this is not the last command in the
+ // list. DRAMPower requires this information for the
+ // correct calculation of the background energy at the end
+ // of the simulation. Ideally we would want to call this
+ // function with true once at the end of the
+ // simulation. However, the discarded energy is extremly
+ // small and does not effect the final results.
+ power.powerlib.updateCounters(false);
+
+ // call the energy function
+ power.powerlib.calcEnergy();
+
+ // Update the stats
+ updatePowerStats();
+
+ DPRINTF(DRAMPower, "%llu,REF,0,%d\n", divCeil(curTick(), memory.tCK) -
+ memory.timeStampOffset, rank);
+
// make sure we did not wait so long that we cannot make up
// for it
- if (refreshDueAt + tREFI < ref_done_at) {
+ if (refreshDueAt + memory.tREFI < ref_done_at) {
fatal("Refresh was delayed so long we cannot catch up\n");
}
// compensate for the delay in actually performing the refresh
// when scheduling the next one
- schedule(refreshEvent, refreshDueAt + tREFI - tRP);
+ schedule(refreshEvent, refreshDueAt + memory.tREFI - memory.tRP);
assert(!powerEvent.scheduled());
@@ -1650,12 +1740,12 @@ DRAMCtrl::processRefreshEvent()
schedulePowerEvent(PWR_IDLE, ref_done_at);
DPRINTF(DRAMState, "Refresh done at %llu and next refresh at %llu\n",
- ref_done_at, refreshDueAt + tREFI);
+ ref_done_at, refreshDueAt + memory.tREFI);
}
}
void
-DRAMCtrl::schedulePowerEvent(PowerState pwr_state, Tick tick)
+DRAMCtrl::Rank::schedulePowerEvent(PowerState pwr_state, Tick tick)
{
// respect causality
assert(tick >= curTick());
@@ -1676,7 +1766,7 @@ DRAMCtrl::schedulePowerEvent(PowerState pwr_state, Tick tick)
}
void
-DRAMCtrl::processPowerEvent()
+DRAMCtrl::Rank::processPowerEvent()
{
// remember where we were, and for how long
Tick duration = curTick() - pwrStateTick;
@@ -1698,8 +1788,10 @@ DRAMCtrl::processPowerEvent()
// kick things into action again
refreshState = REF_IDLE;
- assert(!nextReqEvent.scheduled());
- schedule(nextReqEvent, curTick());
+ // a request event could be already scheduled by the state
+ // machine of the other rank
+ if (!memory.nextReqEvent.scheduled())
+ schedule(memory.nextReqEvent, curTick());
} else {
assert(prev_state == PWR_ACT);
@@ -1730,32 +1822,87 @@ DRAMCtrl::processPowerEvent()
}
void
-DRAMCtrl::updatePowerStats(uint8_t rank)
+DRAMCtrl::Rank::updatePowerStats()
{
// Get the energy and power from DRAMPower
Data::MemoryPowerModel::Energy energy =
- rankPower[rank].powerlib.getEnergy();
- Data::MemoryPowerModel::Power power =
- rankPower[rank].powerlib.getPower();
-
- actEnergy[rank] = energy.act_energy * devicesPerRank;
- preEnergy[rank] = energy.pre_energy * devicesPerRank;
- readEnergy[rank] = energy.read_energy * devicesPerRank;
- writeEnergy[rank] = energy.write_energy * devicesPerRank;
- refreshEnergy[rank] = energy.ref_energy * devicesPerRank;
- actBackEnergy[rank] = energy.act_stdby_energy * devicesPerRank;
- preBackEnergy[rank] = energy.pre_stdby_energy * devicesPerRank;
- totalEnergy[rank] = energy.total_energy * devicesPerRank;
- averagePower[rank] = power.average_power * devicesPerRank;
+ power.powerlib.getEnergy();
+ Data::MemoryPowerModel::Power rank_power =
+ power.powerlib.getPower();
+
+ actEnergy = energy.act_energy * memory.devicesPerRank;
+ preEnergy = energy.pre_energy * memory.devicesPerRank;
+ readEnergy = energy.read_energy * memory.devicesPerRank;
+ writeEnergy = energy.write_energy * memory.devicesPerRank;
+ refreshEnergy = energy.ref_energy * memory.devicesPerRank;
+ actBackEnergy = energy.act_stdby_energy * memory.devicesPerRank;
+ preBackEnergy = energy.pre_stdby_energy * memory.devicesPerRank;
+ totalEnergy = energy.total_energy * memory.devicesPerRank;
+ averagePower = rank_power.average_power * memory.devicesPerRank;
}
void
+DRAMCtrl::Rank::regStats()
+{
+ using namespace Stats;
+
+ pwrStateTime
+ .init(5)
+ .name(name() + ".memoryStateTime")
+ .desc("Time in different power states");
+ pwrStateTime.subname(0, "IDLE");
+ pwrStateTime.subname(1, "REF");
+ pwrStateTime.subname(2, "PRE_PDN");
+ pwrStateTime.subname(3, "ACT");
+ pwrStateTime.subname(4, "ACT_PDN");
+
+ actEnergy
+ .name(name() + ".actEnergy")
+ .desc("Energy for activate commands per rank (pJ)");
+
+ preEnergy
+ .name(name() + ".preEnergy")
+ .desc("Energy for precharge commands per rank (pJ)");
+
+ readEnergy
+ .name(name() + ".readEnergy")
+ .desc("Energy for read commands per rank (pJ)");
+
+ writeEnergy
+ .name(name() + ".writeEnergy")
+ .desc("Energy for write commands per rank (pJ)");
+
+ refreshEnergy
+ .name(name() + ".refreshEnergy")
+ .desc("Energy for refresh commands per rank (pJ)");
+
+ actBackEnergy
+ .name(name() + ".actBackEnergy")
+ .desc("Energy for active background per rank (pJ)");
+
+ preBackEnergy
+ .name(name() + ".preBackEnergy")
+ .desc("Energy for precharge background per rank (pJ)");
+
+ totalEnergy
+ .name(name() + ".totalEnergy")
+ .desc("Total energy per rank (pJ)");
+
+ averagePower
+ .name(name() + ".averagePower")
+ .desc("Core power per rank (mW)");
+}
+void
DRAMCtrl::regStats()
{
using namespace Stats;
AbstractMemory::regStats();
+ for (auto r : ranks) {
+ r->regStats();
+ }
+
readReqs
.name(name() + ".readReqs")
.desc("Number of read requests accepted");
@@ -1967,7 +2114,6 @@ DRAMCtrl::regStats()
.name(name() + ".busUtil")
.desc("Data bus utilization in percentage")
.precision(2);
-
busUtil = (avgRdBW + avgWrBW) / peakBW * 100;
totGap
@@ -2003,61 +2149,6 @@ DRAMCtrl::regStats()
pageHitRate = (writeRowHits + readRowHits) /
(writeBursts - mergedWrBursts + readBursts - servicedByWrQ) * 100;
-
- pwrStateTime
- .init(5)
- .name(name() + ".memoryStateTime")
- .desc("Time in different power states");
- pwrStateTime.subname(0, "IDLE");
- pwrStateTime.subname(1, "REF");
- pwrStateTime.subname(2, "PRE_PDN");
- pwrStateTime.subname(3, "ACT");
- pwrStateTime.subname(4, "ACT_PDN");
-
- actEnergy
- .init(ranksPerChannel)
- .name(name() + ".actEnergy")
- .desc("Energy for activate commands per rank (pJ)");
-
- preEnergy
- .init(ranksPerChannel)
- .name(name() + ".preEnergy")
- .desc("Energy for precharge commands per rank (pJ)");
-
- readEnergy
- .init(ranksPerChannel)
- .name(name() + ".readEnergy")
- .desc("Energy for read commands per rank (pJ)");
-
- writeEnergy
- .init(ranksPerChannel)
- .name(name() + ".writeEnergy")
- .desc("Energy for write commands per rank (pJ)");
-
- refreshEnergy
- .init(ranksPerChannel)
- .name(name() + ".refreshEnergy")
- .desc("Energy for refresh commands per rank (pJ)");
-
- actBackEnergy
- .init(ranksPerChannel)
- .name(name() + ".actBackEnergy")
- .desc("Energy for active background per rank (pJ)");
-
- preBackEnergy
- .init(ranksPerChannel)
- .name(name() + ".preBackEnergy")
- .desc("Energy for precharge background per rank (pJ)");
-
- totalEnergy
- .init(ranksPerChannel)
- .name(name() + ".totalEnergy")
- .desc("Total energy per rank (pJ)");
-
- averagePower
- .init(ranksPerChannel)
- .name(name() + ".averagePower")
- .desc("Core power per rank (mW)");
}
void