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