mem: Convert DRAM controller to new-style stats
authorAndreas Sandberg <andreas.sandberg@arm.com>
Mon, 23 Sep 2019 17:20:23 +0000 (18:20 +0100)
committerAndreas Sandberg <andreas.sandberg@arm.com>
Mon, 30 Sep 2019 12:33:47 +0000 (12:33 +0000)
Note that this changes the stat format used by the DRAM
controller. Previously, it would have a structure looking a bit like
this:

  - system
    - dram: Main DRAM controller
    - dram_0: Rank 0
    - dram_1: Rank 1

This structure can't be replicated with new-world stats since stats
are confined to the SimObject name space. This means that the new
structure looks like this:

  - system
    - dram: Main DRAM controller
      - rank0: Rank 0
      - rank1: Rank 1

Change-Id: I7435cfaf137c94b0c18de619d816362dd0da8125
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/21142
Maintainer: Nikos Nikoleris <nikos.nikoleris@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
Reviewed-by: Wendy Elsasser <wendy.elsasser@arm.com>
src/mem/abstract_mem.cc
src/mem/abstract_mem.hh
src/mem/dram_ctrl.cc
src/mem/dram_ctrl.hh
src/mem/qos/mem_ctrl.cc
src/mem/qos/mem_ctrl.hh

index 6870ba38fc0d96536b964bb61c1ff7736c787424..d913f05d3447a37636ced66e1c77a5fda8b634dc 100644 (file)
@@ -62,7 +62,8 @@ AbstractMemory::AbstractMemory(const Params *p) :
              (MemBackdoor::Flags)(MemBackdoor::Readable |
                                   MemBackdoor::Writeable)),
     confTableReported(p->conf_table_reported), inAddrMap(p->in_addr_map),
-    kvmMap(p->kvm_map), _system(NULL)
+    kvmMap(p->kvm_map), _system(NULL),
+    stats(*this)
 {
 }
 
@@ -88,110 +89,126 @@ AbstractMemory::setBackingStore(uint8_t* pmem_addr)
     pmemAddr = pmem_addr;
 }
 
-void
-AbstractMemory::regStats()
+AbstractMemory::MemStats::MemStats(AbstractMemory &_mem)
+    : Stats::Group(&_mem), mem(_mem),
+    bytesRead(this, "bytes_read",
+              "Number of bytes read from this memory"),
+    bytesInstRead(this, "bytes_inst_read",
+                  "Number of instructions bytes read from this memory"),
+    bytesWritten(this, "bytes_written",
+                 "Number of bytes written to this memory"),
+    numReads(this, "num_reads",
+             "Number of read requests responded to by this memory"),
+    numWrites(this, "num_writes",
+              "Number of write requests responded to by this memory"),
+    numOther(this, "num_other",
+             "Number of other requests responded to by this memory"),
+    bwRead(this, "bw_read",
+           "Total read bandwidth from this memory (bytes/s)"),
+    bwInstRead(this, "bw_inst_read",
+               "Instruction read bandwidth from this memory (bytes/s)"),
+    bwWrite(this, "bw_write",
+            "Write bandwidth from this memory (bytes/s)"),
+    bwTotal(this, "bw_total",
+            "Total bandwidth to/from this memory (bytes/s)")
 {
-    ClockedObject::regStats();
+}
 
