summaryrefslogtreecommitdiff
path: root/ext/drampower/src/CmdScheduler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ext/drampower/src/CmdScheduler.cc')
-rw-r--r--ext/drampower/src/CmdScheduler.cc659
1 files changed, 659 insertions, 0 deletions
diff --git a/ext/drampower/src/CmdScheduler.cc b/ext/drampower/src/CmdScheduler.cc
new file mode 100644
index 000000000..bffc5d3bb
--- /dev/null
+++ b/ext/drampower/src/CmdScheduler.cc
@@ -0,0 +1,659 @@
+/*
+ * Copyright (c) 2012-2014, TU Delft
+ * Copyright (c) 2012-2014, TU Eindhoven
+ * Copyright (c) 2012-2014, TU Kaiserslautern
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Karthik Chandrasekar
+ *
+ */
+#include "CmdScheduler.h"
+
+#include <cassert>
+#include <cmath> // For log2
+
+#include <algorithm> // For max
+
+
+using namespace std;
+using namespace Data;
+
+// Read the traces and get the transaction. Each transaction is executed by
+// scheduling a number of commands to the memory. Hence, the transactions are
+// translated into a sequence of commands which will be used for power analysis.
+void cmdScheduler::transTranslation(MemorySpecification memSpec,
+ ifstream& trans_trace, int grouping, int interleaving, int burst, int powerdown)
+{
+ commands.open("commands.trace", ifstream::out);
+ MemArchitectureSpec& memArchSpec = memSpec.memArchSpec;
+ nBanks = memArchSpec.nbrOfBanks;
+ nColumns = memArchSpec.nbrOfColumns;
+ burstLength = memArchSpec.burstLength;
+ nbrOfBankGroups = memArchSpec.nbrOfBankGroups;
+
+ BGI = grouping;
+ BI = interleaving;
+ BC = burst;
+ power_down = powerdown;
+
+ schedulingInitialization(memSpec);
+ getTrans(trans_trace, memSpec);
+
+ trans_trace.close();
+ commands.close();
+ ACT.erase(ACT.begin(), ACT.end());
+ PRE.erase(PRE.begin(), PRE.end());
+ RDWR.erase(RDWR.begin(), RDWR.end());
+ cmdScheduling.erase(cmdScheduling.begin(), cmdScheduling.end());
+ cmdList.erase(cmdList.begin(), cmdList.end());
+ transTrace.erase(transTrace.begin(), transTrace.end());
+} // cmdScheduler::transTranslation
+
+// initialize the variables and vectors for starting command scheduling.
+void cmdScheduler::schedulingInitialization(MemorySpecification memSpec)
+{
+ MemTimingSpec& memTimingSpec = memSpec.memTimingSpec;
+
+ ACT.resize(2 * memSpec.memArchSpec.nbrOfBanks);
+ RDWR.resize(2 * memSpec.memArchSpec.nbrOfBanks);
+ PRE.resize(memSpec.memArchSpec.nbrOfBanks);
+ bankaccess = memSpec.memArchSpec.nbrOfBanks;
+ if (!ACT.empty()) {
+ ACT.erase(ACT.begin(), ACT.end());
+ }
+ if (!PRE.empty()) {
+ PRE.erase(PRE.begin(), PRE.end());
+ }
+ if (!RDWR.empty()) {
+ RDWR.erase(RDWR.begin(), RDWR.end());
+ }
+
+ ///////////////initialization//////////////
+ for (unsigned i = 0; i < memSpec.memArchSpec.nbrOfBanks; i++) {
+ cmd.Type = PRECHARGE;
+ cmd.bank = i;
+ cmd.name = "PRE";
+ if (memSpec.id == "WIDEIO_SDR")
+ cmd.time = 1 - static_cast<double>(memSpec.memTimingSpec.TAW);
+ else
+ cmd.time = 1 - static_cast<double>(memSpec.memTimingSpec.FAW);
+
+ PRE.push_back(cmd);
+
+ cmd.Type = ACTIVATE;
+ cmd.name = "ACT";
+ ACT.push_back(cmd);
+
+ cmd.Type = WRITE;
+ cmd.name = "WRITE";
+ cmd.time = -1;
+ RDWR[i].push_back(cmd);
+ }
+ tREF = memTimingSpec.REFI;
+ transFinish.time = 0;
+ transFinish.bank = 0;
+
+ PreRDWR.bank = -1;
+ PreRDWR.Type = READ;
+ PreRDWR.name = "RD";
+ PreRDWR.time = -1;
+ startTime = 0;
+} // cmdScheduler::schedulingInitialization
+
+// transactions are generated according to the information read from the traces.
+// Then the command scheduling function is triggered to generate commands and
+// schedule them to the memory according to the timing constraints.
+void cmdScheduler::getTrans(std::ifstream& trans_trace, MemorySpecification memSpec)
+{
+ std::string line;
+
+ transTime = 0;
+ unsigned newtranstime;
+ unsigned transAddr;
+ unsigned transType = 1;
+ trans TransItem;
+
+ if (!transTrace.empty()) {
+ transTrace.erase(transTrace.begin(), transTrace.end());
+ }
+
+ while (getline(trans_trace, line)) {
+ istringstream linestream(line);
+ string item;
+ unsigned itemnum = 0;
+ while (getline(linestream, item, ',')) {
+ if (itemnum == 0) {
+ stringstream timestamp(item);
+ timestamp >> newtranstime;
+ transTime = transTime + newtranstime;
+ } else if (itemnum == 1) {
+ if (item == "write" || item == "WRITE") {
+ transType = WRITE;
+ } else {
+ transType = READ;
+ }
+ } else if (itemnum == 2) {
+ stringstream timestamp(item);
+ timestamp >> std::hex >> transAddr;
+ }
+ itemnum++;
+ }
+ // generate a transaction
+ TransItem.timeStamp = transTime;
+ TransItem.logicalAddress = transAddr;
+ TransItem.type = transType;
+
+ transTrace.push_back(TransItem);
+
+ if (transTrace.size() == MILLION) {
+ // The scheduling is implemented for every MILLION transactions.
+ // It is used to reduce the used memory during the running of this tool.
+ analyticalScheduling(memSpec);
+ transTrace.erase(transTrace.begin(), transTrace.end());
+ }
+ }
+
+ if ((transTrace.size() < MILLION) && (!transTrace.empty())) {
+ analyticalScheduling(memSpec);
+ transTrace.erase(transTrace.begin(), transTrace.end());
+ }
+} // cmdScheduler::getTrans
+
+// Transactions are executed individually and the command scheduling is
+// independent between transactions. The commands for a new transaction cannot
+// be scheduled until all the commands for the current one are scheduled.
+// After the scheduling, a sequence of commands are obtained and they are written
+// into commands.txt which will be used for power analysis.
+void cmdScheduler::analyticalScheduling(MemorySpecification memSpec)
+{
+ int Bs = -1;
+ int transType = -1;
+ double timer = 0;
+ int bankGroupPointer = 0;
+ int bankGroupAddr = 0;
+ bool collisionFound;
+ physicalAddr PhysicalAddress;
+ bool bankGroupSwitch = false;
+ std::vector<unsigned> bankPointer(nbrOfBankGroups, 0);
+ std::vector<int> bankAccessNum(nBanks, -1);
+ std::vector<bool> ACTSchedule(nBanks, false);
+ int bankAddr = -1;
+ double endTime = 0;
+ double tComing_REF = 0;
+
+ Inselfrefresh = 0;
+
+ MemTimingSpec& memTimingSpec = memSpec.memTimingSpec;
+
+ for (unsigned t = 0; t < transTrace.size(); t++) {
+ cmdScheduling.erase(cmdScheduling.begin(), cmdScheduling.end());
+
+ for (unsigned i = 0; i < nBanks; i++) {
+ ACTSchedule[i] = false;
+ bankAccessNum[i] = -1;
+ }
+
+ timingsGet = false;
+ timer = transTrace[t].timeStamp;
+
+ PhysicalAddress = memoryMap(transTrace[t], memSpec);
+
+ for (unsigned i = 0; i < nbrOfBankGroups; i++) {
+ bankPointer[i] = PhysicalAddress.bankAddr; // the bank pointer per group.
+ }
+ bankGroupPointer = PhysicalAddress.bankGroupAddr;
+
+ endTime = max(transFinish.time, PRE[transFinish.bank].time +
+ static_cast<int>(memTimingSpec.RP));
+
+ // Before starting the scheduling for the next transaction, it has to
+ // check whether it is necessary for implementing power down.
+ if (power_down == SELF_REFRESH)
+ pdScheduling(endTime, timer, memSpec);
+ else if (power_down == POWER_DOWN)
+ pdScheduling(endTime, min(timer, tREF), memSpec);
+
+ tComing_REF = tREF;
+
+ ///////////////Scheduling Refresh////////////////////////
+ if (((transFinish.time >= tREF) || (timer >= tREF))) {
+ for (double i = 0; i <= ((timer - tComing_REF) > 0 ? (timer - tComing_REF) /
+ memTimingSpec.REFI : 0); i++) {
+ cmd.bank = 0;
+ cmd.name = "REF";
+ cmd.time = max(max(max(transFinish.time, PRE[transFinish.bank].time
+ + static_cast<int>(memTimingSpec.RP)), tREF), startTime);
+ if (((power_down == SELF_REFRESH) && !Inselfrefresh) ||
+ (power_down != SELF_REFRESH)) {
+ cmdScheduling.push_back(cmd);
+ startTime = cmd.time + memTimingSpec.RFC;
+ }
+ tREF = tREF + memTimingSpec.REFI;
+ // during the refreshing, power down should be taken into account.
+ if (!Inselfrefresh)
+ pdScheduling(endTime, min(timer, tREF), memSpec);
+ }
+ }
+ ///////////////Execution Transactions///////////////////
+ Bs = PhysicalAddress.bankAddr;
+ transType = transTrace[t].type;
+
+ tRWTP = getRWTP(transType, memSpec);
+
+ for (int i = 0; i < BI; i++) {
+ for (int k = 0; k < BC; k++) {
+ if (memSpec.memoryType == MemoryType::DDR4) {
+ bankGroupPointer = PhysicalAddress.bankGroupAddr;
+ }
+
+ for (int j = 0; j < BGI; j++) {
+ bankGroupSwitch = false;
+ if (memSpec.memoryType == MemoryType::DDR4) {
+ if (bankGroupPointer != bankGroupAddr) {
+ bankGroupSwitch = true;
+ }
+ // update to the current bank group address.
+ bankGroupAddr = PhysicalAddress.bankGroupAddr + j;
+ bankAddr = bankGroupAddr * nBanks / nbrOfBankGroups +
+ bankPointer[bankGroupAddr];
+ } else {
+ bankAddr = Bs + i;
+ }
+
+ if (!timingsGet) {
+ getTimingConstraints(bankGroupSwitch, memSpec,
+ PreRDWR.Type, transType);
+ }
+
+ ////////////////ACT Scheduling///////////////////
+ if (!ACTSchedule[bankAddr]) {
+ cmd.bank = bankAddr;
+ cmd.PhysicalAddr.bankAddr = cmd.bank;
+ cmd.PhysicalAddr.rowAddr = PhysicalAddress.rowAddr;
+ cmd.Type = ACTIVATE;
+ cmd.name = "ACT";
+ Inselfrefresh = 0;
+ cmd.time = max(max(ACT[bankaccess - 1].time + tRRD_init,
+ PRE[cmd.bank].time + static_cast<int>(memTimingSpec.RP)),
+ ACT[bankaccess - 4].time +
+ static_cast<int>(memTimingSpec.FAW));
+
+ if (memSpec.memoryType == MemoryType::WIDEIO_SDR) {
+ cmd.time = max(max(ACT[bankaccess - 1].time + tRRD_init,
+ PRE[cmd.bank].time + static_cast<int>(memTimingSpec.RP)),
+ ACT[bankaccess - 2].time +
+ static_cast<int>(memTimingSpec.TAW));
+ }
+
+ if ((i == 0) && (j == 0)) {
+ cmd.time = max(cmd.time, PreRDWR.time + 1);
+ cmd.time = max(cmd.time, timer);
+ cmd.time = max(startTime, cmd.time);
+ }
+
+ //////////collision detection////////////////////
+ for (int n = 1; n <= i * BGI + j; n++) {
+ collisionFound = false;
+ for (unsigned m = 0; m < RDWR[bankaccess - n].size(); m++) {
+ if (RDWR[bankaccess - n][m].time == cmd.time) {
+ cmd.time += 1; // ACT is shifted
+ collisionFound = true;
+ break;
+ }
+ }
+ if (collisionFound) {
+ break;
+ }
+ }
+
+ ACT.push_back(cmd);
+ cmdScheduling.push_back(cmd);
+
+ ACTSchedule[bankAddr] = true;
+ bankAccessNum[bankAddr] = bankaccess;
+ bankaccess++;
+ }
+
+ /////RDWR Scheduling//////
+ cmd.bank = bankAddr;
+ cmd.PhysicalAddr.bankAddr = cmd.bank;
+ cmd.PhysicalAddr.rowAddr = PhysicalAddress.rowAddr;
+ cmd.PhysicalAddr.colAddr = PhysicalAddress.colAddr + k * burstLength;
+ cmd.Type = transType;
+ switch (transType) {
+ case READ:
+ cmd.name = "RD";
+ break;
+
+ case WRITE:
+ cmd.name = "WR";
+ break;
+ }
+ for (int ACTBank = static_cast<int>(ACT.size() - 1);
+ ACTBank >= 0; ACTBank--) {
+ if (ACT[ACTBank].bank == bankAddr) {
+ cmd.time = max(PreRDWR.time + tSwitch_init, ACT.back().time
+ + static_cast<int>(memTimingSpec.RCD));
+ break;
+ }
+ }
+
+ if ((i == BI - 1) && (k == BC - 1) && (j == BGI - 1)) {
+ transFinish.time = cmd.time + 1;
+ transFinish.bank = bankAddr;
+ }
+ if (k == BC - 1) {
+ switch (transType) {
+ case READ:
+ cmd.name = "RDA";
+ break;
+
+ case WRITE:
+ cmd.name = "WRA";
+ break;
+ }
+ }
+ PreRDWR = cmd;
+
+ RDWR[bankAccessNum[bankAddr]].push_back(cmd);
+ cmdScheduling.push_back(cmd);
+
+ ////////////////PRE Scheduling////////////////////
+ if (k == BC - 1) {
+ PRE[bankAddr].bank = bankAddr;
+ PRE[bankAddr].Type = PRECHARGE;
+ PRE[bankAddr].name = "PRE";
+ for (int ACTBank = static_cast<int>(ACT.size() - 1);
+ ACTBank >= 0; ACTBank--) {
+ if (ACT[ACTBank].bank == bankAddr) {
+ PRE[bankAddr].time = max(ACT.back().time +
+ static_cast<int>(memTimingSpec.RAS),
+ PreRDWR.time + tRWTP);
+ break;
+ }
+ }
+ bankPointer[bankGroupAddr] = bankPointer[bankGroupAddr] + 1;
+ }
+
+ bankGroupPointer++;
+ }
+ }
+ }
+
+ // make sure the scheduled commands are stored with an ascending scheduling time
+ sort(cmdScheduling.begin(), cmdScheduling.end(),
+ commandItem::commandItemSorter());
+
+ // write the scheduled commands into commands.txt.
+ for (unsigned i = 0; i < cmdScheduling.size(); i++) {
+ cmdList.push_back(cmdScheduling[i]);
+ }
+
+ /////////////Update Vector Length/////////////////
+ // the vector length is reduced so that less memory is used for running
+ // this tool.
+ if (ACT.size() >= memSpec.memArchSpec.nbrOfBanks) {
+ for (int m = 0; m < BI * BGI; m++) {
+ ACT.erase(ACT.begin());
+ RDWR[0].erase(RDWR[0].begin(), RDWR[0].end());
+ for (int h = 0; h < bankaccess - 1 - m; h++) {
+ RDWR[h].insert(RDWR[h].begin(), RDWR[h + 1].begin(), RDWR[h + 1].end());
+ RDWR[h + 1].resize(0);
+ }
+ }
+ bankaccess = bankaccess - (BI * BGI);
+ }
+ }
+
+ for (unsigned j = 0; j < cmdList.size(); j++) {
+ commands.precision(0);
+ commands << fixed << cmdList[j].time << "," << cmdList[j].name << "," <<
+ cmdList[j].bank << endl;
+ }
+ cmdList.erase(cmdList.begin(), cmdList.end());
+} // cmdScheduler::analyticalScheduling
+
+// to add the power down/up during the command scheduling for transactions.
+// It is called when the command scheduling for a transaction is finished, and it
+// is also called if there is a refresh.
+void cmdScheduler::pdScheduling(double endTime, double timer,
+ MemorySpecification memSpec)
+{
+ double ZERO = 0;
+ MemTimingSpec& memTimingSpec = memSpec.memTimingSpec;
+
+ endTime = max(endTime, startTime);
+ double pdTime = max(ZERO, timer - endTime);
+
+ if ((timer > (endTime + memTimingSpec.CKE)) && (power_down == POWER_DOWN)) {
+ cmd.bank = 0;
+ cmd.name = "PDN_S_PRE";
+ cmd.time = endTime;
+ cmdScheduling.push_back(cmd);
+ cmd.name = "PUP_PRE";
+
+ if (pdTime > memTimingSpec.REFI)
+ cmd.time = cmd.time + memTimingSpec.REFI;
+ else
+ cmd.time = cmd.time + pdTime;
+
+ if (memSpec.memoryType.isLPDDRFamily())
+ startTime = cmd.time + memTimingSpec.XP;
+ else
+ startTime = cmd.time + memTimingSpec.XPDLL - memTimingSpec.RCD;
+
+ cmdScheduling.push_back(cmd);
+ } else if ((timer > (endTime + memTimingSpec.CKESR)) && (power_down == SELF_REFRESH)) {
+ cmd.bank = 0;
+ cmd.name = "SREN";
+ cmd.time = endTime;
+ cmdScheduling.push_back(cmd);
+ Inselfrefresh = 1;
+ cmd.name = "SREX";
+ cmd.time = cmd.time + pdTime;
+
+ if (memSpec.memoryType.isLPDDRFamily())
+ startTime = cmd.time + memTimingSpec.XS;
+ else
+ startTime = cmd.time + memTimingSpec.XSDLL - memTimingSpec.RCD;
+
+ cmdScheduling.push_back(cmd);
+ }
+} // cmdScheduler::pdScheduling
+
+// get the time when a precharge occurs after a read/write command is scheduled.
+// In addition, it copes with different kind of memories.
+int cmdScheduler::getRWTP(int transType, MemorySpecification memSpec)
+{
+ int tRWTP_init = 0;
+ MemTimingSpec& memTimingSpec = memSpec.memTimingSpec;
+ MemArchitectureSpec& memArchSpec = memSpec.memArchSpec;
+
+ if (transType == READ) {
+ switch (memSpec.memoryType) {
+ case MemoryType::LPDDR:
+ case MemoryType::WIDEIO_SDR:
+ tRWTP_init = memArchSpec.burstLength / memArchSpec.dataRate;
+ break;
+
+ case MemoryType::LPDDR2:
+ case MemoryType::LPDDR3:
+ tRWTP_init = memArchSpec.burstLength / memArchSpec.dataRate +
+ max(0, static_cast<int>(memTimingSpec.RTP - 2));
+ break;
+
+ case MemoryType::DDR2:
+ tRWTP_init = memTimingSpec.AL + memArchSpec.burstLength /
+ memArchSpec.dataRate +
+ max(static_cast<int>(memTimingSpec.RTP), 2) - 2;
+ break;
+
+ case MemoryType::DDR3:
+ case MemoryType::DDR4:
+ tRWTP_init = memTimingSpec.RTP;
+ break;
+ default:
+ assert("Unknown memory type" && false);
+ } // switch
+ } else if (transType == WRITE) {
+ if (memSpec.memoryType == MemoryType::WIDEIO_SDR) {
+ tRWTP_init = memTimingSpec.WL + memArchSpec.burstLength /
+ memArchSpec.dataRate - 1 + memSpec.memTimingSpec.WR;
+ } else {
+ tRWTP_init = memTimingSpec.WL + memArchSpec.burstLength /
+ memArchSpec.dataRate + memSpec.memTimingSpec.WR;
+ }
+ if ((memSpec.memoryType == MemoryType::LPDDR2) ||
+ (memSpec.memoryType == MemoryType::LPDDR3)) {
+ tRWTP_init = tRWTP_init + 1;
+ }
+ }
+
+ return tRWTP_init;
+} // cmdScheduler::getRWTP
+
+// get the timings for command scheduling according to different memories.
+// In particular, tSwitch_init is generally used to provide the timings for
+// scheduling a read/write command after a read/write command which have been
+// scheduled to any possible banks within any possible bank groups (DDR4).
+void cmdScheduler::getTimingConstraints(bool BGSwitch, MemorySpecification memSpec,
+ int PreType, int CurrentType)
+{
+ MemTimingSpec& memTimingSpec = memSpec.memTimingSpec;
+ MemArchitectureSpec& memArchSpec = memSpec.memArchSpec;
+
+ if (memSpec.memoryType != MemoryType::DDR4) {
+ tRRD_init = memTimingSpec.RRD;
+ if (PreType == CurrentType) {
+ tSwitch_init = memTimingSpec.CCD;
+ timingsGet = true;
+ }
+
+ if ((PreType == WRITE) && (CurrentType == READ)) {
+ if (memSpec.memoryType == MemoryType::WIDEIO_SDR) {
+ tSwitch_init = memTimingSpec.WL + memArchSpec.burstLength /
+ memArchSpec.dataRate - 1 + memTimingSpec.WTR;
+ } else {
+ tSwitch_init = memTimingSpec.WL + memArchSpec.burstLength /
+ memArchSpec.dataRate + memTimingSpec.WTR;
+ }
+
+ if ((memSpec.memoryType == MemoryType::LPDDR2) ||
+ (memSpec.memoryType == MemoryType::LPDDR3)) {
+ tSwitch_init = tSwitch_init + 1;
+ }
+ }
+ }
+
+ if (memSpec.memoryType == MemoryType::DDR4) {
+ if (BGSwitch) {
+ tCCD_init = memTimingSpec.CCD_S;
+ tRRD_init = memTimingSpec.RRD_S;
+ tWTR_init = memTimingSpec.WTR_S;
+ } else {
+ tCCD_init = memTimingSpec.CCD_L;
+ tRRD_init = memTimingSpec.RRD_L;
+ tWTR_init = memTimingSpec.WTR_L;
+ }
+
+ if (PreType == CurrentType) {
+ tSwitch_init = tCCD_init;
+ timingsGet = true;
+ } else if ((PreType == WRITE) && (CurrentType == READ)) {
+ tSwitch_init = memTimingSpec.WL + memArchSpec.burstLength /
+ memArchSpec.dataRate + tWTR_init;
+ }
+ }
+
+ if ((PreType == READ) && (CurrentType == WRITE)) {
+ tSwitch_init = memTimingSpec.RL + memArchSpec.burstLength /
+ memArchSpec.dataRate + 2 - memTimingSpec.WL;
+ }
+} // cmdScheduler::getTimingConstraints
+
+// The logical address of each transaction is translated into a physical address
+// which consists of bank group (for DDR4), bank, row and column addresses.
+cmdScheduler::physicalAddr cmdScheduler::memoryMap(trans Trans,
+ MemorySpecification memSpec)
+{
+ int DecLogic;
+ physicalAddr PhysicalAddr;
+
+ DecLogic = Trans.logicalAddress;
+
+ // row-bank-column-BI-BC-BGI-BL
+ if ((BGI > 1) && (memSpec.memoryType == MemoryType::DDR4)) {
+ unsigned colBits = static_cast<unsigned>(log2(nColumns));
+ unsigned bankShift = static_cast<unsigned>(colBits + ((BI > 1) ? log2(BI) : 0)
+ + ((BGI > 1) ? log2(BGI) : 0));
+ unsigned bankMask = static_cast<unsigned>(nBanks / (BI * nbrOfBankGroups) - 1)
+ << bankShift;
+ unsigned bankAddr = (DecLogic & bankMask) >>
+ static_cast<unsigned>(colBits + ((BGI > 1) ? log2(BGI) : 0));
+ PhysicalAddr.bankAddr = bankAddr;
+
+ unsigned bankGroupShift = static_cast<unsigned>(log2(burstLength));
+ unsigned bankGroupMask = (nbrOfBankGroups / BGI - 1) << bankGroupShift;
+ unsigned bankGroupAddr = (DecLogic & bankGroupMask) >> bankGroupShift;
+ PhysicalAddr.bankGroupAddr = bankGroupAddr;
+
+ unsigned colShift = static_cast<unsigned>(log2(BC * burstLength) +
+ ((BI > 1) ? log2(BI) : 0) + ((BGI > 1) ? log2(BGI) : 0));
+ unsigned colMask = static_cast<unsigned>(nColumns / (BC * burstLength) - 1)
+ << colShift;
+ unsigned colAddr = (DecLogic & colMask) >>
+ static_cast<unsigned>((colShift - log2(static_cast<unsigned>(BC) * burstLength)));
+ PhysicalAddr.colAddr = colAddr;
+ } else {
+ unsigned colBits = static_cast<unsigned>(log2(nColumns));
+ unsigned bankShift = static_cast<unsigned>(colBits + ((BI > 1) ? log2(BI) : 0));
+ unsigned bankMask = static_cast<unsigned>(nBanks / BI - 1) << bankShift;
+ unsigned bankAddr = (DecLogic & bankMask) >> colBits;
+ PhysicalAddr.bankAddr = bankAddr;
+
+ unsigned colShift = static_cast<unsigned>(log2(BC * burstLength) +
+ ((BI > 1) ? log2(BI) : 0));
+ unsigned colMask = static_cast<unsigned>(nColumns / (BC * burstLength) - 1)
+ << colShift;
+ unsigned colAddr = (DecLogic & colMask) >>
+ static_cast<unsigned>((colShift - log2(static_cast<unsigned>(BC) * burstLength)));
+ PhysicalAddr.colAddr = colAddr;
+
+ PhysicalAddr.bankGroupAddr = 0;
+ }
+
+ unsigned rowShift = static_cast<unsigned>(log2(nColumns * nBanks));
+ unsigned rowMask = static_cast<unsigned>(memSpec.memArchSpec.nbrOfRows - 1)
+ << rowShift;
+ unsigned rowAddr = (DecLogic & rowMask) >> rowShift;
+ PhysicalAddr.rowAddr = rowAddr;
+
+ return PhysicalAddr;
+} // cmdScheduler::memoryMap