changeset 8c6991a00515 in /z/repo/gem5
details: http://repo.gem5.org/gem5?cmd=changeset;node=8c6991a00515
description:
        mem: Add DRAM low-power functionality

        Added power-down state transitions to the DRAM controller model.

        Added per rank parameter, outstandingEvents, which tracks the number
        of outstanding command events and is used to determine when the
        controller should transition to a low power state.
        The controller will only transition when there are no outstanding events
        scheduled and the number of command entries for the given rank is 0.

        The outstandingEvents parameter is incremented for every RD/WR burst,
        PRE, and REF event scheduled.  ACT is implicitly covered by RD/WR
        since burst will always issue and complete after a required ACT.
        The parameter is decremented when the event is serviced (completed).

        The controller will automatically transition to ACT power down,
        PRE power down, or SREF.

        Transition to ACT power down state scheduled from:
        1) The RespondEvent, where read data is received from the memory.
           ACT power-down entry will be scheduled when one or more banks is
           open, all commands for the rank have completed (no more commands
           scheduled), and there are no commands in queue for the rank

        Transition to PRE power down scheduled from:
        1) respondEvent, when all banks are closed, all commands have
           completed, and there are no commands in queue for the rank
        2) prechargeEvent when all banks are closed, all commands have
           completed, and there are no commands in queue for the rank
        3) refreshEvent, after the refresh is complete when the previous
           state was ACT power-down
        4) refreshEvent, after the refresh is complete when the previous
           state was PRE power-down and there are commands in the queue.

        Transition to SREF will be scheduled from:
        1) refreshEvent, after the refresh is completes when the previous
           state was PRE power-down with no commands in queue

        Power-down exit commands are scheduled from:
        1) The refreshEvent, prior to issuing a refresh
        2) doDRAMAccess, to wake-up the rank for RD/WR command issue.

        Self-refresh exit commands are scheduled from:
        1) The next request event, when the queue has commands for the rank
           in the readQueue or there are commands for the rank in the
           writeQueue and the bus state is WRITE.

        Change-Id: I6103f660776e36c686655e71d92ec7b5b752050a
        Reviewed-by: Radhika Jagtap <radhika.jag...@arm.com>

diffstat:

 src/mem/dram_ctrl.cc |  655 ++++++++++++++++++++++++++++++++++++++++++--------
 src/mem/dram_ctrl.hh |  246 +++++++++++++++---
 2 files changed, 742 insertions(+), 159 deletions(-)

diffs (truncated from 1278 to 300 lines):

diff -r beaf1afe2f83 -r 8c6991a00515 src/mem/dram_ctrl.cc
--- a/src/mem/dram_ctrl.cc      Thu Oct 13 19:22:11 2016 +0100
+++ b/src/mem/dram_ctrl.cc      Thu Oct 13 19:22:11 2016 +0100
@@ -41,6 +41,7 @@
  *          Ani Udipi
  *          Neha Agarwal
  *          Omar Naji
+ *          Wendy Elsasser
  */
 
 #include "base/bitfield.hh"
@@ -60,6 +61,7 @@
     port(name() + ".port", *this), isTimingMode(false),
     retryRdReq(false), retryWrReq(false),
     busState(READ),
+    busStateNext(READ),
     nextReqEvent(this), respondEvent(this),
     deviceSize(p->device_size),
     deviceBusWidth(p->device_bus_width), burstLength(p->burst_length),
@@ -481,6 +483,9 @@
 
             readQueue.push_back(dram_pkt);
 
+            // increment read entries of the rank
+            ++dram_pkt->rankRef.readEntries;
+
             // Update stats
             avgRdQLen = readQueue.size() + respQueue.size();
         }
@@ -544,6 +549,9 @@
 
             // Update stats
             avgWrQLen = writeQueue.size();