+void
+AbstractMemory::MemStats::regStats()
+{
     using namespace Stats;
 
-    assert(system());
+    Stats::Group::regStats();
+
+    System *sys = mem.system();
+    assert(sys);
+    const auto max_masters = sys->maxMasters();
 
     bytesRead
-        .init(system()->maxMasters())
-        .name(name() + ".bytes_read")
-        .desc("Number of bytes read from this memory")
+        .init(max_masters)
         .flags(total | nozero | nonan)
         ;
-    for (int i = 0; i < system()->maxMasters(); i++) {
-        bytesRead.subname(i, system()->getMasterName(i));
+    for (int i = 0; i < max_masters; i++) {
+        bytesRead.subname(i, sys->getMasterName(i));
     }
+
     bytesInstRead
-        .init(system()->maxMasters())
-        .name(name() + ".bytes_inst_read")
-        .desc("Number of instructions bytes read from this memory")
+        .init(max_masters)
         .flags(total | nozero | nonan)
         ;
-    for (int i = 0; i < system()->maxMasters(); i++) {
-        bytesInstRead.subname(i, system()->getMasterName(i));
+    for (int i = 0; i < max_masters; i++) {
+        bytesInstRead.subname(i, sys->getMasterName(i));
     }
+
     bytesWritten
-        .init(system()->maxMasters())
-        .name(name() + ".bytes_written")
-        .desc("Number of bytes written to this memory")
+        .init(max_masters)
         .flags(total | nozero | nonan)
         ;
-    for (int i = 0; i < system()->maxMasters(); i++) {
-        bytesWritten.subname(i, system()->getMasterName(i));
+    for (int i = 0; i < max_masters; i++) {
+        bytesWritten.subname(i, sys->getMasterName(i));
     }
+
     numReads
-        .init(system()->maxMasters())
-        .name(name() + ".num_reads")
-        .desc("Number of read requests responded to by this memory")
+        .init(max_masters)
         .flags(total | nozero | nonan)
         ;
-    for (int i = 0; i < system()->maxMasters(); i++) {
-        numReads.subname(i, system()->getMasterName(i));
+    for (int i = 0; i < max_masters; i++) {
+        numReads.subname(i, sys->getMasterName(i));
     }
+
     numWrites
-        .init(system()->maxMasters())
-        .name(name() + ".num_writes")
-        .desc("Number of write requests responded to by this memory")
+        .init(max_masters)
         .flags(total | nozero | nonan)
         ;
-    for (int i = 0; i < system()->maxMasters(); i++) {
-        numWrites.subname(i, system()->getMasterName(i));
+    for (int i = 0; i < max_masters; i++) {
+        numWrites.subname(i, sys->getMasterName(i));
     }
+
     numOther
-        .init(system()->maxMasters())
-        .name(name() + ".num_other")
-        .desc("Number of other requests responded to by this memory")
+        .init(max_masters)
         .flags(total | nozero | nonan)
         ;
-    for (int i = 0; i < system()->maxMasters(); i++) {
-        numOther.subname(i, system()->getMasterName(i));
+    for (int i = 0; i < max_masters; i++) {
+        numOther.subname(i, sys->getMasterName(i));
     }
+
     bwRead
-        .name(name() + ".bw_read")
-        .desc("Total read bandwidth from this memory (bytes/s)")
         .precision(0)
         .prereq(bytesRead)
         .flags(total | nozero | nonan)
         ;
-    for (int i = 0; i < system()->maxMasters(); i++) {
-        bwRead.subname(i, system()->getMasterName(i));
+    for (int i = 0; i < max_masters; i++) {
+        bwRead.subname(i, sys->getMasterName(i));
     }
 
     bwInstRead
-        .name(name() + ".bw_inst_read")
-        .desc("Instruction read bandwidth from this memory (bytes/s)")
         .precision(0)
         .prereq(bytesInstRead)
         .flags(total | nozero | nonan)
         ;
-    for (int i = 0; i < system()->maxMasters(); i++) {
-        bwInstRead.subname(i, system()->getMasterName(i));
+    for (int i = 0; i < max_masters; i++) {
+        bwInstRead.subname(i, sys->getMasterName(i));
     }
+
     bwWrite
-        .name(name() + ".bw_write")
-        .desc("Write bandwidth from this memory (bytes/s)")
         .precision(0)
         .prereq(bytesWritten)
         .flags(total | nozero | nonan)
         ;
-    for (int i = 0; i < system()->maxMasters(); i++) {
-        bwWrite.subname(i, system()->getMasterName(i));
+    for (int i = 0; i < max_masters; i++) {
+        bwWrite.subname(i, sys->getMasterName(i));
     }
+
     bwTotal
-        .name(name() + ".bw_total")
-        .desc("Total bandwidth to/from this memory (bytes/s)")
         .precision(0)
         .prereq(bwTotal)
         .flags(total | nozero | nonan)
         ;
-    for (int i = 0; i < system()->maxMasters(); i++) {
-        bwTotal.subname(i, system()->getMasterName(i));
+    for (int i = 0; i < max_masters; i++) {
+        bwTotal.subname(i, sys->getMasterName(i));
     }
+
     bwRead = bytesRead / simSeconds;
     bwInstRead = bytesInstRead / simSeconds;
     bwWrite = bytesWritten / simSeconds;
@@ -384,7 +401,7 @@ AbstractMemory::access(PacketPtr pkt)
 
             assert(!pkt->req->isInstFetch());
             TRACE_PACKET("Read/Write");
-            numOther[pkt->req->masterId()]++;
+            stats.numOther[pkt->req->masterId()]++;
         }
     } else if (pkt->isRead()) {
         assert(!pkt->isWrite());
@@ -398,10 +415,10 @@ AbstractMemory::access(PacketPtr pkt)
             pkt->setData(hostAddr);
         }
         TRACE_PACKET(pkt->req->isInstFetch() ? "IFetch" : "Read");
-        numReads[pkt->req->masterId()]++;
-        bytesRead[pkt->req->masterId()] += pkt->getSize();
+        stats.numReads[pkt->req->masterId()]++;
+        stats.bytesRead[pkt->req->masterId()] += pkt->getSize();
         if (pkt->req->isInstFetch())
-            bytesInstRead[pkt->req->masterId()] += pkt->getSize();
+            stats.bytesInstRead[pkt->req->masterId()] += pkt->getSize();
     } else if (pkt->isInvalidate() || pkt->isClean()) {
         assert(!pkt->isWrite());
         // in a fastmem system invalidating and/or cleaning packets
@@ -417,8 +434,8 @@ AbstractMemory::access(PacketPtr pkt)
             }
             assert(!pkt->req->isInstFetch());
             TRACE_PACKET("Write");
-            numWrites[pkt->req->masterId()]++;
-            bytesWritten[pkt->req->masterId()] += pkt->getSize();
+            stats.numWrites[pkt->req->masterId()]++;
+            stats.bytesWritten[pkt->req->masterId()] += pkt->getSize();
         }
     } else {
         panic("Unexpected packet %s", pkt->print());
index 8b944b9815e58b9ca21ff894be4a34397310bb71..655fc01152c948c24c58c152ecfff8c8e1a8b6f6 100644 (file)
@@ -161,33 +161,41 @@ class AbstractMemory : public ClockedObject
         }
     }
 
-    /** Number of total bytes read from this memory */
-    Stats::Vector bytesRead;
-    /** Number of instruction bytes read from this memory */
-    Stats::Vector bytesInstRead;
-    /** Number of bytes written to this memory */
-    Stats::Vector bytesWritten;
-    /** Number of read requests */
-    Stats::Vector numReads;
-    /** Number of write requests */
-    Stats::Vector numWrites;
-    /** Number of other requests */
-    Stats::Vector numOther;
-    /** Read bandwidth from this memory */
-    Stats::Formula bwRead;
-    /** Read bandwidth from this memory */
-    Stats::Formula bwInstRead;
-    /** Write bandwidth from this memory */
-    Stats::Formula bwWrite;
-    /** Total bandwidth from this memory */
-    Stats::Formula bwTotal;
-
     /** Pointor to the System object.
      * This is used for getting the number of masters in the system which is
      * needed when registering stats
      */
     System *_system;
 
+    struct MemStats : public Stats::Group {
+        MemStats(AbstractMemory &mem);
+
+        void regStats() override;
+
+        const AbstractMemory &mem;
+
+        /** Number of total bytes read from this memory */
+        Stats::Vector bytesRead;
+        /** Number of instruction bytes read from this memory */
+        Stats::Vector bytesInstRead;
+        /** Number of bytes written to this memory */
+        Stats::Vector bytesWritten;
+        /** Number of read requests */
+        Stats::Vector numReads;
+        /** Number of write requests */
+        Stats::Vector numWrites;
+        /** Number of other requests */
+        Stats::Vector numOther;
+        /** Read bandwidth from this memory */
+        Stats::Formula bwRead;
+        /** Read bandwidth from this memory */
+        Stats::Formula bwInstRead;
+        /** Write bandwidth from this memory */
+        Stats::Formula bwWrite;
+        /** Total bandwidth from this memory */
+        Stats::Formula bwTotal;
+    } stats;
+
 
   private:
 
@@ -318,12 +326,6 @@ class AbstractMemory : public ClockedObject
      * @param pkt Packet performing the access
      */
     void functionalAccess(PacketPtr pkt);
-
-    /**
-     * Register Statistics
-     */
-    void regStats() override;
-
 };
 
 #endif //__MEM_ABSTRACT_MEMORY_HH__
index 06c540ba6faa34e3b219c629bb7b4cc59400254b..1352dbfbe3e3490f24a42a3248c06eb9121240dc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2018 ARM Limited
+ * Copyright (c) 2010-2019 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -96,7 +96,9 @@ DRAMCtrl::DRAMCtrl(const DRAMCtrlParams* p) :
     frontendLatency(p->static_frontend_latency),
     backendLatency(p->static_backend_latency),
     nextBurstAt(0), prevArrival(0),
-    nextReqTime(0), activeRank(0), timeStampOffset(0),
+    nextReqTime(0),
+    stats(*this),
+    activeRank(0), timeStampOffset(0),
     lastStatsResetTick(0), enableDRAMPowerdown(p->enable_dram_powerdown)
 {
     // sanity check the ranks since we rely on bit slicing for the
@@ -429,9 +431,9 @@ DRAMCtrl::addToReadQueue(PacketPtr pkt, unsigned int pktCount)
     for (int cnt = 0; cnt < pktCount; ++cnt) {
         unsigned size = std::min((addr | (burstSize - 1)) + 1,
                         pkt->getAddr() + pkt->getSize()) - addr;
-        readPktSize[ceilLog2(size)]++;
-        readBursts++;
-        masterReadAccesses[pkt->masterId()]++;
+        stats.readPktSize[ceilLog2(size)]++;
+        stats.readBursts++;
+        stats.masterReadAccesses[pkt->masterId()]++;
 
         // First check write buffer to see if the data is already at
         // the controller
@@ -448,13 +450,13 @@ DRAMCtrl::addToReadQueue(PacketPtr pkt, unsigned int pktCount)
                        ((addr + size) <= (p->addr + p->size))) {
 
                         foundInWrQ = true;
-                        servicedByWrQ++;
+                        stats.servicedByWrQ++;
                         pktsServicedByWrQ++;
                         DPRINTF(DRAM,
                                 "Read to addr %lld with size %d serviced by "
                                 "write queue\n",
                                 addr, size);
-                        bytesReadWrQ += burstSize;
+                        stats.bytesReadWrQ += burstSize;
                         break;
                     }
                 }
@@ -476,7 +478,7 @@ DRAMCtrl::addToReadQueue(PacketPtr pkt, unsigned int pktCount)
             dram_pkt->burstHelper = burst_helper;
 
             assert(!readQueueFull(1));
-            rdQLenPdf[totalReadQueueSize + respQueue.size()]++;
+            stats.rdQLenPdf[totalReadQueueSize + respQueue.size()]++;
 
             DPRINTF(DRAM, "Adding to read queue\n");
 
@@ -489,7 +491,7 @@ DRAMCtrl::addToReadQueue(PacketPtr pkt, unsigned int pktCount)
                        dram_pkt->addr, 1);
 
             // Update stats
-            avgRdQLen = totalReadQueueSize + respQueue.size();
+            stats.avgRdQLen = totalReadQueueSize + respQueue.size();
         }
 
         // Starting address of next dram pkt (aligend to burstSize boundary)
@@ -527,9 +529,9 @@ DRAMCtrl::addToWriteQueue(PacketPtr pkt, unsigned int pktCount)
     for (int cnt = 0; cnt < pktCount; ++cnt) {
         unsigned size = std::min((addr | (burstSize - 1)) + 1,
                         pkt->getAddr() + pkt->getSize()) - addr;
-        writePktSize[ceilLog2(size)]++;
-        writeBursts++;
-        masterWriteAccesses[pkt->masterId()]++;
+        stats.writePktSize[ceilLog2(size)]++;
+        stats.writeBursts++;
+        stats.masterWriteAccesses[pkt->masterId()]++;
 
         // see if we can merge with an existing item in the write
         // queue and keep track of whether we have merged or not
@@ -542,7 +544,7 @@ DRAMCtrl::addToWriteQueue(PacketPtr pkt, unsigned int pktCount)
             DRAMPacket* dram_pkt = decodeAddr(pkt, addr, size, false);
 
             assert(totalWriteQueueSize < writeBufferSize);
-            wrQLenPdf[totalWriteQueueSize]++;
+            stats.wrQLenPdf[totalWriteQueueSize]++;
 
             DPRINTF(DRAM, "Adding to write queue\n");
 
@@ -556,7 +558,7 @@ DRAMCtrl::addToWriteQueue(PacketPtr pkt, unsigned int pktCount)
             assert(totalWriteQueueSize == isInWriteQueue.size());
 
             // Update stats
-            avgWrQLen = totalWriteQueueSize;
+            stats.avgWrQLen = totalWriteQueueSize;
 
             // increment write entries of the rank
             ++dram_pkt->rankRef.writeEntries;
@@ -565,7 +567,7 @@ DRAMCtrl::addToWriteQueue(PacketPtr pkt, unsigned int pktCount)
 
             // keep track of the fact that this burst effectively
             // disappeared as it was merged with an existing one
-            mergedWrBursts++;
+            stats.mergedWrBursts++;
         }
 
         // Starting address of next dram pkt (aligend to burstSize boundary)
@@ -627,7 +629,7 @@ DRAMCtrl::recvTimingReq(PacketPtr pkt)
 
     // Calc avg gap between requests
     if (prevArrival != 0) {
-        totGap += curTick() - prevArrival;
+        stats.totGap += curTick() - prevArrival;
     }
     prevArrival = curTick();
 
@@ -650,12 +652,12 @@ DRAMCtrl::recvTimingReq(PacketPtr pkt)
             DPRINTF(DRAM, "Write queue full, not accepting\n");
             // remember that we have to retry this port
             retryWrReq = true;
-            numWrRetry++;
+            stats.numWrRetry++;
             return false;
         } else {
             addToWriteQueue(pkt, dram_pkt_count);
-            writeReqs++;
-            bytesWrittenSys += size;
+            stats.writeReqs++;
+            stats.bytesWrittenSys += size;
         }
     } else {
         assert(pkt->isRead());
@@ -664,12 +666,12 @@ DRAMCtrl::recvTimingReq(PacketPtr pkt)
             DPRINTF(DRAM, "Read queue full, not accepting\n");
             // remember that we have to retry this port
             retryRdReq = true;
-            numRdRetry++;
+            stats.numRdRetry++;
             return false;
         } else {
             addToReadQueue(pkt, dram_pkt_count);
-            readReqs++;
-            bytesReadSys += size;
+            stats.readReqs++;
+            stats.bytesReadSys += size;
         }
     }
 
