summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mem/dram_ctrl.cc286
-rw-r--r--src/mem/dram_ctrl.hh54
2 files changed, 242 insertions, 98 deletions
diff --git a/src/mem/dram_ctrl.cc b/src/mem/dram_ctrl.cc
index c701ac616..13ab60e35 100644
--- a/src/mem/dram_ctrl.cc
+++ b/src/mem/dram_ctrl.cc
@@ -56,8 +56,8 @@ DRAMCtrl::DRAMCtrl(const DRAMCtrlParams* p) :
port(name() + ".port", *this),
retryRdReq(false), retryWrReq(false),
rowHitFlag(false), busState(READ),
- respondEvent(this),
- refreshEvent(this), nextReqEvent(this), drainManager(NULL),
+ respondEvent(this), refreshEvent(this),
+ nextReqEvent(this), drainManager(NULL),
deviceBusWidth(p->device_bus_width), burstLength(p->burst_length),
deviceRowBufferSize(p->device_rowbuffer_size),
devicesPerRank(p->devices_per_rank),
@@ -81,8 +81,8 @@ DRAMCtrl::DRAMCtrl(const DRAMCtrlParams* p) :
maxAccessesPerRow(p->max_accesses_per_row),
frontendLatency(p->static_frontend_latency),
backendLatency(p->static_backend_latency),
- busBusyUntil(0), prevArrival(0),
- nextReqTime(0), startTickPrechargeAll(0), numBanksActive(0)
+ busBusyUntil(0), refreshDueAt(0), refreshState(REF_IDLE), prevArrival(0),
+ nextReqTime(0), idleStartTick(0), numBanksActive(0)
{
// create the bank states based on the dimensions of the ranks and
// banks
@@ -131,6 +131,12 @@ DRAMCtrl::DRAMCtrl(const DRAMCtrlParams* p) :
"address map\n", name());
}
}
+
+ // some basic sanity checks
+ if (tREFI <= tRP || tREFI <= tRFC) {
+ fatal("tREFI (%d) must be larger than tRP (%d) and tRFC (%d)\n",
+ tREFI, tRP, tRFC);
+ }
}
void
@@ -148,7 +154,7 @@ DRAMCtrl::startup()
{
// update the start tick for the precharge accounting to the
// current tick
- startTickPrechargeAll = curTick();
+ idleStartTick = curTick();
// shift the bus busy time sufficiently far ahead that we never
// have to worry about negative values when computing the time for
@@ -159,8 +165,9 @@ DRAMCtrl::startup()
// print the configuration of the controller
printParams();
- // kick off the refresh
- schedule(refreshEvent, curTick() + tREFI);
+ // kick off the refresh, and give ourselves enough time to
+ // precharge
+ schedule(refreshEvent, curTick() + tREFI - tRP);
}
Tick
@@ -835,7 +842,7 @@ DRAMCtrl::estimateLatency(DRAMPacket* dram_pkt, Tick inTime)
// If the there is no open row (open adaptive), then there
// is no precharge delay, otherwise go with tRP
- Tick precharge_delay = bank.openRow == -1 ? 0 : tRP;
+ Tick precharge_delay = bank.openRow == Bank::NO_ROW ? 0 : tRP;
//The bank is free, and you may be able to activate
potentialActTick = inTime + accLat + precharge_delay;
@@ -870,28 +877,39 @@ DRAMCtrl::estimateLatency(DRAMPacket* dram_pkt, Tick inTime)
}
void
-DRAMCtrl::recordActivate(Tick act_tick, uint8_t rank, uint8_t bank)
+DRAMCtrl::recordActivate(Tick act_tick, uint8_t rank, uint8_t bank,
+ uint16_t row)
{
assert(0 <= rank && rank < ranksPerChannel);
assert(actTicks[rank].size() == activationLimit);
DPRINTF(DRAM, "Activate at tick %d\n", act_tick);
- // Tracking accesses after all banks are precharged.
- // startTickPrechargeAll: is the tick when all the banks were again
- // precharged. The difference between act_tick and startTickPrechargeAll
- // gives the time for which DRAM doesn't get any accesses after refreshing
- // or after a page is closed in closed-page or open-adaptive-page policy.
- if ((numBanksActive == 0) && (act_tick > startTickPrechargeAll)) {
- prechargeAllTime += act_tick - startTickPrechargeAll;
+ // idleStartTick is the tick when all the banks were
+ // precharged. Thus, the difference between act_tick and
+ // idleStartTick gives the time for which the DRAM is in an idle
+ // state with all banks precharged. Note that we may end up
+ // "changing history" by scheduling an activation before an
+ // already scheduled precharge, effectively canceling it out.
+ if (numBanksActive == 0 && act_tick > idleStartTick) {
+ prechargeAllTime += act_tick - idleStartTick;
}
- // No need to update number of active banks for closed-page policy as only 1
- // bank will be activated at any given point, which will be instatntly
- // precharged
- if (pageMgmt == Enums::open || pageMgmt == Enums::open_adaptive ||
- pageMgmt == Enums::close_adaptive)
- ++numBanksActive;
+ // update the open row
+ assert(banks[rank][bank].openRow == Bank::NO_ROW);
+ banks[rank][bank].openRow = row;
+
+ // start counting anew, this covers both the case when we
+ // auto-precharged, and when this access is forced to
+ // precharge
+ banks[rank][bank].bytesAccessed = 0;
+ banks[rank][bank].rowAccesses = 0;
+
+ ++numBanksActive;
+ assert(numBanksActive <= banksPerRank * ranksPerChannel);
+
+ DPRINTF(DRAM, "Activate bank at tick %lld, now got %d active\n",
+ act_tick, numBanksActive);
// start by enforcing tRRD
for(int i = 0; i < banksPerRank; i++) {
@@ -936,6 +954,35 @@ DRAMCtrl::recordActivate(Tick act_tick, uint8_t rank, uint8_t bank)
}
void
+DRAMCtrl::prechargeBank(Bank& bank, Tick free_at)
+{
+ // make sure the bank has an open row
+ assert(bank.openRow != Bank::NO_ROW);
+
+ // sample the bytes per activate here since we are closing
+ // the page
+ bytesPerActivate.sample(bank.bytesAccessed);
+
+ bank.openRow = Bank::NO_ROW;
+
+ bank.freeAt = free_at;
+
+ assert(numBanksActive != 0);
+ --numBanksActive;
+
+ DPRINTF(DRAM, "Precharged bank, done at tick %lld, now got %d active\n",
+ bank.freeAt, numBanksActive);
+
+ // if we reached zero, then special conditions apply as we track
+ // if all banks are precharged for the power models
+ if (numBanksActive == 0) {
+ idleStartTick = std::max(idleStartTick, bank.freeAt);
+ DPRINTF(DRAM, "All banks precharged at tick: %ld\n",
+ idleStartTick);
+ }
+}
+
+void
DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
{
@@ -961,30 +1008,30 @@ DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
// Update bank state
if (pageMgmt == Enums::open || pageMgmt == Enums::open_adaptive ||
pageMgmt == Enums::close_adaptive) {
- bank.freeAt = curTick() + addDelay + accessLat;
- // If you activated a new row do to this access, the next access
- // will have to respect tRAS for this bank.
- if (!rowHitFlag) {
+ if (rowHitFlag) {
+ bank.freeAt = curTick() + addDelay + accessLat;
+ } else {
+ // If there is a page open, precharge it.
+ if (bank.openRow != Bank::NO_ROW) {
+ prechargeBank(bank, std::max(std::max(bank.freeAt,
+ bank.tRASDoneAt),
+ curTick()) + tRP);
+ }
+
+ // Any precharge is already part of the latency
+ // estimation, so update the bank free time
+ bank.freeAt = curTick() + addDelay + accessLat;
+
// any waiting for banks account for in freeAt
actTick = bank.freeAt - tCL - tRCD;
+
+ // If you activated a new row do to this access, the next access
+ // will have to respect tRAS for this bank
bank.tRASDoneAt = actTick + tRAS;
- recordActivate(actTick, dram_pkt->rank, dram_pkt->bank);
-
- // if we closed an open row as a result of this access,
- // then sample the number of bytes accessed before
- // resetting it
- if (bank.openRow != -1)
- bytesPerActivate.sample(bank.bytesAccessed);
-
- // update the open row
- bank.openRow = dram_pkt->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;
+
+ recordActivate(actTick, dram_pkt->rank, dram_pkt->bank,
+ dram_pkt->row);
}
// increment the bytes accessed and the accesses per row
@@ -1042,19 +1089,7 @@ DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
// if this access should use auto-precharge, then we are
// closing the row
if (auto_precharge) {
- bank.openRow = -1;
- bank.freeAt = std::max(bank.freeAt, bank.tRASDoneAt) + tRP;
- --numBanksActive;
- if (numBanksActive == 0) {
- startTickPrechargeAll = std::max(startTickPrechargeAll,
- bank.freeAt);
- DPRINTF(DRAM, "All banks precharged at tick: %ld\n",
- startTickPrechargeAll);
- }
-
- // sample the bytes per activate here since we are closing
- // the page
- bytesPerActivate.sample(bank.bytesAccessed);
+ prechargeBank(bank, std::max(bank.freeAt, bank.tRASDoneAt) + tRP);
DPRINTF(DRAM, "Auto-precharged bank: %d\n", dram_pkt->bankId);
}
@@ -1062,16 +1097,17 @@ DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
DPRINTF(DRAM, "doDRAMAccess::bank.freeAt is %lld\n", bank.freeAt);
} else if (pageMgmt == Enums::close) {
actTick = curTick() + addDelay + accessLat - tRCD - tCL;
- recordActivate(actTick, dram_pkt->rank, dram_pkt->bank);
+ recordActivate(actTick, dram_pkt->rank, dram_pkt->bank, dram_pkt->row);
+
+ bank.freeAt = actTick + tRCD + tCL;
+ bank.tRASDoneAt = actTick + tRAS;
- // If the DRAM has a very quick tRAS, bank can be made free
- // after consecutive tCL,tRCD,tRP times. In general, however,
- // an additional wait is required to respect tRAS.
- bank.freeAt = std::max(actTick + tRAS + tRP,
- actTick + tRCD + tCL + tRP);
+ // sample the relevant values when precharging
+ bank.bytesAccessed = burstSize;
+ bank.rowAccesses = 1;
+
+ prechargeBank(bank, std::max(bank.freeAt, bank.tRASDoneAt) + tRP);
DPRINTF(DRAM, "doDRAMAccess::bank.freeAt is %lld\n", bank.freeAt);
- bytesPerActivate.sample(burstSize);
- startTickPrechargeAll = std::max(startTickPrechargeAll, bank.freeAt);
} else
panic("No page management policy chosen\n");
@@ -1187,6 +1223,22 @@ DRAMCtrl::processNextReqEvent()
busState = READ;
}
+ 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) {
@@ -1298,18 +1350,6 @@ DRAMCtrl::processNextReqEvent()
}
}
-Tick
-DRAMCtrl::maxBankFreeAt() const
-{
- Tick banksFree = 0;
-
- for(int i = 0; i < ranksPerChannel; i++)
- for(int j = 0; j < banksPerRank; j++)
- banksFree = std::max(banks[i][j].freeAt, banksFree);
-
- return banksFree;
-}
-
uint64_t
DRAMCtrl::minBankFreeAt(const deque<DRAMPacket*>& queue) const
{
@@ -1345,21 +1385,97 @@ DRAMCtrl::minBankFreeAt(const deque<DRAMPacket*>& queue) const
void
DRAMCtrl::processRefreshEvent()
{
- DPRINTF(DRAM, "Refreshing at tick %ld\n", curTick());
+ // when first preparing the refresh, remember when it was due
+ if (refreshState == REF_IDLE) {
+ // remember when the refresh is due
+ refreshDueAt = curTick();
+
+ // proceed to drain
+ refreshState = REF_DRAIN;
+
+ DPRINTF(DRAM, "Refresh due\n");
+ }
+
+ // let any scheduled read or write go ahead, after which it will
+ // hand control back to this event loop
+ if (refreshState == REF_DRAIN) {
+ if (nextReqEvent.scheduled()) {
+ // hand control over to the request loop until it is
+ // evaluated next
+ DPRINTF(DRAM, "Refresh awaiting draining\n");
+
+ return;
+ } else {
+ refreshState = REF_PRE;
+ }
+ }
- Tick banksFree = std::max(curTick(), maxBankFreeAt()) + tRFC;
+ // at this point, ensure that all banks are precharged
+ if (refreshState == REF_PRE) {
+ DPRINTF(DRAM, "Precharging all\n");
+
+ // precharge any active bank
+ for (int i = 0; i < ranksPerChannel; i++) {
+ for (int j = 0; j < banksPerRank; j++) {
+ if (banks[i][j].openRow != Bank::NO_ROW) {
+ // respect both causality and any existing bank
+ // constraints
+ Tick free_at = std::max(std::max(banks[i][j].freeAt,
+ banks[i][j].tRASDoneAt),
+ curTick()) + tRP;
+
+ prechargeBank(banks[i][j], free_at);
+ }
+ }
+ }
+
+ if (numBanksActive != 0)
+ panic("Refresh scheduled with %d active banks\n", numBanksActive);
+
+ // advance the state
+ refreshState = REF_RUN;
+
+ // call ourselves in the future
+ schedule(refreshEvent, std::max(curTick(), idleStartTick));
+ return;
+ }
- for(int i = 0; i < ranksPerChannel; i++)
- for(int j = 0; j < banksPerRank; j++) {
- banks[i][j].freeAt = banksFree;
- banks[i][j].openRow = -1;
+ // last but not least we perform the actual refresh
+ if (refreshState == REF_RUN) {
+ // should never get here with any banks active
+ assert(numBanksActive == 0);
+
+ Tick banksFree = curTick() + tRFC;
+
+ for (int i = 0; i < ranksPerChannel; i++) {
+ for (int j = 0; j < banksPerRank; j++) {
+ banks[i][j].freeAt = banksFree;
+ }
+ }
+
+ // make sure we did not wait so long that we cannot make up
+ // for it
+ if (refreshDueAt + tREFI < banksFree) {
+ fatal("Refresh was delayed so long we cannot catch up\n");
}
- // updating startTickPrechargeAll, isprechargeAll
- numBanksActive = 0;
- startTickPrechargeAll = banksFree;
+ // compensate for the delay in actually performing the refresh
+ // when scheduling the next one
+ schedule(refreshEvent, refreshDueAt + tREFI - tRP);
+
+ // back to business as usual
+ refreshState = REF_IDLE;
- schedule(refreshEvent, curTick() + tREFI);
+ // we are now refreshing until tRFC is done
+ idleStartTick = banksFree;
+
+ // kick the normal request processing loop into action again
+ // as early as possible, i.e. when the request is done, the
+ // scheduling of this event also prevents any new requests
+ // from going ahead before the scheduled point in time
+ nextReqTime = banksFree;
+ schedule(nextReqEvent, nextReqTime);
+ }
}
void
diff --git a/src/mem/dram_ctrl.hh b/src/mem/dram_ctrl.hh
index 8f2e4825e..af802374a 100644
--- a/src/mem/dram_ctrl.hh
+++ b/src/mem/dram_ctrl.hh
@@ -153,7 +153,7 @@ class DRAMCtrl : public AbstractMemory
public:
- static const uint32_t INVALID_ROW = -1;
+ static const uint32_t NO_ROW = -1;
uint32_t openRow;
@@ -165,7 +165,7 @@ class DRAMCtrl : public AbstractMemory
uint32_t bytesAccessed;
Bank() :
- openRow(INVALID_ROW), freeAt(0), tRASDoneAt(0), actAllowedAt(0),
+ openRow(NO_ROW), freeAt(0), tRASDoneAt(0), actAllowedAt(0),
rowAccesses(0), bytesAccessed(0)
{ }
};
@@ -389,14 +389,6 @@ class DRAMCtrl : public AbstractMemory
void reorderQueue(std::deque<DRAMPacket*>& queue);
/**
- * Looking at all banks, determine the moment in time when they
- * are all free.
- *
- * @return The tick when all banks are free
- */
- Tick maxBankFreeAt() const;
-
- /**
* Find which are the earliest available banks for the enqueued
* requests. Assumes maximum of 64 banks per DIMM
*
@@ -411,7 +403,18 @@ class DRAMCtrl : public AbstractMemory
* method updates the time that the banks become available based
* on the current limits.
*/
- void recordActivate(Tick act_tick, uint8_t rank, uint8_t bank);
+ void recordActivate(Tick act_tick, uint8_t rank, uint8_t bank,
+ uint16_t row);
+
+ /**
+ * Precharge a given bank and also update when the precharge is
+ * done. This will also deal with any stats related to the
+ * accesses to the open page.
+ *
+ * @param bank The bank to precharge
+ * @param free_at Time when the precharge is done
+ */
+ void prechargeBank(Bank& bank, Tick free_at);
void printParams() const;
@@ -523,6 +526,29 @@ class DRAMCtrl : public AbstractMemory
*/
Tick busBusyUntil;
+ /**
+ * Keep track of when a refresh is due.
+ */
+ Tick refreshDueAt;
+
+ /**
+ * The refresh state is used to control the progress of the
+ * refresh scheduling. When normal operation is in progress the
+ * refresh state is idle. From there, it progresses to the refresh
+ * drain state once tREFI has passed. The refresh drain state
+ * captures the DRAM row active state, as it will stay there until
+ * all ongoing accesses complete. Thereafter all banks are
+ * precharged, and lastly, the DRAM is refreshed.
+ */
+ enum RefreshState {
+ REF_IDLE = 0,
+ REF_DRAIN,
+ REF_PRE,
+ REF_RUN
+ };
+
+ RefreshState refreshState;
+
Tick prevArrival;
/**
@@ -597,8 +623,10 @@ class DRAMCtrl : public AbstractMemory
Stats::Formula prechargeAllPercent;
Stats::Scalar prechargeAllTime;
- // To track number of cycles all the banks are precharged
- Tick startTickPrechargeAll;
+ // To track number of cycles the DRAM is idle, i.e. all the banks
+ // are precharged
+ Tick idleStartTick;
+
// To track number of banks which are currently active
unsigned int numBanksActive;