dram_ctrl.cc revision 9587
17150Sgblack@eecs.umich.edu/*
27150Sgblack@eecs.umich.edu * Copyright (c) 2010-2012 ARM Limited
310334Smitch.hayenga@arm.com * All rights reserved
47150Sgblack@eecs.umich.edu *
57150Sgblack@eecs.umich.edu * The license below extends only to copyright in the software and shall
67150Sgblack@eecs.umich.edu * not be construed as granting a license to any other intellectual
77150Sgblack@eecs.umich.edu * property including but not limited to intellectual property relating
87150Sgblack@eecs.umich.edu * to a hardware implementation of the functionality of the software
97150Sgblack@eecs.umich.edu * licensed hereunder.  You may use the software subject to the license
107150Sgblack@eecs.umich.edu * terms below provided that you ensure that this notice is replicated
117150Sgblack@eecs.umich.edu * unmodified and in its entirety in all distributions of the software,
127150Sgblack@eecs.umich.edu * modified or unmodified, in source code or in binary form.
137150Sgblack@eecs.umich.edu *
147150Sgblack@eecs.umich.edu * Redistribution and use in source and binary forms, with or without
157150Sgblack@eecs.umich.edu * modification, are permitted provided that the following conditions are
167150Sgblack@eecs.umich.edu * met: redistributions of source code must retain the above copyright
177150Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer;
187150Sgblack@eecs.umich.edu * redistributions in binary form must reproduce the above copyright
197150Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer in the
207150Sgblack@eecs.umich.edu * documentation and/or other materials provided with the distribution;
217150Sgblack@eecs.umich.edu * neither the name of the copyright holders nor the names of its
227150Sgblack@eecs.umich.edu * contributors may be used to endorse or promote products derived from
237150Sgblack@eecs.umich.edu * this software without specific prior written permission.
247150Sgblack@eecs.umich.edu *
257150Sgblack@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
267150Sgblack@eecs.umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
277150Sgblack@eecs.umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
287150Sgblack@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
297150Sgblack@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
307150Sgblack@eecs.umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
317150Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
327150Sgblack@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
337150Sgblack@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
347150Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
357150Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
367150Sgblack@eecs.umich.edu *
377150Sgblack@eecs.umich.edu * Authors: Andreas Hansson
387150Sgblack@eecs.umich.edu *          Ani Udipi
397150Sgblack@eecs.umich.edu */
407150Sgblack@eecs.umich.edu
417150Sgblack@eecs.umich.edu#include "base/trace.hh"
427150Sgblack@eecs.umich.edu#include "debug/Drain.hh"
437150Sgblack@eecs.umich.edu#include "debug/DRAM.hh"
447150Sgblack@eecs.umich.edu#include "debug/DRAMWR.hh"
457150Sgblack@eecs.umich.edu#include "mem/simple_dram.hh"
467150Sgblack@eecs.umich.edu
477150Sgblack@eecs.umich.eduusing namespace std;
487150Sgblack@eecs.umich.edu
497150Sgblack@eecs.umich.eduSimpleDRAM::SimpleDRAM(const SimpleDRAMParams* p) :
507150Sgblack@eecs.umich.edu    AbstractMemory(p),
5110184SCurtis.Dunham@arm.com    port(name() + ".port", *this),
527150Sgblack@eecs.umich.edu    retryRdReq(false), retryWrReq(false),
537150Sgblack@eecs.umich.edu    rowHitFlag(false), stopReads(false), actTicks(p->activation_limit, 0),
547150Sgblack@eecs.umich.edu    writeEvent(this), respondEvent(this),
557150Sgblack@eecs.umich.edu    refreshEvent(this), nextReqEvent(this), drainManager(NULL),
567848SAli.Saidi@ARM.com    bytesPerCacheLine(0),
577848SAli.Saidi@ARM.com    linesPerRowBuffer(p->lines_per_rowbuffer),
587848SAli.Saidi@ARM.com    ranksPerChannel(p->ranks_per_channel),
597848SAli.Saidi@ARM.com    banksPerRank(p->banks_per_rank), channels(p->channels), rowsPerBank(0),
608146SAli.Saidi@ARM.com    readBufferSize(p->read_buffer_size),
618146SAli.Saidi@ARM.com    writeBufferSize(p->write_buffer_size),
628146SAli.Saidi@ARM.com    writeThresholdPerc(p->write_thresh_perc),
637848SAli.Saidi@ARM.com    tWTR(p->tWTR), tBURST(p->tBURST),
648146SAli.Saidi@ARM.com    tRCD(p->tRCD), tCL(p->tCL), tRP(p->tRP),
657150Sgblack@eecs.umich.edu    tRFC(p->tRFC), tREFI(p->tREFI),
667150Sgblack@eecs.umich.edu    tXAW(p->tXAW), activationLimit(p->activation_limit),
677150Sgblack@eecs.umich.edu    memSchedPolicy(p->mem_sched_policy), addrMapping(p->addr_mapping),
687150Sgblack@eecs.umich.edu    pageMgmt(p->page_policy),
697150Sgblack@eecs.umich.edu    busBusyUntil(0), writeStartTime(0),
707150Sgblack@eecs.umich.edu    prevArrival(0), numReqs(0)
717150Sgblack@eecs.umich.edu{
727150Sgblack@eecs.umich.edu    // create the bank states based on the dimensions of the ranks and
737150Sgblack@eecs.umich.edu    // banks
747150Sgblack@eecs.umich.edu    banks.resize(ranksPerChannel);
757150Sgblack@eecs.umich.edu    for (size_t c = 0; c < ranksPerChannel; ++c) {
768146SAli.Saidi@ARM.com        banks[c].resize(banksPerRank);
779552Sandreas.hansson@arm.com    }
789552Sandreas.hansson@arm.com
799552Sandreas.hansson@arm.com    // round the write threshold percent to a whole number of entries
807150Sgblack@eecs.umich.edu    // in the buffer
817150Sgblack@eecs.umich.edu    writeThreshold = writeBufferSize * writeThresholdPerc / 100.0;
827150Sgblack@eecs.umich.edu}
837150Sgblack@eecs.umich.edu
8410184SCurtis.Dunham@arm.comvoid
857150Sgblack@eecs.umich.eduSimpleDRAM::init()
867150Sgblack@eecs.umich.edu{
877150Sgblack@eecs.umich.edu    if (!port.isConnected()) {
887150Sgblack@eecs.umich.edu        fatal("SimpleDRAM %s is unconnected!\n", name());
897150Sgblack@eecs.umich.edu    } else {
907150Sgblack@eecs.umich.edu        port.sendRangeChange();
917848SAli.Saidi@ARM.com    }
927848SAli.Saidi@ARM.com
937848SAli.Saidi@ARM.com    // get the burst size from the connected port as it is currently
947848SAli.Saidi@ARM.com    // assumed to be equal to the cache line size
958146SAli.Saidi@ARM.com    bytesPerCacheLine = port.peerBlockSize();
968146SAli.Saidi@ARM.com
978146SAli.Saidi@ARM.com    // we could deal with plenty options here, but for now do a quick
987848SAli.Saidi@ARM.com    // sanity check
997150Sgblack@eecs.umich.edu    if (bytesPerCacheLine != 64 && bytesPerCacheLine != 32)
1007150Sgblack@eecs.umich.edu        panic("Unexpected burst size %d", bytesPerCacheLine);
1017150Sgblack@eecs.umich.edu
1027150Sgblack@eecs.umich.edu    // determine the rows per bank by looking at the total capacity
1037150Sgblack@eecs.umich.edu    uint64_t capacity = ULL(1) << ceilLog2(AbstractMemory::size());
1047150Sgblack@eecs.umich.edu
1057150Sgblack@eecs.umich.edu    DPRINTF(DRAM, "Memory capacity %lld (%lld) bytes\n", capacity,
1067150Sgblack@eecs.umich.edu            AbstractMemory::size());
1077150Sgblack@eecs.umich.edu    rowsPerBank = capacity / (bytesPerCacheLine * linesPerRowBuffer *
1087150Sgblack@eecs.umich.edu                              banksPerRank * ranksPerChannel);
1097150Sgblack@eecs.umich.edu
1107150Sgblack@eecs.umich.edu    if (range.interleaved()) {
1117150Sgblack@eecs.umich.edu        if (channels != range.stripes())
1127150Sgblack@eecs.umich.edu            panic("%s has %d interleaved address stripes but %d channel(s)\n",
11310184SCurtis.Dunham@arm.com                  name(), range.stripes(), channels);
1147150Sgblack@eecs.umich.edu
1157150Sgblack@eecs.umich.edu        if (addrMapping == Enums::openmap) {
1167150Sgblack@eecs.umich.edu            if (bytesPerCacheLine * linesPerRowBuffer !=
1177150Sgblack@eecs.umich.edu                range.granularity()) {
1187848SAli.Saidi@ARM.com                panic("Interleaving of %s doesn't match open address map\n",
1197848SAli.Saidi@ARM.com                      name());
1207848SAli.Saidi@ARM.com            }
1217848SAli.Saidi@ARM.com        } else if (addrMapping == Enums::closemap) {
1228146SAli.Saidi@ARM.com            if (bytesPerCacheLine != range.granularity())
1238146SAli.Saidi@ARM.com                panic("Interleaving of %s doesn't match closed address map\n",
1248146SAli.Saidi@ARM.com                      name());
1257848SAli.Saidi@ARM.com        }
1268203SAli.Saidi@ARM.com    }
1278203SAli.Saidi@ARM.com}
1287150Sgblack@eecs.umich.edu
1297150Sgblack@eecs.umich.eduvoid
1307150Sgblack@eecs.umich.eduSimpleDRAM::startup()
1317150Sgblack@eecs.umich.edu{
1327150Sgblack@eecs.umich.edu    // print the configuration of the controller
1337150Sgblack@eecs.umich.edu    printParams();
1347150Sgblack@eecs.umich.edu
1357150Sgblack@eecs.umich.edu    // kick off the refresh
1367150Sgblack@eecs.umich.edu    schedule(refreshEvent, curTick() + tREFI);
1377150Sgblack@eecs.umich.edu}
1387150Sgblack@eecs.umich.edu
1397150Sgblack@eecs.umich.eduTick
1407150Sgblack@eecs.umich.eduSimpleDRAM::recvAtomic(PacketPtr pkt)
1417150Sgblack@eecs.umich.edu{
1427150Sgblack@eecs.umich.edu    DPRINTF(DRAM, "recvAtomic: %s 0x%x\n", pkt->cmdString(), pkt->getAddr());
14310184SCurtis.Dunham@arm.com
1447150Sgblack@eecs.umich.edu    // do the actual memory access and turn the packet into a response
1457150Sgblack@eecs.umich.edu    access(pkt);
1467150Sgblack@eecs.umich.edu
1477150Sgblack@eecs.umich.edu    Tick latency = 0;
1487150Sgblack@eecs.umich.edu    if (!pkt->memInhibitAsserted() && pkt->hasData()) {
1497150Sgblack@eecs.umich.edu        // this value is not supposed to be accurate, just enough to
1507848SAli.Saidi@ARM.com        // keep things going, mimic a closed page
1517848SAli.Saidi@ARM.com        latency = tRP + tRCD + tCL;
1527848SAli.Saidi@ARM.com    }
1537848SAli.Saidi@ARM.com    return latency;
1548146SAli.Saidi@ARM.com}
1558146SAli.Saidi@ARM.com
1568146SAli.Saidi@ARM.combool
1577848SAli.Saidi@ARM.comSimpleDRAM::readQueueFull() const
1588203SAli.Saidi@ARM.com{
1598203SAli.Saidi@ARM.com    DPRINTF(DRAM, "Read queue limit %d current size %d\n",
1607150Sgblack@eecs.umich.edu            readBufferSize, readQueue.size() + respQueue.size());
1617150Sgblack@eecs.umich.edu
1627150Sgblack@eecs.umich.edu    return (readQueue.size() + respQueue.size()) == readBufferSize;
1637150Sgblack@eecs.umich.edu}
1647150Sgblack@eecs.umich.edu
1657150Sgblack@eecs.umich.edubool
1667150Sgblack@eecs.umich.eduSimpleDRAM::writeQueueFull() const
1677150Sgblack@eecs.umich.edu{
1687150Sgblack@eecs.umich.edu    DPRINTF(DRAM, "Write queue limit %d current size %d\n",
1697150Sgblack@eecs.umich.edu            writeBufferSize, writeQueue.size());
1707150Sgblack@eecs.umich.edu    return writeQueue.size() == writeBufferSize;
1717150Sgblack@eecs.umich.edu}
1727150Sgblack@eecs.umich.edu
1737150Sgblack@eecs.umich.eduSimpleDRAM::DRAMPacket*
1747150Sgblack@eecs.umich.eduSimpleDRAM::decodeAddr(PacketPtr pkt)
1757150Sgblack@eecs.umich.edu{
1767150Sgblack@eecs.umich.edu    // decode the address based on the address mapping scheme
1777150Sgblack@eecs.umich.edu    //
1787150Sgblack@eecs.umich.edu    // with R, C, B and K denoting rank, column, bank and rank,
1797150Sgblack@eecs.umich.edu    // respectively, and going from MSB to LSB, the two schemes are
1807150Sgblack@eecs.umich.edu    // RKBC (openmap) and RCKB (closedmap)
1817150Sgblack@eecs.umich.edu    uint8_t rank;
1827150Sgblack@eecs.umich.edu    uint16_t bank;
1837150Sgblack@eecs.umich.edu    uint16_t row;
1847150Sgblack@eecs.umich.edu
1857150Sgblack@eecs.umich.edu    Addr addr = pkt->getAddr();
1867150Sgblack@eecs.umich.edu
1877150Sgblack@eecs.umich.edu    // truncate the address to the access granularity
1887150Sgblack@eecs.umich.edu    addr = addr / bytesPerCacheLine;
1897150Sgblack@eecs.umich.edu
19010184SCurtis.Dunham@arm.com    // we have removed the lowest order address bits that denote the
1917150Sgblack@eecs.umich.edu    // position within the cache line, proceed and select the
1927150Sgblack@eecs.umich.edu    // appropriate bits for bank, rank and row (no column address is
1937150Sgblack@eecs.umich.edu    // needed)
1947150Sgblack@eecs.umich.edu    if (addrMapping == Enums::openmap) {
1957150Sgblack@eecs.umich.edu        // the lowest order bits denote the column to ensure that
1967848SAli.Saidi@ARM.com        // sequential cache lines occupy the same row
1977848SAli.Saidi@ARM.com        addr = addr / linesPerRowBuffer;
1987848SAli.Saidi@ARM.com
1997848SAli.Saidi@ARM.com        // take out the channel part of the address, note that this has
2008146SAli.Saidi@ARM.com        // to match with how accesses are interleaved between the
2018146SAli.Saidi@ARM.com        // controllers in the address mapping
2028146SAli.Saidi@ARM.com        addr = addr / channels;
2037848SAli.Saidi@ARM.com
2047150Sgblack@eecs.umich.edu        // after the column bits, we get the bank bits to interleave
2057150Sgblack@eecs.umich.edu        // over the banks
2067150Sgblack@eecs.umich.edu        bank = addr % banksPerRank;
2077150Sgblack@eecs.umich.edu        addr = addr / banksPerRank;
2087150Sgblack@eecs.umich.edu
2097150Sgblack@eecs.umich.edu        // after the bank, we get the rank bits which thus interleaves
2107150Sgblack@eecs.umich.edu        // over the ranks
2117150Sgblack@eecs.umich.edu        rank = addr % ranksPerChannel;
2127150Sgblack@eecs.umich.edu        addr = addr / ranksPerChannel;
2137150Sgblack@eecs.umich.edu
2147150Sgblack@eecs.umich.edu        // lastly, get the row bits
21510334Smitch.hayenga@arm.com        row = addr % rowsPerBank;
21610334Smitch.hayenga@arm.com        addr = addr / rowsPerBank;
21710334Smitch.hayenga@arm.com    } else if (addrMapping == Enums::closemap) {
21810334Smitch.hayenga@arm.com        // optimise for closed page mode and utilise maximum
2197150Sgblack@eecs.umich.edu        // parallelism of the DRAM (at the cost of power)
2207150Sgblack@eecs.umich.edu
2217150Sgblack@eecs.umich.edu        // take out the channel part of the address, not that this has
2228892Sb.grayson@samsung.com        // to match with how accesses are interleaved between the
2238892Sb.grayson@samsung.com        // controllers in the address mapping
2247150Sgblack@eecs.umich.edu        addr = addr / channels;
22510184SCurtis.Dunham@arm.com
2267150Sgblack@eecs.umich.edu        // start with the bank bits, as this provides the maximum
2277150Sgblack@eecs.umich.edu        // opportunity for parallelism between requests
2287150Sgblack@eecs.umich.edu        bank = addr % banksPerRank;
2297150Sgblack@eecs.umich.edu        addr = addr / banksPerRank;
2307150Sgblack@eecs.umich.edu
2318892Sb.grayson@samsung.com        // next get the rank bits
2327150Sgblack@eecs.umich.edu        rank = addr % ranksPerChannel;
2337150Sgblack@eecs.umich.edu        addr = addr / ranksPerChannel;
2348146SAli.Saidi@ARM.com
2358146SAli.Saidi@ARM.com        // next the column bits which we do not need to keep track of
2368146SAli.Saidi@ARM.com        // and simply skip past
2378146SAli.Saidi@ARM.com        addr = addr / linesPerRowBuffer;
2388146SAli.Saidi@ARM.com
2398146SAli.Saidi@ARM.com        // lastly, get the row bits
2408146SAli.Saidi@ARM.com        row = addr % rowsPerBank;
2418146SAli.Saidi@ARM.com        addr = addr / rowsPerBank;
2428146SAli.Saidi@ARM.com    } else
2438146SAli.Saidi@ARM.com        panic("Unknown address mapping policy chosen!");
2448146SAli.Saidi@ARM.com
2458146SAli.Saidi@ARM.com    assert(rank < ranksPerChannel);
2468146SAli.Saidi@ARM.com    assert(bank < banksPerRank);
2478146SAli.Saidi@ARM.com    assert(row < rowsPerBank);
2488146SAli.Saidi@ARM.com
2498146SAli.Saidi@ARM.com    DPRINTF(DRAM, "Address: %lld Rank %d Bank %d Row %d\n",
2508146SAli.Saidi@ARM.com            pkt->getAddr(), rank, bank, row);
251
252    // create the corresponding DRAM packet with the entry time and
253    // ready time set to the current tick, the latter will be updated
254    // later
255    return new DRAMPacket(pkt, rank, bank, row, pkt->getAddr(),
256                          banks[rank][bank]);
257}
258
259void
260SimpleDRAM::addToReadQueue(PacketPtr pkt)
261{
262    // only add to the read queue here. whenever the request is
263    // eventually done, set the readyTime, and call schedule()
264    assert(!pkt->isWrite());
265
266    // First check write buffer to see if the data is already at
267    // the controller
268    list<DRAMPacket*>::const_iterator i;
269    Addr addr = pkt->getAddr();
270
271    // @todo: add size check
272    for (i = writeQueue.begin(); i != writeQueue.end(); ++i) {
273        if ((*i)->addr == addr){
274            servicedByWrQ++;
275            DPRINTF(DRAM, "Read to %lld serviced by write queue\n", addr);
276            bytesRead += bytesPerCacheLine;
277            bytesConsumedRd += pkt->getSize();
278            accessAndRespond(pkt);
279            return;
280        }
281    }
282
283    DRAMPacket* dram_pkt = decodeAddr(pkt);
284
285    assert(readQueue.size() + respQueue.size() < readBufferSize);
286    rdQLenPdf[readQueue.size() + respQueue.size()]++;
287
288    DPRINTF(DRAM, "Adding to read queue\n");
289
290    readQueue.push_back(dram_pkt);
291
292    // Update stats
293    uint32_t bank_id = banksPerRank * dram_pkt->rank + dram_pkt->bank;
294    assert(bank_id < ranksPerChannel * banksPerRank);
295    perBankRdReqs[bank_id]++;
296
297    avgRdQLen = readQueue.size() + respQueue.size();
298
299    // If we are not already scheduled to get the read request out of
300    // the queue, do so now
301    if (!nextReqEvent.scheduled() && !stopReads) {
302        DPRINTF(DRAM, "Request scheduled immediately\n");
303        schedule(nextReqEvent, curTick());
304    }
305}
306
307void
308SimpleDRAM::processWriteEvent()
309{
310    assert(!writeQueue.empty());
311    uint32_t numWritesThisTime = 0;
312
313    DPRINTF(DRAMWR, "Beginning DRAM Writes\n");
314    Tick temp1 M5_VAR_USED = std::max(curTick(), busBusyUntil);
315    Tick temp2 M5_VAR_USED = std::max(curTick(), maxBankFreeAt());
316
317    // @todo: are there any dangers with the untimed while loop?
318    while (!writeQueue.empty()) {
319        if (numWritesThisTime > writeThreshold) {
320            DPRINTF(DRAMWR, "Hit write threshold %d\n", writeThreshold);
321            break;
322        }
323
324        chooseNextWrite();
325        DRAMPacket* dram_pkt = writeQueue.front();
326        // What's the earliest the request can be put on the bus
327        Tick schedTime = std::max(curTick(), busBusyUntil);
328
329        DPRINTF(DRAMWR, "Asking for latency estimate at %lld\n",
330                schedTime + tBURST);
331
332        pair<Tick, Tick> lat = estimateLatency(dram_pkt, schedTime + tBURST);
333        Tick accessLat = lat.second;
334
335        // look at the rowHitFlag set by estimateLatency
336        if (rowHitFlag)
337            writeRowHits++;
338
339        Bank& bank = dram_pkt->bank_ref;
340
341        if (pageMgmt == Enums::open) {
342            bank.openRow = dram_pkt->row;
343            bank.freeAt = schedTime + tBURST + std::max(accessLat, tCL);
344            busBusyUntil = bank.freeAt - tCL;
345
346            if (!rowHitFlag) {
347                bank.tRASDoneAt = bank.freeAt + tRP;
348                recordActivate(bank.freeAt - tCL - tRCD);
349                busBusyUntil = bank.freeAt - tCL - tRCD;
350            }
351        } else if (pageMgmt == Enums::close) {
352            bank.freeAt = schedTime + tBURST + accessLat + tRP + tRP;
353            // Work backwards from bank.freeAt to determine activate time
354            recordActivate(bank.freeAt - tRP - tRP - tCL - tRCD);
355            busBusyUntil = bank.freeAt - tRP - tRP - tCL - tRCD;
356            DPRINTF(DRAMWR, "processWriteEvent::bank.freeAt for "
357                    "banks_id %d is %lld\n",
358                    dram_pkt->rank * banksPerRank + dram_pkt->bank,
359                    bank.freeAt);
360        } else
361            panic("Unknown page management policy chosen\n");
362
363        DPRINTF(DRAMWR, "Done writing to address %lld\n", dram_pkt->addr);
364
365        DPRINTF(DRAMWR, "schedtime is %lld, tBURST is %lld, "
366                "busbusyuntil is %lld\n",
367                schedTime, tBURST, busBusyUntil);
368
369        writeQueue.pop_front();
370        delete dram_pkt;
371
372        numWritesThisTime++;
373    }
374
375    DPRINTF(DRAMWR, "Completed %d writes, bus busy for %lld ticks,"\
376            "banks busy for %lld ticks\n", numWritesThisTime,
377            busBusyUntil - temp1, maxBankFreeAt() - temp2);
378
379    // Update stats
380    avgWrQLen = writeQueue.size();
381
382    // turn the bus back around for reads again
383    busBusyUntil += tWTR;
384    stopReads = false;
385
386    if (retryWrReq) {
387        retryWrReq = false;
388        port.sendRetry();
389    }
390
391    // if there is nothing left in any queue, signal a drain
392    if (writeQueue.empty() && readQueue.empty() &&
393        respQueue.empty () && drainManager) {
394        drainManager->signalDrainDone();
395        drainManager = NULL;
396    }
397
398    // Once you're done emptying the write queue, check if there's
399    // anything in the read queue, and call schedule if required. The
400    // retry above could already have caused it to be scheduled, so
401    // first check
402    if (!nextReqEvent.scheduled())
403        schedule(nextReqEvent, busBusyUntil);
404}
405
406void
407SimpleDRAM::triggerWrites()
408{
409    DPRINTF(DRAM, "Writes triggered at %lld\n", curTick());
410    // Flag variable to stop any more read scheduling
411    stopReads = true;
412
413    writeStartTime = std::max(busBusyUntil, curTick()) + tWTR;
414
415    DPRINTF(DRAM, "Writes scheduled at %lld\n", writeStartTime);
416
417    assert(writeStartTime >= curTick());
418    assert(!writeEvent.scheduled());
419    schedule(writeEvent, writeStartTime);
420}
421
422void
423SimpleDRAM::addToWriteQueue(PacketPtr pkt)
424{
425    // only add to the write queue here. whenever the request is
426    // eventually done, set the readyTime, and call schedule()
427    assert(pkt->isWrite());
428
429    DRAMPacket* dram_pkt = decodeAddr(pkt);
430
431    assert(writeQueue.size() < writeBufferSize);
432    wrQLenPdf[writeQueue.size()]++;
433
434    DPRINTF(DRAM, "Adding to write queue\n");
435
436    writeQueue.push_back(dram_pkt);
437
438    // Update stats
439    uint32_t bank_id = banksPerRank * dram_pkt->rank + dram_pkt->bank;
440    assert(bank_id < ranksPerChannel * banksPerRank);
441    perBankWrReqs[bank_id]++;
442
443    avgWrQLen = writeQueue.size();
444
445    // we do not wait for the writes to be send to the actual memory,
446    // but instead take responsibility for the consistency here and
447    // snoop the write queue for any upcoming reads
448
449    bytesConsumedWr += pkt->getSize();
450    bytesWritten += bytesPerCacheLine;
451    accessAndRespond(pkt);
452
453    // If your write buffer is starting to fill up, drain it!
454    if (writeQueue.size() > writeThreshold && !stopReads){
455        triggerWrites();
456    }
457}
458
459void
460SimpleDRAM::printParams() const
461{
462    // Sanity check print of important parameters
463    DPRINTF(DRAM,
464            "Memory controller %s physical organization\n"      \
465            "Bytes per cacheline  %d\n"                         \
466            "Lines per row buffer %d\n"                         \
467            "Rows  per bank       %d\n"                         \
468            "Banks per rank       %d\n"                         \
469            "Ranks per channel    %d\n"                         \
470            "Total mem capacity   %u\n",
471            name(), bytesPerCacheLine, linesPerRowBuffer, rowsPerBank,
472            banksPerRank, ranksPerChannel, bytesPerCacheLine *
473            linesPerRowBuffer * rowsPerBank * banksPerRank * ranksPerChannel);
474
475    string scheduler =  memSchedPolicy == Enums::fcfs ? "FCFS" : "FR-FCFS";
476    string address_mapping = addrMapping == Enums::openmap ? "OPENMAP" :
477        "CLOSEMAP";
478    string page_policy = pageMgmt == Enums::open ? "OPEN" : "CLOSE";
479
480    DPRINTF(DRAM,
481            "Memory controller %s characteristics\n"    \
482            "Read buffer size     %d\n"                 \
483            "Write buffer size    %d\n"                 \
484            "Write buffer thresh  %d\n"                 \
485            "Scheduler            %s\n"                 \
486            "Address mapping      %s\n"                 \
487            "Page policy          %s\n",
488            name(), readBufferSize, writeBufferSize, writeThreshold,
489            scheduler, address_mapping, page_policy);
490
491    DPRINTF(DRAM, "Memory controller %s timing specs\n" \
492            "tRCD      %d ticks\n"                        \
493            "tCL       %d ticks\n"                        \
494            "tRP       %d ticks\n"                        \
495            "tBURST    %d ticks\n"                        \
496            "tRFC      %d ticks\n"                        \
497            "tREFI     %d ticks\n"                        \
498            "tWTR      %d ticks\n"                        \
499            "tXAW (%d) %d ticks\n",
500            name(), tRCD, tCL, tRP, tBURST, tRFC, tREFI, tWTR,
501            activationLimit, tXAW);
502}
503
504void
505SimpleDRAM::printQs() const {
506
507    list<DRAMPacket*>::const_iterator i;
508
509    DPRINTF(DRAM, "===READ QUEUE===\n\n");
510    for (i = readQueue.begin() ;  i != readQueue.end() ; ++i) {
511        DPRINTF(DRAM, "Read %lu\n", (*i)->addr);
512    }
513    DPRINTF(DRAM, "\n===RESP QUEUE===\n\n");
514    for (i = respQueue.begin() ;  i != respQueue.end() ; ++i) {
515        DPRINTF(DRAM, "Response %lu\n", (*i)->addr);
516    }
517    DPRINTF(DRAM, "\n===WRITE QUEUE===\n\n");
518    for (i = writeQueue.begin() ;  i != writeQueue.end() ; ++i) {
519        DPRINTF(DRAM, "Write %lu\n", (*i)->addr);
520    }
521}
522
523bool
524SimpleDRAM::recvTimingReq(PacketPtr pkt)
525{
526    /// @todo temporary hack to deal with memory corruption issues until
527    /// 4-phase transactions are complete
528    for (int x = 0; x < pendingDelete.size(); x++)
529        delete pendingDelete[x];
530    pendingDelete.clear();
531
532    // This is where we enter from the outside world
533    DPRINTF(DRAM, "recvTimingReq: request %s addr %lld size %d\n",
534            pkt->cmdString(),pkt->getAddr(), pkt->getSize());
535
536    // simply drop inhibited packets for now
537    if (pkt->memInhibitAsserted()) {
538        DPRINTF(DRAM,"Inhibited packet -- Dropping it now\n");
539        pendingDelete.push_back(pkt);
540        return true;
541    }
542
543   if (pkt->getSize() == bytesPerCacheLine)
544       cpuReqs++;
545
546   // Every million accesses, print the state of the queues
547   if (numReqs % 1000000 == 0)
548       printQs();
549
550    // Calc avg gap between requests
551    if (prevArrival != 0) {
552        totGap += curTick() - prevArrival;
553    }
554    prevArrival = curTick();
555
556    unsigned size = pkt->getSize();
557    if (size > bytesPerCacheLine)
558        panic("Request size %d is greater than burst size %d",
559              size, bytesPerCacheLine);
560
561    // check local buffers and do not accept if full
562    if (pkt->isRead()) {
563        assert(size != 0);
564        if (readQueueFull()) {
565            DPRINTF(DRAM, "Read queue full, not accepting\n");
566            // remember that we have to retry this port
567            retryRdReq = true;
568            numRdRetry++;
569            return false;
570        } else {
571            readPktSize[ceilLog2(size)]++;
572            addToReadQueue(pkt);
573            readReqs++;
574            numReqs++;
575        }
576    } else if (pkt->isWrite()) {
577        assert(size != 0);
578        if (writeQueueFull()) {
579            DPRINTF(DRAM, "Write queue full, not accepting\n");
580            // remember that we have to retry this port
581            retryWrReq = true;
582            numWrRetry++;
583            return false;
584        } else {
585            writePktSize[ceilLog2(size)]++;
586            addToWriteQueue(pkt);
587            writeReqs++;
588            numReqs++;
589        }
590    } else {
591        DPRINTF(DRAM,"Neither read nor write, ignore timing\n");
592        neitherReadNorWrite++;
593        accessAndRespond(pkt);
594    }
595
596    retryRdReq = false;
597    retryWrReq = false;
598    return true;
599}
600
601void
602SimpleDRAM::processRespondEvent()
603{
604    DPRINTF(DRAM,
605            "processRespondEvent(): Some req has reached its readyTime\n");
606
607     PacketPtr pkt = respQueue.front()->pkt;
608
609     // Actually responds to the requestor
610     bytesConsumedRd += pkt->getSize();
611     bytesRead += bytesPerCacheLine;
612     accessAndRespond(pkt);
613
614     delete respQueue.front();
615     respQueue.pop_front();
616
617     // Update stats
618     avgRdQLen = readQueue.size() + respQueue.size();
619
620     if (!respQueue.empty()) {
621         assert(respQueue.front()->readyTime >= curTick());
622         assert(!respondEvent.scheduled());
623         schedule(respondEvent, respQueue.front()->readyTime);
624     } else {
625         // if there is nothing left in any queue, signal a drain
626         if (writeQueue.empty() && readQueue.empty() &&
627             drainManager) {
628             drainManager->signalDrainDone();
629             drainManager = NULL;
630         }
631     }
632
633     // We have made a location in the queue available at this point,
634     // so if there is a read that was forced to wait, retry now
635     if (retryRdReq) {
636         retryRdReq = false;
637         port.sendRetry();
638     }
639}
640
641void
642SimpleDRAM::chooseNextWrite()
643{
644    // This method does the arbitration between write requests. The
645    // chosen packet is simply moved to the head of the write
646    // queue. The other methods know that this is the place to
647    // look. For example, with FCFS, this method does nothing
648    assert(!writeQueue.empty());
649
650    if (writeQueue.size() == 1) {
651        DPRINTF(DRAMWR, "Single write request, nothing to do\n");
652        return;
653    }
654
655    if (memSchedPolicy == Enums::fcfs) {
656        // Do nothing, since the correct request is already head
657    } else if (memSchedPolicy == Enums::frfcfs) {
658        list<DRAMPacket*>::iterator i = writeQueue.begin();
659        bool foundRowHit = false;
660        while (!foundRowHit && i != writeQueue.end()) {
661            DRAMPacket* dram_pkt = *i;
662            const Bank& bank = dram_pkt->bank_ref;
663            if (bank.openRow == dram_pkt->row) { //FR part
664                DPRINTF(DRAMWR, "Write row buffer hit\n");
665                writeQueue.erase(i);
666                writeQueue.push_front(dram_pkt);
667                foundRowHit = true;
668            } else { //FCFS part
669                ;
670            }
671            ++i;
672        }
673    } else
674        panic("No scheduling policy chosen\n");
675
676    DPRINTF(DRAMWR, "Selected next write request\n");
677}
678
679bool
680SimpleDRAM::chooseNextRead()
681{
682    // This method does the arbitration between read requests. The
683    // chosen packet is simply moved to the head of the queue. The
684    // other methods know that this is the place to look. For example,
685    // with FCFS, this method does nothing
686    if (readQueue.empty()) {
687        DPRINTF(DRAM, "No read request to select\n");
688        return false;
689    }
690
691    // If there is only one request then there is nothing left to do
692    if (readQueue.size() == 1)
693        return true;
694
695    if (memSchedPolicy == Enums::fcfs) {
696        // Do nothing, since the request to serve is already the first
697        // one in the read queue
698    } else if (memSchedPolicy == Enums::frfcfs) {
699        for (list<DRAMPacket*>::iterator i = readQueue.begin();
700             i != readQueue.end() ; ++i) {
701            DRAMPacket* dram_pkt = *i;
702            const Bank& bank = dram_pkt->bank_ref;
703            // Check if it is a row hit
704            if (bank.openRow == dram_pkt->row) { //FR part
705                DPRINTF(DRAM, "Row buffer hit\n");
706                readQueue.erase(i);
707                readQueue.push_front(dram_pkt);
708                break;
709            } else { //FCFS part
710                ;
711            }
712        }
713    } else
714        panic("No scheduling policy chosen!\n");
715
716    DPRINTF(DRAM, "Selected next read request\n");
717    return true;
718}
719
720void
721SimpleDRAM::accessAndRespond(PacketPtr pkt)
722{
723    DPRINTF(DRAM, "Responding to Address %lld.. ",pkt->getAddr());
724
725    bool needsResponse = pkt->needsResponse();
726    // do the actual memory access which also turns the packet into a
727    // response
728    access(pkt);
729
730    // turn packet around to go back to requester if response expected
731    if (needsResponse) {
732        // access already turned the packet into a response
733        assert(pkt->isResponse());
734
735        // @todo someone should pay for this
736        pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;
737
738        // queue the packet in the response queue to be sent out the
739        // next tick
740        port.schedTimingResp(pkt, curTick() + 1);
741    } else {
742        // @todo the packet is going to be deleted, and the DRAMPacket
743        // is still having a pointer to it
744        pendingDelete.push_back(pkt);
745    }
746
747    DPRINTF(DRAM, "Done\n");
748
749    return;
750}
751
752pair<Tick, Tick>
753SimpleDRAM::estimateLatency(DRAMPacket* dram_pkt, Tick inTime)
754{
755    // If a request reaches a bank at tick 'inTime', how much time
756    // *after* that does it take to finish the request, depending
757    // on bank status and page open policy. Note that this method
758    // considers only the time taken for the actual read or write
759    // to complete, NOT any additional time thereafter for tRAS or
760    // tRP.
761    Tick accLat = 0;
762    Tick bankLat = 0;
763    rowHitFlag = false;
764
765    const Bank& bank = dram_pkt->bank_ref;
766    if (pageMgmt == Enums::open) { // open-page policy
767        if (bank.openRow == dram_pkt->row) {
768            // When we have a row-buffer hit,
769            // we don't care about tRAS having expired or not,
770            // but do care about bank being free for access
771            rowHitFlag = true;
772
773            if (bank.freeAt < inTime) {
774               // CAS latency only
775               accLat += tCL;
776               bankLat += tCL;
777            } else {
778                accLat += 0;
779                bankLat += 0;
780            }
781
782        } else {
783            // Row-buffer miss, need to close existing row
784            // once tRAS has expired, then open the new one,
785            // then add cas latency.
786            Tick freeTime = std::max(bank.tRASDoneAt, bank.freeAt);
787
788            if (freeTime > inTime)
789               accLat += freeTime - inTime;
790
791            accLat += tRP + tRCD + tCL;
792            bankLat += tRP + tRCD + tCL;
793        }
794    } else if (pageMgmt == Enums::close) {
795        // With a close page policy, no notion of
796        // bank.tRASDoneAt
797        if (bank.freeAt > inTime)
798            accLat += bank.freeAt - inTime;
799
800        // page already closed, simply open the row, and
801        // add cas latency
802        accLat += tRCD + tCL;
803        bankLat += tRCD + tCL;
804    } else
805        panic("No page management policy chosen\n");
806
807    DPRINTF(DRAM, "Returning < %lld, %lld > from estimateLatency()\n",
808            bankLat, accLat);
809
810    return make_pair(bankLat, accLat);
811}
812
813void
814SimpleDRAM::processNextReqEvent()
815{
816    scheduleNextReq();
817}
818
819void
820SimpleDRAM::recordActivate(Tick act_tick)
821{
822    assert(actTicks.size() == activationLimit);
823
824    DPRINTF(DRAM, "Activate at tick %d\n", act_tick);
825
826    // sanity check
827    if (actTicks.back() && (act_tick - actTicks.back()) < tXAW) {
828        panic("Got %d activates in window %d (%d - %d) which is smaller "
829              "than %d\n", activationLimit, act_tick - actTicks.back(),
830              act_tick, actTicks.back(), tXAW);
831    }
832
833    // shift the times used for the book keeping, the last element
834    // (highest index) is the oldest one and hence the lowest value
835    actTicks.pop_back();
836
837    // record an new activation (in the future)
838    actTicks.push_front(act_tick);
839
840    // cannot activate more than X times in time window tXAW, push the
841    // next one (the X + 1'st activate) to be tXAW away from the
842    // oldest in our window of X
843    if (actTicks.back() && (act_tick - actTicks.back()) < tXAW) {
844        DPRINTF(DRAM, "Enforcing tXAW with X = %d, next activate no earlier "
845                "than %d\n", activationLimit, actTicks.back() + tXAW);
846        for(int i = 0; i < ranksPerChannel; i++)
847            for(int j = 0; j < banksPerRank; j++)
848                // next activate must not happen before end of window
849                banks[i][j].freeAt = std::max(banks[i][j].freeAt,
850                                              actTicks.back() + tXAW);
851    }
852}
853
854void
855SimpleDRAM::doDRAMAccess(DRAMPacket* dram_pkt)
856{
857
858    DPRINTF(DRAM, "Timing access to addr %lld, rank/bank/row %d %d %d\n",
859            dram_pkt->addr, dram_pkt->rank, dram_pkt->bank, dram_pkt->row);
860
861    // estimate the bank and access latency
862    pair<Tick, Tick> lat = estimateLatency(dram_pkt, curTick());
863    Tick bankLat = lat.first;
864    Tick accessLat = lat.second;
865
866    // This request was woken up at this time based on a prior call
867    // to estimateLatency(). However, between then and now, both the
868    // accessLatency and/or busBusyUntil may have changed. We need
869    // to correct for that.
870
871    Tick addDelay = (curTick() + accessLat < busBusyUntil) ?
872        busBusyUntil - (curTick() + accessLat) : 0;
873
874    Bank& bank = dram_pkt->bank_ref;
875
876    // Update bank state
877    if (pageMgmt == Enums::open) {
878        bank.openRow = dram_pkt->row;
879        bank.freeAt = curTick() + addDelay + accessLat;
880        // If you activated a new row do to this access, the next access
881        // will have to respect tRAS for this bank. Assume tRAS ~= 3 * tRP.
882        // Also need to account for t_XAW
883        if (!rowHitFlag) {
884            bank.tRASDoneAt = bank.freeAt + tRP;
885            recordActivate(bank.freeAt - tCL - tRCD); //since this is open page,
886                                                      //no tRP by default
887        }
888    } else if (pageMgmt == Enums::close) { // accounting for tRAS also
889        // assuming that tRAS ~= 3 * tRP, and tRC ~= 4 * tRP, as is common
890        // (refer Jacob/Ng/Wang and Micron datasheets)
891        bank.freeAt = curTick() + addDelay + accessLat + tRP + tRP;
892        recordActivate(bank.freeAt - tRP - tRP - tCL - tRCD); //essentially (freeAt - tRC)
893        DPRINTF(DRAM,"doDRAMAccess::bank.freeAt is %lld\n",bank.freeAt);
894    } else
895        panic("No page management policy chosen\n");
896
897    // Update request parameters
898    dram_pkt->readyTime = curTick() + addDelay + accessLat + tBURST;
899
900
901    DPRINTF(DRAM, "Req %lld: curtick is %lld accessLat is %d " \
902                  "readytime is %lld busbusyuntil is %lld. " \
903                  "Scheduling at readyTime\n", dram_pkt->addr,
904                   curTick(), accessLat, dram_pkt->readyTime, busBusyUntil);
905
906    // Make sure requests are not overlapping on the databus
907    assert (dram_pkt->readyTime - busBusyUntil >= tBURST);
908
909    // Update bus state
910    busBusyUntil = dram_pkt->readyTime;
911
912    DPRINTF(DRAM,"Access time is %lld\n",
913            dram_pkt->readyTime - dram_pkt->entryTime);
914
915    // Update stats
916    totMemAccLat += dram_pkt->readyTime - dram_pkt->entryTime;
917    totBankLat += bankLat;
918    totBusLat += tBURST;
919    totQLat += dram_pkt->readyTime - dram_pkt->entryTime - bankLat - tBURST;
920
921    if (rowHitFlag)
922        readRowHits++;
923
924    // At this point we're done dealing with the request
925    // It will be moved to a separate response queue with a
926    // correct readyTime, and eventually be sent back at that
927    //time
928    moveToRespQ();
929
930    // The absolute soonest you have to start thinking about the
931    // next request is the longest access time that can occur before
932    // busBusyUntil. Assuming you need to meet tRAS, then precharge,
933    // open a new row, and access, it is ~4*tRCD.
934
935
936    Tick newTime = (busBusyUntil > 4 * tRCD) ?
937                   std::max(busBusyUntil - 4 * tRCD, curTick()) :
938                   curTick();
939
940    if (!nextReqEvent.scheduled() && !stopReads){
941        schedule(nextReqEvent, newTime);
942    } else {
943        if (newTime < nextReqEvent.when())
944            reschedule(nextReqEvent, newTime);
945    }
946
947
948}
949
950void
951SimpleDRAM::moveToRespQ()
952{
953    // Remove from read queue
954    DRAMPacket* dram_pkt = readQueue.front();
955    readQueue.pop_front();
956
957    // Insert into response queue sorted by readyTime
958    // It will be sent back to the requestor at its
959    // readyTime
960    if (respQueue.empty()) {
961        respQueue.push_front(dram_pkt);
962        assert(!respondEvent.scheduled());
963        assert(dram_pkt->readyTime >= curTick());
964        schedule(respondEvent, dram_pkt->readyTime);
965    } else {
966        bool done = false;
967        list<DRAMPacket*>::iterator i = respQueue.begin();
968        while (!done && i != respQueue.end()) {
969            if ((*i)->readyTime > dram_pkt->readyTime) {
970                respQueue.insert(i, dram_pkt);
971                done = true;
972            }
973            ++i;
974        }
975
976        if (!done)
977            respQueue.push_back(dram_pkt);
978
979        assert(respondEvent.scheduled());
980
981        if (respQueue.front()->readyTime < respondEvent.when()) {
982            assert(respQueue.front()->readyTime >= curTick());
983            reschedule(respondEvent, respQueue.front()->readyTime);
984        }
985    }
986}
987
988void
989SimpleDRAM::scheduleNextReq()
990{
991    DPRINTF(DRAM, "Reached scheduleNextReq()\n");
992
993    // Figure out which read request goes next, and move it to the
994    // front of the read queue
995    if (!chooseNextRead()) {
996        // In the case there is no read request to go next, see if we
997        // are asked to drain, and if so trigger writes, this also
998        // ensures that if we hit the write limit we will do this
999        // multiple times until we are completely drained
1000        if (drainManager && !writeQueue.empty() && !writeEvent.scheduled())
1001            triggerWrites();
1002    } else {
1003        doDRAMAccess(readQueue.front());
1004    }
1005}
1006
1007Tick
1008SimpleDRAM::maxBankFreeAt() const
1009{
1010    Tick banksFree = 0;
1011
1012    for(int i = 0; i < ranksPerChannel; i++)
1013        for(int j = 0; j < banksPerRank; j++)
1014            banksFree = std::max(banks[i][j].freeAt, banksFree);
1015
1016    return banksFree;
1017}
1018
1019void
1020SimpleDRAM::processRefreshEvent()
1021{
1022    DPRINTF(DRAM, "Refreshing at tick %ld\n", curTick());
1023
1024    Tick banksFree = std::max(curTick(), maxBankFreeAt()) + tRFC;
1025
1026    for(int i = 0; i < ranksPerChannel; i++)
1027        for(int j = 0; j < banksPerRank; j++)
1028            banks[i][j].freeAt = banksFree;
1029
1030    schedule(refreshEvent, curTick() + tREFI);
1031}
1032
1033void
1034SimpleDRAM::regStats()
1035{
1036    using namespace Stats;
1037
1038    AbstractMemory::regStats();
1039
1040    readReqs
1041        .name(name() + ".readReqs")
1042        .desc("Total number of read requests seen");
1043
1044    writeReqs
1045        .name(name() + ".writeReqs")
1046        .desc("Total number of write requests seen");
1047
1048    servicedByWrQ
1049        .name(name() + ".servicedByWrQ")
1050        .desc("Number of read reqs serviced by write Q");
1051
1052    cpuReqs
1053        .name(name() + ".cpureqs")
1054        .desc("Reqs generatd by CPU via cache - shady");
1055
1056    neitherReadNorWrite
1057        .name(name() + ".neitherReadNorWrite")
1058        .desc("Reqs where no action is needed");
1059
1060    perBankRdReqs
1061        .init(banksPerRank * ranksPerChannel)
1062        .name(name() + ".perBankRdReqs")
1063        .desc("Track reads on a per bank basis");
1064
1065    perBankWrReqs
1066        .init(banksPerRank * ranksPerChannel)
1067        .name(name() + ".perBankWrReqs")
1068        .desc("Track writes on a per bank basis");
1069
1070    avgRdQLen
1071        .name(name() + ".avgRdQLen")
1072        .desc("Average read queue length over time")
1073        .precision(2);
1074
1075    avgWrQLen
1076        .name(name() + ".avgWrQLen")
1077        .desc("Average write queue length over time")
1078        .precision(2);
1079
1080    totQLat
1081        .name(name() + ".totQLat")
1082        .desc("Total cycles spent in queuing delays");
1083
1084    totBankLat
1085        .name(name() + ".totBankLat")
1086        .desc("Total cycles spent in bank access");
1087
1088    totBusLat
1089        .name(name() + ".totBusLat")
1090        .desc("Total cycles spent in databus access");
1091
1092    totMemAccLat
1093        .name(name() + ".totMemAccLat")
1094        .desc("Sum of mem lat for all requests");
1095
1096    avgQLat
1097        .name(name() + ".avgQLat")
1098        .desc("Average queueing delay per request")
1099        .precision(2);
1100
1101    avgQLat = totQLat / (readReqs - servicedByWrQ);
1102
1103    avgBankLat
1104        .name(name() + ".avgBankLat")
1105        .desc("Average bank access latency per request")
1106        .precision(2);
1107
1108    avgBankLat = totBankLat / (readReqs - servicedByWrQ);
1109
1110    avgBusLat
1111        .name(name() + ".avgBusLat")
1112        .desc("Average bus latency per request")
1113        .precision(2);
1114
1115    avgBusLat = totBusLat / (readReqs - servicedByWrQ);
1116
1117    avgMemAccLat
1118        .name(name() + ".avgMemAccLat")
1119        .desc("Average memory access latency")
1120        .precision(2);
1121
1122    avgMemAccLat = totMemAccLat / (readReqs - servicedByWrQ);
1123
1124    numRdRetry
1125        .name(name() + ".numRdRetry")
1126        .desc("Number of times rd buffer was full causing retry");
1127
1128    numWrRetry
1129        .name(name() + ".numWrRetry")
1130        .desc("Number of times wr buffer was full causing retry");
1131
1132    readRowHits
1133        .name(name() + ".readRowHits")
1134        .desc("Number of row buffer hits during reads");
1135
1136    writeRowHits
1137        .name(name() + ".writeRowHits")
1138        .desc("Number of row buffer hits during writes");
1139
1140    readRowHitRate
1141        .name(name() + ".readRowHitRate")
1142        .desc("Row buffer hit rate for reads")
1143        .precision(2);
1144
1145    readRowHitRate = (readRowHits / (readReqs - servicedByWrQ)) * 100;
1146
1147    writeRowHitRate
1148        .name(name() + ".writeRowHitRate")
1149        .desc("Row buffer hit rate for writes")
1150        .precision(2);
1151
1152    writeRowHitRate = (writeRowHits / writeReqs) * 100;
1153
1154    readPktSize
1155        .init(ceilLog2(bytesPerCacheLine) + 1)
1156        .name(name() + ".readPktSize")
1157        .desc("Categorize read packet sizes");
1158
1159     writePktSize
1160        .init(ceilLog2(bytesPerCacheLine) + 1)
1161        .name(name() + ".writePktSize")
1162        .desc("Categorize write packet sizes");
1163
1164     rdQLenPdf
1165        .init(readBufferSize)
1166        .name(name() + ".rdQLenPdf")
1167        .desc("What read queue length does an incoming req see");
1168
1169     wrQLenPdf
1170        .init(writeBufferSize)
1171        .name(name() + ".wrQLenPdf")
1172        .desc("What write queue length does an incoming req see");
1173
1174
1175    bytesRead
1176        .name(name() + ".bytesRead")
1177        .desc("Total number of bytes read from memory");
1178
1179    bytesWritten
1180        .name(name() + ".bytesWritten")
1181        .desc("Total number of bytes written to memory");
1182
1183    bytesConsumedRd
1184        .name(name() + ".bytesConsumedRd")
1185        .desc("bytesRead derated as per pkt->getSize()");
1186
1187    bytesConsumedWr
1188        .name(name() + ".bytesConsumedWr")
1189        .desc("bytesWritten derated as per pkt->getSize()");
1190
1191    avgRdBW
1192        .name(name() + ".avgRdBW")
1193        .desc("Average achieved read bandwidth in MB/s")
1194        .precision(2);
1195
1196    avgRdBW = (bytesRead / 1000000) / simSeconds;
1197
1198    avgWrBW
1199        .name(name() + ".avgWrBW")
1200        .desc("Average achieved write bandwidth in MB/s")
1201        .precision(2);
1202
1203    avgWrBW = (bytesWritten / 1000000) / simSeconds;
1204
1205    avgConsumedRdBW
1206        .name(name() + ".avgConsumedRdBW")
1207        .desc("Average consumed read bandwidth in MB/s")
1208        .precision(2);
1209
1210    avgConsumedRdBW = (bytesConsumedRd / 1000000) / simSeconds;
1211
1212    avgConsumedWrBW
1213        .name(name() + ".avgConsumedWrBW")
1214        .desc("Average consumed write bandwidth in MB/s")
1215        .precision(2);
1216
1217    avgConsumedWrBW = (bytesConsumedWr / 1000000) / simSeconds;
1218
1219    peakBW
1220        .name(name() + ".peakBW")
1221        .desc("Theoretical peak bandwidth in MB/s")
1222        .precision(2);
1223
1224    peakBW = (SimClock::Frequency / tBURST) * bytesPerCacheLine / 1000000;
1225
1226    busUtil
1227        .name(name() + ".busUtil")
1228        .desc("Data bus utilization in percentage")
1229        .precision(2);
1230
1231    busUtil = (avgRdBW + avgWrBW) / peakBW * 100;
1232
1233    totGap
1234        .name(name() + ".totGap")
1235        .desc("Total gap between requests");
1236
1237    avgGap
1238        .name(name() + ".avgGap")
1239        .desc("Average gap between requests")
1240        .precision(2);
1241
1242    avgGap = totGap / (readReqs + writeReqs);
1243}
1244
1245void
1246SimpleDRAM::recvFunctional(PacketPtr pkt)
1247{
1248    // rely on the abstract memory
1249    functionalAccess(pkt);
1250}
1251
1252BaseSlavePort&
1253SimpleDRAM::getSlavePort(const string &if_name, PortID idx)
1254{
1255    if (if_name != "port") {
1256        return MemObject::getSlavePort(if_name, idx);
1257    } else {
1258        return port;
1259    }
1260}
1261
1262unsigned int
1263SimpleDRAM::drain(DrainManager *dm)
1264{
1265    unsigned int count = port.drain(dm);
1266
1267    // if there is anything in any of our internal queues, keep track
1268    // of that as well
1269    if (!(writeQueue.empty() && readQueue.empty() &&
1270          respQueue.empty())) {
1271        DPRINTF(Drain, "DRAM controller not drained, write: %d, read: %d,"
1272                " resp: %d\n", writeQueue.size(), readQueue.size(),
1273                respQueue.size());
1274        ++count;
1275        drainManager = dm;
1276        // the only part that is not drained automatically over time
1277        // is the write queue, thus trigger writes if there are any
1278        // waiting and no reads waiting, otherwise wait until the
1279        // reads are done
1280        if (readQueue.empty() && !writeQueue.empty() &&
1281            !writeEvent.scheduled())
1282            triggerWrites();
1283    }
1284
1285    if (count)
1286        setDrainState(Drainable::Draining);
1287    else
1288        setDrainState(Drainable::Drained);
1289    return count;
1290}
1291
1292SimpleDRAM::MemoryPort::MemoryPort(const std::string& name, SimpleDRAM& _memory)
1293    : QueuedSlavePort(name, &_memory, queue), queue(_memory, *this),
1294      memory(_memory)
1295{ }
1296
1297AddrRangeList
1298SimpleDRAM::MemoryPort::getAddrRanges() const
1299{
1300    AddrRangeList ranges;
1301    ranges.push_back(memory.getAddrRange());
1302    return ranges;
1303}
1304
1305void
1306SimpleDRAM::MemoryPort::recvFunctional(PacketPtr pkt)
1307{
1308    pkt->pushLabel(memory.name());
1309
1310    if (!queue.checkFunctional(pkt)) {
1311        // Default implementation of SimpleTimingPort::recvFunctional()
1312        // calls recvAtomic() and throws away the latency; we can save a
1313        // little here by just not calculating the latency.
1314        memory.recvFunctional(pkt);
1315    }
1316
1317    pkt->popLabel();
1318}
1319
1320Tick
1321SimpleDRAM::MemoryPort::recvAtomic(PacketPtr pkt)
1322{
1323    return memory.recvAtomic(pkt);
1324}
1325
1326bool
1327SimpleDRAM::MemoryPort::recvTimingReq(PacketPtr pkt)
1328{
1329    // pass it to the memory controller
1330    return memory.recvTimingReq(pkt);
1331}
1332
1333SimpleDRAM*
1334SimpleDRAMParams::create()
1335{
1336    return new SimpleDRAM(this);
1337}
1338