@@ -1059,7 +1061,7 @@ DRAMCtrl::prechargeBank(Rank& rank_ref, Bank& bank, Tick pre_at, bool trace)
 
     // sample the bytes per activate here since we are closing
     // the page
-    bytesPerActivate.sample(bank.bytesAccessed);
+    stats.bytesPerActivate.sample(bank.bytesAccessed);
 
     bank.openRow = Bank::NO_ROW;
 
@@ -1304,26 +1306,26 @@ DRAMCtrl::doDRAMAccess(DRAMPacket* dram_pkt)
     if (dram_pkt->isRead()) {
         ++readsThisTime;
         if (row_hit)
-            readRowHits++;
-        bytesReadDRAM += burstSize;
-        perBankRdBursts[dram_pkt->bankId]++;
+            stats.readRowHits++;
+        stats.bytesReadDRAM += burstSize;
+        stats.perBankRdBursts[dram_pkt->bankId]++;
 
         // Update latency stats
-        totMemAccLat += dram_pkt->readyTime - dram_pkt->entryTime;
-        masterReadTotalLat[dram_pkt->masterId()] +=
+        stats.totMemAccLat += dram_pkt->readyTime - dram_pkt->entryTime;
+        stats.masterReadTotalLat[dram_pkt->masterId()] +=
             dram_pkt->readyTime - dram_pkt->entryTime;
 
-        totBusLat += tBURST;
-        totQLat += cmd_at - dram_pkt->entryTime;
-        masterReadBytes[dram_pkt->masterId()] += dram_pkt->size;
+        stats.totBusLat += tBURST;
+        stats.totQLat += cmd_at - dram_pkt->entryTime;
+        stats.masterReadBytes[dram_pkt->masterId()] += dram_pkt->size;
     } else {
         ++writesThisTime;
         if (row_hit)
-            writeRowHits++;
-        bytesWritten += burstSize;
-        perBankWrBursts[dram_pkt->bankId]++;
-        masterWriteBytes[dram_pkt->masterId()] += dram_pkt->size;
-        masterWriteTotalLat[dram_pkt->masterId()] +=
+            stats.writeRowHits++;
+        stats.bytesWritten += burstSize;
+        stats.perBankWrBursts[dram_pkt->bankId]++;
+        stats.masterWriteBytes[dram_pkt->masterId()] += dram_pkt->size;
+        stats.masterWriteTotalLat[dram_pkt->masterId()] +=
             dram_pkt->readyTime - dram_pkt->entryTime;
     }
 }
@@ -1351,13 +1353,13 @@ DRAMCtrl::processNextReqEvent()
             DPRINTF(DRAM,
                     "Switching to writes after %d reads with %d reads "
                     "waiting\n", readsThisTime, totalReadQueueSize);
-            rdPerTurnAround.sample(readsThisTime);
+            stats.rdPerTurnAround.sample(readsThisTime);
             readsThisTime = 0;
         } else {
             DPRINTF(DRAM,
                     "Switching to reads after %d writes with %d writes "
                     "waiting\n", writesThisTime, totalWriteQueueSize);
-            wrPerTurnAround.sample(writesThisTime);
+            stats.wrPerTurnAround.sample(writesThisTime);
             writesThisTime = 0;
         }
     }
@@ -1732,7 +1734,8 @@ DRAMCtrl::Rank::Rank(DRAMCtrl& _memory, const DRAMCtrlParams* _p, int rank)
       prechargeEvent([this]{ processPrechargeEvent(); }, name()),
       refreshEvent([this]{ processRefreshEvent(); }, name()),
       powerEvent([this]{ processPowerEvent(); }, name()),
-      wakeUpEvent([this]{ processWakeUpEvent(); }, name())
+      wakeUpEvent([this]{ processWakeUpEvent(); }, name()),
+      stats(_memory, *this)
 {
     for (int b = 0; b < _p->banks_per_rank; b++) {
         banks[b].bank = b;
@@ -2237,12 +2240,12 @@ DRAMCtrl::Rank::processPowerEvent()
     PowerState prev_state = pwrState;
 
     // update the accounting
-    pwrStateTime[prev_state] += duration;
+    stats.memoryStateTime[prev_state] += duration;
 
     // track to total idle time
     if ((prev_state == PWR_PRE_PDN) || (prev_state == PWR_ACT_PDN) ||
         (prev_state == PWR_SREF)) {
-        totalIdleTime += duration;
+        stats.totalIdleTime += duration;
     }
 
     pwrState = pwrStateTrans;
@@ -2380,28 +2383,28 @@ DRAMCtrl::Rank::updatePowerStats()
 
     // The energy components inside the power lib are calculated over
     // the window so accumulate into the corresponding gem5 stat
-    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;
-    actPowerDownEnergy += energy.f_act_pd_energy * memory.devicesPerRank;
-    prePowerDownEnergy += energy.f_pre_pd_energy * memory.devicesPerRank;
-    selfRefreshEnergy += energy.sref_energy * memory.devicesPerRank;
+    stats.actEnergy += energy.act_energy * memory.devicesPerRank;
+    stats.preEnergy += energy.pre_energy * memory.devicesPerRank;
+    stats.readEnergy += energy.read_energy * memory.devicesPerRank;
+    stats.writeEnergy += energy.write_energy * memory.devicesPerRank;
+    stats.refreshEnergy += energy.ref_energy * memory.devicesPerRank;
+    stats.actBackEnergy += energy.act_stdby_energy * memory.devicesPerRank;
+    stats.preBackEnergy += energy.pre_stdby_energy * memory.devicesPerRank;
+    stats.actPowerDownEnergy += energy.f_act_pd_energy * memory.devicesPerRank;
+    stats.prePowerDownEnergy += energy.f_pre_pd_energy * memory.devicesPerRank;
+    stats.selfRefreshEnergy += energy.sref_energy * memory.devicesPerRank;
 
     // Accumulate window energy into the total energy.
-    totalEnergy += energy.window_energy * memory.devicesPerRank;
+    stats.totalEnergy += energy.window_energy * memory.devicesPerRank;
     // Average power must not be accumulated but calculated over the time
     // since last stats reset. SimClock::Frequency is tick period not tick
     // frequency.
     //              energy (pJ)     1e-9
     // power (mW) = ----------- * ----------
     //              time (tick)   tick_frequency
-    averagePower = (totalEnergy.value() /
-                    (curTick() - memory.lastStatsResetTick)) *
-                    (SimClock::Frequency / 1000000000.0);
+    stats.averagePower = (stats.totalEnergy.value() /
+                          (curTick() - memory.lastStatsResetTick)) *
+                         (SimClock::Frequency / 1000000000.0);
 }
 
 void
@@ -2413,9 +2416,8 @@ DRAMCtrl::Rank::computeStats()
     updatePowerStats();
 
     // final update of power state times
-    pwrStateTime[pwrState] += (curTick() - pwrStateTick);
+    stats.memoryStateTime[pwrState] += (curTick() - pwrStateTick);
     pwrStateTick = curTick();
-
 }
 
 void
@@ -2428,405 +2430,204 @@ DRAMCtrl::Rank::resetStats() {
 
 }
 
