dram_ctrl.cc revision 9488
16157Snate@binkert.org/* 26157Snate@binkert.org * Copyright (c) 2010-2012 ARM Limited 36157Snate@binkert.org * All rights reserved 46157Snate@binkert.org * 56157Snate@binkert.org * The license below extends only to copyright in the software and shall 66157Snate@binkert.org * not be construed as granting a license to any other intellectual 76157Snate@binkert.org * property including but not limited to intellectual property relating 86157Snate@binkert.org * to a hardware implementation of the functionality of the software 96157Snate@binkert.org * licensed hereunder. You may use the software subject to the license 106157Snate@binkert.org * terms below provided that you ensure that this notice is replicated 116157Snate@binkert.org * unmodified and in its entirety in all distributions of the software, 126157Snate@binkert.org * modified or unmodified, in source code or in binary form. 136157Snate@binkert.org * 146157Snate@binkert.org * Redistribution and use in source and binary forms, with or without 156157Snate@binkert.org * modification, are permitted provided that the following conditions are 166157Snate@binkert.org * met: redistributions of source code must retain the above copyright 176157Snate@binkert.org * notice, this list of conditions and the following disclaimer; 186157Snate@binkert.org * redistributions in binary form must reproduce the above copyright 196157Snate@binkert.org * notice, this list of conditions and the following disclaimer in the 206157Snate@binkert.org * documentation and/or other materials provided with the distribution; 216157Snate@binkert.org * neither the name of the copyright holders nor the names of its 226157Snate@binkert.org * contributors may be used to endorse or promote products derived from 236157Snate@binkert.org * this software without specific prior written permission. 246157Snate@binkert.org * 256157Snate@binkert.org * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 266157Snate@binkert.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 276157Snate@binkert.org * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 286157Snate@binkert.org * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 296157Snate@binkert.org * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 306157Snate@binkert.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 316157Snate@binkert.org * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 326157Snate@binkert.org * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 338492Snilay@cs.wisc.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 346168Snate@binkert.org * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 356168Snate@binkert.org * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 368439Snilay@cs.wisc.edu * 376876Ssteve.reinhardt@amd.com * Authors: Andreas Hansson 388439Snilay@cs.wisc.edu * Ani Udipi 396876Ssteve.reinhardt@amd.com */ 408191SLisa.Hsu@amd.com 416876Ssteve.reinhardt@amd.com#include "base/trace.hh" 429102SNuwan.Jayasena@amd.com#include "debug/Drain.hh" 436876Ssteve.reinhardt@amd.com#include "debug/DRAM.hh" 446286Snate@binkert.org#include "debug/DRAMWR.hh" 456157Snate@binkert.org#include "mem/simple_dram.hh" 467025SBrad.Beckmann@amd.com#include "sim/stat_control.hh" 476782SBrad.Beckmann@amd.com 486157Snate@binkert.orgusing namespace std; 498191SLisa.Hsu@amd.com 509102SNuwan.Jayasena@amd.comSimpleDRAM::SimpleDRAM(const SimpleDRAMParams* p) : 516157Snate@binkert.org AbstractMemory(p), 526797SBrad.Beckmann@amd.com port(name() + ".port", *this), 536286Snate@binkert.org retryRdReq(false), retryWrReq(false), 548706Sandreas.hansson@arm.com rowHitFlag(false), stopReads(false), actTicks(p->activation_limit, 0), 558641Snate@binkert.org writeEvent(this), respondEvent(this), 566157Snate@binkert.org refreshEvent(this), nextReqEvent(this), drainManager(NULL), 576157Snate@binkert.org bytesPerCacheLine(0), 589105SBrad.Beckmann@amd.com linesPerRowBuffer(p->lines_per_rowbuffer), 59 ranksPerChannel(p->ranks_per_channel), 60 banksPerRank(p->banks_per_rank), rowsPerBank(0), 61 readBufferSize(p->read_buffer_size), 62 writeBufferSize(p->write_buffer_size), 63 writeThresholdPerc(p->write_thresh_perc), 64 tWTR(p->tWTR), tBURST(p->tBURST), 65 tRCD(p->tRCD), tCL(p->tCL), tRP(p->tRP), 66 tRFC(p->tRFC), tREFI(p->tREFI), 67 tXAW(p->tXAW), activationLimit(p->activation_limit), 68 memSchedPolicy(p->mem_sched_policy), addrMapping(p->addr_mapping), 69 pageMgmt(p->page_policy), 70 busBusyUntil(0), prevdramaccess(0), writeStartTime(0), 71 prevArrival(0), numReqs(0) 72{ 73 // create the bank states based on the dimensions of the ranks and 74 // banks 75 banks.resize(ranksPerChannel); 76 for (size_t c = 0; c < ranksPerChannel; ++c) { 77 banks[c].resize(banksPerRank); 78 } 79 80 // round the write threshold percent to a whole number of entries 81 // in the buffer 82 writeThreshold = writeBufferSize * writeThresholdPerc / 100.0; 83} 84 85void 86SimpleDRAM::init() 87{ 88 if (!port.isConnected()) { 89 fatal("SimpleDRAM %s is unconnected!\n", name()); 90 } else { 91 port.sendRangeChange(); 92 } 93 94 // get the cache line size from the connected port 95 bytesPerCacheLine = port.peerBlockSize(); 96 97 // we could deal with plenty options here, but for now do a quick 98 // sanity check 99 if (bytesPerCacheLine != 64 && bytesPerCacheLine != 32) 100 panic("Unexpected cache line size %d", bytesPerCacheLine); 101 102 // determine the rows per bank by looking at the total capacity 103 uint64_t capacity = AbstractMemory::size(); 104 uint64_t i = 1; 105 while (i < 64 && capacity > ((1 << i))) { 106 ++i; 107 } 108 109 // rounded up to nearest power of two 110 DPRINTF(DRAM, "i is %lld\n", i); 111 capacity = 1 << i; 112 113 DPRINTF(DRAM, "Memory capacity %lld (%lld) bytes\n", capacity, 114 AbstractMemory::size()); 115 rowsPerBank = capacity / (bytesPerCacheLine * linesPerRowBuffer * 116 banksPerRank * ranksPerChannel); 117 118} 119 120void 121SimpleDRAM::startup() 122{ 123 // print the configuration of the controller 124 printParams(); 125 126 // kick off the refresh 127 schedule(&refreshEvent, curTick() + tREFI); 128} 129 130 131Tick 132SimpleDRAM::recvAtomic(PacketPtr pkt) 133{ 134 DPRINTF(DRAM, "recvAtomic: %s 0x%x\n", pkt->cmdString(), pkt->getAddr()); 135 136 // do the actual memory access and turn the packet into a response 137 access(pkt); 138 139 Tick latency = 0; 140 if (!pkt->memInhibitAsserted() && pkt->hasData()) { 141 // this value is not supposed to be accurate, just enough to 142 // keep things going, mimic a closed page 143 latency = tRP + tRCD + tCL; 144 } 145 return latency; 146} 147 148bool 149SimpleDRAM::readQueueFull() const 150{ 151 DPRINTF(DRAM, "Read queue limit %d current size %d\n", 152 readBufferSize, dramReadQueue.size() + dramRespQueue.size()); 153 154 return (dramReadQueue.size() + dramRespQueue.size()) == readBufferSize; 155} 156 157bool 158SimpleDRAM::writeQueueFull() const 159{ 160 DPRINTF(DRAM, "Write queue limit %d current size %d\n", 161 writeBufferSize, dramWriteQueue.size()); 162 return dramWriteQueue.size() == writeBufferSize; 163} 164 165 166SimpleDRAM::DRAMPacket* 167SimpleDRAM::decodeAddr(PacketPtr pkt) 168{ 169 uint8_t rank; 170 uint16_t bank; 171 uint16_t row; 172 173 Addr addr = pkt->getAddr(); 174 Addr temp = addr; 175 176 // truncate the address to the access granularity 177 addr = addr / bytesPerCacheLine; 178 179 if (addrMapping == Enums::openmap) { 180 addr = addr / linesPerRowBuffer; 181 182 bank = addr % banksPerRank; 183 addr = addr / banksPerRank; 184 185 rank = addr % ranksPerChannel; 186 addr = addr / ranksPerChannel; 187 188 row = addr % rowsPerBank; 189 addr = addr / rowsPerBank; 190 } else if (addrMapping == Enums::closemap) { 191 bank = addr % banksPerRank; 192 addr = addr / banksPerRank; 193 194 rank = addr % ranksPerChannel; 195 addr = addr / ranksPerChannel; 196 197 addr = addr / linesPerRowBuffer; 198 199 row = addr % rowsPerBank; 200 addr = addr / rowsPerBank; 201 } else 202 panic("Unknown address mapping policy chosen!"); 203 204 assert(rank < ranksPerChannel); 205 assert(bank < banksPerRank); 206 assert(row < rowsPerBank); 207 208 DPRINTF(DRAM, "Address: %lld Rank %d Bank %d Row %d\n", 209 temp, rank, bank, row); 210 211 // create the corresponding DRAM packet with the entry time and 212 // ready time set to the current tick, they will be updated later 213 DRAMPacket* dram_pkt = new DRAMPacket(pkt, rank, bank, row, temp, 214 banks[rank][bank]); 215 216 return dram_pkt; 217} 218 219void 220SimpleDRAM::addToReadQueue(PacketPtr pkt) 221{ 222 // only add to the read queue here. whenever the request is 223 // eventually done, set the readyTime, and call schedule() 224 assert(!pkt->isWrite()); 225 226 // First check write buffer to see if the data is already at 227 // the controller 228 std::list<DRAMPacket*>::const_iterator i; 229 Addr addr = pkt->getAddr(); 230 231 // @todo: add size check 232 for (i = dramWriteQueue.begin(); i != dramWriteQueue.end(); ++i) { 233 if ((*i)->addr == addr){ 234 servicedByWrQ++; 235 DPRINTF(DRAM,"Serviced by write Q\n"); 236 bytesRead += bytesPerCacheLine; 237 bytesConsumedRd += pkt->getSize(); 238 accessAndRespond(pkt); 239 return; 240 } 241 } 242 243 DRAMPacket* dram_pkt = decodeAddr(pkt); 244 245 assert(dramReadQueue.size() + dramRespQueue.size() < readBufferSize); 246 rdQLenPdf[dramReadQueue.size() + dramRespQueue.size()]++; 247 248 DPRINTF(DRAM, "Adding to read queue\n"); 249 250 dramReadQueue.push_back(dram_pkt); 251 252 // Update stats 253 uint32_t bank_id = banksPerRank * dram_pkt->rank + dram_pkt->bank; 254 assert(bank_id < ranksPerChannel * banksPerRank); 255 perBankRdReqs[bank_id]++; 256 257 avgRdQLen = dramReadQueue.size() + dramRespQueue.size(); 258 259 // Special case where no arbitration is required between requests 260 if (!nextReqEvent.scheduled() && !stopReads) { 261 DPRINTF(DRAM, "Request %lld - need to schedule immediately"); 262 schedule(&nextReqEvent, curTick() + 1); 263 } 264} 265 266void 267SimpleDRAM::processWriteEvent() 268{ 269 assert(!dramWriteQueue.empty()); 270 uint32_t numWritesThisTime = 0; 271 272 DPRINTF(DRAMWR, "Beginning DRAM Writes\n"); 273 Tick temp1 M5_VAR_USED = std::max(curTick(), busBusyUntil); 274 Tick temp2 M5_VAR_USED = std::max(curTick(), maxBankFreeAt()); 275 276 // @todo: are there any dangers with the untimed while loop? 277 while (!dramWriteQueue.empty()) { 278 if (numWritesThisTime > writeThreshold) 279 break; 280 281 chooseNextWrite(); 282 DRAMPacket* dram_pkt = dramWriteQueue.front(); 283 // What's the earlier the request can be put on the bus 284 Tick schedTime = std::max(curTick(), busBusyUntil); 285 286 DPRINTF(DRAMWR, "Asking for latency estimate at %lld\n", 287 schedTime + tBURST); 288 289 pair<Tick, Tick> lat = estimateLatency(dram_pkt, schedTime + tBURST); 290 Tick accessLat = lat.second; 291 292 // look at the rowHitFlag set by estimateLatency 293 294 // @todo: Race condition here where another packet gives rise 295 // to another call to estimateLatency in the meanwhile? 296 if (rowHitFlag) 297 writeRowHits++; 298 299 Bank& bank = dram_pkt->bank_ref; 300 301 if (pageMgmt == Enums::open) { 302 bank.openRow = dram_pkt->row; 303 bank.freeAt = schedTime + tBURST + std::max(accessLat, tCL); 304 busBusyUntil = bank.freeAt - tCL; 305 306 if (!rowHitFlag) { 307 bank.tRASDoneAt = bank.freeAt + tRP; 308 recordActivate(bank.freeAt - tCL - tRCD); 309 busBusyUntil = bank.freeAt - tCL - tRCD; 310 } 311 } else if (pageMgmt == Enums::close) { 312 bank.freeAt = schedTime + tBURST + accessLat + tRP + tRP; 313 // Work backwards from bank.freeAt to determine activate time 314 recordActivate(bank.freeAt - tRP - tRP - tCL - tRCD); 315 busBusyUntil = bank.freeAt - tRP - tRP - tCL - tRCD; 316 DPRINTF(DRAMWR, "processWriteEvent::bank.freeAt for " 317 "banks_id %d is %lld\n", 318 dram_pkt->rank * banksPerRank + dram_pkt->bank, 319 bank.freeAt); 320 } else 321 panic("Unknown page management policy chosen\n"); 322 323 DPRINTF(DRAMWR,"Done writing to address %lld\n",dram_pkt->addr); 324 325 DPRINTF(DRAMWR,"schedtime is %lld, tBURST is %lld, " 326 "busbusyuntil is %lld\n", 327 schedTime, tBURST, busBusyUntil); 328 329 dramWriteQueue.pop_front(); 330 delete dram_pkt; 331 332 numWritesThisTime++; 333 } 334 335 DPRINTF(DRAMWR, "Completed %d writes, bus busy for %lld ticks,"\ 336 "banks busy for %lld ticks\n", numWritesThisTime, 337 busBusyUntil - temp1, maxBankFreeAt() - temp2); 338 339 // Update stats 340 avgWrQLen = dramWriteQueue.size(); 341 342 // turn the bus back around for reads again 343 busBusyUntil += tWTR; 344 stopReads = false; 345 346 if (retryWrReq) { 347 retryWrReq = false; 348 port.sendRetry(); 349 } 350 351 // if there is nothing left in any queue, signal a drain 352 if (dramWriteQueue.empty() && dramReadQueue.empty() && 353 dramRespQueue.empty () && drainManager) { 354 drainManager->signalDrainDone(); 355 drainManager = NULL; 356 } 357 358 // Once you're done emptying the write queue, check if there's 359 // anything in the read queue, and call schedule if required 360 schedule(&nextReqEvent, busBusyUntil); 361} 362 363void 364SimpleDRAM::triggerWrites() 365{ 366 DPRINTF(DRAM, "Writes triggered at %lld\n", curTick()); 367 // Flag variable to stop any more read scheduling 368 stopReads = true; 369 370 writeStartTime = std::max(busBusyUntil, curTick()) + tWTR; 371 372 DPRINTF(DRAM, "Writes scheduled at %lld\n", writeStartTime); 373 374 assert(writeStartTime >= curTick()); 375 assert(!writeEvent.scheduled()); 376 schedule(&writeEvent, writeStartTime); 377} 378 379void 380SimpleDRAM::addToWriteQueue(PacketPtr pkt) 381{ 382 // only add to the write queue here. whenever the request is 383 // eventually done, set the readyTime, and call schedule() 384 assert(pkt->isWrite()); 385 386 DRAMPacket* dram_pkt = decodeAddr(pkt); 387 388 assert(dramWriteQueue.size() < writeBufferSize); 389 wrQLenPdf[dramWriteQueue.size()]++; 390 391 DPRINTF(DRAM, "Adding to write queue\n"); 392 393 dramWriteQueue.push_back(dram_pkt); 394 395 // Update stats 396 uint32_t bank_id = banksPerRank * dram_pkt->rank + dram_pkt->bank; 397 assert(bank_id < ranksPerChannel * banksPerRank); 398 perBankWrReqs[bank_id]++; 399 400 avgWrQLen = dramWriteQueue.size(); 401 402 // we do not wait for the writes to be send to the actual memory, 403 // but instead take responsibility for the consistency here and 404 // snoop the write queue for any upcoming reads 405 406 bytesConsumedWr += pkt->getSize(); 407 bytesWritten += bytesPerCacheLine; 408 accessAndRespond(pkt); 409 410 // If your write buffer is starting to fill up, drain it! 411 if (dramWriteQueue.size() > writeThreshold && !stopReads){ 412 triggerWrites(); 413 } 414} 415 416void 417SimpleDRAM::printParams() const 418{ 419 // Sanity check print of important parameters 420 DPRINTF(DRAM, 421 "Memory controller %s physical organization\n" \ 422 "Bytes per cacheline %d\n" \ 423 "Lines per row buffer %d\n" \ 424 "Rows per bank %d\n" \ 425 "Banks per rank %d\n" \ 426 "Ranks per channel %d\n" \ 427 "Total mem capacity %u\n", 428 name(), bytesPerCacheLine ,linesPerRowBuffer, rowsPerBank, 429 banksPerRank, ranksPerChannel, bytesPerCacheLine * 430 linesPerRowBuffer * rowsPerBank * banksPerRank * ranksPerChannel); 431 432 string scheduler = memSchedPolicy == Enums::fcfs ? "FCFS" : "FR-FCFS"; 433 string address_mapping = addrMapping == Enums::openmap ? "OPENMAP" : 434 "CLOSEMAP"; 435 string page_policy = pageMgmt == Enums::open ? "OPEN" : "CLOSE"; 436 437 DPRINTF(DRAM, 438 "Memory controller %s characteristics\n" \ 439 "Read buffer size %d\n" \ 440 "Write buffer size %d\n" \ 441 "Write buffer thresh %d\n" \ 442 "Scheduler %s\n" \ 443 "Address mapping %s\n" \ 444 "Page policy %s\n", 445 name(), readBufferSize, writeBufferSize, writeThreshold, 446 scheduler, address_mapping, page_policy); 447 448 DPRINTF(DRAM, "Memory controller %s timing specs\n" \ 449 "tRCD %d ticks\n" \ 450 "tCL %d ticks\n" \ 451 "tRP %d ticks\n" \ 452 "tBURST %d ticks\n" \ 453 "tRFC %d ticks\n" \ 454 "tREFI %d ticks\n" \ 455 "tWTR %d ticks\n", 456 name(), tRCD, tCL, tRP, tBURST, tRFC, tREFI, tWTR); 457} 458 459void 460SimpleDRAM::printQs() const { 461 462 list<DRAMPacket*>::const_iterator i; 463 464 DPRINTF(DRAM, "===READ QUEUE===\n\n"); 465 for (i = dramReadQueue.begin() ; i != dramReadQueue.end() ; ++i) { 466 DPRINTF(DRAM, "Read %lu\n", (*i)->addr); 467 } 468 DPRINTF(DRAM, "\n===RESP QUEUE===\n\n"); 469 for (i = dramRespQueue.begin() ; i != dramRespQueue.end() ; ++i) { 470 DPRINTF(DRAM, "Response %lu\n", (*i)->addr); 471 } 472 DPRINTF(DRAM, "\n===WRITE QUEUE===\n\n"); 473 for (i = dramWriteQueue.begin() ; i != dramWriteQueue.end() ; ++i) { 474 DPRINTF(DRAM, "Write %lu\n", (*i)->addr); 475 } 476} 477 478bool 479SimpleDRAM::recvTimingReq(PacketPtr pkt) 480{ 481 /// @todo temporary hack to deal with memory corruption issues until 482 /// 4-phase transactions are complete 483 for (int x = 0; x < pendingDelete.size(); x++) 484 delete pendingDelete[x]; 485 pendingDelete.clear(); 486 487 488 // This is where we enter from the outside world 489 DPRINTF(DRAM, "Inside recvTimingReq: request %s addr %lld size %d\n", 490 pkt->cmdString(),pkt->getAddr(), pkt->getSize()); 491 492 int index; 493 494 if (pkt->getSize() == bytesPerCacheLine) 495 cpuReqs++; 496 497 if (numReqs % 1000000 == 0) 498 printQs(); 499 500 // Calc avg gap between requests 501 if (prevArrival != 0) { 502 totGap += curTick() - prevArrival; 503 } 504 prevArrival = curTick(); 505 506 // simply drop inhibited packets for now 507 if (pkt->memInhibitAsserted()) { 508 DPRINTF(DRAM,"Inhibited packet -- Dropping it now\n"); 509 pendingDelete.push_back(pkt); 510 return true; 511 } 512 513 unsigned size = pkt->getSize(); 514 if (size > bytesPerCacheLine) 515 panic("Request size %d is greater than cache line size %d", 516 size, bytesPerCacheLine); 517 518 if (size == 0) 519 index = log2(bytesPerCacheLine) + 1; 520 else 521 index = log2(size); 522 523 if (size != 0 && (1 << index) != size) 524 index = log2(bytesPerCacheLine) + 2; 525 526 // @todo: Do we really want to do all this before the packet is 527 // actually accepted? 528 529 /* Index 0 - Size 1 byte 530 Index 1 - Size 2 bytes 531 Index 2 - Size 4 bytes 532 . 533 . 534 Index 6 - Size 64 bytes 535 Index 7 - Size 0 bytes 536 Index 8 - Non-power-of-2 size */ 537 538 if (pkt->isRead()) 539 readPktSize[index]++; 540 else if (pkt->isWrite()) 541 writePktSize[index]++; 542 else 543 neitherPktSize[index]++; 544 545 // check local buffers and do not accept if full 546 if (pkt->isRead()) { 547 if (readQueueFull()) { 548 DPRINTF(DRAM,"Read queue full, not accepting\n"); 549 // remember that we have to retry this port 550 retryRdReq = true; 551 numRdRetry++; 552 return false; 553 } else { 554 addToReadQueue(pkt); 555 readReqs++; 556 numReqs++; 557 } 558 } else if (pkt->isWrite()) { 559 if (writeQueueFull()) { 560 DPRINTF(DRAM,"Write queue full, not accepting\n"); 561 // remember that we have to retry this port 562 retryWrReq = true; 563 numWrRetry++; 564 return false; 565 } else { 566 addToWriteQueue(pkt); 567 writeReqs++; 568 numReqs++; 569 } 570 } else { 571 DPRINTF(DRAM,"Neither read nor write, ignore timing\n"); 572 neitherReadNorWrite++; 573 accessAndRespond(pkt); 574 } 575 576 577 retryRdReq = false; 578 retryWrReq = false; 579 return true; 580} 581 582void 583SimpleDRAM::processRespondEvent() 584{ 585 DPRINTF(DRAM, 586 "processRespondEvent(): Some req has reached its readyTime\n"); 587 588 PacketPtr pkt = dramRespQueue.front()->pkt; 589 590 // Actually responds to the requestor 591 bytesConsumedRd += pkt->getSize(); 592 bytesRead += bytesPerCacheLine; 593 accessAndRespond(pkt); 594 595 DRAMPacket* dram_pkt = dramRespQueue.front(); 596 dramRespQueue.pop_front(); 597 delete dram_pkt; 598 599 // Update stats 600 avgRdQLen = dramReadQueue.size() + dramRespQueue.size(); 601 602 if (!dramRespQueue.empty()){ 603 assert(dramRespQueue.front()->readyTime >= curTick()); 604 assert(!respondEvent.scheduled()); 605 schedule(&respondEvent, dramRespQueue.front()->readyTime); 606 } else { 607 // if there is nothing left in any queue, signal a drain 608 if (dramWriteQueue.empty() && dramReadQueue.empty() && 609 drainManager) { 610 drainManager->signalDrainDone(); 611 drainManager = NULL; 612 } 613 } 614} 615 616void 617SimpleDRAM::chooseNextWrite() 618{ 619 // This method does the arbitration between requests. The chosen 620 // packet is simply moved to the head of the queue. The other 621 // methods know that this is the place to look. For example, with 622 // FCFS, this method does nothing 623 assert(!dramWriteQueue.empty()); 624 625 if (dramWriteQueue.size() == 1) { 626 DPRINTF(DRAMWR, "chooseNextWrite(): Single element, nothing to do\n"); 627 return; 628 } 629 630 if (memSchedPolicy == Enums::fcfs) { 631 632 // Do nothing, since the correct request is already head 633 634 } else if (memSchedPolicy == Enums::frfcfs) { 635 636 list<DRAMPacket*>::iterator i = dramWriteQueue.begin(); 637 bool foundRowHit = false; 638 while (!foundRowHit && i != dramWriteQueue.end()) { 639 DRAMPacket* dram_pkt = *i; 640 const Bank& bank = dram_pkt->bank_ref; 641 if (bank.openRow == dram_pkt->row) { //FR part 642 DPRINTF(DRAMWR,"Row buffer hit\n"); 643 dramWriteQueue.erase(i); 644 dramWriteQueue.push_front(dram_pkt); 645 foundRowHit = true; 646 } else { //FCFS part 647 ; 648 } 649 ++i; 650 } 651 652 } else 653 panic("No scheduling policy chosen\n"); 654 655 DPRINTF(DRAMWR, "chooseNextWrite(): Something chosen\n"); 656} 657 658bool 659SimpleDRAM::chooseNextReq() 660{ 661 // This method does the arbitration between requests. 662 // The chosen packet is simply moved to the head of the 663 // queue. The other methods know that this is the place 664 // to look. For example, with FCFS, this method does nothing 665 list<DRAMPacket*>::iterator i; 666 DRAMPacket* dram_pkt; 667 668 if (dramReadQueue.empty()){ 669 DPRINTF(DRAM, "chooseNextReq(): Returning False\n"); 670 return false; 671 } 672 673 if (dramReadQueue.size() == 1) 674 return true; 675 676 if (memSchedPolicy == Enums::fcfs) { 677 678 // Do nothing, since the correct request is already head 679 680 } else if (memSchedPolicy == Enums::frfcfs) { 681 682 for (i = dramReadQueue.begin() ; i != dramReadQueue.end() ; ++i) { 683 dram_pkt = *i; 684 const Bank& bank = dram_pkt->bank_ref; 685 if (bank.openRow == dram_pkt->row) { //FR part 686 DPRINTF(DRAM, "Row buffer hit\n"); 687 dramReadQueue.erase(i); 688 dramReadQueue.push_front(dram_pkt); 689 break; 690 } else { //FCFS part 691 ; 692 } 693 694 } 695 696 } else 697 panic("No scheduling policy chosen!\n"); 698 699 700 DPRINTF(DRAM,"chooseNextReq(): Chosen something, returning True\n"); 701 return true; 702} 703 704void 705SimpleDRAM::accessAndRespond(PacketPtr pkt) 706{ 707 DPRINTF(DRAM, "Responding to Address %lld.. ",pkt->getAddr()); 708 709 bool needsResponse = pkt->needsResponse(); 710 // do the actual memory access which also turns the packet into a 711 // response 712 access(pkt); 713 714 // turn packet around to go back to requester if response expected 715 if (needsResponse) { 716 // access already turned the packet into a response 717 assert(pkt->isResponse()); 718 719 // queue the packet in the response queue to be sent out the 720 // next tick 721 port.schedTimingResp(pkt, curTick() + 1); 722 } else { 723 } 724 725 DPRINTF(DRAM, "Done\n"); 726 727 return; 728} 729 730pair<Tick, Tick> 731SimpleDRAM::estimateLatency(DRAMPacket* dram_pkt, Tick inTime) 732{ 733 // If a request reaches a bank at tick 'inTime', how much time 734 // *after* that does it take to finish the request, depending 735 // on bank status and page open policy. Note that this method 736 // considers only the time taken for the actual read or write 737 // to complete, NOT any additional time thereafter for tRAS or 738 // tRP. 739 Tick accLat = 0; 740 Tick bankLat = 0; 741 rowHitFlag = false; 742 743 const Bank& bank = dram_pkt->bank_ref; 744 if (pageMgmt == Enums::open) { // open-page policy 745 if (bank.openRow == dram_pkt->row) { 746 // When we have a row-buffer hit, 747 // we don't care about tRAS having expired or not, 748 // but do care about bank being free for access 749 rowHitFlag = true; 750 751 if (bank.freeAt < inTime) { 752 // CAS latency only 753 accLat += tCL; 754 bankLat += tCL; 755 } else { 756 accLat += 0; 757 bankLat += 0; 758 } 759 760 } else { 761 // Row-buffer miss, need to close existing row 762 // once tRAS has expired, then open the new one, 763 // then add cas latency. 764 Tick freeTime = std::max(bank.tRASDoneAt, bank.freeAt); 765 766 if (freeTime > inTime) 767 accLat += freeTime - inTime; 768 769 accLat += tRP + tRCD + tCL; 770 bankLat += tRP + tRCD + tCL; 771 } 772 } else if (pageMgmt == Enums::close) { 773 774 // With a close page policy, no notion of 775 // bank.tRASDoneAt 776 if (bank.freeAt > inTime) 777 accLat += bank.freeAt - inTime; 778 779 // page already closed, simply open the row, and 780 // add cas latency 781 accLat += tRCD + tCL; 782 bankLat += tRCD + tCL; 783 } else 784 panic("No page management policy chosen\n"); 785 786 DPRINTF(DRAM, "Returning < %lld, %lld > from estimateLatency()\n", 787 bankLat, accLat); 788 789 return make_pair(bankLat, accLat); 790} 791 792void 793SimpleDRAM::processNextReqEvent() 794{ 795 scheduleNextReq(); 796} 797 798void 799SimpleDRAM::recordActivate(Tick act_tick) 800{ 801 assert(actTicks.size() == activationLimit); 802 803 DPRINTF(DRAM, "Activate at tick %d\n", act_tick); 804 805 // sanity check 806 if (actTicks.back() && (act_tick - actTicks.back()) < tXAW) { 807 panic("Got %d activates in window %d (%d - %d) which is smaller " 808 "than %d\n", activationLimit, act_tick - actTicks.back(), 809 act_tick, actTicks.back(), tXAW); 810 } 811 812 // shift the times used for the book keeping, the last element 813 // (highest index) is the oldest one and hence the lowest value 814 actTicks.pop_back(); 815 816 // record an new activation (in the future) 817 actTicks.push_front(act_tick); 818 819 // cannot activate more than X times in time window tXAW, push the 820 // next one (the X + 1'st activate) to be tXAW away from the 821 // oldest in our window of X 822 if (actTicks.back() && (act_tick - actTicks.back()) < tXAW) { 823 DPRINTF(DRAM, "Enforcing tXAW with X = %d, next activate no earlier " 824 "than %d\n", activationLimit, actTicks.back() + tXAW); 825 for(int i = 0; i < ranksPerChannel; i++) 826 for(int j = 0; j < banksPerRank; j++) 827 // next activate must not happen before end of window 828 banks[i][j].freeAt = std::max(banks[i][j].freeAt, 829 actTicks.back() + tXAW); 830 } 831} 832 833void 834SimpleDRAM::doDRAMAccess(DRAMPacket* dram_pkt) 835{ 836 837 DPRINTF(DRAM, "Timing access to addr %lld, rank/bank/row %d %d %d\n", 838 dram_pkt->addr, dram_pkt->rank, dram_pkt->bank, dram_pkt->row); 839 840 assert(curTick() >= prevdramaccess); 841 prevdramaccess = curTick(); 842 843 // estimate the bank and access latency 844 pair<Tick, Tick> lat = estimateLatency(dram_pkt, curTick()); 845 Tick bankLat = lat.first; 846 Tick accessLat = lat.second; 847 848 // This request was woken up at this time based on a prior call 849 // to estimateLatency(). However, between then and now, both the 850 // accessLatency and/or busBusyUntil may have changed. We need 851 // to correct for that. 852 853 Tick addDelay = (curTick() + accessLat < busBusyUntil) ? 854 busBusyUntil - (curTick() + accessLat) : 0; 855 856 Bank& bank = dram_pkt->bank_ref; 857 858 // Update bank state 859 if (pageMgmt == Enums::open) { 860 bank.openRow = dram_pkt->row; 861 bank.freeAt = curTick() + addDelay + accessLat; 862 // If you activated a new row do to this access, the next access 863 // will have to respect tRAS for this bank. Assume tRAS ~= 3 * tRP. 864 // Also need to account for t_XAW 865 if (!rowHitFlag) { 866 bank.tRASDoneAt = bank.freeAt + tRP; 867 recordActivate(bank.freeAt - tCL - tRCD); //since this is open page, 868 //no tRP by default 869 } 870 } else if (pageMgmt == Enums::close) { // accounting for tRAS also 871 // assuming that tRAS ~= 3 * tRP, and tRC ~= 4 * tRP, as is common 872 // (refer Jacob/Ng/Wang and Micron datasheets) 873 bank.freeAt = curTick() + addDelay + accessLat + tRP + tRP; 874 recordActivate(bank.freeAt - tRP - tRP - tCL - tRCD); //essentially (freeAt - tRC) 875 DPRINTF(DRAM,"doDRAMAccess::bank.freeAt is %lld\n",bank.freeAt); 876 } else 877 panic("No page management policy chosen\n"); 878 879 // Update request parameters 880 dram_pkt->readyTime = curTick() + addDelay + accessLat + tBURST; 881 882 883 DPRINTF(DRAM, "Req %lld: curtick is %lld accessLat is %d " \ 884 "readytime is %lld busbusyuntil is %lld. " \ 885 "Scheduling at readyTime\n", dram_pkt->addr, 886 curTick(), accessLat, dram_pkt->readyTime, busBusyUntil); 887 888 // Make sure requests are not overlapping on the databus 889 assert (dram_pkt->readyTime - busBusyUntil >= tBURST); 890 891 // Update bus state 892 busBusyUntil = dram_pkt->readyTime; 893 894 DPRINTF(DRAM,"Access time is %lld\n", 895 dram_pkt->readyTime - dram_pkt->entryTime); 896 897 // Update stats 898 totMemAccLat += dram_pkt->readyTime - dram_pkt->entryTime; 899 totBankLat += bankLat; 900 totBusLat += tBURST; 901 totQLat += dram_pkt->readyTime - dram_pkt->entryTime - bankLat - tBURST; 902 903 if (rowHitFlag) 904 readRowHits++; 905 906 // At this point we're done dealing with the request 907 // It will be moved to a separate response queue with a 908 // correct readyTime, and eventually be sent back at that 909 //time 910 moveToRespQ(); 911 912 // The absolute soonest you have to start thinking about the 913 // next request is the longest access time that can occur before 914 // busBusyUntil. Assuming you need to meet tRAS, then precharge, 915 // open a new row, and access, it is ~4*tRCD. 916 917 918 Tick newTime = (busBusyUntil > 4 * tRCD) ? 919 std::max(busBusyUntil - 4 * tRCD, curTick()) : 920 curTick(); 921 922 if (!nextReqEvent.scheduled() && !stopReads){ 923 schedule(&nextReqEvent, newTime); 924 } else { 925 if (newTime < nextReqEvent.when()) 926 reschedule(&nextReqEvent, newTime); 927 } 928 929 930} 931 932void 933SimpleDRAM::moveToRespQ() 934{ 935 // Remove from read queue 936 DRAMPacket* dram_pkt = dramReadQueue.front(); 937 dramReadQueue.pop_front(); 938 939 // Insert into response queue sorted by readyTime 940 // It will be sent back to the requestor at its 941 // readyTime 942 if (dramRespQueue.empty()) { 943 dramRespQueue.push_front(dram_pkt); 944 assert(!respondEvent.scheduled()); 945 assert(dram_pkt->readyTime >= curTick()); 946 schedule(&respondEvent, dram_pkt->readyTime); 947 } else { 948 bool done = false; 949 std::list<DRAMPacket*>::iterator i = dramRespQueue.begin(); 950 while (!done && i != dramRespQueue.end()) { 951 if ((*i)->readyTime > dram_pkt->readyTime) { 952 dramRespQueue.insert(i, dram_pkt); 953 done = true; 954 } 955 ++i; 956 } 957 958 if (!done) 959 dramRespQueue.push_back(dram_pkt); 960 961 assert(respondEvent.scheduled()); 962 963 if (dramRespQueue.front()->readyTime < respondEvent.when()) { 964 assert(dramRespQueue.front()->readyTime >= curTick()); 965 reschedule(&respondEvent, dramRespQueue.front()->readyTime); 966 } 967 } 968 969 if (retryRdReq) { 970 retryRdReq = false; 971 port.sendRetry(); 972 } 973} 974 975void 976SimpleDRAM::scheduleNextReq() 977{ 978 DPRINTF(DRAM, "Reached scheduleNextReq()\n"); 979 980 // Figure out which request goes next, and move it to front() 981 if (!chooseNextReq()) { 982 // In the case there is no read request to go next, see if we 983 // are asked to drain, and if so trigger writes, this also 984 // ensures that if we hit the write limit we will do this 985 // multiple times until we are completely drained 986 if (drainManager && !dramWriteQueue.empty() && !writeEvent.scheduled()) 987 triggerWrites(); 988 } else { 989 doDRAMAccess(dramReadQueue.front()); 990 } 991} 992 993Tick 994SimpleDRAM::maxBankFreeAt() const 995{ 996 Tick banksFree = 0; 997 998 for(int i = 0; i < ranksPerChannel; i++) 999 for(int j = 0; j < banksPerRank; j++) 1000 banksFree = std::max(banks[i][j].freeAt, banksFree); 1001 1002 return banksFree; 1003} 1004 1005void 1006SimpleDRAM::processRefreshEvent() 1007{ 1008 DPRINTF(DRAM, "Refreshing at tick %ld\n", curTick()); 1009 1010 Tick banksFree = std::max(curTick(), maxBankFreeAt()) + tRFC; 1011 1012 for(int i = 0; i < ranksPerChannel; i++) 1013 for(int j = 0; j < banksPerRank; j++) 1014 banks[i][j].freeAt = banksFree; 1015 1016 schedule(&refreshEvent, curTick() + tREFI); 1017} 1018 1019void 1020SimpleDRAM::regStats() 1021{ 1022 using namespace Stats; 1023 1024 AbstractMemory::regStats(); 1025 1026 readReqs 1027 .name(name() + ".readReqs") 1028 .desc("Total number of read requests seen"); 1029 1030 writeReqs 1031 .name(name() + ".writeReqs") 1032 .desc("Total number of write requests seen"); 1033 1034 servicedByWrQ 1035 .name(name() + ".servicedByWrQ") 1036 .desc("Number of read reqs serviced by write Q"); 1037 1038 cpuReqs 1039 .name(name() + ".cpureqs") 1040 .desc("Reqs generatd by CPU via cache - shady"); 1041 1042 neitherReadNorWrite 1043 .name(name() + ".neitherReadNorWrite") 1044 .desc("Reqs where no action is needed"); 1045 1046 perBankRdReqs 1047 .init(banksPerRank * ranksPerChannel) 1048 .name(name() + ".perBankRdReqs") 1049 .desc("Track reads on a per bank basis"); 1050 1051 perBankWrReqs 1052 .init(banksPerRank * ranksPerChannel) 1053 .name(name() + ".perBankWrReqs") 1054 .desc("Track writes on a per bank basis"); 1055 1056 avgRdQLen 1057 .name(name() + ".avgRdQLen") 1058 .desc("Average read queue length over time") 1059 .precision(2); 1060 1061 avgWrQLen 1062 .name(name() + ".avgWrQLen") 1063 .desc("Average write queue length over time") 1064 .precision(2); 1065 1066 totQLat 1067 .name(name() + ".totQLat") 1068 .desc("Total cycles spent in queuing delays"); 1069 1070 totBankLat 1071 .name(name() + ".totBankLat") 1072 .desc("Total cycles spent in bank access"); 1073 1074 totBusLat 1075 .name(name() + ".totBusLat") 1076 .desc("Total cycles spent in databus access"); 1077 1078 totMemAccLat 1079 .name(name() + ".totMemAccLat") 1080 .desc("Sum of mem lat for all requests"); 1081 1082 avgQLat 1083 .name(name() + ".avgQLat") 1084 .desc("Average queueing delay per request") 1085 .precision(2); 1086 1087 avgQLat = totQLat / (readReqs - servicedByWrQ); 1088 1089 avgBankLat 1090 .name(name() + ".avgBankLat") 1091 .desc("Average bank access latency per request") 1092 .precision(2); 1093 1094 avgBankLat = totBankLat / (readReqs - servicedByWrQ); 1095 1096 avgBusLat 1097 .name(name() + ".avgBusLat") 1098 .desc("Average bus latency per request") 1099 .precision(2); 1100 1101 avgBusLat = totBusLat / (readReqs - servicedByWrQ); 1102 1103 avgMemAccLat 1104 .name(name() + ".avgMemAccLat") 1105 .desc("Average memory access latency") 1106 .precision(2); 1107 1108 avgMemAccLat = totMemAccLat / (readReqs - servicedByWrQ); 1109 1110 numRdRetry 1111 .name(name() + ".numRdRetry") 1112 .desc("Number of times rd buffer was full causing retry"); 1113 1114 numWrRetry 1115 .name(name() + ".numWrRetry") 1116 .desc("Number of times wr buffer was full causing retry"); 1117 1118 readRowHits 1119 .name(name() + ".readRowHits") 1120 .desc("Number of row buffer hits during reads"); 1121 1122 writeRowHits 1123 .name(name() + ".writeRowHits") 1124 .desc("Number of row buffer hits during writes"); 1125 1126 readRowHitRate 1127 .name(name() + ".readRowHitRate") 1128 .desc("Row buffer hit rate for reads") 1129 .precision(2); 1130 1131 readRowHitRate = (readRowHits / (readReqs - servicedByWrQ)) * 100; 1132 1133 writeRowHitRate 1134 .name(name() + ".writeRowHitRate") 1135 .desc("Row buffer hit rate for writes") 1136 .precision(2); 1137 1138 writeRowHitRate = (writeRowHits / writeReqs) * 100; 1139 1140 readPktSize 1141 .init(log2(bytesPerCacheLine)+3) 1142 .name(name() + ".readPktSize") 1143 .desc("Categorize read packet sizes"); 1144 1145 writePktSize 1146 .init(log2(bytesPerCacheLine)+3) 1147 .name(name() + ".writePktSize") 1148 .desc("categorize write packet sizes"); 1149 1150 neitherPktSize 1151 .init(log2(bytesPerCacheLine)+3) 1152 .name(name() + ".neitherpktsize") 1153 .desc("categorize neither packet sizes"); 1154 1155 rdQLenPdf 1156 .init(readBufferSize + 1) 1157 .name(name() + ".rdQLenPdf") 1158 .desc("What read queue length does an incoming req see"); 1159 1160 wrQLenPdf 1161 .init(writeBufferSize + 1) 1162 .name(name() + ".wrQLenPdf") 1163 .desc("What write queue length does an incoming req see"); 1164 1165 1166 bytesRead 1167 .name(name() + ".bytesRead") 1168 .desc("Total number of bytes read from memory"); 1169 1170 bytesWritten 1171 .name(name() + ".bytesWritten") 1172 .desc("Total number of bytes written to memory"); 1173 1174 bytesConsumedRd 1175 .name(name() + ".bytesConsumedRd") 1176 .desc("bytesRead derated as per pkt->getSize()"); 1177 1178 bytesConsumedWr 1179 .name(name() + ".bytesConsumedWr") 1180 .desc("bytesWritten derated as per pkt->getSize()"); 1181 1182 avgRdBW 1183 .name(name() + ".avgRdBW") 1184 .desc("Average achieved read bandwidth in MB/s") 1185 .precision(2); 1186 1187 avgRdBW = (bytesRead / 1000000) / simSeconds; 1188 1189 avgWrBW 1190 .name(name() + ".avgWrBW") 1191 .desc("Average achieved write bandwidth in MB/s") 1192 .precision(2); 1193 1194 avgWrBW = (bytesWritten / 1000000) / simSeconds; 1195 1196 avgConsumedRdBW 1197 .name(name() + ".avgConsumedRdBW") 1198 .desc("Average consumed read bandwidth in MB/s") 1199 .precision(2); 1200 1201 avgConsumedRdBW = (bytesConsumedRd / 1000000) / simSeconds; 1202 1203 avgConsumedWrBW 1204 .name(name() + ".avgConsumedWrBW") 1205 .desc("Average consumed write bandwidth in MB/s") 1206 .precision(2); 1207 1208 avgConsumedWrBW = (bytesConsumedWr / 1000000) / simSeconds; 1209 1210 peakBW 1211 .name(name() + ".peakBW") 1212 .desc("Theoretical peak bandwidth in MB/s") 1213 .precision(2); 1214 1215 peakBW = (SimClock::Frequency / tBURST) * bytesPerCacheLine / 1000000; 1216 1217 busUtil 1218 .name(name() + ".busUtil") 1219 .desc("Data bus utilization in percentage") 1220 .precision(2); 1221 1222 busUtil = (avgRdBW + avgWrBW) / peakBW * 100; 1223 1224 totGap 1225 .name(name() + ".totGap") 1226 .desc("Total gap between requests"); 1227 1228 avgGap 1229 .name(name() + ".avgGap") 1230 .desc("Average gap between requests") 1231 .precision(2); 1232 1233 avgGap = totGap / (readReqs + writeReqs); 1234} 1235 1236void 1237SimpleDRAM::recvFunctional(PacketPtr pkt) 1238{ 1239 // rely on the abstract memory 1240 functionalAccess(pkt); 1241} 1242 1243BaseSlavePort& 1244SimpleDRAM::getSlavePort(const string &if_name, PortID idx) 1245{ 1246 if (if_name != "port") { 1247 return MemObject::getSlavePort(if_name, idx); 1248 } else { 1249 return port; 1250 } 1251} 1252 1253unsigned int 1254SimpleDRAM::drain(DrainManager *dm) 1255{ 1256 unsigned int count = port.drain(dm); 1257 1258 // if there is anything in any of our internal queues, keep track 1259 // of that as well 1260 if (!(dramWriteQueue.empty() && dramReadQueue.empty() && 1261 dramRespQueue.empty())) { 1262 DPRINTF(Drain, "DRAM controller not drained, write: %d, read: %d," 1263 " resp: %d\n", dramWriteQueue.size(), dramReadQueue.size(), 1264 dramRespQueue.size()); 1265 ++count; 1266 drainManager = dm; 1267 // the only part that is not drained automatically over time 1268 // is the write queue, thus trigger writes if there are any 1269 // waiting and no reads waiting, otherwise wait until the 1270 // reads are done 1271 if (dramReadQueue.empty() && !dramWriteQueue.empty() && 1272 !writeEvent.scheduled()) 1273 triggerWrites(); 1274 } 1275 1276 if (count) 1277 setDrainState(Drainable::Draining); 1278 else 1279 setDrainState(Drainable::Drained); 1280 return count; 1281} 1282 1283SimpleDRAM::MemoryPort::MemoryPort(const std::string& name, SimpleDRAM& _memory) 1284 : QueuedSlavePort(name, &_memory, queue), queue(_memory, *this), 1285 memory(_memory) 1286{ } 1287 1288AddrRangeList 1289SimpleDRAM::MemoryPort::getAddrRanges() const 1290{ 1291 AddrRangeList ranges; 1292 ranges.push_back(memory.getAddrRange()); 1293 return ranges; 1294} 1295 1296void 1297SimpleDRAM::MemoryPort::recvFunctional(PacketPtr pkt) 1298{ 1299 pkt->pushLabel(memory.name()); 1300 1301 if (!queue.checkFunctional(pkt)) { 1302 // Default implementation of SimpleTimingPort::recvFunctional() 1303 // calls recvAtomic() and throws away the latency; we can save a 1304 // little here by just not calculating the latency. 1305 memory.recvFunctional(pkt); 1306 } 1307 1308 pkt->popLabel(); 1309} 1310 1311Tick 1312SimpleDRAM::MemoryPort::recvAtomic(PacketPtr pkt) 1313{ 1314 return memory.recvAtomic(pkt); 1315} 1316 1317bool 1318SimpleDRAM::MemoryPort::recvTimingReq(PacketPtr pkt) 1319{ 1320 // pass it to the memory controller 1321 return memory.recvTimingReq(pkt); 1322} 1323 1324SimpleDRAM* 1325SimpleDRAMParams::create() 1326{ 1327 return new SimpleDRAM(this); 1328} 1329