ruby: Replace Time with Cycles in SequencerMessage
[gem5.git] / src / mem / simple_dram.cc
index 0f6e9511c5858d448c59d99901d38a25d1cb0e16..32a13eef01e0eef75f490e6c1415f7205604e209 100644 (file)
@@ -38,6 +38,8 @@
  *          Ani Udipi
  */
 
+#include "base/trace.hh"
+#include "debug/Drain.hh"
 #include "debug/DRAM.hh"
 #include "debug/DRAMWR.hh"
 #include "mem/simple_dram.hh"
@@ -49,9 +51,9 @@ SimpleDRAM::SimpleDRAM(const SimpleDRAMParams* p) :
     AbstractMemory(p),
     port(name() + ".port", *this),
     retryRdReq(false), retryWrReq(false),
-    rowHitFlag(false), stopReads(false),
+    rowHitFlag(false), stopReads(false), actTicks(p->activation_limit, 0),
     writeEvent(this), respondEvent(this),
-    refreshEvent(this), nextReqEvent(this), drainEvent(NULL),
+    refreshEvent(this), nextReqEvent(this), drainManager(NULL),
     bytesPerCacheLine(0),
     linesPerRowBuffer(p->lines_per_rowbuffer),
     ranksPerChannel(p->ranks_per_channel),
@@ -62,6 +64,7 @@ SimpleDRAM::SimpleDRAM(const SimpleDRAMParams* p) :
     tWTR(p->tWTR), tBURST(p->tBURST),
     tRCD(p->tRCD), tCL(p->tCL), tRP(p->tRP),
     tRFC(p->tRFC), tREFI(p->tREFI),
+    tXAW(p->tXAW), activationLimit(p->activation_limit),
     memSchedPolicy(p->mem_sched_policy), addrMapping(p->addr_mapping),
     pageMgmt(p->page_policy),
     busBusyUntil(0), prevdramaccess(0), writeStartTime(0),