-void
-DRAMCtrl::Rank::regStats()
+DRAMCtrl::DRAMStats::DRAMStats(DRAMCtrl &_dram)
+    : Stats::Group(&_dram),
+    dram(_dram),
+
+    ADD_STAT(readReqs, "Number of read requests accepted"),
+    ADD_STAT(writeReqs, "Number of write requests accepted"),
+
+    ADD_STAT(readBursts,
+             "Number of DRAM read bursts, "
+             "including those serviced by the write queue"),
+    ADD_STAT(writeBursts,
+             "Number of DRAM write bursts, "
+             "including those merged in the write queue"),
+    ADD_STAT(servicedByWrQ,
+             "Number of DRAM read bursts serviced by the write queue"),
+    ADD_STAT(mergedWrBursts,
+             "Number of DRAM write bursts merged with an existing one"),
+
+    ADD_STAT(neitherReadNorWriteReqs,
+             "Number of requests that are neither read nor write"),
+
+    ADD_STAT(perBankRdBursts, "Per bank write bursts"),
+    ADD_STAT(perBankWrBursts, "Per bank write bursts"),
+
+    ADD_STAT(avgRdQLen, "Average read queue length when enqueuing"),
+    ADD_STAT(avgWrQLen, "Average write queue length when enqueuing"),
+
+    ADD_STAT(totQLat, "Total ticks spent queuing"),
+    ADD_STAT(totBusLat, "Total ticks spent in databus transfers"),
+    ADD_STAT(totMemAccLat,
+             "Total ticks spent from burst creation until serviced "
+             "by the DRAM"),
+    ADD_STAT(avgQLat, "Average queueing delay per DRAM burst"),
+    ADD_STAT(avgBusLat, "Average bus latency per DRAM burst"),
+    ADD_STAT(avgMemAccLat, "Average memory access latency per DRAM burst"),
+
+    ADD_STAT(numRdRetry, "Number of times read queue was full causing retry"),
+    ADD_STAT(numWrRetry, "Number of times write queue was full causing retry"),
+
+    ADD_STAT(readRowHits, "Number of row buffer hits during reads"),
+    ADD_STAT(writeRowHits, "Number of row buffer hits during writes"),
+    ADD_STAT(readRowHitRate, "Row buffer hit rate for reads"),
+    ADD_STAT(writeRowHitRate, "Row buffer hit rate for writes"),
+
+    ADD_STAT(readPktSize, "Read request sizes (log2)"),
+    ADD_STAT(writePktSize, "Write request sizes (log2)"),
+
+    ADD_STAT(rdQLenPdf, "What read queue length does an incoming req see"),
+    ADD_STAT(wrQLenPdf, "What write queue length does an incoming req see"),
+
+    ADD_STAT(bytesPerActivate, "Bytes accessed per row activation"),
+
+    ADD_STAT(rdPerTurnAround,
+             "Reads before turning the bus around for writes"),
+    ADD_STAT(wrPerTurnAround,
+             "Writes before turning the bus around for reads"),
+
+    ADD_STAT(bytesReadDRAM, "Total number of bytes read from DRAM"),
+    ADD_STAT(bytesReadWrQ, "Total number of bytes read from write queue"),
+    ADD_STAT(bytesWritten, "Total number of bytes written to DRAM"),
+    ADD_STAT(bytesReadSys, "Total read bytes from the system interface side"),
+    ADD_STAT(bytesWrittenSys,
+             "Total written bytes from the system interface side"),
+
+    ADD_STAT(avgRdBW, "Average DRAM read bandwidth in MiByte/s"),
+    ADD_STAT(avgWrBW, "Average achieved write bandwidth in MiByte/s"),
+    ADD_STAT(avgRdBWSys, "Average system read bandwidth in MiByte/s"),
+    ADD_STAT(avgWrBWSys, "Average system write bandwidth in MiByte/s"),
+    ADD_STAT(peakBW, "Theoretical peak bandwidth in MiByte/s"),
+
+    ADD_STAT(busUtil, "Data bus utilization in percentage"),
+    ADD_STAT(busUtilRead, "Data bus utilization in percentage for reads"),
+    ADD_STAT(busUtilWrite, "Data bus utilization in percentage for writes"),
+
+    ADD_STAT(totGap, "Total gap between requests"),
+    ADD_STAT(avgGap, "Average gap between requests"),
+
+    ADD_STAT(masterReadBytes, "Per-master bytes read from memory"),
+    ADD_STAT(masterWriteBytes, "Per-master bytes write to memory"),
+    ADD_STAT(masterReadRate,
+             "Per-master bytes read from memory rate (Bytes/sec)"),
+    ADD_STAT(masterWriteRate,
+             "Per-master bytes write to memory rate (Bytes/sec)"),
+    ADD_STAT(masterReadAccesses,
+             "Per-master read serviced memory accesses"),
+    ADD_STAT(masterWriteAccesses,
+             "Per-master write serviced memory accesses"),
+    ADD_STAT(masterReadTotalLat,
+             "Per-master read total memory access latency"),
+    ADD_STAT(masterWriteTotalLat,
+             "Per-master write total memory access latency"),
+    ADD_STAT(masterReadAvgLat,
+             "Per-master read average memory access latency"),
+    ADD_STAT(masterWriteAvgLat,
+             "Per-master write average memory access latency"),
+
+    ADD_STAT(pageHitRate, "Row buffer hit rate, read and write combined")
 {
-    pwrStateTime
-        .init(6)
-        .name(name() + ".memoryStateTime")
-        .desc("Time in different power states");
-    pwrStateTime.subname(0, "IDLE");
-    pwrStateTime.subname(1, "REF");
-    pwrStateTime.subname(2, "SREF");
-    pwrStateTime.subname(3, "PRE_PDN");
-    pwrStateTime.subname(4, "ACT");
-    pwrStateTime.subname(5, "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)");
-
-    actPowerDownEnergy
-        .name(name() + ".actPowerDownEnergy")
-        .desc("Energy for active power-down per rank (pJ)");
-
-    prePowerDownEnergy
-        .name(name() + ".prePowerDownEnergy")
-        .desc("Energy for precharge power-down per rank (pJ)");
-
-    selfRefreshEnergy
-        .name(name() + ".selfRefreshEnergy")
-        .desc("Energy for self refresh per rank (pJ)");
-
-    totalEnergy
-        .name(name() + ".totalEnergy")
-        .desc("Total energy per rank (pJ)");
-
-    averagePower
-        .name(name() + ".averagePower")
-        .desc("Core power per rank (mW)");
-
-    totalIdleTime
-        .name(name() + ".totalIdleTime")
-        .desc("Total Idle time Per DRAM Rank");
-
-    Stats::registerDumpCallback(new RankDumpCallback(this));
-    Stats::registerResetCallback(new RankResetCallback(this));
 }
+
 void
