mem: DRAMPower integration for on-line DRAM power stats
authorOmar Naji <Omar.Naji@arm.com>
Tue, 29 Jul 2014 16:22:44 +0000 (17:22 +0100)
committerOmar Naji <Omar.Naji@arm.com>
Tue, 29 Jul 2014 16:22:44 +0000 (17:22 +0100)
This patch takes the final step in integrating DRAMPower and adds the
appropriate calls in the DRAM controller to provide the command trace
and extract the power and energy stats. The debug printouts are still
left in place, but will eventually be removed.

At the moment the DRAM power calculation is always on when using the
DRAM controller model. The run-time impact of this addition is around
1.5% when looking at the total host seconds of the regressions. We
deem this a sensible trade-off to avoid the complication of adding an
enable/disable mechanism.

src/mem/dram_ctrl.cc
src/mem/dram_ctrl.hh

index 198cb52a4f18656a12c2e8f6e8f3de6b44fe4e3c..eac9ca353a0a0048cfd6768630396dc46a19fd23 100644 (file)
@@ -52,6 +52,7 @@
 #include "sim/system.hh"
 
 using namespace std;
+using namespace Data;
 
 DRAMCtrl::DRAMCtrl(const DRAMCtrlParams* p) :
     AbstractMemory(p),
@@ -90,11 +91,18 @@ DRAMCtrl::DRAMCtrl(const DRAMCtrlParams* p) :
     busBusyUntil(0), refreshDueAt(0), refreshState(REF_IDLE),
     pwrStateTrans(PWR_IDLE), pwrState(PWR_IDLE), prevArrival(0),
     nextReqTime(0), pwrStateTick(0), numBanksActive(0),
-    activeRank(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);
+    }
+
     actTicks.resize(ranksPerChannel);
     for (size_t c = 0; c < ranksPerChannel; ++c) {
         banks[c].resize(banksPerRank);
@@ -227,6 +235,8 @@ DRAMCtrl::init()
 void
 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();
@@ -861,8 +871,12 @@ DRAMCtrl::activateBank(Bank& bank, Tick act_tick, uint32_t row)
     DPRINTF(DRAM, "Activate bank %d, rank %d at tick %lld, now got %d active\n",
             bank.bank, bank.rank, act_tick, numBanksActive);
 
-    DPRINTF(DRAMPower, "%llu,ACT,%d,%d\n", divCeil(act_tick, tCK), bank.bank,
-            bank.rank);
+    rankPower[bank.rank].powerlib.doCommand(MemCommand::ACT, bank.bank,
+                                            divCeil(act_tick, tCK) -
+                                            timeStampOffset);
+
+    DPRINTF(DRAMPower, "%llu,ACT,%d,%d\n", divCeil(act_tick, tCK) -
+            timeStampOffset, bank.bank, bank.rank);
 
     // The next access has to respect tRAS for this bank
     bank.preAllowedAt = act_tick + tRAS;
@@ -965,10 +979,14 @@ DRAMCtrl::prechargeBank(Bank& bank, Tick pre_at, bool trace)
     DPRINTF(DRAM, "Precharging bank %d, rank %d at tick %lld, now got "
             "%d active\n", bank.bank, bank.rank, pre_at, numBanksActive);
 
-    if (trace)
-        DPRINTF(DRAMPower, "%llu,PRE,%d,%d\n", divCeil(pre_at, tCK),
-                bank.bank, bank.rank);
+    if (trace) {
 
+        rankPower[bank.rank].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);
+    }
     // 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
@@ -1140,12 +1158,16 @@ DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
     // DRAMPower trace command to be written
     std::string mem_cmd = dram_pkt->isRead ? "RD" : "WR";
 
+    // MemCommand required for DRAMPower library
+    MemCommand::cmds command = (mem_cmd == "RD") ? MemCommand::RD :
+                                                   MemCommand::WR;
+
     // if this access should use auto-precharge, then we are
     // closing the row
     if (auto_precharge) {
-        prechargeBank(bank, std::max(curTick(), bank.preAllowedAt), false);
-
-        mem_cmd.append("A");
+        // 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));
 
         DPRINTF(DRAM, "Auto-precharged bank: %d\n", dram_pkt->bankId);
     }
@@ -1156,8 +1178,12 @@ 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);
 
