dram_ctrl.cc revision 9833
1360SN/A/* 21458SN/A * Copyright (c) 2010-2013 ARM Limited 3360SN/A * All rights reserved 4360SN/A * 5360SN/A * The license below extends only to copyright in the software and shall 6360SN/A * not be construed as granting a license to any other intellectual 7360SN/A * property including but not limited to intellectual property relating 8360SN/A * to a hardware implementation of the functionality of the software 9360SN/A * licensed hereunder. You may use the software subject to the license 10360SN/A * terms below provided that you ensure that this notice is replicated 11360SN/A * unmodified and in its entirety in all distributions of the software, 12360SN/A * modified or unmodified, in source code or in binary form. 13360SN/A * 14360SN/A * Copyright (c) 2013 Amin Farmahini-Farahani 15360SN/A * All rights reserved. 16360SN/A * 17360SN/A * Redistribution and use in source and binary forms, with or without 18360SN/A * modification, are permitted provided that the following conditions are 19360SN/A * met: redistributions of source code must retain the above copyright 20360SN/A * notice, this list of conditions and the following disclaimer; 21360SN/A * redistributions in binary form must reproduce the above copyright 22360SN/A * notice, this list of conditions and the following disclaimer in the 23360SN/A * documentation and/or other materials provided with the distribution; 24360SN/A * neither the name of the copyright holders nor the names of its 25360SN/A * contributors may be used to endorse or promote products derived from 26360SN/A * this software without specific prior written permission. 272665Ssaidi@eecs.umich.edu * 282665Ssaidi@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 292665Ssaidi@eecs.umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30360SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31360SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 321354SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 331354SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34360SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 352764Sstever@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 362764Sstever@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 372064SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38360SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39360SN/A * 40360SN/A * Authors: Andreas Hansson 41360SN/A * Ani Udipi 42360SN/A */ 43360SN/A 441809SN/A#include "base/trace.hh" 455543Ssaidi@eecs.umich.edu#include "debug/Drain.hh" 461809SN/A#include "debug/DRAM.hh" 473113Sgblack@eecs.umich.edu#include "debug/DRAMWR.hh" 487075Snate@binkert.org#include "mem/simple_dram.hh" 493113Sgblack@eecs.umich.edu#include "sim/system.hh" 501999SN/A 517075Snate@binkert.orgusing namespace std; 527075Snate@binkert.org 537075Snate@binkert.orgSimpleDRAM::SimpleDRAM(const SimpleDRAMParams* p) : 54360SN/A AbstractMemory(p), 552474SN/A port(name() + ".port", *this), 565543Ssaidi@eecs.umich.edu retryRdReq(false), retryWrReq(false), 572462SN/A rowHitFlag(false), stopReads(false), actTicks(p->activation_limit, 0), 581354SN/A writeEvent(this), respondEvent(this), 596216Snate@binkert.org refreshEvent(this), nextReqEvent(this), drainManager(NULL), 606658Snate@binkert.org deviceBusWidth(p->device_bus_width), burstLength(p->burst_length), 612474SN/A deviceRowBufferSize(p->device_rowbuffer_size), 622680Sktlim@umich.edu devicesPerRank(p->devices_per_rank), 632474SN/A burstSize((devicesPerRank * burstLength * deviceBusWidth) / 8), 642474SN/A rowBufferSize(devicesPerRank * deviceRowBufferSize), 656640Svince@csl.cornell.edu ranksPerChannel(p->ranks_per_channel), 661354SN/A banksPerRank(p->banks_per_rank), channels(p->channels), rowsPerBank(0), 67360SN/A readBufferSize(p->read_buffer_size), 68360SN/A writeBufferSize(p->write_buffer_size), 69360SN/A writeThresholdPerc(p->write_thresh_perc), 70360SN/A tWTR(p->tWTR), tBURST(p->tBURST), 71360SN/A tRCD(p->tRCD), tCL(p->tCL), tRP(p->tRP), 72360SN/A tRFC(p->tRFC), tREFI(p->tREFI), 73360SN/A tXAW(p->tXAW), activationLimit(p->activation_limit), 74360SN/A memSchedPolicy(p->mem_sched_policy), addrMapping(p->addr_mapping), 75378SN/A pageMgmt(p->page_policy), 761450SN/A frontendLatency(p->static_frontend_latency), 773114Sgblack@eecs.umich.edu backendLatency(p->static_backend_latency), 78360SN/A busBusyUntil(0), writeStartTime(0), 795543Ssaidi@eecs.umich.edu prevArrival(0), numReqs(0) 805543Ssaidi@eecs.umich.edu{ 815543Ssaidi@eecs.umich.edu // create the bank states based on the dimensions of the ranks and 82360SN/A // banks 83360SN/A banks.resize(ranksPerChannel); 84360SN/A for (size_t c = 0; c < ranksPerChannel; ++c) { 85360SN/A banks[c].resize(banksPerRank); 86360SN/A } 872680Sktlim@umich.edu 88360SN/A // round the write threshold percent to a whole number of entries 89360SN/A // in the buffer 90360SN/A writeThreshold = writeBufferSize * writeThresholdPerc / 100.0; 91360SN/A} 92360SN/A 93360SN/Avoid 94360SN/ASimpleDRAM::init() 95360SN/A{ 96360SN/A if (!port.isConnected()) { 97360SN/A fatal("SimpleDRAM %s is unconnected!\n", name()); 98360SN/A } else { 993114Sgblack@eecs.umich.edu port.sendRangeChange(); 100360SN/A } 101360SN/A 102360SN/A // we could deal with plenty options here, but for now do a quick 103360SN/A // sanity check 104360SN/A DPRINTF(DRAM, "Burst size %d bytes\n", burstSize); 105360SN/A 106360SN/A // determine the rows per bank by looking at the total capacity 107360SN/A uint64_t capacity = ULL(1) << ceilLog2(AbstractMemory::size()); 108360SN/A 109360SN/A DPRINTF(DRAM, "Memory capacity %lld (%lld) bytes\n", capacity, 110360SN/A AbstractMemory::size()); 111360SN/A 112360SN/A columnsPerRowBuffer = rowBufferSize / burstSize; 113360SN/A 114360SN/A DPRINTF(DRAM, "Row buffer size %d bytes with %d columns per row buffer\n", 115360SN/A rowBufferSize, columnsPerRowBuffer); 116360SN/A 117360SN/A rowsPerBank = capacity / (rowBufferSize * banksPerRank * ranksPerChannel); 118360SN/A 119360SN/A if (range.interleaved()) { 120360SN/A if (channels != range.stripes()) 1212400SN/A panic("%s has %d interleaved address stripes but %d channel(s)\n", 122360SN/A name(), range.stripes(), channels); 1232461SN/A 1245543Ssaidi@eecs.umich.edu if (addrMapping == Enums::RaBaChCo) { 125360SN/A if (rowBufferSize != range.granularity()) { 126360SN/A panic("Interleaving of %s doesn't match RaBaChCo address map\n", 127360SN/A name()); 128360SN/A } 129360SN/A } else if (addrMapping == Enums::RaBaCoCh) { 1302400SN/A if (burstSize != range.granularity()) { 131360SN/A panic("Interleaving of %s doesn't match RaBaCoCh address map\n", 1322461SN/A name()); 1335543Ssaidi@eecs.umich.edu } 134360SN/A } else if (addrMapping == Enums::CoRaBaCh) { 135360SN/A if (burstSize != range.granularity()) 136360SN/A panic("Interleaving of %s doesn't match CoRaBaCh address map\n", 137360SN/A name()); 138360SN/A } 139360SN/A } 140360SN/A} 141360SN/A 142360SN/Avoid 143360SN/ASimpleDRAM::startup() 144360SN/A{ 145360SN/A // print the configuration of the controller 146360SN/A printParams(); 1475543Ssaidi@eecs.umich.edu 148360SN/A // kick off the refresh 149360SN/A schedule(refreshEvent, curTick() + tREFI); 150360SN/A} 151360SN/A 152360SN/ATick 153360SN/ASimpleDRAM::recvAtomic(PacketPtr pkt) 154360SN/A{ 155360SN/A DPRINTF(DRAM, "recvAtomic: %s 0x%x\n", pkt->cmdString(), pkt->getAddr()); 156360SN/A 157360SN/A // do the actual memory access and turn the packet into a response 158360SN/A access(pkt); 159360SN/A 160360SN/A Tick latency = 0; 161360SN/A if (!pkt->memInhibitAsserted() && pkt->hasData()) { 162360SN/A // this value is not supposed to be accurate, just enough to 163360SN/A // keep things going, mimic a closed page 164360SN/A latency = tRP + tRCD + tCL; 1655543Ssaidi@eecs.umich.edu } 1665543Ssaidi@eecs.umich.edu return latency; 167502SN/A} 168360SN/A 169360SN/Abool 170360SN/ASimpleDRAM::readQueueFull(unsigned int neededEntries) const 171360SN/A{ 172360SN/A DPRINTF(DRAM, "Read queue limit %d, current size %d, entries needed %d\n", 173360SN/A readBufferSize, readQueue.size() + respQueue.size(), 174360SN/A neededEntries); 175360SN/A 176360SN/A return 177360SN/A (readQueue.size() + respQueue.size() + neededEntries) > readBufferSize; 178360SN/A} 179378SN/A 1801706SN/Abool 1813114Sgblack@eecs.umich.eduSimpleDRAM::writeQueueFull(unsigned int neededEntries) const 182378SN/A{ 183378SN/A DPRINTF(DRAM, "Write queue limit %d, current size %d, entries needed %d\n", 184378SN/A writeBufferSize, writeQueue.size(), neededEntries); 185378SN/A return (writeQueue.size() + neededEntries) > writeBufferSize; 186378SN/A} 1871706SN/A 1883114Sgblack@eecs.umich.eduSimpleDRAM::DRAMPacket* 189360SN/ASimpleDRAM::decodeAddr(PacketPtr pkt, Addr dramPktAddr, unsigned size) 1906109Ssanchezd@stanford.edu{ 1911706SN/A // decode the address based on the address mapping scheme, with 1923114Sgblack@eecs.umich.edu // Ra, Co, Ba and Ch denoting rank, column, bank and channel, 193378SN/A // respectively 1946109Ssanchezd@stanford.edu uint8_t rank; 1956109Ssanchezd@stanford.edu uint16_t bank; 1966109Ssanchezd@stanford.edu uint16_t row; 1976109Ssanchezd@stanford.edu 198378SN/A // truncate the address to the access granularity 1991706SN/A Addr addr = dramPktAddr / burstSize; 2003114Sgblack@eecs.umich.edu 201378SN/A // we have removed the lowest order address bits that denote the 2025748SSteve.Reinhardt@amd.com // position within the column 2035748SSteve.Reinhardt@amd.com if (addrMapping == Enums::RaBaChCo) { 2045748SSteve.Reinhardt@amd.com // the lowest order bits denote the column to ensure that 205378SN/A // sequential cache lines occupy the same row 206378SN/A addr = addr / columnsPerRowBuffer; 2071706SN/A 2083114Sgblack@eecs.umich.edu // take out the channel part of the address 209378SN/A addr = addr / channels; 210378SN/A 2111706SN/A // after the channel bits, get the bank bits to interleave 2123114Sgblack@eecs.umich.edu // over the banks 213378SN/A bank = addr % banksPerRank; 214378SN/A addr = addr / banksPerRank; 2151706SN/A 2163114Sgblack@eecs.umich.edu // after the bank, we get the rank bits which thus interleaves 217378SN/A // over the ranks 218378SN/A rank = addr % ranksPerChannel; 2191706SN/A addr = addr / ranksPerChannel; 2203114Sgblack@eecs.umich.edu 221378SN/A // lastly, get the row bits 2224118Sgblack@eecs.umich.edu row = addr % rowsPerBank; 2234118Sgblack@eecs.umich.edu addr = addr / rowsPerBank; 2244118Sgblack@eecs.umich.edu } else if (addrMapping == Enums::RaBaCoCh) { 2254118Sgblack@eecs.umich.edu // take out the channel part of the address 226378SN/A addr = addr / channels; 2271706SN/A 2283114Sgblack@eecs.umich.edu // next, the column 229378SN/A addr = addr / columnsPerRowBuffer; 230378SN/A 2311706SN/A // after the column bits, we get the bank bits to interleave 2323114Sgblack@eecs.umich.edu // over the banks 233360SN/A bank = addr % banksPerRank; 2345513SMichael.Adler@intel.com addr = addr / banksPerRank; 2355513SMichael.Adler@intel.com 2365513SMichael.Adler@intel.com // after the bank, we get the rank bits which thus interleaves 2375513SMichael.Adler@intel.com // over the ranks 2385513SMichael.Adler@intel.com rank = addr % ranksPerChannel; 2395513SMichael.Adler@intel.com addr = addr / ranksPerChannel; 2405513SMichael.Adler@intel.com 2415513SMichael.Adler@intel.com // lastly, get the row bits 242511SN/A row = addr % rowsPerBank; 2431706SN/A addr = addr / rowsPerBank; 2443114Sgblack@eecs.umich.edu } else if (addrMapping == Enums::CoRaBaCh) { 245511SN/A // optimise for closed page mode and utilise maximum 2465513SMichael.Adler@intel.com // parallelism of the DRAM (at the cost of power) 2475513SMichael.Adler@intel.com 2485513SMichael.Adler@intel.com // take out the channel part of the address, not that this has 2495513SMichael.Adler@intel.com // to match with how accesses are interleaved between the 250511SN/A // controllers in the address mapping 2511706SN/A addr = addr / channels; 2523114Sgblack@eecs.umich.edu 2531706SN/A // start with the bank bits, as this provides the maximum 2541706SN/A // opportunity for parallelism between requests 2551706SN/A bank = addr % banksPerRank; 2561706SN/A addr = addr / banksPerRank; 2573114Sgblack@eecs.umich.edu 2581706SN/A // next get the rank bits 2591706SN/A rank = addr % ranksPerChannel; 2601706SN/A addr = addr / ranksPerChannel; 2611706SN/A 2623114Sgblack@eecs.umich.edu // next the column bits which we do not need to keep track of 2631706SN/A // and simply skip past 264511SN/A addr = addr / columnsPerRowBuffer; 2656703Svince@csl.cornell.edu 2666703Svince@csl.cornell.edu // lastly, get the row bits 2676703Svince@csl.cornell.edu row = addr % rowsPerBank; 2686703Svince@csl.cornell.edu addr = addr / rowsPerBank; 2696685Stjones1@inf.ed.ac.uk } else 2706685Stjones1@inf.ed.ac.uk panic("Unknown address mapping policy chosen!"); 2716685Stjones1@inf.ed.ac.uk 2726685Stjones1@inf.ed.ac.uk assert(rank < ranksPerChannel); 2736685Stjones1@inf.ed.ac.uk assert(bank < banksPerRank); 2745513SMichael.Adler@intel.com assert(row < rowsPerBank); 2755513SMichael.Adler@intel.com 2765513SMichael.Adler@intel.com DPRINTF(DRAM, "Address: %lld Rank %d Bank %d Row %d\n", 2775513SMichael.Adler@intel.com dramPktAddr, rank, bank, row); 2785513SMichael.Adler@intel.com 2791999SN/A // create the corresponding DRAM packet with the entry time and 2801999SN/A // ready time set to the current tick, the latter will be updated 2813114Sgblack@eecs.umich.edu // later 2821999SN/A return new DRAMPacket(pkt, rank, bank, row, dramPktAddr, size, 2831999SN/A banks[rank][bank]); 2841999SN/A} 2851999SN/A 2863114Sgblack@eecs.umich.eduvoid 2871999SN/ASimpleDRAM::addToReadQueue(PacketPtr pkt, unsigned int pktCount) 2883079Sstever@eecs.umich.edu{ 2893079Sstever@eecs.umich.edu // only add to the read queue here. whenever the request is 2903114Sgblack@eecs.umich.edu // eventually done, set the readyTime, and call schedule() 2913079Sstever@eecs.umich.edu assert(!pkt->isWrite()); 2922093SN/A 2932093SN/A assert(pktCount != 0); 2943114Sgblack@eecs.umich.edu 2952093SN/A // if the request size is larger than burst size, the pkt is split into 2962687Sksewell@umich.edu // multiple DRAM packets 2972687Sksewell@umich.edu // Note if the pkt starting address is not aligened to burst size, the 2983114Sgblack@eecs.umich.edu // address of first DRAM packet is kept unaliged. Subsequent DRAM packets 2992687Sksewell@umich.edu // are aligned to burst size boundaries. This is to ensure we accurately 3002238SN/A // check read packets against packets in write queue. 3012238SN/A Addr addr = pkt->getAddr(); 3023114Sgblack@eecs.umich.edu unsigned pktsServicedByWrQ = 0; 3032238SN/A BurstHelper* burst_helper = NULL; 3042238SN/A for (int cnt = 0; cnt < pktCount; ++cnt) { 3052238SN/A unsigned size = std::min((addr | (burstSize - 1)) + 1, 3063114Sgblack@eecs.umich.edu pkt->getAddr() + pkt->getSize()) - addr; 3072238SN/A readPktSize[ceilLog2(size)]++; 3082238SN/A readBursts++; 3092238SN/A 3103114Sgblack@eecs.umich.edu // First check write buffer to see if the data is already at 3112238SN/A // the controller 3122238SN/A bool foundInWrQ = false; 3132238SN/A for (auto i = writeQueue.begin(); i != writeQueue.end(); ++i) { 3143114Sgblack@eecs.umich.edu // check if the read is subsumed in the write entry we are 3152238SN/A // looking at 3162238SN/A if ((*i)->addr <= addr && 3172238SN/A (addr + size) <= ((*i)->addr + (*i)->size)) { 3183114Sgblack@eecs.umich.edu foundInWrQ = true; 3192238SN/A servicedByWrQ++; 3202238SN/A pktsServicedByWrQ++; 3212238SN/A DPRINTF(DRAM, "Read to addr %lld with size %d serviced by " 3223114Sgblack@eecs.umich.edu "write queue\n", addr, size); 3232238SN/A bytesRead += burstSize; 3242238SN/A bytesConsumedRd += size; 3252238SN/A break; 3263114Sgblack@eecs.umich.edu } 3272238SN/A } 3286109Ssanchezd@stanford.edu 3296109Ssanchezd@stanford.edu // If not found in the write q, make a DRAM packet and 3306109Ssanchezd@stanford.edu // push it onto the read queue 3312238SN/A if (!foundInWrQ) { 3322238SN/A 3332238SN/A // Make the burst helper for split packets 3342238SN/A if (pktCount > 1 && burst_helper == NULL) { 3352238SN/A DPRINTF(DRAM, "Read to addr %lld translates to %d " 3363114Sgblack@eecs.umich.edu "dram requests\n", pkt->getAddr(), pktCount); 3372238SN/A burst_helper = new BurstHelper(pktCount); 3382238SN/A } 3392238SN/A 3403114Sgblack@eecs.umich.edu DRAMPacket* dram_pkt = decodeAddr(pkt, addr, size); 3412238SN/A dram_pkt->burstHelper = burst_helper; 3422238SN/A 3432238SN/A assert(!readQueueFull(1)); 3443114Sgblack@eecs.umich.edu rdQLenPdf[readQueue.size() + respQueue.size()]++; 3452238SN/A 3462238SN/A DPRINTF(DRAM, "Adding to read queue\n"); 3472238SN/A 3483114Sgblack@eecs.umich.edu readQueue.push_back(dram_pkt); 3492238SN/A 3502238SN/A // Update stats 3511354SN/A uint32_t bank_id = banksPerRank * dram_pkt->rank + dram_pkt->bank; 3521354SN/A assert(bank_id < ranksPerChannel * banksPerRank); 3531354SN/A perBankRdReqs[bank_id]++; 3541354SN/A 3551354SN/A avgRdQLen = readQueue.size() + respQueue.size(); 3561354SN/A } 3571354SN/A 3581354SN/A // Starting address of next dram pkt (aligend to burstSize boundary) 3591354SN/A addr = (addr | (burstSize - 1)) + 1; 3601354SN/A } 3611354SN/A 3621354SN/A // If all packets are serviced by write queue, we send the repsonse back 3631354SN/A if (pktsServicedByWrQ == pktCount) { 3641354SN/A accessAndRespond(pkt, frontendLatency); 3657064Snate@binkert.org return; 3661354SN/A } 3671354SN/A 3681354SN/A // Update how many split packets are serviced by write queue 3691354SN/A if (burst_helper != NULL) 370360SN/A burst_helper->burstsServiced = pktsServicedByWrQ; 371360SN/A 372360SN/A // If we are not already scheduled to get the read request out of 373360SN/A // the queue, do so now 374360SN/A if (!nextReqEvent.scheduled() && !stopReads) { 375360SN/A DPRINTF(DRAM, "Request scheduled immediately\n"); 376360SN/A schedule(nextReqEvent, curTick()); 3773113Sgblack@eecs.umich.edu } 3783113Sgblack@eecs.umich.edu} 3793113Sgblack@eecs.umich.edu 3803113Sgblack@eecs.umich.eduvoid 3813113Sgblack@eecs.umich.eduSimpleDRAM::processWriteEvent() 3823113Sgblack@eecs.umich.edu{ 3833113Sgblack@eecs.umich.edu assert(!writeQueue.empty()); 3843113Sgblack@eecs.umich.edu uint32_t numWritesThisTime = 0; 3853113Sgblack@eecs.umich.edu 3863113Sgblack@eecs.umich.edu DPRINTF(DRAMWR, "Beginning DRAM Writes\n"); 3873113Sgblack@eecs.umich.edu Tick temp1 M5_VAR_USED = std::max(curTick(), busBusyUntil); 3883113Sgblack@eecs.umich.edu Tick temp2 M5_VAR_USED = std::max(curTick(), maxBankFreeAt()); 3893113Sgblack@eecs.umich.edu 3903113Sgblack@eecs.umich.edu // @todo: are there any dangers with the untimed while loop? 3913113Sgblack@eecs.umich.edu while (!writeQueue.empty()) { 3923113Sgblack@eecs.umich.edu if (numWritesThisTime > writeThreshold) { 3934189Sgblack@eecs.umich.edu DPRINTF(DRAMWR, "Hit write threshold %d\n", writeThreshold); 3944189Sgblack@eecs.umich.edu break; 3953113Sgblack@eecs.umich.edu } 3963113Sgblack@eecs.umich.edu 3973113Sgblack@eecs.umich.edu chooseNextWrite(); 3983113Sgblack@eecs.umich.edu DRAMPacket* dram_pkt = writeQueue.front(); 3993113Sgblack@eecs.umich.edu 4003113Sgblack@eecs.umich.edu // sanity check 4013113Sgblack@eecs.umich.edu assert(dram_pkt->size <= burstSize); 4023277Sgblack@eecs.umich.edu 4035515SMichael.Adler@intel.com // What's the earliest the request can be put on the bus 4045515SMichael.Adler@intel.com Tick schedTime = std::max(curTick(), busBusyUntil); 4055515SMichael.Adler@intel.com 4065515SMichael.Adler@intel.com DPRINTF(DRAMWR, "Asking for latency estimate at %lld\n", 4075515SMichael.Adler@intel.com schedTime + tBURST); 4083277Sgblack@eecs.umich.edu 4093277Sgblack@eecs.umich.edu pair<Tick, Tick> lat = estimateLatency(dram_pkt, schedTime + tBURST); 4103277Sgblack@eecs.umich.edu Tick accessLat = lat.second; 4113277Sgblack@eecs.umich.edu 4123277Sgblack@eecs.umich.edu // look at the rowHitFlag set by estimateLatency 4133277Sgblack@eecs.umich.edu if (rowHitFlag) 4143277Sgblack@eecs.umich.edu writeRowHits++; 4153113Sgblack@eecs.umich.edu 4163113Sgblack@eecs.umich.edu Bank& bank = dram_pkt->bank_ref; 4173113Sgblack@eecs.umich.edu 4183113Sgblack@eecs.umich.edu if (pageMgmt == Enums::open) { 4193113Sgblack@eecs.umich.edu bank.openRow = dram_pkt->row; 4203113Sgblack@eecs.umich.edu bank.freeAt = schedTime + tBURST + std::max(accessLat, tCL); 4213113Sgblack@eecs.umich.edu busBusyUntil = bank.freeAt - tCL; 4223114Sgblack@eecs.umich.edu bank.bytesAccessed += burstSize; 4233113Sgblack@eecs.umich.edu 4243114Sgblack@eecs.umich.edu if (!rowHitFlag) { 4253113Sgblack@eecs.umich.edu bank.tRASDoneAt = bank.freeAt + tRP; 4263114Sgblack@eecs.umich.edu recordActivate(bank.freeAt - tCL - tRCD); 4273113Sgblack@eecs.umich.edu busBusyUntil = bank.freeAt - tCL - tRCD; 4284061Sgblack@eecs.umich.edu 4294061Sgblack@eecs.umich.edu // sample the number of bytes accessed and reset it as 4304061Sgblack@eecs.umich.edu // we are now closing this row 4313113Sgblack@eecs.umich.edu bytesPerActivate.sample(bank.bytesAccessed); 4323113Sgblack@eecs.umich.edu bank.bytesAccessed = 0; 4333113Sgblack@eecs.umich.edu } 4343113Sgblack@eecs.umich.edu } else if (pageMgmt == Enums::close) { 4353113Sgblack@eecs.umich.edu bank.freeAt = schedTime + tBURST + accessLat + tRP + tRP; 4363113Sgblack@eecs.umich.edu // Work backwards from bank.freeAt to determine activate time 4373113Sgblack@eecs.umich.edu recordActivate(bank.freeAt - tRP - tRP - tCL - tRCD); 4383113Sgblack@eecs.umich.edu busBusyUntil = bank.freeAt - tRP - tRP - tCL - tRCD; 4393113Sgblack@eecs.umich.edu DPRINTF(DRAMWR, "processWriteEvent::bank.freeAt for " 4403113Sgblack@eecs.umich.edu "banks_id %d is %lld\n", 4413113Sgblack@eecs.umich.edu dram_pkt->rank * banksPerRank + dram_pkt->bank, 4424189Sgblack@eecs.umich.edu bank.freeAt); 4434189Sgblack@eecs.umich.edu bytesPerActivate.sample(burstSize); 4443113Sgblack@eecs.umich.edu } else 4453113Sgblack@eecs.umich.edu panic("Unknown page management policy chosen\n"); 4463113Sgblack@eecs.umich.edu 4473113Sgblack@eecs.umich.edu DPRINTF(DRAMWR, "Done writing to address %lld\n", dram_pkt->addr); 4483113Sgblack@eecs.umich.edu 4493113Sgblack@eecs.umich.edu DPRINTF(DRAMWR, "schedtime is %lld, tBURST is %lld, " 4503113Sgblack@eecs.umich.edu "busbusyuntil is %lld\n", 4513113Sgblack@eecs.umich.edu schedTime, tBURST, busBusyUntil); 4523113Sgblack@eecs.umich.edu 4533113Sgblack@eecs.umich.edu writeQueue.pop_front(); 4543113Sgblack@eecs.umich.edu delete dram_pkt; 4553113Sgblack@eecs.umich.edu 4563113Sgblack@eecs.umich.edu numWritesThisTime++; 4573113Sgblack@eecs.umich.edu } 4583113Sgblack@eecs.umich.edu 4593113Sgblack@eecs.umich.edu DPRINTF(DRAMWR, "Completed %d writes, bus busy for %lld ticks,"\ 4603113Sgblack@eecs.umich.edu "banks busy for %lld ticks\n", numWritesThisTime, 4613113Sgblack@eecs.umich.edu busBusyUntil - temp1, maxBankFreeAt() - temp2); 4623113Sgblack@eecs.umich.edu 4633113Sgblack@eecs.umich.edu // Update stats 4643113Sgblack@eecs.umich.edu avgWrQLen = writeQueue.size(); 4653113Sgblack@eecs.umich.edu 4663113Sgblack@eecs.umich.edu // turn the bus back around for reads again 4673113Sgblack@eecs.umich.edu busBusyUntil += tWTR; 4683113Sgblack@eecs.umich.edu stopReads = false; 4693113Sgblack@eecs.umich.edu 4703113Sgblack@eecs.umich.edu if (retryWrReq) { 4713113Sgblack@eecs.umich.edu retryWrReq = false; 4723113Sgblack@eecs.umich.edu port.sendRetry(); 4733113Sgblack@eecs.umich.edu } 4743113Sgblack@eecs.umich.edu 4753113Sgblack@eecs.umich.edu // if there is nothing left in any queue, signal a drain 4763113Sgblack@eecs.umich.edu if (writeQueue.empty() && readQueue.empty() && 4773113Sgblack@eecs.umich.edu respQueue.empty () && drainManager) { 4786686Stjones1@inf.ed.ac.uk drainManager->signalDrainDone(); 4793113Sgblack@eecs.umich.edu drainManager = NULL; 4803113Sgblack@eecs.umich.edu } 4813113Sgblack@eecs.umich.edu 482378SN/A // Once you're done emptying the write queue, check if there's 483378SN/A // anything in the read queue, and call schedule if required. The 484378SN/A // retry above could already have caused it to be scheduled, so 485360SN/A // first check 4861450SN/A if (!nextReqEvent.scheduled()) 4873114Sgblack@eecs.umich.edu schedule(nextReqEvent, busBusyUntil); 4882680Sktlim@umich.edu} 489360SN/A 4906701Sgblack@eecs.umich.eduvoid 4916701Sgblack@eecs.umich.eduSimpleDRAM::triggerWrites() 4926701Sgblack@eecs.umich.edu{ 493360SN/A DPRINTF(DRAM, "Writes triggered at %lld\n", curTick()); 4941969SN/A // Flag variable to stop any more read scheduling 495360SN/A stopReads = true; 496360SN/A 497360SN/A writeStartTime = std::max(busBusyUntil, curTick()) + tWTR; 4981458SN/A 499360SN/A DPRINTF(DRAM, "Writes scheduled at %lld\n", writeStartTime); 500360SN/A 501360SN/A assert(writeStartTime >= curTick()); 5024131Sbinkertn@umich.edu assert(!writeEvent.scheduled()); 5034131Sbinkertn@umich.edu schedule(writeEvent, writeStartTime); 5044131Sbinkertn@umich.edu} 5054131Sbinkertn@umich.edu 5064131Sbinkertn@umich.eduvoid 5074131Sbinkertn@umich.eduSimpleDRAM::addToWriteQueue(PacketPtr pkt, unsigned int pktCount) 5084131Sbinkertn@umich.edu{ 5094131Sbinkertn@umich.edu // only add to the write queue here. whenever the request is 5106689Stjones1@inf.ed.ac.uk // eventually done, set the readyTime, and call schedule() 5111458SN/A assert(pkt->isWrite()); 512360SN/A 513360SN/A // if the request size is larger than burst size, the pkt is split into 5141706SN/A // multiple DRAM packets 5152680Sktlim@umich.edu Addr addr = pkt->getAddr(); 516360SN/A for (int cnt = 0; cnt < pktCount; ++cnt) { 517360SN/A unsigned size = std::min((addr | (burstSize - 1)) + 1, 518360SN/A pkt->getAddr() + pkt->getSize()) - addr; 519378SN/A writePktSize[ceilLog2(size)]++; 520360SN/A writeBursts++; 5211450SN/A 5223114Sgblack@eecs.umich.edu // see if we can merge with an existing item in the write 5232680Sktlim@umich.edu // queue and keep track of whether we have merged or not, as 524360SN/A // there is only ever one item to merge with 525360SN/A bool merged = false; 526360SN/A auto w = writeQueue.begin(); 5276701Sgblack@eecs.umich.edu 5286701Sgblack@eecs.umich.edu while(!merged && w != writeQueue.end()) { 5296701Sgblack@eecs.umich.edu // either of the two could be first, if they are the same 5301458SN/A // it does not matter which way we go 531360SN/A if ((*w)->addr >= addr) { 532360SN/A if ((addr + size) >= ((*w)->addr + (*w)->size)) { 533360SN/A // check if the existing one is completely 534360SN/A // subsumed in the new one 5351706SN/A DPRINTF(DRAM, "Merging write covering existing burst\n"); 5361458SN/A merged = true; 537360SN/A // update both the address and the size 538360SN/A (*w)->addr = addr; 5396701Sgblack@eecs.umich.edu (*w)->size = size; 5406701Sgblack@eecs.umich.edu } else if ((addr + size) >= (*w)->addr && 541360SN/A ((*w)->addr + (*w)->size - addr) <= burstSize) { 542360SN/A // the new one is just before or partially 543360SN/A // overlapping with the existing one, and together 544360SN/A // they fit within a burst 545360SN/A DPRINTF(DRAM, "Merging write before existing burst\n"); 546360SN/A merged = true; 547360SN/A // the existing queue item needs to be adjusted with 548360SN/A // respect to both address and size 549360SN/A (*w)->addr = addr; 550360SN/A (*w)->size = (*w)->addr + (*w)->size - addr; 551360SN/A } 552360SN/A } else { 5531706SN/A if (((*w)->addr + (*w)->size) >= (addr + size)) { 554360SN/A // check if the new one is completely subsumed in the 555360SN/A // existing one 556360SN/A DPRINTF(DRAM, "Merging write into existing burst\n"); 557360SN/A merged = true; 558360SN/A // no adjustments necessary 5593669Sbinkertn@umich.edu } else if (((*w)->addr + (*w)->size) >= addr && 5603669Sbinkertn@umich.edu (addr + size - (*w)->addr) <= burstSize) { 5613669Sbinkertn@umich.edu // the existing one is just before or partially 5621706SN/A // overlapping with the new one, and together 5631706SN/A // they fit within a burst 5645795Ssaidi@eecs.umich.edu DPRINTF(DRAM, "Merging write after existing burst\n"); 5655795Ssaidi@eecs.umich.edu merged = true; 5665795Ssaidi@eecs.umich.edu // the address is right, and only the size has 5675795Ssaidi@eecs.umich.edu // to be adjusted 5685795Ssaidi@eecs.umich.edu (*w)->size = addr + size - (*w)->addr; 5695795Ssaidi@eecs.umich.edu } 5705795Ssaidi@eecs.umich.edu } 5715795Ssaidi@eecs.umich.edu ++w; 5725795Ssaidi@eecs.umich.edu } 5735795Ssaidi@eecs.umich.edu 5745795Ssaidi@eecs.umich.edu // if the item was not merged we need to create a new write 575360SN/A // and enqueue it 576360SN/A if (!merged) { 577360SN/A DRAMPacket* dram_pkt = decodeAddr(pkt, addr, size); 5786640Svince@csl.cornell.edu 5796640Svince@csl.cornell.edu assert(writeQueue.size() < writeBufferSize); 5806640Svince@csl.cornell.edu wrQLenPdf[writeQueue.size()]++; 5816640Svince@csl.cornell.edu 5826640Svince@csl.cornell.edu DPRINTF(DRAM, "Adding to write queue\n"); 5836640Svince@csl.cornell.edu 5846640Svince@csl.cornell.edu writeQueue.push_back(dram_pkt); 5856701Sgblack@eecs.umich.edu 5866701Sgblack@eecs.umich.edu // Update stats 5876701Sgblack@eecs.umich.edu uint32_t bank_id = banksPerRank * dram_pkt->rank + dram_pkt->bank; 5886640Svince@csl.cornell.edu assert(bank_id < ranksPerChannel * banksPerRank); 5896701Sgblack@eecs.umich.edu perBankWrReqs[bank_id]++; 5906701Sgblack@eecs.umich.edu 5916640Svince@csl.cornell.edu avgWrQLen = writeQueue.size(); 5926701Sgblack@eecs.umich.edu } 5936640Svince@csl.cornell.edu 5946701Sgblack@eecs.umich.edu bytesConsumedWr += size; 5956640Svince@csl.cornell.edu bytesWritten += burstSize; 596360SN/A 5971999SN/A // Starting address of next dram pkt (aligend to burstSize boundary) 5981999SN/A addr = (addr | (burstSize - 1)) + 1; 5991999SN/A } 6003114Sgblack@eecs.umich.edu 6012680Sktlim@umich.edu // we do not wait for the writes to be send to the actual memory, 6021999SN/A // but instead take responsibility for the consistency here and 6031999SN/A // snoop the write queue for any upcoming reads 6041999SN/A // @todo, if a pkt size is larger than burst size, we might need a 6056701Sgblack@eecs.umich.edu // different front end latency 6066701Sgblack@eecs.umich.edu accessAndRespond(pkt, frontendLatency); 6076701Sgblack@eecs.umich.edu 6081999SN/A // If your write buffer is starting to fill up, drain it! 6096701Sgblack@eecs.umich.edu if (writeQueue.size() > writeThreshold && !stopReads){ 6101999SN/A triggerWrites(); 6116701Sgblack@eecs.umich.edu } 6121999SN/A} 6131999SN/A 6141999SN/Avoid 6151999SN/ASimpleDRAM::printParams() const 6161999SN/A{ 6173669Sbinkertn@umich.edu // Sanity check print of important parameters 6183669Sbinkertn@umich.edu DPRINTF(DRAM, 6193669Sbinkertn@umich.edu "Memory controller %s physical organization\n" \ 6201999SN/A "Number of devices per rank %d\n" \ 6211999SN/A "Device bus width (in bits) %d\n" \ 6221999SN/A "DRAM data bus burst %d\n" \ 6232218SN/A "Row buffer size %d\n" \ 6241999SN/A "Columns per row buffer %d\n" \ 6251999SN/A "Rows per bank %d\n" \ 6261999SN/A "Banks per rank %d\n" \ 6271999SN/A "Ranks per channel %d\n" \ 6281999SN/A "Total mem capacity %u\n", 6291999SN/A name(), devicesPerRank, deviceBusWidth, burstSize, rowBufferSize, 6301999SN/A columnsPerRowBuffer, rowsPerBank, banksPerRank, ranksPerChannel, 6311999SN/A rowBufferSize * rowsPerBank * banksPerRank * ranksPerChannel); 6323114Sgblack@eecs.umich.edu 6332680Sktlim@umich.edu string scheduler = memSchedPolicy == Enums::fcfs ? "FCFS" : "FR-FCFS"; 6341999SN/A string address_mapping = addrMapping == Enums::RaBaChCo ? "RaBaChCo" : 6356701Sgblack@eecs.umich.edu (addrMapping == Enums::RaBaCoCh ? "RaBaCoCh" : "CoRaBaCh"); 6366701Sgblack@eecs.umich.edu string page_policy = pageMgmt == Enums::open ? "OPEN" : "CLOSE"; 6371999SN/A 6381999SN/A DPRINTF(DRAM, 6391999SN/A "Memory controller %s characteristics\n" \ 6401999SN/A "Read buffer size %d\n" \ 6411999SN/A "Write buffer size %d\n" \ 6426701Sgblack@eecs.umich.edu "Write buffer thresh %d\n" \ 6431999SN/A "Scheduler %s\n" \ 6441999SN/A "Address mapping %s\n" \ 6451999SN/A "Page policy %s\n", 6461999SN/A name(), readBufferSize, writeBufferSize, writeThreshold, 6471999SN/A scheduler, address_mapping, page_policy); 6481999SN/A 6491999SN/A DPRINTF(DRAM, "Memory controller %s timing specs\n" \ 6501999SN/A "tRCD %d ticks\n" \ 6512218SN/A "tCL %d ticks\n" \ 6521999SN/A "tRP %d ticks\n" \ 6531999SN/A "tBURST %d ticks\n" \ 6541999SN/A "tRFC %d ticks\n" \ 6551999SN/A "tREFI %d ticks\n" \ 6565877Shsul@eecs.umich.edu "tWTR %d ticks\n" \ 6575877Shsul@eecs.umich.edu "tXAW (%d) %d ticks\n", 6585877Shsul@eecs.umich.edu name(), tRCD, tCL, tRP, tBURST, tRFC, tREFI, tWTR, 6595877Shsul@eecs.umich.edu activationLimit, tXAW); 6605877Shsul@eecs.umich.edu} 6616701Sgblack@eecs.umich.edu 6626701Sgblack@eecs.umich.eduvoid 6636701Sgblack@eecs.umich.eduSimpleDRAM::printQs() const { 6646701Sgblack@eecs.umich.edu DPRINTF(DRAM, "===READ QUEUE===\n\n"); 6656701Sgblack@eecs.umich.edu for (auto i = readQueue.begin() ; i != readQueue.end() ; ++i) { 6665877Shsul@eecs.umich.edu DPRINTF(DRAM, "Read %lu\n", (*i)->addr); 6675877Shsul@eecs.umich.edu } 6685877Shsul@eecs.umich.edu DPRINTF(DRAM, "\n===RESP QUEUE===\n\n"); 6695877Shsul@eecs.umich.edu for (auto i = respQueue.begin() ; i != respQueue.end() ; ++i) { 6705877Shsul@eecs.umich.edu DPRINTF(DRAM, "Response %lu\n", (*i)->addr); 6715877Shsul@eecs.umich.edu } 6725877Shsul@eecs.umich.edu DPRINTF(DRAM, "\n===WRITE QUEUE===\n\n"); 6735877Shsul@eecs.umich.edu for (auto i = writeQueue.begin() ; i != writeQueue.end() ; ++i) { 6745877Shsul@eecs.umich.edu DPRINTF(DRAM, "Write %lu\n", (*i)->addr); 6755877Shsul@eecs.umich.edu } 6765877Shsul@eecs.umich.edu} 6775877Shsul@eecs.umich.edu 6785877Shsul@eecs.umich.edubool 6795877Shsul@eecs.umich.eduSimpleDRAM::recvTimingReq(PacketPtr pkt) 6805877Shsul@eecs.umich.edu{ 6815877Shsul@eecs.umich.edu /// @todo temporary hack to deal with memory corruption issues until 6825877Shsul@eecs.umich.edu /// 4-phase transactions are complete 6835877Shsul@eecs.umich.edu for (int x = 0; x < pendingDelete.size(); x++) 6845877Shsul@eecs.umich.edu delete pendingDelete[x]; 6855877Shsul@eecs.umich.edu pendingDelete.clear(); 6865877Shsul@eecs.umich.edu 6875877Shsul@eecs.umich.edu // This is where we enter from the outside world 6885877Shsul@eecs.umich.edu DPRINTF(DRAM, "recvTimingReq: request %s addr %lld size %d\n", 6895877Shsul@eecs.umich.edu pkt->cmdString(), pkt->getAddr(), pkt->getSize()); 6905877Shsul@eecs.umich.edu 6915877Shsul@eecs.umich.edu // simply drop inhibited packets for now 6925877Shsul@eecs.umich.edu if (pkt->memInhibitAsserted()) { 6935877Shsul@eecs.umich.edu DPRINTF(DRAM,"Inhibited packet -- Dropping it now\n"); 6945877Shsul@eecs.umich.edu pendingDelete.push_back(pkt); 6955877Shsul@eecs.umich.edu return true; 6965877Shsul@eecs.umich.edu } 6975877Shsul@eecs.umich.edu 6985877Shsul@eecs.umich.edu // Every million accesses, print the state of the queues 6995877Shsul@eecs.umich.edu if (numReqs % 1000000 == 0) 7005877Shsul@eecs.umich.edu printQs(); 7015877Shsul@eecs.umich.edu 7021999SN/A // Calc avg gap between requests 703378SN/A if (prevArrival != 0) { 704360SN/A totGap += curTick() - prevArrival; 7051450SN/A } 7063114Sgblack@eecs.umich.edu prevArrival = curTick(); 7072680Sktlim@umich.edu 708360SN/A 709360SN/A // Find out how many dram packets a pkt translates to 710360SN/A // If the burst size is equal or larger than the pkt size, then a pkt 7116701Sgblack@eecs.umich.edu // translates to only one dram packet. Otherwise, a pkt translates to 7126701Sgblack@eecs.umich.edu // multiple dram packets 7136701Sgblack@eecs.umich.edu unsigned size = pkt->getSize(); 7146701Sgblack@eecs.umich.edu unsigned offset = pkt->getAddr() & (burstSize - 1); 7156701Sgblack@eecs.umich.edu unsigned int dram_pkt_count = divCeil(offset + size, burstSize); 7166701Sgblack@eecs.umich.edu 717360SN/A // check local buffers and do not accept if full 7183669Sbinkertn@umich.edu if (pkt->isRead()) { 7193669Sbinkertn@umich.edu assert(size != 0); 7203669Sbinkertn@umich.edu if (readQueueFull(dram_pkt_count)) { 721360SN/A DPRINTF(DRAM, "Read queue full, not accepting\n"); 722360SN/A // remember that we have to retry this port 723360SN/A retryRdReq = true; 724360SN/A numRdRetry++; 7252218SN/A return false; 726360SN/A } else { 7276701Sgblack@eecs.umich.edu addToReadQueue(pkt, dram_pkt_count); 728360SN/A readReqs++; 7291458SN/A numReqs++; 730360SN/A } 731360SN/A } else if (pkt->isWrite()) { 732360SN/A assert(size != 0); 7335074Ssaidi@eecs.umich.edu if (writeQueueFull(dram_pkt_count)) { 7345074Ssaidi@eecs.umich.edu DPRINTF(DRAM, "Write queue full, not accepting\n"); 7355074Ssaidi@eecs.umich.edu // remember that we have to retry this port 7365074Ssaidi@eecs.umich.edu retryWrReq = true; 7375074Ssaidi@eecs.umich.edu numWrRetry++; 7385074Ssaidi@eecs.umich.edu return false; 7395074Ssaidi@eecs.umich.edu } else { 7405074Ssaidi@eecs.umich.edu addToWriteQueue(pkt, dram_pkt_count); 7416701Sgblack@eecs.umich.edu writeReqs++; 7426701Sgblack@eecs.umich.edu numReqs++; 7436701Sgblack@eecs.umich.edu } 7445074Ssaidi@eecs.umich.edu } else { 7456701Sgblack@eecs.umich.edu DPRINTF(DRAM,"Neither read nor write, ignore timing\n"); 7465074Ssaidi@eecs.umich.edu neitherReadNorWrite++; 7475074Ssaidi@eecs.umich.edu accessAndRespond(pkt, 1); 7485074Ssaidi@eecs.umich.edu } 7495074Ssaidi@eecs.umich.edu 7505208Ssaidi@eecs.umich.edu retryRdReq = false; 7515208Ssaidi@eecs.umich.edu retryWrReq = false; 7525208Ssaidi@eecs.umich.edu return true; 7535208Ssaidi@eecs.umich.edu} 7545074Ssaidi@eecs.umich.edu 7555074Ssaidi@eecs.umich.eduvoid 7565208Ssaidi@eecs.umich.eduSimpleDRAM::processRespondEvent() 7575074Ssaidi@eecs.umich.edu{ 7585074Ssaidi@eecs.umich.edu DPRINTF(DRAM, 7595074Ssaidi@eecs.umich.edu "processRespondEvent(): Some req has reached its readyTime\n"); 7605074Ssaidi@eecs.umich.edu 7616701Sgblack@eecs.umich.edu DRAMPacket* dram_pkt = respQueue.front(); 7625074Ssaidi@eecs.umich.edu 7635074Ssaidi@eecs.umich.edu // Actually responds to the requestor 7645074Ssaidi@eecs.umich.edu bytesConsumedRd += dram_pkt->size; 7655074Ssaidi@eecs.umich.edu bytesRead += burstSize; 7665074Ssaidi@eecs.umich.edu if (dram_pkt->burstHelper) { 7671999SN/A // it is a split packet 7681999SN/A dram_pkt->burstHelper->burstsServiced++; 7691999SN/A if (dram_pkt->burstHelper->burstsServiced == 7703114Sgblack@eecs.umich.edu dram_pkt->burstHelper->burstCount) { 7712680Sktlim@umich.edu // we have now serviced all children packets of a system packet 7721999SN/A // so we can now respond to the requester 7736701Sgblack@eecs.umich.edu // @todo we probably want to have a different front end and back 7746701Sgblack@eecs.umich.edu // end latency for split packets 7756701Sgblack@eecs.umich.edu accessAndRespond(dram_pkt->pkt, frontendLatency + backendLatency); 7761999SN/A delete dram_pkt->burstHelper; 7771999SN/A dram_pkt->burstHelper = NULL; 7781999SN/A } 7791999SN/A } else { 7801999SN/A // it is not a split packet 7812764Sstever@eecs.umich.edu accessAndRespond(dram_pkt->pkt, frontendLatency + backendLatency); 7822064SN/A } 7832064SN/A 7842064SN/A delete respQueue.front(); 7852064SN/A respQueue.pop_front(); 7861999SN/A 7872064SN/A // Update stats 7881999SN/A avgRdQLen = readQueue.size() + respQueue.size(); 7891999SN/A 7902218SN/A if (!respQueue.empty()) { 7911999SN/A assert(respQueue.front()->readyTime >= curTick()); 7926701Sgblack@eecs.umich.edu assert(!respondEvent.scheduled()); 7931999SN/A schedule(respondEvent, respQueue.front()->readyTime); 7941999SN/A } else { 7951999SN/A // if there is nothing left in any queue, signal a drain 7961999SN/A if (writeQueue.empty() && readQueue.empty() && 7971999SN/A drainManager) { 798378SN/A drainManager->signalDrainDone(); 799360SN/A drainManager = NULL; 8001450SN/A } 8013114Sgblack@eecs.umich.edu } 8022680Sktlim@umich.edu 803360SN/A // We have made a location in the queue available at this point, 804360SN/A // so if there is a read that was forced to wait, retry now 805360SN/A if (retryRdReq) { 8066701Sgblack@eecs.umich.edu retryRdReq = false; 8076701Sgblack@eecs.umich.edu port.sendRetry(); 8086701Sgblack@eecs.umich.edu } 8096701Sgblack@eecs.umich.edu} 8106701Sgblack@eecs.umich.edu 8116701Sgblack@eecs.umich.eduvoid 812360SN/ASimpleDRAM::chooseNextWrite() 8133669Sbinkertn@umich.edu{ 8143669Sbinkertn@umich.edu // This method does the arbitration between write requests. The 8153669Sbinkertn@umich.edu // chosen packet is simply moved to the head of the write 816360SN/A // queue. The other methods know that this is the place to 817360SN/A // look. For example, with FCFS, this method does nothing 818360SN/A assert(!writeQueue.empty()); 819360SN/A 8201458SN/A if (writeQueue.size() == 1) { 821360SN/A DPRINTF(DRAMWR, "Single write request, nothing to do\n"); 8226701Sgblack@eecs.umich.edu return; 823360SN/A } 8241458SN/A 825360SN/A if (memSchedPolicy == Enums::fcfs) { 826360SN/A // Do nothing, since the correct request is already head 8271999SN/A } else if (memSchedPolicy == Enums::frfcfs) { 8281999SN/A auto i = writeQueue.begin(); 8291999SN/A bool foundRowHit = false; 8303114Sgblack@eecs.umich.edu while (!foundRowHit && i != writeQueue.end()) { 8312680Sktlim@umich.edu DRAMPacket* dram_pkt = *i; 8321999SN/A const Bank& bank = dram_pkt->bank_ref; 8331999SN/A if (bank.openRow == dram_pkt->row) { //FR part 8341999SN/A DPRINTF(DRAMWR, "Write row buffer hit\n"); 8356701Sgblack@eecs.umich.edu writeQueue.erase(i); 8366701Sgblack@eecs.umich.edu writeQueue.push_front(dram_pkt); 8376701Sgblack@eecs.umich.edu foundRowHit = true; 8386701Sgblack@eecs.umich.edu } else { //FCFS part 8396701Sgblack@eecs.umich.edu ; 8406701Sgblack@eecs.umich.edu } 8411999SN/A ++i; 8423669Sbinkertn@umich.edu } 8433669Sbinkertn@umich.edu } else 8443669Sbinkertn@umich.edu panic("No scheduling policy chosen\n"); 8452764Sstever@eecs.umich.edu 8462064SN/A DPRINTF(DRAMWR, "Selected next write request\n"); 8472064SN/A} 8482064SN/A 8491999SN/Abool 8501999SN/ASimpleDRAM::chooseNextRead() 8512064SN/A{ 8521999SN/A // This method does the arbitration between read requests. The 8531999SN/A // chosen packet is simply moved to the head of the queue. The 8541999SN/A // other methods know that this is the place to look. For example, 8551999SN/A // with FCFS, this method does nothing 8566701Sgblack@eecs.umich.edu if (readQueue.empty()) { 8571999SN/A DPRINTF(DRAM, "No read request to select\n"); 8581999SN/A return false; 8591999SN/A } 8601999SN/A 861378SN/A // If there is only one request then there is nothing left to do 862360SN/A if (readQueue.size() == 1) 8631450SN/A return true; 8643114Sgblack@eecs.umich.edu 8652680Sktlim@umich.edu if (memSchedPolicy == Enums::fcfs) { 866360SN/A // Do nothing, since the request to serve is already the first 8676701Sgblack@eecs.umich.edu // one in the read queue 8686701Sgblack@eecs.umich.edu } else if (memSchedPolicy == Enums::frfcfs) { 8696701Sgblack@eecs.umich.edu for (auto i = readQueue.begin(); i != readQueue.end() ; ++i) { 870360SN/A DRAMPacket* dram_pkt = *i; 8711969SN/A const Bank& bank = dram_pkt->bank_ref; 872360SN/A // Check if it is a row hit 873360SN/A if (bank.openRow == dram_pkt->row) { //FR part 8741458SN/A DPRINTF(DRAM, "Row buffer hit\n"); 875360SN/A readQueue.erase(i); 876360SN/A readQueue.push_front(dram_pkt); 877360SN/A break; 878360SN/A } else { //FCFS part 879360SN/A ; 8801458SN/A } 881360SN/A } 8826701Sgblack@eecs.umich.edu } else 8832021SN/A panic("No scheduling policy chosen!\n"); 8841458SN/A 885360SN/A DPRINTF(DRAM, "Selected next read request\n"); 886360SN/A return true; 887360SN/A} 8881706SN/A 8891706SN/Avoid 8901706SN/ASimpleDRAM::accessAndRespond(PacketPtr pkt, Tick static_latency) 8913114Sgblack@eecs.umich.edu{ 8922680Sktlim@umich.edu DPRINTF(DRAM, "Responding to Address %lld.. ",pkt->getAddr()); 8931706SN/A 8941706SN/A bool needsResponse = pkt->needsResponse(); 8951706SN/A // do the actual memory access which also turns the packet into a 8966701Sgblack@eecs.umich.edu // response 8976701Sgblack@eecs.umich.edu access(pkt); 8986701Sgblack@eecs.umich.edu 8996701Sgblack@eecs.umich.edu // turn packet around to go back to requester if response expected 9006701Sgblack@eecs.umich.edu if (needsResponse) { 9016701Sgblack@eecs.umich.edu // access already turned the packet into a response 9021706SN/A assert(pkt->isResponse()); 9033669Sbinkertn@umich.edu 9043669Sbinkertn@umich.edu // @todo someone should pay for this 9053669Sbinkertn@umich.edu pkt->busFirstWordDelay = pkt->busLastWordDelay = 0; 9061706SN/A 9071706SN/A // queue the packet in the response queue to be sent out after 9081706SN/A // the static latency has passed 9091706SN/A port.schedTimingResp(pkt, curTick() + static_latency); 9102218SN/A } else { 9111706SN/A // @todo the packet is going to be deleted, and the DRAMPacket 9126701Sgblack@eecs.umich.edu // is still having a pointer to it 9131706SN/A pendingDelete.push_back(pkt); 9141706SN/A } 9151706SN/A 9161706SN/A DPRINTF(DRAM, "Done\n"); 9171706SN/A 9181706SN/A return; 9191706SN/A} 9201706SN/A 9213114Sgblack@eecs.umich.edupair<Tick, Tick> 9222680Sktlim@umich.eduSimpleDRAM::estimateLatency(DRAMPacket* dram_pkt, Tick inTime) 9231706SN/A{ 9246701Sgblack@eecs.umich.edu // If a request reaches a bank at tick 'inTime', how much time 9256701Sgblack@eecs.umich.edu // *after* that does it take to finish the request, depending 9266701Sgblack@eecs.umich.edu // on bank status and page open policy. Note that this method 9271706SN/A // considers only the time taken for the actual read or write 9281706SN/A // to complete, NOT any additional time thereafter for tRAS or 9291706SN/A // tRP. 9301706SN/A Tick accLat = 0; 9311706SN/A Tick bankLat = 0; 9321706SN/A rowHitFlag = false; 9331706SN/A 9341706SN/A const Bank& bank = dram_pkt->bank_ref; 9352218SN/A if (pageMgmt == Enums::open) { // open-page policy 9361706SN/A if (bank.openRow == dram_pkt->row) { 9376701Sgblack@eecs.umich.edu // When we have a row-buffer hit, 9381706SN/A // we don't care about tRAS having expired or not, 9391706SN/A // but do care about bank being free for access 9401706SN/A rowHitFlag = true; 9411706SN/A 9421706SN/A if (bank.freeAt < inTime) { 9431999SN/A // CAS latency only 9441999SN/A accLat += tCL; 9451999SN/A bankLat += tCL; 9463114Sgblack@eecs.umich.edu } else { 9472680Sktlim@umich.edu accLat += 0; 9481999SN/A bankLat += 0; 9496701Sgblack@eecs.umich.edu } 9506701Sgblack@eecs.umich.edu 9511999SN/A } else { 9521999SN/A // Row-buffer miss, need to close existing row 9531999SN/A // once tRAS has expired, then open the new one, 9541999SN/A // then add cas latency. 9551999SN/A Tick freeTime = std::max(bank.tRASDoneAt, bank.freeAt); 9562680Sktlim@umich.edu 9576701Sgblack@eecs.umich.edu if (freeTime > inTime) 9586701Sgblack@eecs.umich.edu accLat += freeTime - inTime; 9591999SN/A 9606227Snate@binkert.org accLat += tRP + tRCD + tCL; 9611999SN/A bankLat += tRP + tRCD + tCL; 9622461SN/A } 9632461SN/A } else if (pageMgmt == Enums::close) { 9642461SN/A // With a close page policy, no notion of 9652091SN/A // bank.tRASDoneAt 9661999SN/A if (bank.freeAt > inTime) 9672461SN/A accLat += bank.freeAt - inTime; 9682461SN/A 9691999SN/A // page already closed, simply open the row, and 9701999SN/A // add cas latency 9711999SN/A accLat += tRCD + tCL; 9721999SN/A bankLat += tRCD + tCL; 9736227Snate@binkert.org } else 9741999SN/A panic("No page management policy chosen\n"); 9751999SN/A 9761999SN/A DPRINTF(DRAM, "Returning < %lld, %lld > from estimateLatency()\n", 9772218SN/A bankLat, accLat); 9781999SN/A 9791999SN/A return make_pair(bankLat, accLat); 9801999SN/A} 9811999SN/A 9821999SN/Avoid 983378SN/ASimpleDRAM::processNextReqEvent() 984378SN/A{ 985378SN/A scheduleNextReq(); 986378SN/A} 987378SN/A 988378SN/Avoid 989378SN/ASimpleDRAM::recordActivate(Tick act_tick) 990378SN/A{ 991360SN/A assert(actTicks.size() == activationLimit); 992378SN/A 993378SN/A DPRINTF(DRAM, "Activate at tick %d\n", act_tick); 994378SN/A 995360SN/A // if the activation limit is disabled then we are done 9961450SN/A if (actTicks.empty()) 9973114Sgblack@eecs.umich.edu return; 998360SN/A 9996701Sgblack@eecs.umich.edu // sanity check 10006701Sgblack@eecs.umich.edu if (actTicks.back() && (act_tick - actTicks.back()) < tXAW) { 10016701Sgblack@eecs.umich.edu // @todo For now, stick with a warning 10026701Sgblack@eecs.umich.edu warn("Got %d activates in window %d (%d - %d) which is smaller " 10036701Sgblack@eecs.umich.edu "than %d\n", activationLimit, act_tick - actTicks.back(), 10046701Sgblack@eecs.umich.edu act_tick, actTicks.back(), tXAW); 10056701Sgblack@eecs.umich.edu } 1006360SN/A 10075877Shsul@eecs.umich.edu // shift the times used for the book keeping, the last element 10082544SN/A // (highest index) is the oldest one and hence the lowest value 10092544SN/A actTicks.pop_back(); 10102544SN/A 10112544SN/A // record an new activation (in the future) 10122544SN/A actTicks.push_front(act_tick); 10132544SN/A 1014360SN/A // cannot activate more than X times in time window tXAW, push the 1015360SN/A // next one (the X + 1'st activate) to be tXAW away from the 10162544SN/A // oldest in our window of X 10172544SN/A if (actTicks.back() && (act_tick - actTicks.back()) < tXAW) { 10182544SN/A DPRINTF(DRAM, "Enforcing tXAW with X = %d, next activate no earlier " 10192544SN/A "than %d\n", activationLimit, actTicks.back() + tXAW); 10202544SN/A for(int i = 0; i < ranksPerChannel; i++) 10212544SN/A for(int j = 0; j < banksPerRank; j++) 10226672Sgblack@eecs.umich.edu // next activate must not happen before end of window 10236672Sgblack@eecs.umich.edu banks[i][j].freeAt = std::max(banks[i][j].freeAt, 10246672Sgblack@eecs.umich.edu actTicks.back() + tXAW); 10256672Sgblack@eecs.umich.edu } 10266672Sgblack@eecs.umich.edu} 10276672Sgblack@eecs.umich.edu 10286672Sgblack@eecs.umich.eduvoid 10292544SN/ASimpleDRAM::doDRAMAccess(DRAMPacket* dram_pkt) 10302544SN/A{ 10312553SN/A 10321969SN/A DPRINTF(DRAM, "Timing access to addr %lld, rank/bank/row %d %d %d\n", 10336701Sgblack@eecs.umich.edu dram_pkt->addr, dram_pkt->rank, dram_pkt->bank, dram_pkt->row); 1034360SN/A 1035360SN/A // estimate the bank and access latency 10361458SN/A pair<Tick, Tick> lat = estimateLatency(dram_pkt, curTick()); 1037360SN/A Tick bankLat = lat.first; 1038360SN/A Tick accessLat = lat.second; 1039378SN/A 1040360SN/A // This request was woken up at this time based on a prior call 10411450SN/A // to estimateLatency(). However, between then and now, both the 10423114Sgblack@eecs.umich.edu // accessLatency and/or busBusyUntil may have changed. We need 10432680Sktlim@umich.edu // to correct for that. 1044360SN/A 10456701Sgblack@eecs.umich.edu Tick addDelay = (curTick() + accessLat < busBusyUntil) ? 10466701Sgblack@eecs.umich.edu busBusyUntil - (curTick() + accessLat) : 0; 10476701Sgblack@eecs.umich.edu 1048360SN/A Bank& bank = dram_pkt->bank_ref; 1049360SN/A 10502064SN/A // Update bank state 10515877Shsul@eecs.umich.edu if (pageMgmt == Enums::open) { 10522064SN/A bank.openRow = dram_pkt->row; 10532091SN/A bank.freeAt = curTick() + addDelay + accessLat; 10542091SN/A bank.bytesAccessed += burstSize; 10552064SN/A 1056360SN/A // If you activated a new row do to this access, the next access 10575877Shsul@eecs.umich.edu // will have to respect tRAS for this bank. Assume tRAS ~= 3 * tRP. 10585877Shsul@eecs.umich.edu // Also need to account for t_XAW 10595877Shsul@eecs.umich.edu if (!rowHitFlag) { 10605877Shsul@eecs.umich.edu bank.tRASDoneAt = bank.freeAt + tRP; 10615877Shsul@eecs.umich.edu recordActivate(bank.freeAt - tCL - tRCD); //since this is open page, 10625877Shsul@eecs.umich.edu //no tRP by default 10635877Shsul@eecs.umich.edu // sample the number of bytes accessed and reset it as 10642064SN/A // we are now closing this row 10652064SN/A bytesPerActivate.sample(bank.bytesAccessed); 10662064SN/A bank.bytesAccessed = 0; 10672064SN/A } 10682064SN/A } else if (pageMgmt == Enums::close) { // accounting for tRAS also 1069360SN/A // assuming that tRAS ~= 3 * tRP, and tRC ~= 4 * tRP, as is common 1070360SN/A // (refer Jacob/Ng/Wang and Micron datasheets) 10712680Sktlim@umich.edu bank.freeAt = curTick() + addDelay + accessLat + tRP + tRP; 10721458SN/A recordActivate(bank.freeAt - tRP - tRP - tCL - tRCD); //essentially (freeAt - tRC) 1073360SN/A DPRINTF(DRAM,"doDRAMAccess::bank.freeAt is %lld\n",bank.freeAt); 1074360SN/A bytesPerActivate.sample(burstSize); 1075378SN/A } else 1076360SN/A panic("No page management policy chosen\n"); 10771450SN/A 10783114Sgblack@eecs.umich.edu // Update request parameters 10792680Sktlim@umich.edu dram_pkt->readyTime = curTick() + addDelay + accessLat + tBURST; 1080360SN/A 10816701Sgblack@eecs.umich.edu 10826701Sgblack@eecs.umich.edu DPRINTF(DRAM, "Req %lld: curtick is %lld accessLat is %d " \ 1083360SN/A "readytime is %lld busbusyuntil is %lld. " \ 1084360SN/A "Scheduling at readyTime\n", dram_pkt->addr, 1085360SN/A curTick(), accessLat, dram_pkt->readyTime, busBusyUntil); 10866109Ssanchezd@stanford.edu 10876109Ssanchezd@stanford.edu // Make sure requests are not overlapping on the databus 1088360SN/A assert (dram_pkt->readyTime - busBusyUntil >= tBURST); 10892680Sktlim@umich.edu 1090360SN/A // Update bus state 10911458SN/A busBusyUntil = dram_pkt->readyTime; 1092360SN/A 1093360SN/A DPRINTF(DRAM,"Access time is %lld\n", 1094360SN/A dram_pkt->readyTime - dram_pkt->entryTime); 10951999SN/A 10961999SN/A // Update stats 10971999SN/A totMemAccLat += dram_pkt->readyTime - dram_pkt->entryTime; 10983114Sgblack@eecs.umich.edu totBankLat += bankLat; 10992680Sktlim@umich.edu totBusLat += tBURST; 11001999SN/A totQLat += dram_pkt->readyTime - dram_pkt->entryTime - bankLat - tBURST; 11011999SN/A 11021999SN/A if (rowHitFlag) 11036701Sgblack@eecs.umich.edu readRowHits++; 11046701Sgblack@eecs.umich.edu 11056701Sgblack@eecs.umich.edu // At this point we're done dealing with the request 11066701Sgblack@eecs.umich.edu // It will be moved to a separate response queue with a 11076701Sgblack@eecs.umich.edu // correct readyTime, and eventually be sent back at that 11081999SN/A //time 11096701Sgblack@eecs.umich.edu moveToRespQ(); 11106701Sgblack@eecs.umich.edu 11112680Sktlim@umich.edu // The absolute soonest you have to start thinking about the 11121999SN/A // next request is the longest access time that can occur before 11131999SN/A // busBusyUntil. Assuming you need to meet tRAS, then precharge, 11141999SN/A // open a new row, and access, it is ~4*tRCD. 11151999SN/A 11162091SN/A 11172091SN/A Tick newTime = (busBusyUntil > 4 * tRCD) ? 11181999SN/A std::max(busBusyUntil - 4 * tRCD, curTick()) : 11193669Sbinkertn@umich.edu curTick(); 11203669Sbinkertn@umich.edu 11213669Sbinkertn@umich.edu if (!nextReqEvent.scheduled() && !stopReads){ 11223669Sbinkertn@umich.edu schedule(nextReqEvent, newTime); 11231999SN/A } else { 11241999SN/A if (newTime < nextReqEvent.when()) 11251999SN/A reschedule(nextReqEvent, newTime); 11261999SN/A } 11271999SN/A 11281999SN/A 11291999SN/A} 1130378SN/A 1131360SN/Avoid 11321450SN/ASimpleDRAM::moveToRespQ() 11333114Sgblack@eecs.umich.edu{ 11342680Sktlim@umich.edu // Remove from read queue 1135360SN/A DRAMPacket* dram_pkt = readQueue.front(); 11366701Sgblack@eecs.umich.edu readQueue.pop_front(); 11376701Sgblack@eecs.umich.edu 11386701Sgblack@eecs.umich.edu // sanity check 1139360SN/A assert(dram_pkt->size <= burstSize); 11403670Sbinkertn@umich.edu 11413670Sbinkertn@umich.edu // Insert into response queue sorted by readyTime 1142360SN/A // It will be sent back to the requestor at its 1143360SN/A // readyTime 1144360SN/A if (respQueue.empty()) { 1145360SN/A respQueue.push_front(dram_pkt); 1146360SN/A assert(!respondEvent.scheduled()); 1147360SN/A assert(dram_pkt->readyTime >= curTick()); 1148360SN/A schedule(respondEvent, dram_pkt->readyTime); 1149360SN/A } else { 1150360SN/A bool done = false; 1151360SN/A auto i = respQueue.begin(); 1152360SN/A while (!done && i != respQueue.end()) { 1153360SN/A if ((*i)->readyTime > dram_pkt->readyTime) { 1154360SN/A respQueue.insert(i, dram_pkt); 1155360SN/A done = true; 1156360SN/A } 1157360SN/A ++i; 1158360SN/A } 11593670Sbinkertn@umich.edu 11603670Sbinkertn@umich.edu if (!done) 11613670Sbinkertn@umich.edu respQueue.push_back(dram_pkt); 11623670Sbinkertn@umich.edu 11633670Sbinkertn@umich.edu assert(respondEvent.scheduled()); 11643670Sbinkertn@umich.edu 11653670Sbinkertn@umich.edu if (respQueue.front()->readyTime < respondEvent.when()) { 11663670Sbinkertn@umich.edu assert(respQueue.front()->readyTime >= curTick()); 11673670Sbinkertn@umich.edu reschedule(respondEvent, respQueue.front()->readyTime); 11683670Sbinkertn@umich.edu } 11693670Sbinkertn@umich.edu } 11703670Sbinkertn@umich.edu} 11713670Sbinkertn@umich.edu 11723670Sbinkertn@umich.eduvoid 11733670Sbinkertn@umich.eduSimpleDRAM::scheduleNextReq() 11743670Sbinkertn@umich.edu{ 11753670Sbinkertn@umich.edu DPRINTF(DRAM, "Reached scheduleNextReq()\n"); 11763670Sbinkertn@umich.edu 11772680Sktlim@umich.edu // Figure out which read request goes next, and move it to the 1178360SN/A // front of the read queue 11791458SN/A if (!chooseNextRead()) { 1180360SN/A // In the case there is no read request to go next, see if we 1181360SN/A // are asked to drain, and if so trigger writes, this also 11826683Stjones1@inf.ed.ac.uk // ensures that if we hit the write limit we will do this 11836683Stjones1@inf.ed.ac.uk // multiple times until we are completely drained 11846683Stjones1@inf.ed.ac.uk if (drainManager && !writeQueue.empty() && !writeEvent.scheduled()) 11856683Stjones1@inf.ed.ac.uk triggerWrites(); 11866683Stjones1@inf.ed.ac.uk } else { 11876683Stjones1@inf.ed.ac.uk doDRAMAccess(readQueue.front()); 11886701Sgblack@eecs.umich.edu } 11896701Sgblack@eecs.umich.edu} 11906683Stjones1@inf.ed.ac.uk 11916683Stjones1@inf.ed.ac.ukTick 11927064Snate@binkert.orgSimpleDRAM::maxBankFreeAt() const 11936683Stjones1@inf.ed.ac.uk{ 11946683Stjones1@inf.ed.ac.uk Tick banksFree = 0; 11956683Stjones1@inf.ed.ac.uk 11966683Stjones1@inf.ed.ac.uk for(int i = 0; i < ranksPerChannel; i++) 11976683Stjones1@inf.ed.ac.uk for(int j = 0; j < banksPerRank; j++) 11986683Stjones1@inf.ed.ac.uk banksFree = std::max(banks[i][j].freeAt, banksFree); 11996683Stjones1@inf.ed.ac.uk 12006683Stjones1@inf.ed.ac.uk return banksFree; 12016683Stjones1@inf.ed.ac.uk} 12026683Stjones1@inf.ed.ac.uk 12036683Stjones1@inf.ed.ac.ukvoid 12046683Stjones1@inf.ed.ac.ukSimpleDRAM::processRefreshEvent() 12056683Stjones1@inf.ed.ac.uk{ 12066683Stjones1@inf.ed.ac.uk DPRINTF(DRAM, "Refreshing at tick %ld\n", curTick()); 12072553SN/A 12086684Stjones1@inf.ed.ac.uk Tick banksFree = std::max(curTick(), maxBankFreeAt()) + tRFC; 12096684Stjones1@inf.ed.ac.uk 12106684Stjones1@inf.ed.ac.uk for(int i = 0; i < ranksPerChannel; i++) 12116684Stjones1@inf.ed.ac.uk for(int j = 0; j < banksPerRank; j++) 12126684Stjones1@inf.ed.ac.uk banks[i][j].freeAt = banksFree; 12136684Stjones1@inf.ed.ac.uk 12146684Stjones1@inf.ed.ac.uk schedule(refreshEvent, curTick() + tREFI); 12156684Stjones1@inf.ed.ac.uk} 12166684Stjones1@inf.ed.ac.uk 12176684Stjones1@inf.ed.ac.ukvoid 12186701Sgblack@eecs.umich.eduSimpleDRAM::regStats() 12196701Sgblack@eecs.umich.edu{ 12206684Stjones1@inf.ed.ac.uk using namespace Stats; 12216684Stjones1@inf.ed.ac.uk 12226684Stjones1@inf.ed.ac.uk AbstractMemory::regStats(); 12236684Stjones1@inf.ed.ac.uk 12246684Stjones1@inf.ed.ac.uk readReqs 12256684Stjones1@inf.ed.ac.uk .name(name() + ".readReqs") 12266684Stjones1@inf.ed.ac.uk .desc("Total number of read requests accepted by DRAM controller"); 12276684Stjones1@inf.ed.ac.uk 12282553SN/A writeReqs 12292553SN/A .name(name() + ".writeReqs") 12301354SN/A .desc("Total number of write requests accepted by DRAM controller"); 1231 1232 readBursts 1233 .name(name() + ".readBursts") 1234 .desc("Total number of DRAM read bursts. " 1235 "Each DRAM read request translates to either one or multiple " 1236 "DRAM read bursts"); 1237 1238 writeBursts 1239 .name(name() + ".writeBursts") 1240 .desc("Total number of DRAM write bursts. " 1241 "Each DRAM write request translates to either one or multiple " 1242 "DRAM write bursts"); 1243 1244 servicedByWrQ 1245 .name(name() + ".servicedByWrQ") 1246 .desc("Number of DRAM read bursts serviced by write Q"); 1247 1248 neitherReadNorWrite 1249 .name(name() + ".neitherReadNorWrite") 1250 .desc("Reqs where no action is needed"); 1251 1252 perBankRdReqs 1253 .init(banksPerRank * ranksPerChannel) 1254 .name(name() + ".perBankRdReqs") 1255 .desc("Track reads on a per bank basis"); 1256 1257 perBankWrReqs 1258 .init(banksPerRank * ranksPerChannel) 1259 .name(name() + ".perBankWrReqs") 1260 .desc("Track writes on a per bank basis"); 1261 1262 avgRdQLen 1263 .name(name() + ".avgRdQLen") 1264 .desc("Average read queue length over time") 1265 .precision(2); 1266 1267 avgWrQLen 1268 .name(name() + ".avgWrQLen") 1269 .desc("Average write queue length over time") 1270 .precision(2); 1271 1272 totQLat 1273 .name(name() + ".totQLat") 1274 .desc("Total cycles spent in queuing delays"); 1275 1276 totBankLat 1277 .name(name() + ".totBankLat") 1278 .desc("Total cycles spent in bank access"); 1279 1280 totBusLat 1281 .name(name() + ".totBusLat") 1282 .desc("Total cycles spent in databus access"); 1283 1284 totMemAccLat 1285 .name(name() + ".totMemAccLat") 1286 .desc("Sum of mem lat for all requests"); 1287 1288 avgQLat 1289 .name(name() + ".avgQLat") 1290 .desc("Average queueing delay per request") 1291 .precision(2); 1292 1293 avgQLat = totQLat / (readBursts - servicedByWrQ); 1294 1295 avgBankLat 1296 .name(name() + ".avgBankLat") 1297 .desc("Average bank access latency per request") 1298 .precision(2); 1299 1300 avgBankLat = totBankLat / (readBursts - servicedByWrQ); 1301 1302 avgBusLat 1303 .name(name() + ".avgBusLat") 1304 .desc("Average bus latency per request") 1305 .precision(2); 1306 1307 avgBusLat = totBusLat / (readBursts - servicedByWrQ); 1308 1309 avgMemAccLat 1310 .name(name() + ".avgMemAccLat") 1311 .desc("Average memory access latency") 1312 .precision(2); 1313 1314 avgMemAccLat = totMemAccLat / (readBursts - servicedByWrQ); 1315 1316 numRdRetry 1317 .name(name() + ".numRdRetry") 1318 .desc("Number of times rd buffer was full causing retry"); 1319 1320 numWrRetry 1321 .name(name() + ".numWrRetry") 1322 .desc("Number of times wr buffer was full causing retry"); 1323 1324 readRowHits 1325 .name(name() + ".readRowHits") 1326 .desc("Number of row buffer hits during reads"); 1327 1328 writeRowHits 1329 .name(name() + ".writeRowHits") 1330 .desc("Number of row buffer hits during writes"); 1331 1332 readRowHitRate 1333 .name(name() + ".readRowHitRate") 1334 .desc("Row buffer hit rate for reads") 1335 .precision(2); 1336 1337 readRowHitRate = (readRowHits / (readBursts - servicedByWrQ)) * 100; 1338 1339 writeRowHitRate 1340 .name(name() + ".writeRowHitRate") 1341 .desc("Row buffer hit rate for writes") 1342 .precision(2); 1343 1344 writeRowHitRate = (writeRowHits / writeBursts) * 100; 1345 1346 readPktSize 1347 .init(ceilLog2(burstSize) + 1) 1348 .name(name() + ".readPktSize") 1349 .desc("Categorize read packet sizes"); 1350 1351 writePktSize 1352 .init(ceilLog2(burstSize) + 1) 1353 .name(name() + ".writePktSize") 1354 .desc("Categorize write packet sizes"); 1355 1356 rdQLenPdf 1357 .init(readBufferSize) 1358 .name(name() + ".rdQLenPdf") 1359 .desc("What read queue length does an incoming req see"); 1360 1361 wrQLenPdf 1362 .init(writeBufferSize) 1363 .name(name() + ".wrQLenPdf") 1364 .desc("What write queue length does an incoming req see"); 1365 1366 bytesPerActivate 1367 .init(rowBufferSize) 1368 .name(name() + ".bytesPerActivate") 1369 .desc("Bytes accessed per row activation") 1370 .flags(nozero); 1371 1372 bytesRead 1373 .name(name() + ".bytesRead") 1374 .desc("Total number of bytes read from memory"); 1375 1376 bytesWritten 1377 .name(name() + ".bytesWritten") 1378 .desc("Total number of bytes written to memory"); 1379 1380 bytesConsumedRd 1381 .name(name() + ".bytesConsumedRd") 1382 .desc("bytesRead derated as per pkt->getSize()"); 1383 1384 bytesConsumedWr 1385 .name(name() + ".bytesConsumedWr") 1386 .desc("bytesWritten derated as per pkt->getSize()"); 1387 1388 avgRdBW 1389 .name(name() + ".avgRdBW") 1390 .desc("Average achieved read bandwidth in MB/s") 1391 .precision(2); 1392 1393 avgRdBW = (bytesRead / 1000000) / simSeconds; 1394 1395 avgWrBW 1396 .name(name() + ".avgWrBW") 1397 .desc("Average achieved write bandwidth in MB/s") 1398 .precision(2); 1399 1400 avgWrBW = (bytesWritten / 1000000) / simSeconds; 1401 1402 avgConsumedRdBW 1403 .name(name() + ".avgConsumedRdBW") 1404 .desc("Average consumed read bandwidth in MB/s") 1405 .precision(2); 1406 1407 avgConsumedRdBW = (bytesConsumedRd / 1000000) / simSeconds; 1408 1409 avgConsumedWrBW 1410 .name(name() + ".avgConsumedWrBW") 1411 .desc("Average consumed write bandwidth in MB/s") 1412 .precision(2); 1413 1414 avgConsumedWrBW = (bytesConsumedWr / 1000000) / simSeconds; 1415 1416 peakBW 1417 .name(name() + ".peakBW") 1418 .desc("Theoretical peak bandwidth in MB/s") 1419 .precision(2); 1420 1421 peakBW = (SimClock::Frequency / tBURST) * burstSize / 1000000; 1422 1423 busUtil 1424 .name(name() + ".busUtil") 1425 .desc("Data bus utilization in percentage") 1426 .precision(2); 1427 1428 busUtil = (avgRdBW + avgWrBW) / peakBW * 100; 1429 1430 totGap 1431 .name(name() + ".totGap") 1432 .desc("Total gap between requests"); 1433 1434 avgGap 1435 .name(name() + ".avgGap") 1436 .desc("Average gap between requests") 1437 .precision(2); 1438 1439 avgGap = totGap / (readReqs + writeReqs); 1440} 1441 1442void 1443SimpleDRAM::recvFunctional(PacketPtr pkt) 1444{ 1445 // rely on the abstract memory 1446 functionalAccess(pkt); 1447} 1448 1449BaseSlavePort& 1450SimpleDRAM::getSlavePort(const string &if_name, PortID idx) 1451{ 1452 if (if_name != "port") { 1453 return MemObject::getSlavePort(if_name, idx); 1454 } else { 1455 return port; 1456 } 1457} 1458 1459unsigned int 1460SimpleDRAM::drain(DrainManager *dm) 1461{ 1462 unsigned int count = port.drain(dm); 1463 1464 // if there is anything in any of our internal queues, keep track 1465 // of that as well 1466 if (!(writeQueue.empty() && readQueue.empty() && 1467 respQueue.empty())) { 1468 DPRINTF(Drain, "DRAM controller not drained, write: %d, read: %d," 1469 " resp: %d\n", writeQueue.size(), readQueue.size(), 1470 respQueue.size()); 1471 ++count; 1472 drainManager = dm; 1473 // the only part that is not drained automatically over time 1474 // is the write queue, thus trigger writes if there are any 1475 // waiting and no reads waiting, otherwise wait until the 1476 // reads are done 1477 if (readQueue.empty() && !writeQueue.empty() && 1478 !writeEvent.scheduled()) 1479 triggerWrites(); 1480 } 1481 1482 if (count) 1483 setDrainState(Drainable::Draining); 1484 else 1485 setDrainState(Drainable::Drained); 1486 return count; 1487} 1488 1489SimpleDRAM::MemoryPort::MemoryPort(const std::string& name, SimpleDRAM& _memory) 1490 : QueuedSlavePort(name, &_memory, queue), queue(_memory, *this), 1491 memory(_memory) 1492{ } 1493 1494AddrRangeList 1495SimpleDRAM::MemoryPort::getAddrRanges() const 1496{ 1497 AddrRangeList ranges; 1498 ranges.push_back(memory.getAddrRange()); 1499 return ranges; 1500} 1501 1502void 1503SimpleDRAM::MemoryPort::recvFunctional(PacketPtr pkt) 1504{ 1505 pkt->pushLabel(memory.name()); 1506 1507 if (!queue.checkFunctional(pkt)) { 1508 // Default implementation of SimpleTimingPort::recvFunctional() 1509 // calls recvAtomic() and throws away the latency; we can save a 1510 // little here by just not calculating the latency. 1511 memory.recvFunctional(pkt); 1512 } 1513 1514 pkt->popLabel(); 1515} 1516 1517Tick 1518SimpleDRAM::MemoryPort::recvAtomic(PacketPtr pkt) 1519{ 1520 return memory.recvAtomic(pkt); 1521} 1522 1523bool 1524SimpleDRAM::MemoryPort::recvTimingReq(PacketPtr pkt) 1525{ 1526 // pass it to the memory controller 1527 return memory.recvTimingReq(pkt); 1528} 1529 1530SimpleDRAM* 1531SimpleDRAMParams::create() 1532{ 1533 return new SimpleDRAM(this); 1534} 1535