-DRAMCtrl::regStats()
+DRAMCtrl::DRAMStats::regStats()
 {
     using namespace Stats;
 
-    MemCtrl::regStats();
-
-    for (auto r : ranks) {
-        r->regStats();
-    }
-
-    registerResetCallback(new MemResetCallback(this));
-
-    readReqs
-        .name(name() + ".readReqs")
-        .desc("Number of read requests accepted");
-
-    writeReqs
-        .name(name() + ".writeReqs")
-        .desc("Number of write requests accepted");
-
-    readBursts
-        .name(name() + ".readBursts")
-        .desc("Number of DRAM read bursts, "
-              "including those serviced by the write queue");
-
-    writeBursts
-        .name(name() + ".writeBursts")
-        .desc("Number of DRAM write bursts, "
-              "including those merged in the write queue");
-
-    servicedByWrQ
-        .name(name() + ".servicedByWrQ")
-        .desc("Number of DRAM read bursts serviced by the write queue");
-
-    mergedWrBursts
-        .name(name() + ".mergedWrBursts")
-        .desc("Number of DRAM write bursts merged with an existing one");
-
-    neitherReadNorWrite
-        .name(name() + ".neitherReadNorWriteReqs")
-        .desc("Number of requests that are neither read nor write");
-
-    perBankRdBursts
-        .init(banksPerRank * ranksPerChannel)
-        .name(name() + ".perBankRdBursts")
-        .desc("Per bank write bursts");
-
-    perBankWrBursts
-        .init(banksPerRank * ranksPerChannel)
-        .name(name() + ".perBankWrBursts")
-        .desc("Per bank write bursts");
-
-    avgRdQLen
-        .name(name() + ".avgRdQLen")
-        .desc("Average read queue length when enqueuing")
-        .precision(2);
-
-    avgWrQLen
-        .name(name() + ".avgWrQLen")
-        .desc("Average write queue length when enqueuing")
-        .precision(2);
-
-    totQLat
-        .name(name() + ".totQLat")
-        .desc("Total ticks spent queuing");
-
-    totBusLat
-        .name(name() + ".totBusLat")
-        .desc("Total ticks spent in databus transfers");
-
-    totMemAccLat
-        .name(name() + ".totMemAccLat")
-        .desc("Total ticks spent from burst creation until serviced "
-              "by the DRAM");
-
-    avgQLat
-        .name(name() + ".avgQLat")
-        .desc("Average queueing delay per DRAM burst")
-        .precision(2);
-
-    avgQLat = totQLat / (readBursts - servicedByWrQ);
-
-    avgBusLat
-        .name(name() + ".avgBusLat")
-        .desc("Average bus latency per DRAM burst")
-        .precision(2);
+    System *sys = dram._system;
+    assert(sys);
+    const auto max_masters = dram._system->maxMasters();
 
-    avgBusLat = totBusLat / (readBursts - servicedByWrQ);
+    perBankRdBursts.init(dram.banksPerRank * dram.ranksPerChannel);
+    perBankWrBursts.init(dram.banksPerRank * dram.ranksPerChannel);
 
-    avgMemAccLat
-        .name(name() + ".avgMemAccLat")
-        .desc("Average memory access latency per DRAM burst")
-        .precision(2);
+    avgRdQLen.precision(2);
+    avgWrQLen.precision(2);
+    avgQLat.precision(2);
+    avgBusLat.precision(2);
+    avgMemAccLat.precision(2);
 
-    avgMemAccLat = totMemAccLat / (readBursts - servicedByWrQ);
+    readRowHitRate.precision(2);
+    writeRowHitRate.precision(2);
 
-    numRdRetry
-        .name(name() + ".numRdRetry")
-        .desc("Number of times read queue was full causing retry");
+    readPktSize.init(ceilLog2(dram.burstSize) + 1);
+    writePktSize.init(ceilLog2(dram.burstSize) + 1);
 
-    numWrRetry
-        .name(name() + ".numWrRetry")
-        .desc("Number of times write queue was full causing retry");
-
-    readRowHits
-        .name(name() + ".readRowHits")
-        .desc("Number of row buffer hits during reads");
-
-    writeRowHits
-        .name(name() + ".writeRowHits")
-        .desc("Number of row buffer hits during writes");
-
-    readRowHitRate
-        .name(name() + ".readRowHitRate")
-        .desc("Row buffer hit rate for reads")
-        .precision(2);
+    rdQLenPdf.init(dram.readBufferSize);
+    wrQLenPdf.init(dram.writeBufferSize);
 
-    readRowHitRate = (readRowHits / (readBursts - servicedByWrQ)) * 100;
-
-    writeRowHitRate
-        .name(name() + ".writeRowHitRate")
-        .desc("Row buffer hit rate for writes")
-        .precision(2);
-
-    writeRowHitRate = (writeRowHits / (writeBursts - mergedWrBursts)) * 100;
-
-    readPktSize
-        .init(ceilLog2(burstSize) + 1)
-        .name(name() + ".readPktSize")
-        .desc("Read request sizes (log2)");
-
-     writePktSize
-        .init(ceilLog2(burstSize) + 1)
-        .name(name() + ".writePktSize")
-        .desc("Write request sizes (log2)");
-
-     rdQLenPdf
-        .init(readBufferSize)
-        .name(name() + ".rdQLenPdf")
-        .desc("What read queue length does an incoming req see");
-
-     wrQLenPdf
-        .init(writeBufferSize)
-        .name(name() + ".wrQLenPdf")
-        .desc("What write queue length does an incoming req see");
-
-     bytesPerActivate
-         .init(maxAccessesPerRow ? maxAccessesPerRow : rowBufferSize)
-         .name(name() + ".bytesPerActivate")
-         .desc("Bytes accessed per row activation")
-         .flags(nozero);
-
-     rdPerTurnAround
-         .init(readBufferSize)
-         .name(name() + ".rdPerTurnAround")
-         .desc("Reads before turning the bus around for writes")
-         .flags(nozero);
-
-     wrPerTurnAround
-         .init(writeBufferSize)
-         .name(name() + ".wrPerTurnAround")
-         .desc("Writes before turning the bus around for reads")
-         .flags(nozero);
-
-    bytesReadDRAM
-        .name(name() + ".bytesReadDRAM")
-        .desc("Total number of bytes read from DRAM");
-
-    bytesReadWrQ
-        .name(name() + ".bytesReadWrQ")
-        .desc("Total number of bytes read from write queue");
-
-    bytesWritten
-        .name(name() + ".bytesWritten")
-        .desc("Total number of bytes written to DRAM");
-
-    bytesReadSys
-        .name(name() + ".bytesReadSys")
-        .desc("Total read bytes from the system interface side");
-
-    bytesWrittenSys
-        .name(name() + ".bytesWrittenSys")
-        .desc("Total written bytes from the system interface side");
-
-    avgRdBW
-        .name(name() + ".avgRdBW")
-        .desc("Average DRAM read bandwidth in MiByte/s")
-        .precision(2);
-
-    avgRdBW = (bytesReadDRAM / 1000000) / simSeconds;
-
-    avgWrBW
-        .name(name() + ".avgWrBW")
-        .desc("Average achieved write bandwidth in MiByte/s")
-        .precision(2);
-
-    avgWrBW = (bytesWritten / 1000000) / simSeconds;
-
-    avgRdBWSys
-        .name(name() + ".avgRdBWSys")
-        .desc("Average system read bandwidth in MiByte/s")
-        .precision(2);
-
-    avgRdBWSys = (bytesReadSys / 1000000) / simSeconds;
-
-    avgWrBWSys
-        .name(name() + ".avgWrBWSys")
-        .desc("Average system write bandwidth in MiByte/s")
-        .precision(2);
-
-    avgWrBWSys = (bytesWrittenSys / 1000000) / simSeconds;
-
-    peakBW
-        .name(name() + ".peakBW")
-        .desc("Theoretical peak bandwidth in MiByte/s")
-        .precision(2);
-
-    peakBW = (SimClock::Frequency / tBURST) * burstSize / 1000000;
-
-    busUtil
-        .name(name() + ".busUtil")
-        .desc("Data bus utilization in percentage")
-        .precision(2);
-    busUtil = (avgRdBW + avgWrBW) / peakBW * 100;
-
-    totGap
-        .name(name() + ".totGap")
-        .desc("Total gap between requests");
-
-    avgGap
-        .name(name() + ".avgGap")
-        .desc("Average gap between requests")
-        .precision(2);
-
-    avgGap = totGap / (readReqs + writeReqs);
-
-    // Stats for DRAM Power calculation based on Micron datasheet
-    busUtilRead
-        .name(name() + ".busUtilRead")
-        .desc("Data bus utilization in percentage for reads")
-        .precision(2);
-
-    busUtilRead = avgRdBW / peakBW * 100;
-
-    busUtilWrite
-        .name(name() + ".busUtilWrite")
-        .desc("Data bus utilization in percentage for writes")
-        .precision(2);
+    bytesPerActivate
+        .init(dram.maxAccessesPerRow ?
+              dram.maxAccessesPerRow : dram.rowBufferSize)
+        .flags(nozero);
 
-    busUtilWrite = avgWrBW / peakBW * 100;
+    rdPerTurnAround
+        .init(dram.readBufferSize)
+        .flags(nozero);
+    wrPerTurnAround
+        .init(dram.writeBufferSize)
+        .flags(nozero);
 
-    pageHitRate
-        .name(name() + ".pageHitRate")
-        .desc("Row buffer hit rate, read and write combined")
-        .precision(2);
+    avgRdBW.precision(2);
+    avgWrBW.precision(2);
+    avgRdBWSys.precision(2);
+    avgWrBWSys.precision(2);
+    peakBW.precision(2);
+    busUtil.precision(2);
+    avgGap.precision(2);
+    busUtilWrite.precision(2);
+    pageHitRate.precision(2);
 
-    pageHitRate = (writeRowHits + readRowHits) /
-        (writeBursts - mergedWrBursts + readBursts - servicedByWrQ) * 100;
 
     // per-master bytes read and written to memory
     masterReadBytes
-        .init(_system->maxMasters())
-        .name(name() + ".masterReadBytes")
-        .desc("Per-master bytes read from memory")
+        .init(max_masters)
         .flags(nozero | nonan);
 
     masterWriteBytes
-        .init(_system->maxMasters())
-        .name(name() + ".masterWriteBytes")
-        .desc("Per-master bytes write to memory")
+        .init(max_masters)
         .flags(nozero | nonan);
 
     // per-master bytes read and written to memory rate
-    masterReadRate.name(name() + ".masterReadRate")
-        .desc("Per-master bytes read from memory rate (Bytes/sec)")
+    masterReadRate
         .flags(nozero | nonan)
         .precision(12);
 
-    masterReadRate = masterReadBytes/simSeconds;
-
-    masterWriteRate
-        .name(name() + ".masterWriteRate")
-        .desc("Per-master bytes write to memory rate (Bytes/sec)")
-        .flags(nozero | nonan)
-        .precision(12);
-
-    masterWriteRate = masterWriteBytes/simSeconds;
-
     masterReadAccesses
-        .init(_system->maxMasters())
-        .name(name() + ".masterReadAccesses")
-        .desc("Per-master read serviced memory accesses")
+        .init(max_masters)
         .flags(nozero);
 
     masterWriteAccesses
-        .init(_system->maxMasters())
-        .name(name() + ".masterWriteAccesses")
-        .desc("Per-master write serviced memory accesses")
+        .init(max_masters)
         .flags(nozero);
 
-
     masterReadTotalLat
-        .init(_system->maxMasters())
-        .name(name() + ".masterReadTotalLat")
-        .desc("Per-master read total memory access latency")
+        .init(max_masters)
         .flags(nozero | nonan);
 
-    masterReadAvgLat.name(name() + ".masterReadAvgLat")
-        .desc("Per-master read average memory access latency")
+    masterReadAvgLat
         .flags(nonan)
         .precision(2);
 
-    masterReadAvgLat = masterReadTotalLat/masterReadAccesses;
+
+    busUtilRead
+        .precision(2);
+
+    masterWriteRate
+        .flags(nozero | nonan)
+        .precision(12);
 
     masterWriteTotalLat
-        .init(_system->maxMasters())
-        .name(name() + ".masterWriteTotalLat")
-        .desc("Per-master write total memory access latency")
+        .init(max_masters)
         .flags(nozero | nonan);
 
-    masterWriteAvgLat.name(name() + ".masterWriteAvgLat")
-        .desc("Per-master write average memory access latency")
+    masterWriteAvgLat
         .flags(nonan)
         .precision(2);
 
-    masterWriteAvgLat = masterWriteTotalLat/masterWriteAccesses;
-
-    for (int i = 0; i < _system->maxMasters(); i++) {
-        const std::string master = _system->getMasterName(i);
+    for (int i = 0; i < max_masters; i++) {
+        const std::string master = dram._system->getMasterName(i);
         masterReadBytes.subname(i, master);
         masterReadRate.subname(i, master);
         masterWriteBytes.subname(i, master);
@@ -2838,6 +2639,96 @@ DRAMCtrl::regStats()
         masterWriteTotalLat.subname(i, master);
         masterWriteAvgLat.subname(i, master);
     }
+
+    // Formula stats
+    avgQLat = totQLat / (readBursts - servicedByWrQ);
+    avgBusLat = totBusLat / (readBursts - servicedByWrQ);
+    avgMemAccLat = totMemAccLat / (readBursts - servicedByWrQ);
+
+    readRowHitRate = (readRowHits / (readBursts - servicedByWrQ)) * 100;
+    writeRowHitRate = (writeRowHits / (writeBursts - mergedWrBursts)) * 100;
+
+    avgRdBW = (bytesReadDRAM / 1000000) / simSeconds;
+    avgWrBW = (bytesWritten / 1000000) / simSeconds;
+    avgRdBWSys = (bytesReadSys / 1000000) / simSeconds;
+    avgWrBWSys = (bytesWrittenSys / 1000000) / simSeconds;
+    peakBW = (SimClock::Frequency / dram.tBURST) * dram.burstSize / 1000000;
+
+    busUtil = (avgRdBW + avgWrBW) / peakBW * 100;
+
+    avgGap = totGap / (readReqs + writeReqs);
+
+    busUtilRead = avgRdBW / peakBW * 100;
+    busUtilWrite = avgWrBW / peakBW * 100;
+
+    pageHitRate = (writeRowHits + readRowHits) /
+        (writeBursts - mergedWrBursts + readBursts - servicedByWrQ) * 100;
+
+    masterReadRate = masterReadBytes / simSeconds;
+    masterWriteRate = masterWriteBytes / simSeconds;
+    masterReadAvgLat = masterReadTotalLat / masterReadAccesses;
+    masterWriteAvgLat = masterWriteTotalLat / masterWriteAccesses;
+}
+
+void
+DRAMCtrl::DRAMStats::resetStats()
+{
+    dram.lastStatsResetTick = curTick();
+}
+
+DRAMCtrl::RankStats::RankStats(DRAMCtrl &_memory, Rank &_rank)
+    : Stats::Group(&_memory, csprintf("rank%d", _rank.rank).c_str()),
+    rank(_rank),
+
+    ADD_STAT(actEnergy, "Energy for activate commands per rank (pJ)"),
+    ADD_STAT(preEnergy, "Energy for precharge commands per rank (pJ)"),
+    ADD_STAT(readEnergy, "Energy for read commands per rank (pJ)"),
+    ADD_STAT(writeEnergy, "Energy for write commands per rank (pJ)"),
+    ADD_STAT(refreshEnergy, "Energy for refresh commands per rank (pJ)"),
+    ADD_STAT(actBackEnergy, "Energy for active background per rank (pJ)"),
+    ADD_STAT(preBackEnergy, "Energy for precharge background per rank (pJ)"),
+    ADD_STAT(actPowerDownEnergy,
+             "Energy for active power-down per rank (pJ)"),
+    ADD_STAT(prePowerDownEnergy,
+             "Energy for precharge power-down per rank (pJ)"),
+    ADD_STAT(selfRefreshEnergy, "Energy for self refresh per rank (pJ)"),
+
+    ADD_STAT(totalEnergy, "Total energy per rank (pJ)"),
+    ADD_STAT(averagePower, "Core power per rank (mW)"),
+
+    ADD_STAT(totalIdleTime, "Total Idle time Per DRAM Rank"),
+    ADD_STAT(memoryStateTime, "Time in different power states")
+{
+}
+
+void
+DRAMCtrl::RankStats::regStats()
+{
+    Stats::Group::regStats();
+
+    memoryStateTime.init(6);
+    memoryStateTime.subname(0, "IDLE");
+    memoryStateTime.subname(1, "REF");
+    memoryStateTime.subname(2, "SREF");
+    memoryStateTime.subname(3, "PRE_PDN");
+    memoryStateTime.subname(4, "ACT");
+    memoryStateTime.subname(5, "ACT_PDN");
+}
+
+void
+DRAMCtrl::RankStats::resetStats()
+{
+    Stats::Group::resetStats();
+
+    rank.resetStats();
+}
+
+void
+DRAMCtrl::RankStats::preDumpStats()
+{
+    Stats::Group::preDumpStats();
+
+    rank.computeStats();
 }
 
 void
index 7de0872ea3298ebda51cc12a81a43cc106910059..8c8c24552fb4c69f7c8b228476369e2d5364a40c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2018 ARM Limited
+ * Copyright (c) 2012-2019 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -270,43 +270,16 @@ class DRAMCtrl : public QoS::MemCtrl
          REF_RUN
      };
 