@@ -163,6 +166,11 @@ SimpleDRAM::writeQueueFull() const
 SimpleDRAM::DRAMPacket*
 SimpleDRAM::decodeAddr(PacketPtr pkt)
 {
+    // decode the address based on the address mapping scheme
+    //
+    // with R, C, B and K denoting rank, column, bank and rank,
+    // respectively, and going from MSB to LSB, the two schemes are
+    // RKBC (openmap) and RCKB (closedmap)
     uint8_t rank;
     uint16_t bank;
     uint16_t row;
@@ -173,26 +181,46 @@ SimpleDRAM::decodeAddr(PacketPtr pkt)
     // truncate the address to the access granularity
     addr = addr / bytesPerCacheLine;
 
+    // we have removed the lowest order address bits that denote the
+    // position within the cache line, proceed and select the
+    // appropriate bits for bank, rank and row (no column address is
+    // needed)
     if (addrMapping == Enums::openmap) {
+        // the lowest order bits denote the column to ensure that
+        // sequential cache lines occupy the same row
         addr = addr / linesPerRowBuffer;
 
+        // after the column bits, we get the bank bits to interleave
+        // over the banks
         bank = addr % banksPerRank;
         addr = addr / banksPerRank;
 
+        // after the bank, we get the rank bits which thus interleaves
+        // over the ranks
         rank = addr % ranksPerChannel;
         addr = addr / ranksPerChannel;
 
+        // lastly, get the row bits
         row = addr % rowsPerBank;
         addr = addr / rowsPerBank;
     } else if (addrMapping == Enums::closemap) {
+        // optimise for closed page mode and utilise maximum
+        // parallelism of the DRAM (at the cost of power)
+
+        // start with the bank bits, as this provides the maximum
+        // opportunity for parallelism between requests
         bank = addr % banksPerRank;
         addr = addr / banksPerRank;
 
+        // next get the rank bits
         rank = addr % ranksPerChannel;
         addr = addr / ranksPerChannel;
 
+        // next the column bits which we do not need to keep track of
+        // and simply skip past
         addr = addr / linesPerRowBuffer;
 
+        // lastly, get the row bits
         row = addr % rowsPerBank;
         addr = addr / rowsPerBank;
     } else
@@ -297,13 +325,19 @@ SimpleDRAM::processWriteEvent()
 
         if (pageMgmt == Enums::open) {
             bank.openRow = dram_pkt->row;
-            bank.freeAt = schedTime + tBURST + accessLat;
+            bank.freeAt = schedTime + tBURST + std::max(accessLat, tCL);
+            busBusyUntil = bank.freeAt - tCL;
 
-            if (!rowHitFlag)
+            if (!rowHitFlag) {
                 bank.tRASDoneAt = bank.freeAt + tRP;
-
+                recordActivate(bank.freeAt - tCL - tRCD);
+                busBusyUntil = bank.freeAt - tCL - tRCD;
+            }
         } else if (pageMgmt == Enums::close) {
             bank.freeAt = schedTime + tBURST + accessLat + tRP + tRP;
+            // Work backwards from bank.freeAt to determine activate time
+            recordActivate(bank.freeAt - tRP - tRP - tCL - tRCD);
+            busBusyUntil = bank.freeAt - tRP - tRP - tCL - tRCD;
             DPRINTF(DRAMWR, "processWriteEvent::bank.freeAt for "
                     "banks_id %d is %lld\n",
                     dram_pkt->rank * banksPerRank + dram_pkt->bank,
@@ -311,13 +345,8 @@ SimpleDRAM::processWriteEvent()
         } else
             panic("Unknown page management policy chosen\n");
 
-        // @todo: As of now, write goes on the databus asap, maybe
-        // be held up at bank. May want to change it to delay the
-        // schedTime itself.
-        busBusyUntil = schedTime + tBURST;
         DPRINTF(DRAMWR,"Done writing to address %lld\n",dram_pkt->addr);
 
-
         DPRINTF(DRAMWR,"schedtime is %lld, tBURST is %lld, "
                 "busbusyuntil is %lld\n",
                 schedTime, tBURST, busBusyUntil);
@@ -346,9 +375,9 @@ SimpleDRAM::processWriteEvent()
 
     // if there is nothing left in any queue, signal a drain
     if (dramWriteQueue.empty() && dramReadQueue.empty() &&
-        dramRespQueue.empty () && drainEvent) {
-        drainEvent->process();
-        drainEvent = NULL;
+        dramRespQueue.empty () && drainManager) {
+        drainManager->signalDrainDone();
+        drainManager = NULL;
     }
 
     // Once you're done emptying the write queue, check if there's
@@ -474,6 +503,13 @@ SimpleDRAM::printQs() const {
 bool
 SimpleDRAM::recvTimingReq(PacketPtr pkt)
 {
+    /// @todo temporary hack to deal with memory corruption issues until
+    /// 4-phase transactions are complete
+    for (int x = 0; x < pendingDelete.size(); x++)
+        delete pendingDelete[x];
+    pendingDelete.clear();
+
+
     // This is where we enter from the outside world
     DPRINTF(DRAM, "Inside recvTimingReq: request %s addr %lld size %d\n",
             pkt->cmdString(),pkt->getAddr(), pkt->getSize());
@@ -495,7 +531,7 @@ SimpleDRAM::recvTimingReq(PacketPtr pkt)
     // simply drop inhibited packets for now
     if (pkt->memInhibitAsserted()) {
         DPRINTF(DRAM,"Inhibited packet -- Dropping it now\n");
-        delete pkt;
+        pendingDelete.push_back(pkt);
         return true;
     }
 
@@ -595,9 +631,9 @@ SimpleDRAM::processRespondEvent()
      } else {
          // if there is nothing left in any queue, signal a drain
          if (dramWriteQueue.empty() && dramReadQueue.empty() &&
-             drainEvent) {
-             drainEvent->process();
-             drainEvent = NULL;
+             drainManager) {
+             drainManager->signalDrainDone();
+             drainManager = NULL;
          }
      }
 }
@@ -772,7 +808,8 @@ SimpleDRAM::estimateLatency(DRAMPacket* dram_pkt, Tick inTime)
     } else
         panic("No page management policy chosen\n");
 
-    DPRINTF(DRAM, "Returning %lld from estimateLatency()\n",accLat);
+    DPRINTF(DRAM, "Returning < %lld, %lld > from estimateLatency()\n",
+            bankLat, accLat);
 
     return make_pair(bankLat, accLat);
 }
@@ -783,6 +820,41 @@ SimpleDRAM::processNextReqEvent()
     scheduleNextReq();
 }
 
