1/*
2 * Copyright (c) 2012-2014, TU Delft
3 * Copyright (c) 2012-2014, TU Eindhoven
4 * Copyright (c) 2012-2014, TU Kaiserslautern
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * 3. Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived from
20 * this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
23 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 *
34 * Authors: Karthik Chandrasekar, Yonghui Li, Sven Goossens
35 *
36 */
37#include "CmdScheduler.h"
38
39#include <cassert>
40#include <cmath>  // For log2
41
42#include <algorithm>  // For max
43
44
45#define MILLION 1000000
46
47
48using namespace std;
49using namespace Data;
50
51// Read the traces and get the transaction. Each transaction is executed by
52// scheduling a number of commands to the memory. Hence, the transactions are
53// translated into a sequence of commands which will be used for power analysis.
54void cmdScheduler::transTranslation(const MemorySpecification& memSpec,
55                                    ifstream& trans_trace, int grouping, int interleaving, int burst, int powerdown)
56{
57  commands.open("commands.trace", ifstream::out);
58  const MemArchitectureSpec& memArchSpec = memSpec.memArchSpec;
59  nBanks          = memArchSpec.nbrOfBanks;
60  nColumns        = memArchSpec.nbrOfColumns;
61  burstLength     = memArchSpec.burstLength;
62  nbrOfBankGroups = memArchSpec.nbrOfBankGroups;
63
64  BGI             = grouping;
65  BI = interleaving;
66  BC = burst;
67  power_down      = powerdown;
68
69  schedulingInitialization(memSpec);
70  getTrans(trans_trace, memSpec);
71
72  trans_trace.close();
73  commands.close();
74  ACT.erase(ACT.begin(), ACT.end());
75  PRE.erase(PRE.begin(), PRE.end());
76  RDWR.erase(RDWR.begin(), RDWR.end());
77  cmdScheduling.erase(cmdScheduling.begin(), cmdScheduling.end());
78  cmdList.erase(cmdList.begin(), cmdList.end());
79  transTrace.erase(transTrace.begin(), transTrace.end());
80} // cmdScheduler::transTranslation
81
82// initialize the variables and vectors for starting command scheduling.
83void cmdScheduler::schedulingInitialization(const MemorySpecification& memSpec)
84{
85  const MemTimingSpec& memTimingSpec = memSpec.memTimingSpec;
86
87  const size_t numBanks = static_cast<size_t>(memSpec.memArchSpec.nbrOfBanks);
88  ACT.resize(2 * numBanks);
89  RDWR.resize(2 * numBanks);
90  PRE.resize(numBanks);
91  bankaccess = memSpec.memArchSpec.nbrOfBanks;
92  if (!ACT.empty()) {
93    ACT.erase(ACT.begin(), ACT.end());
94  }
95  if (!PRE.empty()) {
96    PRE.erase(PRE.begin(), PRE.end());
97  }
98  if (!RDWR.empty()) {
99    RDWR.erase(RDWR.begin(), RDWR.end());
100  }
101
102  ///////////////initialization//////////////
103  for (int64_t i = 0; i < memSpec.memArchSpec.nbrOfBanks; i++) {
104    cmd.Type = PRECHARGE;
105    cmd.bank = static_cast<unsigned>(i);
106    cmd.name = "PRE";
107    if (memSpec.id == "WIDEIO_SDR") {
108      cmd.time = 1 - memSpec.memTimingSpec.TAW;
109    } else {
110      cmd.time = 1 - memSpec.memTimingSpec.FAW;
111    }
112
113    PRE.push_back(cmd);
114
115    cmd.Type = ACTIVATE;
116    cmd.name = "ACT";
117    ACT.push_back(cmd);
118
119    cmd.Type = WRITE;
120    cmd.name = "WRITE";
121    cmd.time = -1;
122    RDWR[static_cast<size_t>(i)].push_back(cmd);
123  }
124  tREF             = memTimingSpec.REFI;
125  transFinish.time = 0;
126  transFinish.bank = 0;
127
128  PreRDWR.bank     = -1;
129  PreRDWR.Type     = READ;
130  PreRDWR.name     = "RD";
131  PreRDWR.time     = -1;
132  startTime        = 0;
133} // cmdScheduler::schedulingInitialization
134
135// transactions are generated according to the information read from the traces.
136// Then the command scheduling function is triggered to generate commands and
137// schedule them to the memory according to the timing constraints.
138void cmdScheduler::getTrans(std::ifstream& trans_trace, const MemorySpecification& memSpec)
139{
140  std::string line;
141
142  transTime = 0;
143  uint64_t newtranstime;
144  uint64_t transAddr;
145  int64_t transType = 1;
146  trans    TransItem;
147
148  if (!transTrace.empty()) {
149    transTrace.erase(transTrace.begin(), transTrace.end());
150  }
151
152  while (getline(trans_trace, line)) {
153    istringstream linestream(line);
154    string item;
155    uint64_t itemnum = 0;
156    while (getline(linestream, item, ',')) {
157      if (itemnum == 0) {
158        stringstream timestamp(item);
159        timestamp >> newtranstime;
160        transTime = transTime + static_cast<int64_t>(newtranstime);
161      } else if (itemnum == 1) {
162        if (item  == "write" || item == "WRITE") {
163          transType = WRITE;
164        } else   {
165          transType = READ;
166        }
167      } else if (itemnum == 2) {
168        stringstream timestamp(item);
169        timestamp >> std::hex >> transAddr;
170      }
171      itemnum++;
172    }
173    // generate a transaction
174    TransItem.timeStamp      = transTime;
175    TransItem.logicalAddress = transAddr;
176    TransItem.type           = transType;
177
178    transTrace.push_back(TransItem);
179
180    if (transTrace.size() == MILLION) {
181      // The scheduling is implemented for every MILLION transactions.
182      // It is used to reduce the used memory during the running of this tool.
183      analyticalScheduling(memSpec);
184      transTrace.erase(transTrace.begin(), transTrace.end());
185    }
186  }
187
188  if ((transTrace.size() < MILLION) && (!transTrace.empty())) {
189    analyticalScheduling(memSpec);
190    transTrace.erase(transTrace.begin(), transTrace.end());
191  }
192} // cmdScheduler::getTrans
193
194// Transactions are executed individually and the command scheduling is
195// independent between transactions. The commands for a new transaction cannot
196// be scheduled until all the commands for the current one are scheduled.
197// After the scheduling, a sequence of commands are obtained and they are written
198// into commands.txt which will be used for power analysis.
199void cmdScheduler::analyticalScheduling(const MemorySpecification& memSpec)
200{
201  int64_t  transType        = -1;
202  int64_t timer          = 0;
203  uint64_t  bankGroupPointer = 0;
204  uint64_t  bankGroupAddr    = 0;
205  bool collisionFound;
206  physicalAddr PhysicalAddress;
207  bool bankGroupSwitch  = false;
208  std::vector<uint64_t> bankPointer(static_cast<size_t>(nbrOfBankGroups), 0);
209  std::vector<int64_t>  bankAccessNum(static_cast<size_t>(nBanks), -1);
210  std::vector<bool> ACTSchedule(static_cast<size_t>(nBanks), false);
211  uint64_t bankAddr   = 0;
212  int64_t endTime     = 0;
213  int64_t tComing_REF = 0;
214
215  Inselfrefresh = 0;
216
217  const MemTimingSpec& memTimingSpec = memSpec.memTimingSpec;
218
219  for (uint64_t t = 0; t < transTrace.size(); t++) {
220    cmdScheduling.erase(cmdScheduling.begin(), cmdScheduling.end());
221
222    for (auto a : ACTSchedule) {
223      a = false;
224    }
225
226    for (auto& b : bankAccessNum) {
227      b = -1;
228    }
229
230    timingsGet      = false;
231    timer           = transTrace[t].timeStamp;
232
233    PhysicalAddress = memoryMap(transTrace[t], memSpec);
234
235    for (auto& b : bankPointer) {
236      b = PhysicalAddress.bankAddr; // the bank pointer per group.
237    }
238    bankGroupPointer = PhysicalAddress.bankGroupAddr;
239
240    endTime = max(transFinish.time, PRE[static_cast<size_t>(transFinish.bank)].time +
241                                    static_cast<int>(memTimingSpec.RP));
242
243    // Before starting the scheduling for the next transaction, it has to
244    // check whether it is necessary for implementing power down.
245    if (power_down == SELF_REFRESH)
246      pdScheduling(endTime, timer, memSpec);
247    else if (power_down == POWER_DOWN)
248      pdScheduling(endTime, min(timer, tREF), memSpec);
249
250    tComing_REF = tREF;
251
252    ///////////////Scheduling Refresh////////////////////////
253    if (((transFinish.time >= tREF) || (timer >= tREF))) {
254      for (int64_t i = 0; i <= ((timer - tComing_REF) > 0 ? (timer - tComing_REF) /
255                               memTimingSpec.REFI : 0); i++) {
256        cmd.bank = 0;
257        cmd.name = "REF";
258        cmd.time = max(max(max(transFinish.time, PRE[static_cast<size_t>(transFinish.bank)].time + memTimingSpec.RP), tREF), startTime);
259        if ((power_down == SELF_REFRESH && !Inselfrefresh) || power_down != SELF_REFRESH) {
260          cmdScheduling.push_back(cmd);
261          startTime = cmd.time + memTimingSpec.RFC;
262        }
263        tREF = tREF + memTimingSpec.REFI;
264        // during the refreshing, power down should be taken into account.
265        if (!Inselfrefresh)
266          pdScheduling(endTime, min(timer, tREF), memSpec);
267      }
268    }
269    ///////////////Execution Transactions///////////////////
270    uint64_t Bs = PhysicalAddress.bankAddr;
271    transType = transTrace[t].type;
272
273    tRWTP     = getRWTP(transType, memSpec);
274
275    for (int i = 0; i < BI; i++) {
276      for (int k = 0; k < BC; k++) {
277        if (memSpec.memoryType == MemoryType::DDR4) {
278          bankGroupPointer = PhysicalAddress.bankGroupAddr;
279        }
280
281        for (int j = 0; j < BGI; j++) {
282          bankGroupSwitch = false;
283          if (memSpec.memoryType == MemoryType::DDR4) {
284            if (bankGroupPointer != bankGroupAddr) {
285              bankGroupSwitch = true;
286            }
287            // update to the current bank group address.
288            bankGroupAddr = PhysicalAddress.bankGroupAddr + static_cast<uint64_t>(j);
289            bankAddr = bankGroupAddr * static_cast<uint64_t>(nBanks) / nbrOfBankGroups + bankPointer[bankGroupAddr];
290          } else   {
291            bankAddr = Bs + i;
292          }
293
294          if (!timingsGet) {
295            getTimingConstraints(bankGroupSwitch, memSpec,
296                                 PreRDWR.Type, transType);
297          }
298
299          ////////////////ACT Scheduling///////////////////
300          if (!ACTSchedule[bankAddr]) {
301            cmd.bank                  = bankAddr;
302            cmd.PhysicalAddr.bankAddr = cmd.bank;
303            cmd.PhysicalAddr.rowAddr  = PhysicalAddress.rowAddr;
304            cmd.Type                  = ACTIVATE;
305            cmd.name                  = "ACT";
306            Inselfrefresh             = 0;
307            cmd.time                  = max(max(ACT[bankaccess - 1].time + tRRD_init,
308                                                PRE[cmd.bank].time + static_cast<int>(memTimingSpec.RP)),
309                                            ACT[bankaccess - 4].time +
310                                            static_cast<int>(memTimingSpec.FAW));
311
312            if (memSpec.memoryType == MemoryType::WIDEIO_SDR) {
313              cmd.time = max(max(ACT[bankaccess - 1].time + tRRD_init,
314                                 PRE[cmd.bank].time + static_cast<int>(memTimingSpec.RP)),
315                             ACT[bankaccess - 2].time +
316                             static_cast<int>(memTimingSpec.TAW));
317            }
318
319            if (i == 0 && j == 0) {
320              cmd.time = max(cmd.time, PreRDWR.time + 1);
321              cmd.time = max(cmd.time, timer);
322              cmd.time = max(startTime, cmd.time);
323            }
324
325            //////////collision detection////////////////////
326            for (int n = 1; n <= i * BGI + j; n++) {
327              collisionFound = false;
328              for (unsigned m = 0; m < RDWR[bankaccess - n].size(); m++) {
329                if (RDWR[bankaccess - n][m].time == cmd.time) {
330                  cmd.time      += 1; // ACT is shifted
331                  collisionFound = true;
332                  break;
333                }
334              }
335              if (collisionFound) {
336                break;
337              }
338            }
339
340            ACT.push_back(cmd);
341            cmdScheduling.push_back(cmd);
342
343            ACTSchedule[bankAddr]   = true;
344            bankAccessNum[bankAddr] = bankaccess;
345            bankaccess++;
346          }
347
348          /////RDWR Scheduling//////
349          cmd.bank = bankAddr;
350          cmd.PhysicalAddr.bankAddr = cmd.bank;
351          cmd.PhysicalAddr.rowAddr  = PhysicalAddress.rowAddr;
352          cmd.PhysicalAddr.colAddr  = PhysicalAddress.colAddr + k * burstLength;
353          cmd.Type = transType;
354          switch (transType) {
355          case READ:
356            cmd.name = "RD";
357            break;
358
359          case WRITE:
360            cmd.name = "WR";
361            break;
362          }
363          for (int ACTBank = static_cast<int>(ACT.size() - 1);
364               ACTBank >= 0; ACTBank--) {
365            if (ACT[ACTBank].bank == static_cast<int64_t>(bankAddr)) {
366              cmd.time = max(PreRDWR.time + tSwitch_init, ACT.back().time
367                             + static_cast<int>(memTimingSpec.RCD));
368              break;
369            }
370          }
371
372          if ((i == BI - 1) && (k == BC - 1) && (j == BGI - 1)) {
373            transFinish.time = cmd.time + 1;
374            transFinish.bank = bankAddr;
375          }
376          if (k == BC - 1) {
377            switch (transType) {
378            case READ:
379              cmd.name = "RDA";
380              break;
381
382            case WRITE:
383              cmd.name = "WRA";
384              break;
385            }
386          }
387          PreRDWR = cmd;
388
389          RDWR[bankAccessNum[bankAddr]].push_back(cmd);
390          cmdScheduling.push_back(cmd);
391
392          ////////////////PRE Scheduling////////////////////
393          if (k == BC - 1) {
394            PRE[bankAddr].bank = bankAddr;
395            PRE[bankAddr].Type = PRECHARGE;
396            PRE[bankAddr].name = "PRE";
397            for (int ACTBank = static_cast<int>(ACT.size() - 1);
398                 ACTBank >= 0; ACTBank--) {
399              if (ACT[ACTBank].bank == static_cast<int64_t>(bankAddr)) {
400                PRE[bankAddr].time = max(ACT.back().time +
401                                         static_cast<int>(memTimingSpec.RAS),
402                                         PreRDWR.time + tRWTP);
403                break;
404              }
405            }
406            bankPointer[bankGroupAddr] = bankPointer[bankGroupAddr] + 1;
407          }
408
409          bankGroupPointer++;
410        }
411      }
412    }
413
414    // make sure the scheduled commands are stored with an ascending scheduling time
415    sort(cmdScheduling.begin(), cmdScheduling.end(),
416         commandItem::commandItemSorter());
417
418    // write the scheduled commands into commands.txt.
419    for (unsigned i = 0; i < cmdScheduling.size(); i++) {
420      cmdList.push_back(cmdScheduling[i]);
421    }
422
423    /////////////Update Vector Length/////////////////
424    // the vector length is reduced so that less memory is used for running
425    // this tool.
426    if (ACT.size() >= static_cast<size_t>(memSpec.memArchSpec.nbrOfBanks)) {
427      for (int m = 0; m < BI * BGI; m++) {
428        ACT.erase(ACT.begin());
429        RDWR[0].erase(RDWR[0].begin(), RDWR[0].end());
430        for (int h = 0; h < bankaccess - 1 - m; h++) {
431          RDWR[h].insert(RDWR[h].begin(), RDWR[h + 1].begin(), RDWR[h + 1].end());
432          RDWR[h + 1].resize(0);
433        }
434      }
435      bankaccess = bankaccess - (BI * BGI);
436    }
437  }
438
439  for (unsigned j = 0; j < cmdList.size(); j++) {
440    commands.precision(0);
441    commands << fixed << cmdList[j].time << "," << cmdList[j].name << "," <<
442      cmdList[j].bank << endl;
443  }
444  cmdList.erase(cmdList.begin(), cmdList.end());
445} // cmdScheduler::analyticalScheduling
446
447// to add the power down/up during the command scheduling for transactions.
448// It is called when the command scheduling for a transaction is finished, and it
449// is also called if there is a refresh.
450void cmdScheduler::pdScheduling(int64_t endTime, int64_t timer,
451                                const MemorySpecification& memSpec)
452{
453  int64_t ZERO = 0;
454  const MemTimingSpec& memTimingSpec = memSpec.memTimingSpec;
455
456  endTime = max(endTime, startTime);
457  int64_t pdTime = max(ZERO, timer - endTime);
458
459  if ((timer > (endTime + memTimingSpec.CKE)) && (power_down == POWER_DOWN)) {
460    cmd.bank = 0;
461    cmd.name = "PDN_S_PRE";
462    cmd.time = endTime;
463    cmdScheduling.push_back(cmd);
464    cmd.name = "PUP_PRE";
465
466    if (pdTime > memTimingSpec.REFI)
467      cmd.time = cmd.time + memTimingSpec.REFI;
468    else
469      cmd.time = cmd.time + pdTime;
470
471    if (memSpec.memoryType.isLPDDRFamily())
472      startTime = cmd.time + memTimingSpec.XP;
473    else
474      startTime = cmd.time + memTimingSpec.XPDLL - memTimingSpec.RCD;
475
476    cmdScheduling.push_back(cmd);
477  } else if ((timer > (endTime + memTimingSpec.CKESR)) && (power_down == SELF_REFRESH))    {
478    cmd.bank      = 0;
479    cmd.name      = "SREN";
480    cmd.time      = endTime;
481    cmdScheduling.push_back(cmd);
482    Inselfrefresh = 1;
483    cmd.name      = "SREX";
484    cmd.time      = cmd.time + pdTime;
485
486    if (memSpec.memoryType.isLPDDRFamily())
487      startTime = cmd.time + memTimingSpec.XS;
488    else
489      startTime = cmd.time + memTimingSpec.XSDLL - memTimingSpec.RCD;
490
491    cmdScheduling.push_back(cmd);
492  }
493} // cmdScheduler::pdScheduling
494
495// get the time when a precharge occurs after a read/write command is scheduled.
496// In addition, it copes with different kind of memories.
497int64_t cmdScheduler::getRWTP(int64_t transType, const MemorySpecification& memSpec)
498{
499  int64_t tRWTP_init = 0;
500  const MemTimingSpec& memTimingSpec     = memSpec.memTimingSpec;
501  const MemArchitectureSpec& memArchSpec = memSpec.memArchSpec;
502
503  if (transType == READ) {
504    switch (memSpec.memoryType) {
505    case MemoryType::LPDDR:
506    case MemoryType::WIDEIO_SDR:
507      tRWTP_init = memArchSpec.burstLength / memArchSpec.dataRate;
508      break;
509
510    case MemoryType::LPDDR2:
511    case MemoryType::LPDDR3:
512      tRWTP_init = memArchSpec.burstLength / memArchSpec.dataRate +
513                   max(int64_t(0), memTimingSpec.RTP - 2);
514      break;
515
516    case MemoryType::DDR2:
517      tRWTP_init = memTimingSpec.AL + memArchSpec.burstLength /
518                   memArchSpec.dataRate +
519                   max(memTimingSpec.RTP, int64_t(2)) - 2;
520      break;
521
522    case MemoryType::DDR3:
523    case MemoryType::DDR4:
524      tRWTP_init = memTimingSpec.RTP;
525      break;
526    default:
527      assert("Unknown memory type" && false);
528    } // switch
529  } else if (transType == WRITE)    {
530    if (memSpec.memoryType == MemoryType::WIDEIO_SDR) {
531      tRWTP_init = memTimingSpec.WL + memArchSpec.burstLength /
532                   memArchSpec.dataRate - 1 + memTimingSpec.WR;
533    } else   {
534      tRWTP_init = memTimingSpec.WL + memArchSpec.burstLength /
535                   memArchSpec.dataRate + memTimingSpec.WR;
536    }
537    if ((memSpec.memoryType == MemoryType::LPDDR2) ||
538        (memSpec.memoryType == MemoryType::LPDDR3)) {
539      tRWTP_init = tRWTP_init + 1;
540    }
541  }
542
543  return tRWTP_init;
544} // cmdScheduler::getRWTP
545
546// get the timings for command scheduling according to different memories.
547// In particular, tSwitch_init is generally used to provide the timings for
548// scheduling a read/write command after a read/write command which have been
549// scheduled to any possible banks within any possible bank groups (DDR4).
550void cmdScheduler::getTimingConstraints(bool BGSwitch, const MemorySpecification& memSpec,
551                                        int64_t PreType, int64_t CurrentType)
552{
553  const MemTimingSpec& memTimingSpec     = memSpec.memTimingSpec;
554  const MemArchitectureSpec& memArchSpec = memSpec.memArchSpec;
555
556  if (memSpec.memoryType != MemoryType::DDR4) {
557    tRRD_init = memTimingSpec.RRD;
558    if (PreType == CurrentType) {
559      tSwitch_init = memTimingSpec.CCD;
560      timingsGet   = true;
561    }
562
563    if ((PreType == WRITE) && (CurrentType == READ)) {
564      if (memSpec.memoryType == MemoryType::WIDEIO_SDR) {
565        tSwitch_init = memTimingSpec.WL + memArchSpec.burstLength /
566                       memArchSpec.dataRate - 1 + memTimingSpec.WTR;
567      } else   {
568        tSwitch_init = memTimingSpec.WL + memArchSpec.burstLength /
569                       memArchSpec.dataRate + memTimingSpec.WTR;
570      }
571
572      if ((memSpec.memoryType == MemoryType::LPDDR2) ||
573          (memSpec.memoryType == MemoryType::LPDDR3)) {
574        tSwitch_init = tSwitch_init + 1;
575      }
576    }
577  }
578
579  if (memSpec.memoryType == MemoryType::DDR4) {
580    if (BGSwitch) {
581      tCCD_init = memTimingSpec.CCD_S;
582      tRRD_init = memTimingSpec.RRD_S;
583      tWTR_init = memTimingSpec.WTR_S;
584    } else   {
585      tCCD_init = memTimingSpec.CCD_L;
586      tRRD_init = memTimingSpec.RRD_L;
587      tWTR_init = memTimingSpec.WTR_L;
588    }
589
590    if (PreType == CurrentType) {
591      tSwitch_init = tCCD_init;
592      timingsGet   = true;
593    } else if (PreType == WRITE && CurrentType == READ) {
594      tSwitch_init = memTimingSpec.WL + memArchSpec.burstLength /
595                     memArchSpec.dataRate + tWTR_init;
596    }
597  }
598
599  if ((PreType == READ) && (CurrentType == WRITE)) {
600    tSwitch_init = memTimingSpec.RL + memArchSpec.burstLength /
601                   memArchSpec.dataRate + 2 - memTimingSpec.WL;
602  }
603} // cmdScheduler::getTimingConstraints
604
605// The logical address of each transaction is translated into a physical address
606// which consists of bank group (for DDR4), bank, row and column addresses.
607cmdScheduler::physicalAddr cmdScheduler::memoryMap(trans               Trans,
608                                                   const MemorySpecification& memSpec)
609{
610  int64_t DecLogic;
611  physicalAddr PhysicalAddr;
612
613  DecLogic = Trans.logicalAddress;
614
615  // row-bank-column-BI-BC-BGI-BL
616  if (BGI > 1 && memSpec.memoryType == MemoryType::DDR4) {
617    uint64_t colBits   = uintLog2(nColumns);
618    uint64_t bankShift = colBits + ((BI > 1) ? uintLog2(BI) : 0)  + ((BGI > 1) ? uintLog2(BGI) : 0);
619    uint64_t bankMask  = (nBanks / (BI * nbrOfBankGroups) - 1) << bankShift;
620    uint64_t bankAddr  = (DecLogic & bankMask) >> (colBits + ((BGI > 1) ? uintLog2(BGI) : 0));
621    PhysicalAddr.bankAddr = bankAddr;
622
623    uint64_t bankGroupShift = uintLog2(burstLength);
624    uint64_t bankGroupMask  = (nbrOfBankGroups / BGI - 1) << bankGroupShift;
625    uint64_t bankGroupAddr  = (DecLogic & bankGroupMask) >> bankGroupShift;
626    PhysicalAddr.bankGroupAddr = bankGroupAddr;
627
628    uint64_t colShift       = uintLog2(BC * burstLength) +
629                                                    ((BI > 1) ? uintLog2(BI) : 0) + ((BGI > 1) ? uintLog2(BGI) : 0);
630    uint64_t colMask        = (nColumns / (BC * burstLength) - 1) << colShift;
631    uint64_t colAddr        = (DecLogic & colMask) >> (colShift - uintLog2(static_cast<uint64_t>(BC) * burstLength));
632    PhysicalAddr.colAddr = colAddr;
633  } else   {
634    uint64_t colBits   = uintLog2(nColumns);
635    uint64_t bankShift = colBits + ((BI > 1) ? uintLog2(BI) : 0);
636    uint64_t bankMask  = (nBanks / BI - 1) << bankShift;
637    uint64_t bankAddr  = (DecLogic & bankMask) >> colBits;
638    PhysicalAddr.bankAddr = bankAddr;
639
640    uint64_t colShift  = (uintLog2(BC * burstLength) + ((BI > 1) ? uintLog2(BI) : 0));
641    uint64_t colMask   = (nColumns / (BC * burstLength) - 1) << colShift;
642    uint64_t colAddr   = (DecLogic & colMask) >> (colShift - uintLog2(BC * burstLength));
643    PhysicalAddr.colAddr       = colAddr;
644
645    PhysicalAddr.bankGroupAddr = 0;
646  }
647
648  uint64_t rowShift = uintLog2(nColumns * nBanks);
649  uint64_t rowMask  = (memSpec.memArchSpec.nbrOfRows - 1) << rowShift;
650  uint64_t rowAddr  = (DecLogic & rowMask) >> rowShift;
651  PhysicalAddr.rowAddr = rowAddr;
652
653  return PhysicalAddr;
654} // cmdScheduler::memoryMap
655
656uint64_t cmdScheduler::uintLog2(uint64_t in)
657{
658  return static_cast<uint64_t>(log2(in));
659}