-    /**
-     * Rank class includes a vector of banks. Refresh and Power state
-     * machines are defined per rank. Events required to change the
-     * state of the refresh and power state machine are scheduled per
-     * rank. This class allows the implementation of rank-wise refresh
-     * and rank-wise power-down.
-     */
-    class Rank : public EventManager
+    class Rank;
+    struct RankStats : public Stats::Group
     {
+        RankStats(DRAMCtrl& memory, Rank &rank);
 
-      private:
-
-        /**
-         * A reference to the parent DRAMCtrl instance
-         */
-        DRAMCtrl& memory;
-
-        /**
-         * Since we are taking decisions out of order, we need to keep
-         * track of what power transition is happening at what time
-         */
-        PowerState pwrStateTrans;
-
-        /**
-         * Previous low-power state, which will be re-entered after refresh.
-         */
-        PowerState pwrStatePostRefresh;
-
-        /**
-         * Track when we transitioned to the current power state
-         */
-        Tick pwrStateTick;
+        void regStats() override;
+        void resetStats() override;
+        void preDumpStats() override;
 
-        /**
-         * Keep track of when a refresh is due.
-         */
-        Tick refreshDueAt;
+        Rank &rank;
 
         /*
          * Command energies
@@ -354,7 +327,46 @@ class DRAMCtrl : public QoS::MemCtrl
         /**
          * Track time spent in each power state.
          */
-        Stats::Vector pwrStateTime;
+        Stats::Vector memoryStateTime;
+    };
+
+    /**
+     * Rank class includes a vector of banks. Refresh and Power state
+     * machines are defined per rank. Events required to change the
+     * state of the refresh and power state machine are scheduled per
+     * rank. This class allows the implementation of rank-wise refresh
+     * and rank-wise power-down.
+     */
+    class Rank : public EventManager
+    {
+
+      private:
+
+        /**
+         * A reference to the parent DRAMCtrl instance
+         */
+        DRAMCtrl& memory;
+
+        /**
+         * Since we are taking decisions out of order, we need to keep
+         * track of what power transition is happening at what time
+         */
+        PowerState pwrStateTrans;
+
+        /**
+         * Previous low-power state, which will be re-entered after refresh.
+         */
+        PowerState pwrStatePostRefresh;
+
+        /**
+         * Track when we transitioned to the current power state
+         */
+        Tick pwrStateTick;
+
+        /**
+         * Keep track of when a refresh is due.
+         */
+        Tick refreshDueAt;
 
         /**
          * Function to update Power Stats
@@ -566,44 +578,8 @@ class DRAMCtrl : public QoS::MemCtrl
         void processWakeUpEvent();
         EventFunctionWrapper wakeUpEvent;
 
-    };
-
-    /**
-     * Define the process to compute stats on a stats dump event, e.g. on
-     * simulation exit or intermediate stats dump. This is defined per rank
-     * as the per rank stats are based on state transition and periodically
-     * updated, requiring re-sync at exit.
-     */
-    class RankDumpCallback : public Callback
-    {
-        Rank *ranks;
-      public:
-        RankDumpCallback(Rank *r) : ranks(r) {}
-        virtual void process() { ranks->computeStats(); };
-    };
-
-    /** Define a process to clear power lib counters on a stats reset */
-    class RankResetCallback : public Callback
-    {
-      private:
-        /** Pointer to the rank, thus we instantiate per rank */
-        Rank *rank;
-
-      public:
-        RankResetCallback(Rank *r) : rank(r) {}
-        virtual void process() { rank->resetStats(); };
-    };
-
-    /** Define a process to store the time on a stats reset */
-    class MemResetCallback : public Callback
-    {
-      private:
-        /** A reference to the DRAMCtrl instance */
-        DRAMCtrl *mem;
-
-      public:
-        MemResetCallback(DRAMCtrl *_mem) : mem(_mem) {}
-        virtual void process() { mem->lastStatsResetTick = curTick(); };
+      protected:
+        RankStats stats;
     };
 
     /**
@@ -1051,85 +1027,101 @@ class DRAMCtrl : public QoS::MemCtrl
      */
     Tick nextReqTime;
 
-    // All statistics that the model needs to capture
-    Stats::Scalar readReqs;
-    Stats::Scalar writeReqs;
-    Stats::Scalar readBursts;
-    Stats::Scalar writeBursts;
-    Stats::Scalar bytesReadDRAM;
-    Stats::Scalar bytesReadWrQ;
-    Stats::Scalar bytesWritten;
-    Stats::Scalar bytesReadSys;
-    Stats::Scalar bytesWrittenSys;
-    Stats::Scalar servicedByWrQ;
-    Stats::Scalar mergedWrBursts;
-    Stats::Scalar neitherReadNorWrite;
-    Stats::Vector perBankRdBursts;
-    Stats::Vector perBankWrBursts;
-    Stats::Scalar numRdRetry;
-    Stats::Scalar numWrRetry;
-    Stats::Scalar totGap;
-    Stats::Vector readPktSize;
-    Stats::Vector writePktSize;
-    Stats::Vector rdQLenPdf;
-    Stats::Vector wrQLenPdf;
-    Stats::Histogram bytesPerActivate;
-    Stats::Histogram rdPerTurnAround;
-    Stats::Histogram wrPerTurnAround;
-
-    // per-master bytes read and written to memory
-    Stats::Vector masterReadBytes;
-    Stats::Vector masterWriteBytes;
-
-    // per-master bytes read and written to memory rate
-    Stats::Formula masterReadRate;
-    Stats::Formula masterWriteRate;
-
-    // per-master read and write serviced memory accesses
-    Stats::Vector masterReadAccesses;
-    Stats::Vector masterWriteAccesses;
-
-    // per-master read and write total memory access latency
-    Stats::Vector masterReadTotalLat;
-    Stats::Vector masterWriteTotalLat;
-
-    // per-master raed and write average memory access latency
-    Stats::Formula masterReadAvgLat;
-    Stats::Formula masterWriteAvgLat;
-
-    // Latencies summed over all requests
-    Stats::Scalar totQLat;
-    Stats::Scalar totMemAccLat;
-    Stats::Scalar totBusLat;
-
-    // Average latencies per request
-    Stats::Formula avgQLat;
-    Stats::Formula avgBusLat;
-    Stats::Formula avgMemAccLat;
-
-    // Average bandwidth
-    Stats::Formula avgRdBW;
-    Stats::Formula avgWrBW;
-    Stats::Formula avgRdBWSys;
-    Stats::Formula avgWrBWSys;
-    Stats::Formula peakBW;
-    Stats::Formula busUtil;
-    Stats::Formula busUtilRead;
-    Stats::Formula busUtilWrite;
-
-    // Average queue lengths
-    Stats::Average avgRdQLen;
-    Stats::Average avgWrQLen;
-
-    // Row hit count and rate
-    Stats::Scalar readRowHits;
-    Stats::Scalar writeRowHits;
-    Stats::Formula readRowHitRate;
-    Stats::Formula writeRowHitRate;
-    Stats::Formula avgGap;
-
-    // DRAM Power Calculation
-    Stats::Formula pageHitRate;
+    /** All statistics that the model needs to capture */
+    struct DRAMStats : public Stats::Group {
+        DRAMStats(DRAMCtrl &dram);
+
+        void regStats() override;
+        void resetStats() override;
+
+        DRAMCtrl &dram;
+
+        Stats::Scalar readReqs;
+        Stats::Scalar writeReqs;
+        Stats::Scalar readBursts;
+        Stats::Scalar writeBursts;
+        Stats::Scalar servicedByWrQ;
+        Stats::Scalar mergedWrBursts;
+        Stats::Scalar neitherReadNorWriteReqs;
+        Stats::Vector perBankRdBursts;
+        Stats::Vector perBankWrBursts;
+
+        // Average queue lengths
+        Stats::Average avgRdQLen;
+        Stats::Average avgWrQLen;
+
+        // Latencies summed over all requests
+        Stats::Scalar totQLat;
+        Stats::Scalar totBusLat;
+        Stats::Scalar totMemAccLat;
+
+        // Average latencies per request
+        Stats::Formula avgQLat;
+        Stats::Formula avgBusLat;
+        Stats::Formula avgMemAccLat;
+
+        Stats::Scalar numRdRetry;
+        Stats::Scalar numWrRetry;
+
+        // Row hit count and rate
+        Stats::Scalar readRowHits;
+        Stats::Scalar writeRowHits;
+        Stats::Formula readRowHitRate;
+        Stats::Formula writeRowHitRate;
+
+        Stats::Vector readPktSize;
+        Stats::Vector writePktSize;
+        Stats::Vector rdQLenPdf;
+        Stats::Vector wrQLenPdf;
+        Stats::Histogram bytesPerActivate;
+        Stats::Histogram rdPerTurnAround;
+        Stats::Histogram wrPerTurnAround;
+
+        Stats::Scalar bytesReadDRAM;
+        Stats::Scalar bytesReadWrQ;
+        Stats::Scalar bytesWritten;
+        Stats::Scalar bytesReadSys;
+        Stats::Scalar bytesWrittenSys;
+
+        // Average bandwidth
+        Stats::Formula avgRdBW;
+        Stats::Formula avgWrBW;
+        Stats::Formula avgRdBWSys;
+        Stats::Formula avgWrBWSys;
+        Stats::Formula peakBW;
+
+        Stats::Formula busUtil;
+        Stats::Formula busUtilRead;
+        Stats::Formula busUtilWrite;
+
+        Stats::Scalar totGap;
+        Stats::Formula avgGap;
+
+        // per-master bytes read and written to memory
+        Stats::Vector masterReadBytes;
+        Stats::Vector masterWriteBytes;
+
+        // per-master bytes read and written to memory rate
+        Stats::Formula masterReadRate;
+        Stats::Formula masterWriteRate;
+
+        // per-master read and write serviced memory accesses
+        Stats::Vector masterReadAccesses;
+        Stats::Vector masterWriteAccesses;
+
+        // per-master read and write total memory access latency
+        Stats::Vector masterReadTotalLat;
+        Stats::Vector masterWriteTotalLat;
+
+        // per-master raed and write average memory access latency
+        Stats::Formula masterReadAvgLat;
+        Stats::Formula masterWriteAvgLat;
+
+        // DRAM Power Calculation
+        Stats::Formula pageHitRate;
+    };
+
+    DRAMStats stats;
 
     // Holds the value of the rank of burst issued
     uint8_t activeRank;
@@ -1172,9 +1164,6 @@ class DRAMCtrl : public QoS::MemCtrl
     };
 
   public:
-
-    void regStats() override;
-
     DRAMCtrl(const DRAMCtrlParams* p);
 
     DrainState drain() override;
index 27ff0eb6115d55f70d1256d05a93c2a330bc8bf3..5ced58ae8f35482985972d658ff8c1c3d7cb4dfe 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2018 ARM Limited
+ * Copyright (c) 2017-2019 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -52,7 +52,8 @@ MemCtrl::MemCtrl(const QoSMemCtrlParams * p)
     qosPriorityEscalation(p->qos_priority_escalation),
     qosSyncroScheduler(p->qos_syncro_scheduler),
     totalReadQueueSize(0), totalWriteQueueSize(0),
-    busState(READ), busStateNext(READ)
+    busState(READ), busStateNext(READ),
+    stats(*this)
 {
     // Set the priority policy
     if (policy) {
@@ -113,7 +114,7 @@ MemCtrl::logRequest(BusState dir, MasterID m_id, uint8_t qos,
     }
 
     // Record statistics
-    avgPriority[m_id].sample(qos);
+    stats.avgPriority[m_id].sample(qos);
 
     // Compute avg priority distance
 
@@ -122,7 +123,7 @@ MemCtrl::logRequest(BusState dir, MasterID m_id, uint8_t qos,
             (abs(int(qos) - int(i))) * packetPriorities[m_id][i];
 
         if (distance > 0) {
-            avgPriorityDistance[m_id].sample(distance);
+            stats.avgPriorityDistance[m_id].sample(distance);
             DPRINTF(QOS,
                     "QoSMemCtrl::logRequest MASTER %s [id %d]"
                     " registering priority distance %d for priority %d"
@@ -191,13 +192,13 @@ MemCtrl::logResponse(BusState dir, MasterID m_id, uint8_t qos,
 
         if (latency > 0) {
             // Record per-priority latency stats
-            if (priorityMaxLatency[qos].value() < latency) {
-                priorityMaxLatency[qos] = latency;
+            if (stats.priorityMaxLatency[qos].value() < latency) {
+                stats.priorityMaxLatency[qos] = latency;
             }
 
-            if (priorityMinLatency[qos].value() > latency
-                    || priorityMinLatency[qos].value() == 0) {
-                priorityMinLatency[qos] = latency;
+            if (stats.priorityMinLatency[qos].value() > latency
+                    || stats.priorityMinLatency[qos].value() == 0) {
+                stats.priorityMinLatency[qos] = latency;
             }
         }
     }
@@ -280,52 +281,71 @@ MemCtrl::addMaster(MasterID m_id)
     }
 }
 
+MemCtrl::MemCtrlStats::MemCtrlStats(MemCtrl &mc)
+    : Stats::Group(&mc),
+    memCtrl(mc),
+
+    ADD_STAT(avgPriority,
+             "Average QoS priority value for accepted requests"),
+    ADD_STAT(avgPriorityDistance,
+             "Average QoS priority distance between assigned and "
+             "queued values"),
+
+    ADD_STAT(priorityMinLatency,
+             "per QoS priority minimum request to response latency (s)"),
+    ADD_STAT(priorityMaxLatency,
+        "per QoS priority maximum request to response latency (s)"),
+    ADD_STAT(numReadWriteTurnArounds,
+             "Number of turnarounds from READ to WRITE"),
+    ADD_STAT(numWriteReadTurnArounds,
+             "Number of turnarounds from WRITE to READ"),
+    ADD_STAT(numStayReadState,
+             "Number of times bus staying in READ state"),
+    ADD_STAT(numStayWriteState,
+             "Number of times bus staying in WRITE state")
+{
+}
+
 void
-MemCtrl::regStats()
+MemCtrl::MemCtrlStats::regStats()
 {
-    AbstractMemory::regStats();
+    Stats::Group::regStats();
 
     using namespace Stats;
 
-    // Initializes per master statistics
-    avgPriority.init(_system->maxMasters()).name(name() + ".avgPriority")
-        .desc("Average QoS priority value for accepted requests")
-        .flags(nozero | nonan).precision(2);
-
-    avgPriorityDistance.init(_system->maxMasters())
-        .name(name() + ".avgPriorityDistance")
-        .desc("Average QoS priority distance between assigned and "
-        "queued values").flags(nozero | nonan);
-
-    priorityMinLatency.init(numPriorities())
-        .name(name() + ".priorityMinLatency")
-        .desc("per QoS priority minimum request to response latency (s)")
-        .precision(12);
+    System *system = memCtrl._system;
+    const auto max_masters = system->maxMasters();
+    const auto num_priorities = memCtrl.numPriorities();
 
-    priorityMaxLatency.init(numPriorities())
-        .name(name() + ".priorityMaxLatency")
-        .desc("per QoS priority maximum request to response latency (s)")
-        .precision(12);
-
-    numReadWriteTurnArounds.name(name() + ".numReadWriteTurnArounds")
-        .desc("Number of turnarounds from READ to WRITE");
-
-    numWriteReadTurnArounds.name(name() + ".numWriteReadTurnArounds")
-        .desc("Number of turnarounds from WRITE to READ");
-
-    numStayReadState.name(name() + ".numStayReadState")
-        .desc("Number of times bus staying in READ state");
-
-    numStayWriteState.name(name() + ".numStayWriteState")
-        .desc("Number of times bus staying in WRITE state");
-
-    for (int i = 0; i < _system->maxMasters(); i++) {
-        const std::string master = _system->getMasterName(i);
+    // Initializes per master statistics
+    avgPriority
+        .init(max_masters)
+        .flags(nozero | nonan)
+        .precision(2)
+        ;
+
+    avgPriorityDistance
+        .init(max_masters)
+        .flags(nozero | nonan)
+        ;
+
+    priorityMinLatency
+        .init(num_priorities)
+        .precision(12)
+        ;
+
+    priorityMaxLatency
+        .init(num_priorities)
+        .precision(12)
+        ;
+
+    for (int i = 0; i < max_masters; i++) {
+        const std::string master = system->getMasterName(i);
         avgPriority.subname(i, master);
         avgPriorityDistance.subname(i, master);
     }
 
-    for (int j = 0; j < numPriorities(); ++j) {
+    for (int j = 0; j < num_priorities; ++j) {
         priorityMinLatency.subname(j, std::to_string(j));
         priorityMaxLatency.subname(j, std::to_string(j));
     }
@@ -336,15 +356,15 @@ MemCtrl::recordTurnaroundStats()
 {
     if (busStateNext != busState) {
         if (busState == READ) {
-            numWriteReadTurnArounds++;
+            stats.numWriteReadTurnArounds++;
         } else if (busState == WRITE) {
-            numReadWriteTurnArounds++;
+            stats.numReadWriteTurnArounds++;
         }
     } else {
         if (busState == READ) {
-            numStayReadState++;
+            stats.numStayReadState++;
         } else if (busState == WRITE) {
-            numStayWriteState++;
+            stats.numStayWriteState++;
         }
     }
 }
index db85f276d7e1ecaf35dfa69c5cbb8c67874507d1..e31f21b234ee5c162cfd61254facfa808c1ee7fc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 ARM Limited
+ * Copyright (c) 2019 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -123,26 +123,35 @@ class MemCtrl: public AbstractMemory
     /** bus state for next request event triggered */
     BusState busStateNext;
 
-    /** per-master average QoS priority */
-    Stats::VectorStandardDeviation avgPriority;
-    /** per-master average QoS distance between assigned and queued values */
-    Stats::VectorStandardDeviation avgPriorityDistance;
-
-    /** per-priority minimum latency */
-    Stats::Vector priorityMinLatency;
-    /** per-priority maximum latency */
-    Stats::Vector priorityMaxLatency;
-    /** Count the number of turnarounds READ to WRITE */
-    Stats::Scalar numReadWriteTurnArounds;
-    /** Count the number of turnarounds WRITE to READ */
-    Stats::Scalar numWriteReadTurnArounds;
-    /** Count the number of times bus staying in READ state */
-    Stats::Scalar numStayReadState;
-    /** Count the number of times bus staying in WRITE state */
-    Stats::Scalar numStayWriteState;
-
-    /** registers statistics */
-    void regStats() override;
+    struct MemCtrlStats : public Stats::Group
+    {
+        MemCtrlStats(MemCtrl &mc);
+
+        void regStats() override;
+
+        const MemCtrl &memCtrl;
+
+        /** per-master average QoS priority */
+        Stats::VectorStandardDeviation avgPriority;
+        /**
+         * per-master average QoS distance between assigned and
+         * queued values
+         */
+        Stats::VectorStandardDeviation avgPriorityDistance;
+
+        /** per-priority minimum latency */
+        Stats::Vector priorityMinLatency;
+        /** per-priority maximum latency */
+        Stats::Vector priorityMaxLatency;
+        /** Count the number of turnarounds READ to WRITE */
+        Stats::Scalar numReadWriteTurnArounds;
+        /** Count the number of turnarounds WRITE to READ */
+        Stats::Scalar numWriteReadTurnArounds;
+        /** Count the number of times bus staying in READ state */
+        Stats::Scalar numStayReadState;
+        /** Count the number of times bus staying in WRITE state */
+        Stats::Scalar numStayWriteState;
+    } stats;
 
     /**
      * Initializes dynamically counters and