+void
+SimpleDRAM::recordActivate(Tick act_tick)
+{
+    assert(actTicks.size() == activationLimit);
+
+    DPRINTF(DRAM, "Activate at tick %d\n", act_tick);
+
+    // sanity check
+    if (actTicks.back() && (act_tick - actTicks.back()) < tXAW) {
+        panic("Got %d activates in window %d (%d - %d) which is smaller "
+              "than %d\n", activationLimit, act_tick - actTicks.back(),
+              act_tick, actTicks.back(), tXAW);
+    }
+
+    // shift the times used for the book keeping, the last element
+    // (highest index) is the oldest one and hence the lowest value
+    actTicks.pop_back();
+
+    // record an new activation (in the future)
+    actTicks.push_front(act_tick);
+
+    // cannot activate more than X times in time window tXAW, push the
+    // next one (the X + 1'st activate) to be tXAW away from the
+    // oldest in our window of X
+    if (actTicks.back() && (act_tick - actTicks.back()) < tXAW) {
+        DPRINTF(DRAM, "Enforcing tXAW with X = %d, next activate no earlier "
+                "than %d\n", activationLimit, actTicks.back() + tXAW);
+        for(int i = 0; i < ranksPerChannel; i++)
+            for(int j = 0; j < banksPerRank; j++)
+                // next activate must not happen before end of window
+                banks[i][j].freeAt = std::max(banks[i][j].freeAt,
+                                              actTicks.back() + tXAW);
+    }
+}
+
 void
 SimpleDRAM::doDRAMAccess(DRAMPacket* dram_pkt)
 {
@@ -813,14 +885,18 @@ SimpleDRAM::doDRAMAccess(DRAMPacket* dram_pkt)
         bank.openRow = dram_pkt->row;
         bank.freeAt = curTick() + addDelay + accessLat;
         // If you activated a new row do to this access, the next access
-        // will have to respect tRAS for this bank. Assume tRAS ~= 3 * tRP
-        if (!rowHitFlag)
+        // will have to respect tRAS for this bank. Assume tRAS ~= 3 * tRP.
+        // Also need to account for t_XAW
+        if (!rowHitFlag) {
             bank.tRASDoneAt = bank.freeAt + tRP;
-
+            recordActivate(bank.freeAt - tCL - tRCD); //since this is open page,
+                                                      //no tRP by default
+        }
     } else if (pageMgmt == Enums::close) { // accounting for tRAS also
-        // assuming that tRAS ~= 3 * tRP, and tRAS ~= 4 * tRP, as is common
+        // assuming that tRAS ~= 3 * tRP, and tRC ~= 4 * tRP, as is common
         // (refer Jacob/Ng/Wang and Micron datasheets)
         bank.freeAt = curTick() + addDelay + accessLat + tRP + tRP;
+        recordActivate(bank.freeAt - tRP - tRP - tCL - tRCD); //essentially (freeAt - tRC)
         DPRINTF(DRAM,"doDRAMAccess::bank.freeAt is %lld\n",bank.freeAt);
     } else
         panic("No page management policy chosen\n");
@@ -927,15 +1003,18 @@ SimpleDRAM::scheduleNextReq()
     DPRINTF(DRAM, "Reached scheduleNextReq()\n");
 
     // Figure out which request goes next, and move it to front()
-    if (!chooseNextReq())
-        return;
-
-    doDRAMAccess(dramReadQueue.front());
+    if (!chooseNextReq()) {
+        // In the case there is no read request to go next, see if we
+        // are asked to drain, and if so trigger writes, this also
+        // ensures that if we hit the write limit we will do this
+        // multiple times until we are completely drained
+        if (drainManager && !dramWriteQueue.empty() && !writeEvent.scheduled())
+            triggerWrites();
+    } else {
+        doDRAMAccess(dramReadQueue.front());
+    }
 }
 
-
-
-
 Tick
 SimpleDRAM::maxBankFreeAt() const
 {
@@ -1197,22 +1276,32 @@ SimpleDRAM::getSlavePort(const string &if_name, PortID idx)
 }
 
 unsigned int
-SimpleDRAM::drain(Event *de)
+SimpleDRAM::drain(DrainManager *dm)
 {
-    unsigned int count = port.drain(de);
+    unsigned int count = port.drain(dm);
 
     // if there is anything in any of our internal queues, keep track
     // of that as well
     if (!(dramWriteQueue.empty() && dramReadQueue.empty() &&
           dramRespQueue.empty())) {
+        DPRINTF(Drain, "DRAM controller not drained, write: %d, read: %d,"
+                " resp: %d\n", dramWriteQueue.size(), dramReadQueue.size(),
+                dramRespQueue.size());
         ++count;
-        drainEvent = de;
+        drainManager = dm;
+        // the only part that is not drained automatically over time
+        // is the write queue, thus trigger writes if there are any
+        // waiting and no reads waiting, otherwise wait until the
+        // reads are done
+        if (dramReadQueue.empty() && !dramWriteQueue.empty() &&
+            !writeEvent.scheduled())
+            triggerWrites();
     }
 
     if (count)
-        changeState(Draining);
+        setDrainState(Drainable::Draining);
     else
-        changeState(Drained);
+        setDrainState(Drainable::Drained);
     return count;
 }