* Ani Udipi
*/
+#include "base/trace.hh"
+#include "debug/Drain.hh"
#include "debug/DRAM.hh"
#include "debug/DRAMWR.hh"
#include "mem/simple_dram.hh"
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), drainManager(NULL),
bytesPerCacheLine(0),
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),
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;
// 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
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,
} 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);
} 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);
}
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)
{
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");
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
{
// 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;
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)