86e9a6ffec
Sync DRAMPower to external tool This patch syncs the DRAMPower library of gem5 to the external one on github (https://github.com/ravenrd/DRAMPower) of which I am a maintainer. The version used is the commit: 902a00a1797c48a9df97ec88868f20e847680ae6 from 07. May. 2016. Committed by Jason Lowe-Power <jason@lowepower.com>
659 lines
24 KiB
C++
659 lines
24 KiB
C++
/*
|
|
* 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, Yonghui Li, Sven Goossens
|
|
*
|
|
*/
|
|
#include "CmdScheduler.h"
|
|
|
|
#include <cassert>
|
|
#include <cmath> // For log2
|
|
|
|
#include <algorithm> // For max
|
|
|
|
|
|
#define MILLION 1000000
|
|
|
|
|
|
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(const MemorySpecification& memSpec,
|
|
ifstream& trans_trace, int grouping, int interleaving, int burst, int powerdown)
|
|
{
|
|
commands.open("commands.trace", ifstream::out);
|
|
const 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(const MemorySpecification& memSpec)
|
|
{
|
|
const MemTimingSpec& memTimingSpec = memSpec.memTimingSpec;
|
|
|
|
const size_t numBanks = static_cast<size_t>(memSpec.memArchSpec.nbrOfBanks);
|
|
ACT.resize(2 * numBanks);
|
|
RDWR.resize(2 * numBanks);
|
|
PRE.resize(numBanks);
|
|
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 (int64_t i = 0; i < memSpec.memArchSpec.nbrOfBanks; i++) {
|
|
cmd.Type = PRECHARGE;
|
|
cmd.bank = static_cast<unsigned>(i);
|
|
cmd.name = "PRE";
|
|
if (memSpec.id == "WIDEIO_SDR") {
|
|
cmd.time = 1 - memSpec.memTimingSpec.TAW;
|
|
} else {
|
|
cmd.time = 1 - 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[static_cast<size_t>(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, const MemorySpecification& memSpec)
|
|
{
|
|
std::string line;
|
|
|
|
transTime = 0;
|
|
uint64_t newtranstime;
|
|
uint64_t transAddr;
|
|
int64_t transType = 1;
|
|
trans TransItem;
|
|
|
|
if (!transTrace.empty()) {
|
|
transTrace.erase(transTrace.begin(), transTrace.end());
|
|
}
|
|
|
|
while (getline(trans_trace, line)) {
|
|
istringstream linestream(line);
|
|
string item;
|
|
uint64_t itemnum = 0;
|
|
while (getline(linestream, item, ',')) {
|
|
if (itemnum == 0) {
|
|
stringstream timestamp(item);
|
|
timestamp >> newtranstime;
|
|
transTime = transTime + static_cast<int64_t>(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(const MemorySpecification& memSpec)
|
|
{
|
|
int64_t transType = -1;
|
|
int64_t timer = 0;
|
|
uint64_t bankGroupPointer = 0;
|
|
uint64_t bankGroupAddr = 0;
|
|
bool collisionFound;
|
|
physicalAddr PhysicalAddress;
|
|
bool bankGroupSwitch = false;
|
|
std::vector<uint64_t> bankPointer(static_cast<size_t>(nbrOfBankGroups), 0);
|
|
std::vector<int64_t> bankAccessNum(static_cast<size_t>(nBanks), -1);
|
|
std::vector<bool> ACTSchedule(static_cast<size_t>(nBanks), false);
|
|
uint64_t bankAddr = 0;
|
|
int64_t endTime = 0;
|
|
int64_t tComing_REF = 0;
|
|
|
|
Inselfrefresh = 0;
|
|
|
|
const MemTimingSpec& memTimingSpec = memSpec.memTimingSpec;
|
|
|
|
for (uint64_t t = 0; t < transTrace.size(); t++) {
|
|
cmdScheduling.erase(cmdScheduling.begin(), cmdScheduling.end());
|
|
|
|
for (auto a : ACTSchedule) {
|
|
a = false;
|
|
}
|
|
|
|
for (auto& b : bankAccessNum) {
|
|
b = -1;
|
|
}
|
|
|
|
timingsGet = false;
|
|
timer = transTrace[t].timeStamp;
|
|
|
|
PhysicalAddress = memoryMap(transTrace[t], memSpec);
|
|
|
|
for (auto& b : bankPointer) {
|
|
b = PhysicalAddress.bankAddr; // the bank pointer per group.
|
|
}
|
|
bankGroupPointer = PhysicalAddress.bankGroupAddr;
|
|
|
|
endTime = max(transFinish.time, PRE[static_cast<size_t>(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 (int64_t 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[static_cast<size_t>(transFinish.bank)].time + 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///////////////////
|
|
uint64_t 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 + static_cast<uint64_t>(j);
|
|
bankAddr = bankGroupAddr * static_cast<uint64_t>(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 == static_cast<int64_t>(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 == static_cast<int64_t>(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() >= static_cast<size_t>(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(int64_t endTime, int64_t timer,
|
|
const MemorySpecification& memSpec)
|
|
{
|
|
int64_t ZERO = 0;
|
|
const MemTimingSpec& memTimingSpec = memSpec.memTimingSpec;
|
|
|
|
endTime = max(endTime, startTime);
|
|
int64_t 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.
|
|
int64_t cmdScheduler::getRWTP(int64_t transType, const MemorySpecification& memSpec)
|
|
{
|
|
int64_t tRWTP_init = 0;
|
|
const MemTimingSpec& memTimingSpec = memSpec.memTimingSpec;
|
|
const 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(int64_t(0), memTimingSpec.RTP - 2);
|
|
break;
|
|
|
|
case MemoryType::DDR2:
|
|
tRWTP_init = memTimingSpec.AL + memArchSpec.burstLength /
|
|
memArchSpec.dataRate +
|
|
max(memTimingSpec.RTP, int64_t(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 + memTimingSpec.WR;
|
|
} else {
|
|
tRWTP_init = memTimingSpec.WL + memArchSpec.burstLength /
|
|
memArchSpec.dataRate + 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, const MemorySpecification& memSpec,
|
|
int64_t PreType, int64_t CurrentType)
|
|
{
|
|
const MemTimingSpec& memTimingSpec = memSpec.memTimingSpec;
|
|
const 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,
|
|
const MemorySpecification& memSpec)
|
|
{
|
|
int64_t DecLogic;
|
|
physicalAddr PhysicalAddr;
|
|
|
|
DecLogic = Trans.logicalAddress;
|
|
|
|
// row-bank-column-BI-BC-BGI-BL
|
|
if (BGI > 1 && memSpec.memoryType == MemoryType::DDR4) {
|
|
uint64_t colBits = uintLog2(nColumns);
|
|
uint64_t bankShift = colBits + ((BI > 1) ? uintLog2(BI) : 0) + ((BGI > 1) ? uintLog2(BGI) : 0);
|
|
uint64_t bankMask = (nBanks / (BI * nbrOfBankGroups) - 1) << bankShift;
|
|
uint64_t bankAddr = (DecLogic & bankMask) >> (colBits + ((BGI > 1) ? uintLog2(BGI) : 0));
|
|
PhysicalAddr.bankAddr = bankAddr;
|
|
|
|
uint64_t bankGroupShift = uintLog2(burstLength);
|
|
uint64_t bankGroupMask = (nbrOfBankGroups / BGI - 1) << bankGroupShift;
|
|
uint64_t bankGroupAddr = (DecLogic & bankGroupMask) >> bankGroupShift;
|
|
PhysicalAddr.bankGroupAddr = bankGroupAddr;
|
|
|
|
uint64_t colShift = uintLog2(BC * burstLength) +
|
|
((BI > 1) ? uintLog2(BI) : 0) + ((BGI > 1) ? uintLog2(BGI) : 0);
|
|
uint64_t colMask = (nColumns / (BC * burstLength) - 1) << colShift;
|
|
uint64_t colAddr = (DecLogic & colMask) >> (colShift - uintLog2(static_cast<uint64_t>(BC) * burstLength));
|
|
PhysicalAddr.colAddr = colAddr;
|
|
} else {
|
|
uint64_t colBits = uintLog2(nColumns);
|
|
uint64_t bankShift = colBits + ((BI > 1) ? uintLog2(BI) : 0);
|
|
uint64_t bankMask = (nBanks / BI - 1) << bankShift;
|
|
uint64_t bankAddr = (DecLogic & bankMask) >> colBits;
|
|
PhysicalAddr.bankAddr = bankAddr;
|
|
|
|
uint64_t colShift = (uintLog2(BC * burstLength) + ((BI > 1) ? uintLog2(BI) : 0));
|
|
uint64_t colMask = (nColumns / (BC * burstLength) - 1) << colShift;
|
|
uint64_t colAddr = (DecLogic & colMask) >> (colShift - uintLog2(BC * burstLength));
|
|
PhysicalAddr.colAddr = colAddr;
|
|
|
|
PhysicalAddr.bankGroupAddr = 0;
|
|
}
|
|
|
|
uint64_t rowShift = uintLog2(nColumns * nBanks);
|
|
uint64_t rowMask = (memSpec.memArchSpec.nbrOfRows - 1) << rowShift;
|
|
uint64_t rowAddr = (DecLogic & rowMask) >> rowShift;
|
|
PhysicalAddr.rowAddr = rowAddr;
|
|
|
|
return PhysicalAddr;
|
|
} // cmdScheduler::memoryMap
|
|
|
|
uint64_t cmdScheduler::uintLog2(uint64_t in)
|
|
{
|
|
return static_cast<uint64_t>(log2(in));
|
|
} |