+
+            // increment write entries of the rank
+            ++dram_pkt->rankRef.writeEntries;
         } else {
             DPRINTF(DRAM, "Merging write burst with existing queue entry\n");
 
@@ -656,6 +664,47 @@
 
     DRAMPacket* dram_pkt = respQueue.front();
 
+    // if a read has reached its ready-time, decrement the number of reads
+    // At this point the packet has been handled and there is a possibility
+    // to switch to low-power mode if no other packet is available
+    --dram_pkt->rankRef.readEntries;
+    DPRINTF(DRAM, "number of read entries for rank %d is %d\n",
+            dram_pkt->rank, dram_pkt->rankRef.readEntries);
+
+    // counter should at least indicate one outstanding request
+    // for this read
+    assert(dram_pkt->rankRef.outstandingEvents > 0);
+    // read response received, decrement count
+    --dram_pkt->rankRef.outstandingEvents;
+
+    // at this moment should be either ACT or IDLE depending on
+    // if PRE has occurred to close all banks
+    assert((dram_pkt->rankRef.pwrState == PWR_ACT) ||
+           (dram_pkt->rankRef.pwrState == PWR_IDLE));
+
+    // track if this is the last packet before idling
+    // and that there are no outstanding commands to this rank
+    if (dram_pkt->rankRef.lowPowerEntryReady()) {
+        // verify that there are no events scheduled
+        assert(!dram_pkt->rankRef.activateEvent.scheduled());
+        assert(!dram_pkt->rankRef.prechargeEvent.scheduled());
+        assert(dram_pkt->rankRef.refreshState == REF_IDLE);
+
+        // if coming from active state, schedule power event to
+        // active power-down else go to precharge power-down
+        DPRINTF(DRAMState, "Rank %d sleep at tick %d; current power state is "
+                "%d\n", dram_pkt->rank, curTick(), dram_pkt->rankRef.pwrState);
+
+        // default to ACT power-down unless already in IDLE state
+        // could be in IDLE if PRE issued before data returned
+        PowerState next_pwr_state = PWR_ACT_PDN;
+        if (dram_pkt->rankRef.pwrState == PWR_IDLE) {
+            next_pwr_state = PWR_PRE_PDN;
+        }
+
+        dram_pkt->rankRef.powerDownSleep(next_pwr_state, curTick());
+    }
+
     if (dram_pkt->burstHelper) {
         // it is a split packet
         dram_pkt->burstHelper->burstsServiced++;
@@ -1012,10 +1061,13 @@
     // 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 (!rank_ref.prechargeEvent.scheduled())
+    if (!rank_ref.prechargeEvent.scheduled()) {
         schedule(rank_ref.prechargeEvent, pre_done_at);
-    else if (rank_ref.prechargeEvent.when() < pre_done_at)
+        // New event, increment count
+        ++rank_ref.outstandingEvents;
+    } else if (rank_ref.prechargeEvent.when() < pre_done_at) {
         reschedule(rank_ref.prechargeEvent, pre_done_at);
+    }
 }
 
 void
@@ -1027,6 +1079,14 @@
     // get the rank
     Rank& rank = dram_pkt->rankRef;
 
+    // are we in or transitioning to a low-power state and have not scheduled
+    // a power-up event?
+    // if so, wake up from power down to issue RD/WR burst
+    if (rank.inLowPowerState) {
+        assert(rank.pwrState != PWR_SREF);
+        rank.scheduleWakeUpEvent(tXP);
+    }
+
     // get the bank
     Bank& bank = dram_pkt->bankRef;
 
@@ -1229,12 +1289,33 @@
     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 (r->pwrState != PWR_SREF) {
+                // rank is busy refreshing
+                DPRINTF(DRAMState, "Rank %d is not available\n", r->rank);
+                busyRanks++;
+
+                // let the rank know that if it was waiting to drain, it
+                // is now done and ready to proceed
+                r->checkDrainDone();
+            }
+
+            // check if we were in self-refresh and haven't started
+            // to transition out
+            if ((r->pwrState == PWR_SREF) && r->inLowPowerState) {
+                DPRINTF(DRAMState, "Rank %d is in self-refresh\n", r->rank);
+                // if we have commands queued to this rank and we don't have
+                // a minimum number of active commands enqueued,
+                // exit self-refresh
+                if (r->forceSelfRefreshExit()) {
+                    DPRINTF(DRAMState, "rank %d was in self refresh and"
+                           " should wake up\n", r->rank);
+                    //wake up from self-refresh
+                    r->scheduleWakeUpEvent(tXS);
+                    // things are brought back into action once a refresh is
+                    // performed after self-refresh
+                    // continue with selection for other ranks
+                }
+            }
         }
     }
 
@@ -1245,30 +1326,32 @@
         return;
     }
 
-    // pre-emptively set to false.  Overwrite if in READ_TO_WRITE
-    // or WRITE_TO_READ state
+    // pre-emptively set to false.  Overwrite if in transitioning to
+    // a new state
     bool switched_cmd_type = false;
