2 * Copyright (c) 2010-2012 ARM Limited
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Authors: Andreas Hansson
41 #include "debug/DRAM.hh"
42 #include "debug/DRAMWR.hh"
43 #include "mem/simple_dram.hh"
44 #include "sim/stat_control.hh"
48 SimpleDRAM::SimpleDRAM(const SimpleDRAMParams
* p
) :
50 port(name() + ".port", *this),
51 retryRdReq(false), retryWrReq(false),
52 rowHitFlag(false), stopReads(false),
53 writeEvent(this), respondEvent(this),
54 refreshEvent(this), nextReqEvent(this), drainManager(NULL
),
56 linesPerRowBuffer(p
->lines_per_rowbuffer
),
57 ranksPerChannel(p
->ranks_per_channel
),
58 banksPerRank(p
->banks_per_rank
), rowsPerBank(0),
59 readBufferSize(p
->read_buffer_size
),
60 writeBufferSize(p
->write_buffer_size
),
61 writeThresholdPerc(p
->write_thresh_perc
),
62 tWTR(p
->tWTR
), tBURST(p
->tBURST
),
63 tRCD(p
->tRCD
), tCL(p
->tCL
), tRP(p
->tRP
),
64 tRFC(p
->tRFC
), tREFI(p
->tREFI
),
65 memSchedPolicy(p
->mem_sched_policy
), addrMapping(p
->addr_mapping
),
66 pageMgmt(p
->page_policy
),
67 busBusyUntil(0), prevdramaccess(0), writeStartTime(0),
68 prevArrival(0), numReqs(0)
70 // create the bank states based on the dimensions of the ranks and
72 banks
.resize(ranksPerChannel
);
73 for (size_t c
= 0; c
< ranksPerChannel
; ++c
) {
74 banks
[c
].resize(banksPerRank
);
77 // round the write threshold percent to a whole number of entries
79 writeThreshold
= writeBufferSize
* writeThresholdPerc
/ 100.0;
85 if (!port
.isConnected()) {
86 fatal("SimpleDRAM %s is unconnected!\n", name());
88 port
.sendRangeChange();
91 // get the cache line size from the connected port
92 bytesPerCacheLine
= port
.peerBlockSize();
94 // we could deal with plenty options here, but for now do a quick
96 if (bytesPerCacheLine
!= 64 && bytesPerCacheLine
!= 32)
97 panic("Unexpected cache line size %d", bytesPerCacheLine
);
99 // determine the rows per bank by looking at the total capacity
100 uint64_t capacity
= AbstractMemory::size();
102 while (i
< 64 && capacity
> ((1 << i
))) {
106 // rounded up to nearest power of two
107 DPRINTF(DRAM
, "i is %lld\n", i
);
110 DPRINTF(DRAM
, "Memory capacity %lld (%lld) bytes\n", capacity
,
111 AbstractMemory::size());
112 rowsPerBank
= capacity
/ (bytesPerCacheLine
* linesPerRowBuffer
*
113 banksPerRank
* ranksPerChannel
);
118 SimpleDRAM::startup()
120 // print the configuration of the controller
123 // kick off the refresh
124 schedule(&refreshEvent
, curTick() + tREFI
);
129 SimpleDRAM::recvAtomic(PacketPtr pkt
)
131 DPRINTF(DRAM
, "recvAtomic: %s 0x%x\n", pkt
->cmdString(), pkt
->getAddr());
133 // do the actual memory access and turn the packet into a response
137 if (!pkt
->memInhibitAsserted() && pkt
->hasData()) {
138 // this value is not supposed to be accurate, just enough to
139 // keep things going, mimic a closed page
140 latency
= tRP
+ tRCD
+ tCL
;
146 SimpleDRAM::readQueueFull() const
148 DPRINTF(DRAM
, "Read queue limit %d current size %d\n",
149 readBufferSize
, dramReadQueue
.size() + dramRespQueue
.size());
151 return (dramReadQueue
.size() + dramRespQueue
.size()) == readBufferSize
;
155 SimpleDRAM::writeQueueFull() const
157 DPRINTF(DRAM
, "Write queue limit %d current size %d\n",
158 writeBufferSize
, dramWriteQueue
.size());
159 return dramWriteQueue
.size() == writeBufferSize
;
163 SimpleDRAM::DRAMPacket
*
164 SimpleDRAM::decodeAddr(PacketPtr pkt
)
170 Addr addr
= pkt
->getAddr();
173 // truncate the address to the access granularity
174 addr
= addr
/ bytesPerCacheLine
;
176 if (addrMapping
== Enums::openmap
) {
177 addr
= addr
/ linesPerRowBuffer
;
179 bank
= addr
% banksPerRank
;
180 addr
= addr
/ banksPerRank
;
182 rank
= addr
% ranksPerChannel
;
183 addr
= addr
/ ranksPerChannel
;
185 row
= addr
% rowsPerBank
;
186 addr
= addr
/ rowsPerBank
;
187 } else if (addrMapping
== Enums::closemap
) {
188 bank
= addr
% banksPerRank
;
189 addr
= addr
/ banksPerRank
;
191 rank
= addr
% ranksPerChannel
;
192 addr
= addr
/ ranksPerChannel
;
194 addr
= addr
/ linesPerRowBuffer
;
196 row
= addr
% rowsPerBank
;
197 addr
= addr
/ rowsPerBank
;
199 panic("Unknown address mapping policy chosen!");
201 assert(rank
< ranksPerChannel
);
202 assert(bank
< banksPerRank
);
203 assert(row
< rowsPerBank
);
205 DPRINTF(DRAM
, "Address: %lld Rank %d Bank %d Row %d\n",
206 temp
, rank
, bank
, row
);
208 // create the corresponding DRAM packet with the entry time and
209 // ready time set to the current tick, they will be updated later
210 DRAMPacket
* dram_pkt
= new DRAMPacket(pkt
, rank
, bank
, row
, temp
,
217 SimpleDRAM::addToReadQueue(PacketPtr pkt
)
219 // only add to the read queue here. whenever the request is
220 // eventually done, set the readyTime, and call schedule()
221 assert(!pkt
->isWrite());
223 // First check write buffer to see if the data is already at
225 std::list
<DRAMPacket
*>::const_iterator i
;
226 Addr addr
= pkt
->getAddr();
228 // @todo: add size check
229 for (i
= dramWriteQueue
.begin(); i
!= dramWriteQueue
.end(); ++i
) {
230 if ((*i
)->addr
== addr
){
232 DPRINTF(DRAM
,"Serviced by write Q\n");
233 bytesRead
+= bytesPerCacheLine
;
234 bytesConsumedRd
+= pkt
->getSize();
235 accessAndRespond(pkt
);
240 DRAMPacket
* dram_pkt
= decodeAddr(pkt
);
242 assert(dramReadQueue
.size() + dramRespQueue
.size() < readBufferSize
);
243 rdQLenPdf
[dramReadQueue
.size() + dramRespQueue
.size()]++;
245 DPRINTF(DRAM
, "Adding to read queue\n");
247 dramReadQueue
.push_back(dram_pkt
);
250 uint32_t bank_id
= banksPerRank
* dram_pkt
->rank
+ dram_pkt
->bank
;
251 assert(bank_id
< ranksPerChannel
* banksPerRank
);
252 perBankRdReqs
[bank_id
]++;
254 avgRdQLen
= dramReadQueue
.size() + dramRespQueue
.size();
256 // Special case where no arbitration is required between requests
257 if (!nextReqEvent
.scheduled() && !stopReads
) {
258 DPRINTF(DRAM
, "Request %lld - need to schedule immediately");
259 schedule(&nextReqEvent
, curTick() + 1);
264 SimpleDRAM::processWriteEvent()
266 assert(!dramWriteQueue
.empty());
267 uint32_t numWritesThisTime
= 0;
269 DPRINTF(DRAMWR
, "Beginning DRAM Writes\n");
270 Tick temp1 M5_VAR_USED
= std::max(curTick(), busBusyUntil
);
271 Tick temp2 M5_VAR_USED
= std::max(curTick(), maxBankFreeAt());
273 // @todo: are there any dangers with the untimed while loop?
274 while (!dramWriteQueue
.empty()) {
275 if (numWritesThisTime
> writeThreshold
)
279 DRAMPacket
* dram_pkt
= dramWriteQueue
.front();
280 // What's the earlier the request can be put on the bus
281 Tick schedTime
= std::max(curTick(), busBusyUntil
);
283 DPRINTF(DRAMWR
, "Asking for latency estimate at %lld\n",
286 pair
<Tick
, Tick
> lat
= estimateLatency(dram_pkt
, schedTime
+ tBURST
);
287 Tick accessLat
= lat
.second
;
289 // look at the rowHitFlag set by estimateLatency
291 // @todo: Race condition here where another packet gives rise
292 // to another call to estimateLatency in the meanwhile?
296 Bank
& bank
= dram_pkt
->bank_ref
;
298 if (pageMgmt
== Enums::open
) {
299 bank
.openRow
= dram_pkt
->row
;
300 bank
.freeAt
= schedTime
+ tBURST
+ accessLat
;
303 bank
.tRASDoneAt
= bank
.freeAt
+ tRP
;
305 } else if (pageMgmt
== Enums::close
) {
306 bank
.freeAt
= schedTime
+ tBURST
+ accessLat
+ tRP
+ tRP
;
307 DPRINTF(DRAMWR
, "processWriteEvent::bank.freeAt for "
308 "banks_id %d is %lld\n",
309 dram_pkt
->rank
* banksPerRank
+ dram_pkt
->bank
,
312 panic("Unknown page management policy chosen\n");
314 // @todo: As of now, write goes on the databus asap, maybe
315 // be held up at bank. May want to change it to delay the
317 busBusyUntil
= schedTime
+ tBURST
;
318 DPRINTF(DRAMWR
,"Done writing to address %lld\n",dram_pkt
->addr
);
321 DPRINTF(DRAMWR
,"schedtime is %lld, tBURST is %lld, "
322 "busbusyuntil is %lld\n",
323 schedTime
, tBURST
, busBusyUntil
);
325 dramWriteQueue
.pop_front();
331 DPRINTF(DRAMWR
, "Completed %d writes, bus busy for %lld ticks,"\
332 "banks busy for %lld ticks\n", numWritesThisTime
,
333 busBusyUntil
- temp1
, maxBankFreeAt() - temp2
);
336 avgWrQLen
= dramWriteQueue
.size();
338 // turn the bus back around for reads again
339 busBusyUntil
+= tWTR
;
347 // if there is nothing left in any queue, signal a drain
348 if (dramWriteQueue
.empty() && dramReadQueue
.empty() &&
349 dramRespQueue
.empty () && drainManager
) {
350 drainManager
->signalDrainDone();
354 // Once you're done emptying the write queue, check if there's
355 // anything in the read queue, and call schedule if required
356 schedule(&nextReqEvent
, busBusyUntil
);
360 SimpleDRAM::triggerWrites()
362 DPRINTF(DRAM
, "Writes triggered at %lld\n", curTick());
363 // Flag variable to stop any more read scheduling
366 writeStartTime
= std::max(busBusyUntil
, curTick()) + tWTR
;
368 DPRINTF(DRAM
, "Writes scheduled at %lld\n", writeStartTime
);
370 assert(writeStartTime
>= curTick());
371 assert(!writeEvent
.scheduled());
372 schedule(&writeEvent
, writeStartTime
);
376 SimpleDRAM::addToWriteQueue(PacketPtr pkt
)
378 // only add to the write queue here. whenever the request is
379 // eventually done, set the readyTime, and call schedule()
380 assert(pkt
->isWrite());
382 DRAMPacket
* dram_pkt
= decodeAddr(pkt
);
384 assert(dramWriteQueue
.size() < writeBufferSize
);
385 wrQLenPdf
[dramWriteQueue
.size()]++;
387 DPRINTF(DRAM
, "Adding to write queue\n");
389 dramWriteQueue
.push_back(dram_pkt
);
392 uint32_t bank_id
= banksPerRank
* dram_pkt
->rank
+ dram_pkt
->bank
;
393 assert(bank_id
< ranksPerChannel
* banksPerRank
);
394 perBankWrReqs
[bank_id
]++;
396 avgWrQLen
= dramWriteQueue
.size();
398 // we do not wait for the writes to be send to the actual memory,
399 // but instead take responsibility for the consistency here and
400 // snoop the write queue for any upcoming reads
402 bytesConsumedWr
+= pkt
->getSize();
403 bytesWritten
+= bytesPerCacheLine
;
404 accessAndRespond(pkt
);
406 // If your write buffer is starting to fill up, drain it!
407 if (dramWriteQueue
.size() > writeThreshold
&& !stopReads
){
413 SimpleDRAM::printParams() const
415 // Sanity check print of important parameters
417 "Memory controller %s physical organization\n" \
418 "Bytes per cacheline %d\n" \
419 "Lines per row buffer %d\n" \
420 "Rows per bank %d\n" \
421 "Banks per rank %d\n" \
422 "Ranks per channel %d\n" \
423 "Total mem capacity %u\n",
424 name(), bytesPerCacheLine
,linesPerRowBuffer
, rowsPerBank
,
425 banksPerRank
, ranksPerChannel
, bytesPerCacheLine
*
426 linesPerRowBuffer
* rowsPerBank
* banksPerRank
* ranksPerChannel
);
428 string scheduler
= memSchedPolicy
== Enums::fcfs
? "FCFS" : "FR-FCFS";
429 string address_mapping
= addrMapping
== Enums::openmap
? "OPENMAP" :
431 string page_policy
= pageMgmt
== Enums::open
? "OPEN" : "CLOSE";
434 "Memory controller %s characteristics\n" \
435 "Read buffer size %d\n" \
436 "Write buffer size %d\n" \
437 "Write buffer thresh %d\n" \
439 "Address mapping %s\n" \
441 name(), readBufferSize
, writeBufferSize
, writeThreshold
,
442 scheduler
, address_mapping
, page_policy
);
444 DPRINTF(DRAM
, "Memory controller %s timing specs\n" \
448 "tBURST %d ticks\n" \
452 name(), tRCD
, tCL
, tRP
, tBURST
, tRFC
, tREFI
, tWTR
);
456 SimpleDRAM::printQs() const {
458 list
<DRAMPacket
*>::const_iterator i
;
460 DPRINTF(DRAM
, "===READ QUEUE===\n\n");
461 for (i
= dramReadQueue
.begin() ; i
!= dramReadQueue
.end() ; ++i
) {
462 DPRINTF(DRAM
, "Read %lu\n", (*i
)->addr
);
464 DPRINTF(DRAM
, "\n===RESP QUEUE===\n\n");
465 for (i
= dramRespQueue
.begin() ; i
!= dramRespQueue
.end() ; ++i
) {
466 DPRINTF(DRAM
, "Response %lu\n", (*i
)->addr
);
468 DPRINTF(DRAM
, "\n===WRITE QUEUE===\n\n");
469 for (i
= dramWriteQueue
.begin() ; i
!= dramWriteQueue
.end() ; ++i
) {
470 DPRINTF(DRAM
, "Write %lu\n", (*i
)->addr
);
475 SimpleDRAM::recvTimingReq(PacketPtr pkt
)
477 // This is where we enter from the outside world
478 DPRINTF(DRAM
, "Inside recvTimingReq: request %s addr %lld size %d\n",
479 pkt
->cmdString(),pkt
->getAddr(), pkt
->getSize());
483 if (pkt
->getSize() == bytesPerCacheLine
)
486 if (numReqs
% 1000000 == 0)
489 // Calc avg gap between requests
490 if (prevArrival
!= 0) {
491 totGap
+= curTick() - prevArrival
;
493 prevArrival
= curTick();
495 // simply drop inhibited packets for now
496 if (pkt
->memInhibitAsserted()) {
497 DPRINTF(DRAM
,"Inhibited packet -- Dropping it now\n");
502 unsigned size
= pkt
->getSize();
503 if (size
> bytesPerCacheLine
)
504 panic("Request size %d is greater than cache line size %d",
505 size
, bytesPerCacheLine
);
508 index
= log2(bytesPerCacheLine
) + 1;
512 if (size
!= 0 && (1 << index
) != size
)
513 index
= log2(bytesPerCacheLine
) + 2;
515 // @todo: Do we really want to do all this before the packet is
516 // actually accepted?
518 /* Index 0 - Size 1 byte
519 Index 1 - Size 2 bytes
520 Index 2 - Size 4 bytes
523 Index 6 - Size 64 bytes
524 Index 7 - Size 0 bytes
525 Index 8 - Non-power-of-2 size */
528 readPktSize
[index
]++;
529 else if (pkt
->isWrite())
530 writePktSize
[index
]++;
532 neitherPktSize
[index
]++;
534 // check local buffers and do not accept if full
536 if (readQueueFull()) {
537 DPRINTF(DRAM
,"Read queue full, not accepting\n");
538 // remember that we have to retry this port
547 } else if (pkt
->isWrite()) {
548 if (writeQueueFull()) {
549 DPRINTF(DRAM
,"Write queue full, not accepting\n");
550 // remember that we have to retry this port
555 addToWriteQueue(pkt
);
560 DPRINTF(DRAM
,"Neither read nor write, ignore timing\n");
561 neitherReadNorWrite
++;
562 accessAndRespond(pkt
);
572 SimpleDRAM::processRespondEvent()
575 "processRespondEvent(): Some req has reached its readyTime\n");
577 PacketPtr pkt
= dramRespQueue
.front()->pkt
;
579 // Actually responds to the requestor
580 bytesConsumedRd
+= pkt
->getSize();
581 bytesRead
+= bytesPerCacheLine
;
582 accessAndRespond(pkt
);
584 DRAMPacket
* dram_pkt
= dramRespQueue
.front();
585 dramRespQueue
.pop_front();
589 avgRdQLen
= dramReadQueue
.size() + dramRespQueue
.size();
591 if (!dramRespQueue
.empty()){
592 assert(dramRespQueue
.front()->readyTime
>= curTick());
593 assert(!respondEvent
.scheduled());
594 schedule(&respondEvent
, dramRespQueue
.front()->readyTime
);
596 // if there is nothing left in any queue, signal a drain
597 if (dramWriteQueue
.empty() && dramReadQueue
.empty() &&
599 drainManager
->signalDrainDone();
606 SimpleDRAM::chooseNextWrite()
608 // This method does the arbitration between requests. The chosen
609 // packet is simply moved to the head of the queue. The other
610 // methods know that this is the place to look. For example, with
611 // FCFS, this method does nothing
612 assert(!dramWriteQueue
.empty());
614 if (dramWriteQueue
.size() == 1) {
615 DPRINTF(DRAMWR
, "chooseNextWrite(): Single element, nothing to do\n");
619 if (memSchedPolicy
== Enums::fcfs
) {
621 // Do nothing, since the correct request is already head
623 } else if (memSchedPolicy
== Enums::frfcfs
) {
625 list
<DRAMPacket
*>::iterator i
= dramWriteQueue
.begin();
626 bool foundRowHit
= false;
627 while (!foundRowHit
&& i
!= dramWriteQueue
.end()) {
628 DRAMPacket
* dram_pkt
= *i
;
629 const Bank
& bank
= dram_pkt
->bank_ref
;
630 if (bank
.openRow
== dram_pkt
->row
) { //FR part
631 DPRINTF(DRAMWR
,"Row buffer hit\n");
632 dramWriteQueue
.erase(i
);
633 dramWriteQueue
.push_front(dram_pkt
);
642 panic("No scheduling policy chosen\n");
644 DPRINTF(DRAMWR
, "chooseNextWrite(): Something chosen\n");
648 SimpleDRAM::chooseNextReq()
650 // This method does the arbitration between requests.
651 // The chosen packet is simply moved to the head of the
652 // queue. The other methods know that this is the place
653 // to look. For example, with FCFS, this method does nothing
654 list
<DRAMPacket
*>::iterator i
;
655 DRAMPacket
* dram_pkt
;
657 if (dramReadQueue
.empty()){
658 DPRINTF(DRAM
, "chooseNextReq(): Returning False\n");
662 if (dramReadQueue
.size() == 1)
665 if (memSchedPolicy
== Enums::fcfs
) {
667 // Do nothing, since the correct request is already head
669 } else if (memSchedPolicy
== Enums::frfcfs
) {
671 for (i
= dramReadQueue
.begin() ; i
!= dramReadQueue
.end() ; ++i
) {
673 const Bank
& bank
= dram_pkt
->bank_ref
;
674 if (bank
.openRow
== dram_pkt
->row
) { //FR part
675 DPRINTF(DRAM
, "Row buffer hit\n");
676 dramReadQueue
.erase(i
);
677 dramReadQueue
.push_front(dram_pkt
);
686 panic("No scheduling policy chosen!\n");
689 DPRINTF(DRAM
,"chooseNextReq(): Chosen something, returning True\n");
694 SimpleDRAM::accessAndRespond(PacketPtr pkt
)
696 DPRINTF(DRAM
, "Responding to Address %lld.. ",pkt
->getAddr());
698 bool needsResponse
= pkt
->needsResponse();
699 // do the actual memory access which also turns the packet into a
703 // turn packet around to go back to requester if response expected
705 // access already turned the packet into a response
706 assert(pkt
->isResponse());
708 // queue the packet in the response queue to be sent out the
710 port
.schedTimingResp(pkt
, curTick() + 1);
714 DPRINTF(DRAM
, "Done\n");
720 SimpleDRAM::estimateLatency(DRAMPacket
* dram_pkt
, Tick inTime
)
722 // If a request reaches a bank at tick 'inTime', how much time
723 // *after* that does it take to finish the request, depending
724 // on bank status and page open policy. Note that this method
725 // considers only the time taken for the actual read or write
726 // to complete, NOT any additional time thereafter for tRAS or
732 const Bank
& bank
= dram_pkt
->bank_ref
;
733 if (pageMgmt
== Enums::open
) { // open-page policy
734 if (bank
.openRow
== dram_pkt
->row
) {
735 // When we have a row-buffer hit,
736 // we don't care about tRAS having expired or not,
737 // but do care about bank being free for access
740 if (bank
.freeAt
< inTime
) {
750 // Row-buffer miss, need to close existing row
751 // once tRAS has expired, then open the new one,
752 // then add cas latency.
753 Tick freeTime
= std::max(bank
.tRASDoneAt
, bank
.freeAt
);
755 if (freeTime
> inTime
)
756 accLat
+= freeTime
- inTime
;
758 accLat
+= tRP
+ tRCD
+ tCL
;
759 bankLat
+= tRP
+ tRCD
+ tCL
;
761 } else if (pageMgmt
== Enums::close
) {
763 // With a close page policy, no notion of
765 if (bank
.freeAt
> inTime
)
766 accLat
+= bank
.freeAt
- inTime
;
768 // page already closed, simply open the row, and
770 accLat
+= tRCD
+ tCL
;
771 bankLat
+= tRCD
+ tCL
;
773 panic("No page management policy chosen\n");
775 DPRINTF(DRAM
, "Returning %lld from estimateLatency()\n",accLat
);
777 return make_pair(bankLat
, accLat
);
781 SimpleDRAM::processNextReqEvent()
787 SimpleDRAM::doDRAMAccess(DRAMPacket
* dram_pkt
)
790 DPRINTF(DRAM
, "Timing access to addr %lld, rank/bank/row %d %d %d\n",
791 dram_pkt
->addr
, dram_pkt
->rank
, dram_pkt
->bank
, dram_pkt
->row
);
793 assert(curTick() >= prevdramaccess
);
794 prevdramaccess
= curTick();
796 // estimate the bank and access latency
797 pair
<Tick
, Tick
> lat
= estimateLatency(dram_pkt
, curTick());
798 Tick bankLat
= lat
.first
;
799 Tick accessLat
= lat
.second
;
801 // This request was woken up at this time based on a prior call
802 // to estimateLatency(). However, between then and now, both the
803 // accessLatency and/or busBusyUntil may have changed. We need
804 // to correct for that.
806 Tick addDelay
= (curTick() + accessLat
< busBusyUntil
) ?
807 busBusyUntil
- (curTick() + accessLat
) : 0;
809 Bank
& bank
= dram_pkt
->bank_ref
;
812 if (pageMgmt
== Enums::open
) {
813 bank
.openRow
= dram_pkt
->row
;
814 bank
.freeAt
= curTick() + addDelay
+ accessLat
;
815 // If you activated a new row do to this access, the next access
816 // will have to respect tRAS for this bank. Assume tRAS ~= 3 * tRP
818 bank
.tRASDoneAt
= bank
.freeAt
+ tRP
;
820 } else if (pageMgmt
== Enums::close
) { // accounting for tRAS also
821 // assuming that tRAS ~= 3 * tRP, and tRAS ~= 4 * tRP, as is common
822 // (refer Jacob/Ng/Wang and Micron datasheets)
823 bank
.freeAt
= curTick() + addDelay
+ accessLat
+ tRP
+ tRP
;
824 DPRINTF(DRAM
,"doDRAMAccess::bank.freeAt is %lld\n",bank
.freeAt
);
826 panic("No page management policy chosen\n");
828 // Update request parameters
829 dram_pkt
->readyTime
= curTick() + addDelay
+ accessLat
+ tBURST
;
832 DPRINTF(DRAM
, "Req %lld: curtick is %lld accessLat is %d " \
833 "readytime is %lld busbusyuntil is %lld. " \
834 "Scheduling at readyTime\n", dram_pkt
->addr
,
835 curTick(), accessLat
, dram_pkt
->readyTime
, busBusyUntil
);
837 // Make sure requests are not overlapping on the databus
838 assert (dram_pkt
->readyTime
- busBusyUntil
>= tBURST
);
841 busBusyUntil
= dram_pkt
->readyTime
;
843 DPRINTF(DRAM
,"Access time is %lld\n",
844 dram_pkt
->readyTime
- dram_pkt
->entryTime
);
847 totMemAccLat
+= dram_pkt
->readyTime
- dram_pkt
->entryTime
;
848 totBankLat
+= bankLat
;
850 totQLat
+= dram_pkt
->readyTime
- dram_pkt
->entryTime
- bankLat
- tBURST
;
855 // At this point we're done dealing with the request
856 // It will be moved to a separate response queue with a
857 // correct readyTime, and eventually be sent back at that
861 // The absolute soonest you have to start thinking about the
862 // next request is the longest access time that can occur before
863 // busBusyUntil. Assuming you need to meet tRAS, then precharge,
864 // open a new row, and access, it is ~4*tRCD.
867 Tick newTime
= (busBusyUntil
> 4 * tRCD
) ?
868 std::max(busBusyUntil
- 4 * tRCD
, curTick()) :
871 if (!nextReqEvent
.scheduled() && !stopReads
){
872 schedule(&nextReqEvent
, newTime
);
874 if (newTime
< nextReqEvent
.when())
875 reschedule(&nextReqEvent
, newTime
);
882 SimpleDRAM::moveToRespQ()
884 // Remove from read queue
885 DRAMPacket
* dram_pkt
= dramReadQueue
.front();
886 dramReadQueue
.pop_front();
888 // Insert into response queue sorted by readyTime
889 // It will be sent back to the requestor at its
891 if (dramRespQueue
.empty()) {
892 dramRespQueue
.push_front(dram_pkt
);
893 assert(!respondEvent
.scheduled());
894 assert(dram_pkt
->readyTime
>= curTick());
895 schedule(&respondEvent
, dram_pkt
->readyTime
);
898 std::list
<DRAMPacket
*>::iterator i
= dramRespQueue
.begin();
899 while (!done
&& i
!= dramRespQueue
.end()) {
900 if ((*i
)->readyTime
> dram_pkt
->readyTime
) {
901 dramRespQueue
.insert(i
, dram_pkt
);
908 dramRespQueue
.push_back(dram_pkt
);
910 assert(respondEvent
.scheduled());
912 if (dramRespQueue
.front()->readyTime
< respondEvent
.when()) {
913 assert(dramRespQueue
.front()->readyTime
>= curTick());
914 reschedule(&respondEvent
, dramRespQueue
.front()->readyTime
);
925 SimpleDRAM::scheduleNextReq()
927 DPRINTF(DRAM
, "Reached scheduleNextReq()\n");
929 // Figure out which request goes next, and move it to front()
930 if (!chooseNextReq())
933 doDRAMAccess(dramReadQueue
.front());
940 SimpleDRAM::maxBankFreeAt() const
944 for(int i
= 0; i
< ranksPerChannel
; i
++)
945 for(int j
= 0; j
< banksPerRank
; j
++)
946 banksFree
= std::max(banks
[i
][j
].freeAt
, banksFree
);
952 SimpleDRAM::processRefreshEvent()
954 DPRINTF(DRAM
, "Refreshing at tick %ld\n", curTick());
956 Tick banksFree
= std::max(curTick(), maxBankFreeAt()) + tRFC
;
958 for(int i
= 0; i
< ranksPerChannel
; i
++)
959 for(int j
= 0; j
< banksPerRank
; j
++)
960 banks
[i
][j
].freeAt
= banksFree
;
962 schedule(&refreshEvent
, curTick() + tREFI
);
966 SimpleDRAM::regStats()
968 using namespace Stats
;
970 AbstractMemory::regStats();
973 .name(name() + ".readReqs")
974 .desc("Total number of read requests seen");
977 .name(name() + ".writeReqs")
978 .desc("Total number of write requests seen");
981 .name(name() + ".servicedByWrQ")
982 .desc("Number of read reqs serviced by write Q");
985 .name(name() + ".cpureqs")
986 .desc("Reqs generatd by CPU via cache - shady");
989 .name(name() + ".neitherReadNorWrite")
990 .desc("Reqs where no action is needed");
993 .init(banksPerRank
* ranksPerChannel
)
994 .name(name() + ".perBankRdReqs")
995 .desc("Track reads on a per bank basis");
998 .init(banksPerRank
* ranksPerChannel
)
999 .name(name() + ".perBankWrReqs")
1000 .desc("Track writes on a per bank basis");
1003 .name(name() + ".avgRdQLen")
1004 .desc("Average read queue length over time")
1008 .name(name() + ".avgWrQLen")
1009 .desc("Average write queue length over time")
1013 .name(name() + ".totQLat")
1014 .desc("Total cycles spent in queuing delays");
1017 .name(name() + ".totBankLat")
1018 .desc("Total cycles spent in bank access");
1021 .name(name() + ".totBusLat")
1022 .desc("Total cycles spent in databus access");
1025 .name(name() + ".totMemAccLat")
1026 .desc("Sum of mem lat for all requests");
1029 .name(name() + ".avgQLat")
1030 .desc("Average queueing delay per request")
1033 avgQLat
= totQLat
/ (readReqs
- servicedByWrQ
);
1036 .name(name() + ".avgBankLat")
1037 .desc("Average bank access latency per request")
1040 avgBankLat
= totBankLat
/ (readReqs
- servicedByWrQ
);
1043 .name(name() + ".avgBusLat")
1044 .desc("Average bus latency per request")
1047 avgBusLat
= totBusLat
/ (readReqs
- servicedByWrQ
);
1050 .name(name() + ".avgMemAccLat")
1051 .desc("Average memory access latency")
1054 avgMemAccLat
= totMemAccLat
/ (readReqs
- servicedByWrQ
);
1057 .name(name() + ".numRdRetry")
1058 .desc("Number of times rd buffer was full causing retry");
1061 .name(name() + ".numWrRetry")
1062 .desc("Number of times wr buffer was full causing retry");
1065 .name(name() + ".readRowHits")
1066 .desc("Number of row buffer hits during reads");
1069 .name(name() + ".writeRowHits")
1070 .desc("Number of row buffer hits during writes");
1073 .name(name() + ".readRowHitRate")
1074 .desc("Row buffer hit rate for reads")
1077 readRowHitRate
= (readRowHits
/ (readReqs
- servicedByWrQ
)) * 100;
1080 .name(name() + ".writeRowHitRate")
1081 .desc("Row buffer hit rate for writes")
1084 writeRowHitRate
= (writeRowHits
/ writeReqs
) * 100;
1087 .init(log2(bytesPerCacheLine
)+3)
1088 .name(name() + ".readPktSize")
1089 .desc("Categorize read packet sizes");
1092 .init(log2(bytesPerCacheLine
)+3)
1093 .name(name() + ".writePktSize")
1094 .desc("categorize write packet sizes");
1097 .init(log2(bytesPerCacheLine
)+3)
1098 .name(name() + ".neitherpktsize")
1099 .desc("categorize neither packet sizes");
1102 .init(readBufferSize
+ 1)
1103 .name(name() + ".rdQLenPdf")
1104 .desc("What read queue length does an incoming req see");
1107 .init(writeBufferSize
+ 1)
1108 .name(name() + ".wrQLenPdf")
1109 .desc("What write queue length does an incoming req see");
1113 .name(name() + ".bytesRead")
1114 .desc("Total number of bytes read from memory");
1117 .name(name() + ".bytesWritten")
1118 .desc("Total number of bytes written to memory");
1121 .name(name() + ".bytesConsumedRd")
1122 .desc("bytesRead derated as per pkt->getSize()");
1125 .name(name() + ".bytesConsumedWr")
1126 .desc("bytesWritten derated as per pkt->getSize()");
1129 .name(name() + ".avgRdBW")
1130 .desc("Average achieved read bandwidth in MB/s")
1133 avgRdBW
= (bytesRead
/ 1000000) / simSeconds
;
1136 .name(name() + ".avgWrBW")
1137 .desc("Average achieved write bandwidth in MB/s")
1140 avgWrBW
= (bytesWritten
/ 1000000) / simSeconds
;
1143 .name(name() + ".avgConsumedRdBW")
1144 .desc("Average consumed read bandwidth in MB/s")
1147 avgConsumedRdBW
= (bytesConsumedRd
/ 1000000) / simSeconds
;
1150 .name(name() + ".avgConsumedWrBW")
1151 .desc("Average consumed write bandwidth in MB/s")
1154 avgConsumedWrBW
= (bytesConsumedWr
/ 1000000) / simSeconds
;
1157 .name(name() + ".peakBW")
1158 .desc("Theoretical peak bandwidth in MB/s")
1161 peakBW
= (SimClock::Frequency
/ tBURST
) * bytesPerCacheLine
/ 1000000;
1164 .name(name() + ".busUtil")
1165 .desc("Data bus utilization in percentage")
1168 busUtil
= (avgRdBW
+ avgWrBW
) / peakBW
* 100;
1171 .name(name() + ".totGap")
1172 .desc("Total gap between requests");
1175 .name(name() + ".avgGap")
1176 .desc("Average gap between requests")
1179 avgGap
= totGap
/ (readReqs
+ writeReqs
);
1183 SimpleDRAM::recvFunctional(PacketPtr pkt
)
1185 // rely on the abstract memory
1186 functionalAccess(pkt
);
1190 SimpleDRAM::getSlavePort(const string
&if_name
, PortID idx
)
1192 if (if_name
!= "port") {
1193 return MemObject::getSlavePort(if_name
, idx
);
1200 SimpleDRAM::drain(DrainManager
*dm
)
1202 unsigned int count
= port
.drain(dm
);
1204 // if there is anything in any of our internal queues, keep track
1206 if (!(dramWriteQueue
.empty() && dramReadQueue
.empty() &&
1207 dramRespQueue
.empty())) {
1213 setDrainState(Drainable::Draining
);
1215 setDrainState(Drainable::Drained
);
1219 SimpleDRAM::MemoryPort::MemoryPort(const std::string
& name
, SimpleDRAM
& _memory
)
1220 : QueuedSlavePort(name
, &_memory
, queue
), queue(_memory
, *this),
1225 SimpleDRAM::MemoryPort::getAddrRanges() const
1227 AddrRangeList ranges
;
1228 ranges
.push_back(memory
.getAddrRange());
1233 SimpleDRAM::MemoryPort::recvFunctional(PacketPtr pkt
)
1235 pkt
->pushLabel(memory
.name());
1237 if (!queue
.checkFunctional(pkt
)) {
1238 // Default implementation of SimpleTimingPort::recvFunctional()
1239 // calls recvAtomic() and throws away the latency; we can save a
1240 // little here by just not calculating the latency.
1241 memory
.recvFunctional(pkt
);
1248 SimpleDRAM::MemoryPort::recvAtomic(PacketPtr pkt
)
1250 return memory
.recvAtomic(pkt
);
1254 SimpleDRAM::MemoryPort::recvTimingReq(PacketPtr pkt
)
1256 // pass it to the memory controller
1257 return memory
.recvTimingReq(pkt
);
1261 SimpleDRAMParams::create()
1263 return new SimpleDRAM(this);