#include "base/bitfield.hh"
#include "base/trace.hh"
#include "debug/DRAM.hh"
+#include "debug/DRAMState.hh"
#include "debug/Drain.hh"
#include "mem/dram_ctrl.hh"
#include "sim/system.hh"
port(name() + ".port", *this),
retryRdReq(false), retryWrReq(false),
rowHitFlag(false), busState(READ),
- respondEvent(this), refreshEvent(this),
- nextReqEvent(this), drainManager(NULL),
+ nextReqEvent(this), respondEvent(this), activateEvent(this),
+ prechargeEvent(this), refreshEvent(this), powerEvent(this),
+ drainManager(NULL),
deviceBusWidth(p->device_bus_width), burstLength(p->burst_length),
deviceRowBufferSize(p->device_rowbuffer_size),
devicesPerRank(p->devices_per_rank),
maxAccessesPerRow(p->max_accesses_per_row),
frontendLatency(p->static_frontend_latency),
backendLatency(p->static_backend_latency),
- busBusyUntil(0), refreshDueAt(0), refreshState(REF_IDLE), prevArrival(0),
- nextReqTime(0), idleStartTick(0), numBanksActive(0)
+ busBusyUntil(0), refreshDueAt(0), refreshState(REF_IDLE),
+ pwrStateTrans(PWR_IDLE), pwrState(PWR_IDLE), prevArrival(0),
+ nextReqTime(0), pwrStateTick(0), numBanksActive(0)
{
// create the bank states based on the dimensions of the ranks and
// banks
{
// update the start tick for the precharge accounting to the
// current tick
- idleStartTick = curTick();
+ pwrStateTick = curTick();
// shift the bus busy time sufficiently far ahead that we never
// have to worry about negative values when computing the time for
DPRINTF(DRAM, "Activate at tick %d\n", act_tick);
- // 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;
- }
-
// update the open row
assert(banks[rank][bank].openRow == Bank::NO_ROW);
banks[rank][bank].openRow = row;
// next activate must not happen before tRRD
banks[rank][i].actAllowedAt = act_tick + tRRD;
}
+
// tRC should be added to activation tick of the bank currently accessed,
// where tRC = tRAS + tRP, this is just for a check as actAllowedAt for same
// bank is already captured by bank.freeAt and bank.tRASDoneAt
// next activate must not happen before end of window
banks[rank][j].actAllowedAt = actTicks[rank].back() + tXAW;
}
+
+ // 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)
+ // move it sooner in time
+ reschedule(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
DPRINTF(DRAM, "Precharged bank, done at tick %lld, now got %d active\n",
bank.freeAt, numBanksActive);
+ // 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
+ // undone by an activate that is scheduled to happen before we
+ // 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, free_at);
+ else if (prechargeEvent.when() < free_at)
+ reschedule(prechargeEvent, free_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) {
- idleStartTick = std::max(idleStartTick, bank.freeAt);
- DPRINTF(DRAM, "All banks precharged at tick: %ld\n",
- idleStartTick);
+ // we should transition to the idle state when the last bank
+ // is precharged
+ schedulePowerEvent(PWR_IDLE, curTick());
}
}
// 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);
+ // precharge any active bank if we are not already in the idle
+ // state
+ if (pwrState != PWR_IDLE) {
+ DPRINTF(DRAM, "Precharging all\n");
+ 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);
+ }
}
}
- }
+ } else {
+ DPRINTF(DRAM, "All banks already precharged, starting refresh\n");
- if (numBanksActive != 0)
- panic("Refresh scheduled with %d active banks\n", numBanksActive);
+ // go ahead and kick the power state machine into gear if
+ // we are already idle
+ schedulePowerEvent(PWR_REF, curTick());
+ }
- // advance the state
refreshState = REF_RUN;
+ assert(numBanksActive == 0);
- // call ourselves in the future
- schedule(refreshEvent, std::max(curTick(), idleStartTick));
+ // wait for all banks to be precharged, at which point the
+ // power state machine will transition to the idle state, and
+ // automatically move to a refresh, at that point it will also
+ // call this method to get the refresh event loop going again
return;
}
if (refreshState == REF_RUN) {
// should never get here with any banks active
assert(numBanksActive == 0);
+ assert(pwrState == PWR_REF);
Tick banksFree = curTick() + tRFC;
// when scheduling the next one
schedule(refreshEvent, refreshDueAt + tREFI - tRP);
- // back to business as usual
- refreshState = REF_IDLE;
+ assert(!powerEvent.scheduled());
+
+ // move to the idle power state once the refresh is done, this
+ // will also move the refresh state machine to the refresh
+ // idle state
+ schedulePowerEvent(PWR_IDLE, banksFree);
+
+ DPRINTF(DRAMState, "Refresh done at %llu and next refresh at %llu\n",
+ banksFree, refreshDueAt + tREFI);
+ }
+}
+
+void
+DRAMCtrl::schedulePowerEvent(PowerState pwr_state, Tick tick)
+{
+ // respect causality
+ assert(tick >= curTick());
+
+ if (!powerEvent.scheduled()) {
+ DPRINTF(DRAMState, "Scheduling power event at %llu to state %d\n",
+ tick, pwr_state);
+
+ // insert the new transition
+ pwrStateTrans = pwr_state;
+
+ schedule(powerEvent, tick);
+ } else {
+ panic("Scheduled power event at %llu to state %d, "
+ "with scheduled event at %llu to %d\n", tick, pwr_state,
+ powerEvent.when(), pwrStateTrans);
+ }
+}
+
+void
+DRAMCtrl::processPowerEvent()
+{
+ // remember where we were, and for how long
+ Tick duration = curTick() - pwrStateTick;
+ PowerState prev_state = pwrState;
+
+ // update the accounting
+ pwrStateTime[prev_state] += duration;
- // we are now refreshing until tRFC is done
- idleStartTick = banksFree;
+ pwrState = pwrStateTrans;
+ pwrStateTick = curTick();
- // 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);
+ if (pwrState == PWR_IDLE) {
+ DPRINTF(DRAMState, "All banks precharged\n");
+
+ // if we were refreshing, make sure we start scheduling requests again
+ if (prev_state == PWR_REF) {
+ DPRINTF(DRAMState, "Was refreshing for %llu ticks\n", duration);
+ assert(pwrState == PWR_IDLE);
+
+ // kick things into action again
+ refreshState = REF_IDLE;
+ assert(!nextReqEvent.scheduled());
+ schedule(nextReqEvent, curTick());
+ } else {
+ assert(prev_state == PWR_ACT);
+
+ // if we have a pending refresh, and are now moving to
+ // the idle state, direclty transition to a refresh
+ if (refreshState == REF_RUN) {
+ // there should be nothing waiting at this point
+ assert(!powerEvent.scheduled());
+
+ // update the state in zero time and proceed below
+ pwrState = PWR_REF;
+ }
+ }
+ }
+
+ // we transition to the refresh state, let the refresh state
+ // machine know of this state update and let it deal with the
+ // scheduling of the next power state transition as well as the
+ // following refresh
+ if (pwrState == PWR_REF) {
+ DPRINTF(DRAMState, "Refreshing\n");
+ // kick the refresh event loop into action again, and that
+ // in turn will schedule a transition to the idle power
+ // state once the refresh is done
+ assert(refreshState == REF_RUN);
+ processRefreshEvent();
}
}
pageHitRate = (writeRowHits + readRowHits) /
(writeBursts - mergedWrBursts + readBursts - servicedByWrQ) * 100;
- prechargeAllPercent
- .name(name() + ".prechargeAllPercent")
- .desc("Percentage of time for which DRAM has all the banks in "
- "precharge state")
- .precision(2);
-
- prechargeAllPercent = prechargeAllTime / simTicks * 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");
}
void
* processRespondEvent is called; no parameters are allowed
* in these methods
*/
+ void processNextReqEvent();
+ EventWrapper<DRAMCtrl,&DRAMCtrl::processNextReqEvent> nextReqEvent;
+
void processRespondEvent();
EventWrapper<DRAMCtrl, &DRAMCtrl::processRespondEvent> respondEvent;
+ void processActivateEvent();
+ EventWrapper<DRAMCtrl, &DRAMCtrl::processActivateEvent> activateEvent;
+
+ void processPrechargeEvent();
+ EventWrapper<DRAMCtrl, &DRAMCtrl::processPrechargeEvent> prechargeEvent;
+
void processRefreshEvent();
EventWrapper<DRAMCtrl, &DRAMCtrl::processRefreshEvent> refreshEvent;
- void processNextReqEvent();
- EventWrapper<DRAMCtrl,&DRAMCtrl::processNextReqEvent> nextReqEvent;
-
+ void processPowerEvent();
+ EventWrapper<DRAMCtrl,&DRAMCtrl::processPowerEvent> powerEvent;
/**
* Check if the read queue has room for more entries
RefreshState refreshState;
+ /**
+ * The power state captures the different operational states of
+ * the DRAM and interacts with the bus read/write state machine,
+ * and the refresh state machine. In the idle state all banks are
+ * precharged. From there we either go to an auto refresh (as
+ * determined by the refresh state machine), or to a precharge
+ * power down mode. From idle the memory can also go to the active
+ * state (with one or more banks active), and in turn from there
+ * to active power down. At the moment we do not capture the deep
+ * power down and self-refresh state.
+ */
+ enum PowerState {
+ PWR_IDLE = 0,
+ PWR_REF,
+ PWR_PRE_PDN,
+ PWR_ACT,
+ PWR_ACT_PDN
+ };
+
+ /**
+ * Since we are taking decisions out of order, we need to keep
+ * track of what power transition is happening at what time, such
+ * that we can go back in time and change history. For example, if
+ * we precharge all banks and schedule going to the idle state, we
+ * might at a later point decide to activate a bank before the
+ * transition to idle would have taken place.
+ */
+ PowerState pwrStateTrans;
+
+ /**
+ * Current power state.
+ */
+ PowerState pwrState;
+
+ /**
+ * Schedule a power state transition in the future, and
+ * potentially override an already scheduled transition.
+ *
+ * @param pwr_state Power state to transition to
+ * @param tick Tick when transition should take place
+ */
+ void schedulePowerEvent(PowerState pwr_state, Tick tick);
+
Tick prevArrival;
/**
// DRAM Power Calculation
Stats::Formula pageHitRate;
- Stats::Formula prechargeAllPercent;
- Stats::Scalar prechargeAllTime;
+ Stats::Vector pwrStateTime;
- // To track number of cycles the DRAM is idle, i.e. all the banks
- // are precharged
- Tick idleStartTick;
+ // Track when we transitioned to the current power state
+ Tick pwrStateTick;
// To track number of banks which are currently active
unsigned int numBanksActive;