-    if (busState == READ_TO_WRITE) {
-        DPRINTF(DRAM, "Switching to writes after %d reads with %d reads "
-                "waiting\n", readsThisTime, readQueue.size());
-
-        // sample and reset the read-related stats as we are now
-        // transitioning to writes, and all reads are done
-        rdPerTurnAround.sample(readsThisTime);
-        readsThisTime = 0;
-
-        // now proceed to do the actual writes
-        busState = WRITE;
-        switched_cmd_type = true;
-    } else if (busState == WRITE_TO_READ) {
-        DPRINTF(DRAM, "Switching to reads after %d writes with %d writes "
-                "waiting\n", writesThisTime, writeQueue.size());
-
-        wrPerTurnAround.sample(writesThisTime);
-        writesThisTime = 0;
-
-        busState = READ;
-        switched_cmd_type = true;
+    if (busState != busStateNext) {
+        if (busState == READ) {
+            DPRINTF(DRAM, "Switching to writes after %d reads with %d reads "
+                    "waiting\n", readsThisTime, readQueue.size());
+
+            // sample and reset the read-related stats as we are now
+            // transitioning to writes, and all reads are done
+            rdPerTurnAround.sample(readsThisTime);
+            readsThisTime = 0;
+
+            // now proceed to do the actual writes
+            switched_cmd_type = true;
+        } else {
+            DPRINTF(DRAM, "Switching to reads after %d writes with %d writes "
+                    "waiting\n", writesThisTime, writeQueue.size());
+
+            wrPerTurnAround.sample(writesThisTime);
+            writesThisTime = 0;
+
+            switched_cmd_type = true;
+        }
+        // update busState to match next state until next transition
+        busState = busStateNext;
     }
 
     // when we get here it is either a read or a write
@@ -1323,6 +1406,7 @@
 
             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
@@ -1337,6 +1421,9 @@
             // At this point we're done dealing with the request
             readQueue.pop_front();
 
+            // Every respQueue which will generate an event, increment count
+            ++dram_pkt->rankRef.outstandingEvents;
+
             // sanity check
             assert(dram_pkt->size <= burstSize);
             assert(dram_pkt->readyTime >= curTick());
@@ -1364,7 +1451,7 @@
         // draining), or because the writes hit the hight threshold
         if (switch_to_writes) {
             // transition to writing
-            busState = READ_TO_WRITE;
+            busStateNext = WRITE;
         }
     } else {
         // bool to check if write to free rank is found
@@ -1398,6 +1485,26 @@
         doDRAMAccess(dram_pkt);
 
         writeQueue.pop_front();
+
+        // removed write from queue, decrement count
+        --dram_pkt->rankRef.writeEntries;
+
+        // Schedule write done event to decrement event count
+        // after the readyTime has been reached
+        // Only schedule latest write event to minimize events
+        // required; only need to ensure that final event scheduled covers
+        // the time that writes are outstanding and bus is active
+        // to holdoff power-down entry events
+        if (!dram_pkt->rankRef.writeDoneEvent.scheduled()) {
+            schedule(dram_pkt->rankRef.writeDoneEvent, dram_pkt->readyTime);
+            // New event, increment count
+            ++dram_pkt->rankRef.outstandingEvents;
+
+        } else if (dram_pkt->rankRef.writeDoneEvent.when() <
+                   dram_pkt-> readyTime) {
+            reschedule(dram_pkt->rankRef.writeDoneEvent, dram_pkt->readyTime);
+        }
+
         isInWriteQueue.erase(burstAlign(dram_pkt->addr));
         delete dram_pkt;
 
@@ -1410,7 +1517,7 @@
              drainState() != DrainState::Draining) ||
             (!readQueue.empty() && writesThisTime >= minWritesPerSwitch)) {
             // turn the bus back around for reads again
-            busState = WRITE_TO_READ;
+            busStateNext = READ;
 
             // note that the we switch back to reads also in the idle
             // case, which eventually will check for any draining and
@@ -1518,11 +1625,13 @@
 
 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)
+      pwrStateTrans(PWR_IDLE), pwrStatePostRefresh(PWR_IDLE),
+      pwrStateTick(0), refreshDueAt(0), pwrState(PWR_IDLE),
+      refreshState(REF_IDLE), inLowPowerState(false), rank(0),
+      readEntries(0), writeEntries(0), outstandingEvents(0),
+      wakeUpAllowedAt(0), power(_p, false), numBanksActive(0),
+      writeDoneEvent(*this), activateEvent(*this), prechargeEvent(*this),
+      refreshEvent(*this), powerEvent(*this), wakeUpEvent(*this)
 { }
 
 void
@@ -1544,6 +1653,27 @@
 
     // Update the stats
     updatePowerStats();
_______________________________________________
gem5-dev mailing list
gem5-dev@gem5.org
http://m5sim.org/mailman/listinfo/gem5-dev

Reply via email to