-    DPRINTF(DRAMPower, "%llu,%s,%d,%d\n", divCeil(cmd_at, tCK), mem_cmd,
-            dram_pkt->bank, dram_pkt->rank);
+    rankPower[dram_pkt->rank].powerlib.doCommand(command, dram_pkt->bank,
+                                                 divCeil(cmd_at, tCK) -
+                                                 timeStampOffset);
+
+    DPRINTF(DRAMPower, "%llu,%s,%d,%d\n", divCeil(cmd_at, tCK) -
+            timeStampOffset, mem_cmd, dram_pkt->bank, dram_pkt->rank);
 
     // Update the minimum timing between the requests, this is a
     // conservative estimate of when we have to schedule the next
@@ -1510,8 +1536,12 @@ DRAMCtrl::processRefreshEvent()
                 }
 
                 // at the moment this affects all ranks
-                DPRINTF(DRAMPower, "%llu,PREA,0,%d\n", divCeil(pre_at, tCK),
-                        i);
+                rankPower[i].powerlib.doCommand(MemCommand::PREA, 0,
+                                                divCeil(pre_at, tCK) -
+                                                timeStampOffset);
+
+                DPRINTF(DRAMPower, "%llu,PREA,0,%d\n", divCeil(pre_at, tCK) -
+                        timeStampOffset, i);
             }
         } else {
             DPRINTF(DRAM, "All banks already precharged, starting refresh\n");
@@ -1545,7 +1575,33 @@ DRAMCtrl::processRefreshEvent()
             }
 
             // at the moment this affects all ranks
-            DPRINTF(DRAMPower, "%llu,REF,0,%d\n", divCeil(curTick(), tCK), i);
+            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);
         }
 
         // make sure we did not wait so long that we cannot make up
@@ -1645,6 +1701,26 @@ DRAMCtrl::processPowerEvent()
     }
 }
 
+void
+DRAMCtrl::updatePowerStats(uint8_t rank)
+{
+    // 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;
+}
+
 void
 DRAMCtrl::regStats()
 {
@@ -1909,6 +1985,51 @@ DRAMCtrl::regStats()
     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
index cc2bd13fd2303ca941b96015dbf88e3ba0cb6b2c..cb2197841f69eafb0dbc438df4c710cbf5d85e9d 100644 (file)
@@ -60,6 +60,7 @@
 #include "mem/qport.hh"
 #include "params/DRAMCtrl.hh"
 #include "sim/eventq.hh"
+#include "mem/drampower.hh"
 
 /**
  * The DRAM controller is a single-channel memory controller capturing
@@ -677,6 +678,20 @@ class DRAMCtrl : public AbstractMemory
     Stats::Formula pageHitRate;
     Stats::Vector pwrStateTime;
 
+    //Command energies
+    Stats::Vector actEnergy;
+    Stats::Vector preEnergy;
+    Stats::Vector readEnergy;
+    Stats::Vector writeEnergy;
+    Stats::Vector refreshEnergy;
+    //Active Background Energy
+    Stats::Vector actBackEnergy;
+    //Precharge Background Energy
+    Stats::Vector preBackEnergy;
+    Stats::Vector totalEnergy;
+    //Power Consumed
+    Stats::Vector averagePower;
+
     // Track when we transitioned to the current power state
     Tick pwrStateTick;
 
@@ -686,12 +701,42 @@ class DRAMCtrl : public AbstractMemory
     // Holds the value of the rank of burst issued
     uint8_t activeRank;
 
+    // timestamp offset
+    uint64_t timeStampOffset;
+
     /** @todo this is a temporary workaround until the 4-phase code is
      * committed. upstream caches needs this packet until true is returned, so
      * hold onto it for deletion until a subsequent call
      */
     std::vector<PacketPtr> pendingDelete;
 
+    // One DRAMPower instance per rank
+    std::vector<DRAMPower> rankPower;
+
+    /**
+      * This function increments the energy when called. If stats are
+      * dumped periodically, note accumulated energy values will
+      * appear in the stats (even if the stats are reset). This is a
+      * result of the energy values coming from DRAMPower, and there
+      * is currently no support for resetting the state.
+      *
+      * @param rank Currrent rank
+      */
+    void updatePowerStats(uint8_t rank);
+
+    /**
+     * Function for sorting commands in the command list of DRAMPower.
+     *
+     * @param a Memory Command in command list of DRAMPower library
+     * @param next Memory Command in command list of DRAMPower
+     * @return true if timestamp of Command 1 < timestamp of Command 2
+     */
+    static bool sortTime(const Data::MemCommand& m1,
+                         const Data::MemCommand& m2) {
+        return m1.getTime() < m2.getTime();
+    };
+
+
   public:
 
     void regStats();