summaryrefslogtreecommitdiff
path: root/ext/drampower/src/CmdScheduler.cc
diff options
context:
space:
mode:
authorAndreas Hansson <andreas.hansson@arm.com>2014-10-09 17:52:03 -0400
committerAndreas Hansson <andreas.hansson@arm.com>2014-10-09 17:52:03 -0400
commite0e8b08a42210011205b52c3628749f60658e58c (patch)
treee6daae3c8cd249f52c6504ee2b7fc07805085429 /ext/drampower/src/CmdScheduler.cc
parentc81517c293cdd3f612efae94d1143fb0cf002287 (diff)
downloadgem5-e0e8b08a42210011205b52c3628749f60658e58c.tar.xz
ext: Add DRAMPower to enable on-line DRAM power modelling
This patch adds the open-source (BSD 3-clause) tool DRAMPower, commit 8d3cf4bbb10aa202d850ef5e5e3e4f53aa668fa6, to be built as a part of the simulator. We have chosen this specific version of DRAMPower as it provides the necessary functionality, and future updates will be coordinated with the DRAMPower development team. The files added only include the bits needed to build the library, thus excluding all memory specifications, traces, and the stand-alone DRAMPower command-line tool. A future patch includes the DRAMPower functionality in the DRAM controller, to enable on-line DRAM power modelling, and avoid using post-processing of traces.
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