flash_device.cc revision 11321:02e930db812d
111723Sar4jc@virginia.edu/*
211723Sar4jc@virginia.edu * Copyright (c) 2013-2015 ARM Limited
311723Sar4jc@virginia.edu * All rights reserved
412808Srobert.scheffel1@tu-dresden.de *
511723Sar4jc@virginia.edu * The license below extends only to copyright in the software and shall
611723Sar4jc@virginia.edu * not be construed as granting a license to any other intellectual
711723Sar4jc@virginia.edu * property including but not limited to intellectual property relating
811723Sar4jc@virginia.edu * to a hardware implementation of the functionality of the software
911723Sar4jc@virginia.edu * licensed hereunder.  You may use the software subject to the license
1011723Sar4jc@virginia.edu * terms below provided that you ensure that this notice is replicated
1111723Sar4jc@virginia.edu * unmodified and in its entirety in all distributions of the software,
1211723Sar4jc@virginia.edu * modified or unmodified, in source code or in binary form.
1311723Sar4jc@virginia.edu *
1411723Sar4jc@virginia.edu * Redistribution and use in source and binary forms, with or without
1511723Sar4jc@virginia.edu * modification, are permitted provided that the following conditions are
1611723Sar4jc@virginia.edu * met: redistributions of source code must retain the above copyright
1711723Sar4jc@virginia.edu * notice, this list of conditions and the following disclaimer;
1811723Sar4jc@virginia.edu * redistributions in binary form must reproduce the above copyright
1911723Sar4jc@virginia.edu * notice, this list of conditions and the following disclaimer in the
2011723Sar4jc@virginia.edu * documentation and/or other materials provided with the distribution;
2111723Sar4jc@virginia.edu * neither the name of the copyright holders nor the names of its
2211723Sar4jc@virginia.edu * contributors may be used to endorse or promote products derived from
2311723Sar4jc@virginia.edu * this software without specific prior written permission.
2411723Sar4jc@virginia.edu *
2511723Sar4jc@virginia.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2611723Sar4jc@virginia.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2711723Sar4jc@virginia.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2811723Sar4jc@virginia.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2911723Sar4jc@virginia.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3011723Sar4jc@virginia.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3112808Srobert.scheffel1@tu-dresden.de * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3211723Sar4jc@virginia.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3311723Sar4jc@virginia.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3411723Sar4jc@virginia.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3512848Sar4jc@virginia.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3612848Sar4jc@virginia.edu *
3712808Srobert.scheffel1@tu-dresden.de * Authors: Rene de Jong
3811723Sar4jc@virginia.edu */
3912808Srobert.scheffel1@tu-dresden.de
4011723Sar4jc@virginia.edu/** @file
4111723Sar4jc@virginia.edu * This simplistic flash model is designed to model managed SLC NAND flash.
4211723Sar4jc@virginia.edu * This device will need an interface module (such as NVMe or UFS); Note that
4311723Sar4jc@virginia.edu * this model only calculates the delay and does not perform the actual
4412848Sar4jc@virginia.edu * transaction.
4512848Sar4jc@virginia.edu *
4611723Sar4jc@virginia.edu * To access the memory, use either readMemory or writeMemory. This will
4711723Sar4jc@virginia.edu * schedule an event at the tick where the action will finish. If a callback
4812848Sar4jc@virginia.edu * has been given as argument then that function will be called on completion
4911723Sar4jc@virginia.edu * of that event. Note that this does not guarantee that there are no other
5011723Sar4jc@virginia.edu * actions pending in the flash device.
5111723Sar4jc@virginia.edu *
5211723Sar4jc@virginia.edu * IMPORTANT: number of planes should be a power of 2.
5311723Sar4jc@virginia.edu */
5411723Sar4jc@virginia.edu
5511723Sar4jc@virginia.edu#include "dev/arm/flash_device.hh"
5612848Sar4jc@virginia.edu
5712848Sar4jc@virginia.edu#include "debug/Drain.hh"
5811723Sar4jc@virginia.edu
5912848Sar4jc@virginia.edu/**
6012848Sar4jc@virginia.edu * Create this device
6112848Sar4jc@virginia.edu */
6212848Sar4jc@virginia.edu
6312848Sar4jc@virginia.eduFlashDevice*
6413548Salec.roelke@gmail.comFlashDeviceParams::create()
6513548Salec.roelke@gmail.com{
6613548Salec.roelke@gmail.com    return new FlashDevice(this);
6713548Salec.roelke@gmail.com}
6813548Salec.roelke@gmail.com
6913548Salec.roelke@gmail.com
7013548Salec.roelke@gmail.com/**
7113548Salec.roelke@gmail.com * Flash Device constructor and destructor
7213548Salec.roelke@gmail.com */
7313548Salec.roelke@gmail.com
7413548Salec.roelke@gmail.comFlashDevice::FlashDevice(const FlashDeviceParams* p):
7513548Salec.roelke@gmail.com    AbstractNVM(p),
7613548Salec.roelke@gmail.com    diskSize(0),
7713548Salec.roelke@gmail.com    blockSize(p->blk_size),
7813548Salec.roelke@gmail.com    pageSize(p->page_size),
7913548Salec.roelke@gmail.com    GCActivePercentage(p->GC_active),
8013548Salec.roelke@gmail.com    readLatency(p->read_lat),
8113548Salec.roelke@gmail.com    writeLatency(p->write_lat),
8212848Sar4jc@virginia.edu    eraseLatency(p->erase_lat),
8312848Sar4jc@virginia.edu    dataDistribution(p->data_distribution),
8412848Sar4jc@virginia.edu    numPlanes(p->num_planes),
8512849Sar4jc@virginia.edu    pagesPerBlock(0),
8612848Sar4jc@virginia.edu    pagesPerDisk(0),
8712848Sar4jc@virginia.edu    blocksPerDisk(0),
8812848Sar4jc@virginia.edu    planeMask(numPlanes - 1),
8912848Sar4jc@virginia.edu    planeEventQueue(numPlanes),
9012848Sar4jc@virginia.edu    planeEvent(this)
9112849Sar4jc@virginia.edu{
9212848Sar4jc@virginia.edu
9312848Sar4jc@virginia.edu    /*
9412848Sar4jc@virginia.edu     * Let 'a' be a power of two of n bits, written such that a-n is the msb
9512848Sar4jc@virginia.edu     * and a-0 is the lsb. Since it is a power of two, only one bit (a-x,
9612848Sar4jc@virginia.edu     * with 0 <= x <= n) is set. If we subtract one from this number the bits
9712848Sar4jc@virginia.edu     * a-(x-1) to a-0 are set and all the other bits are cleared. Hence a
9812848Sar4jc@virginia.edu     * bitwise AND with those two numbers results in an integer with all bits
9912848Sar4jc@virginia.edu     * cleared.
10012849Sar4jc@virginia.edu     */
10112848Sar4jc@virginia.edu    if (numPlanes & planeMask)
10212848Sar4jc@virginia.edu        fatal("Number of planes is not a power of 2 in flash device.\n");
10312848Sar4jc@virginia.edu}
10412848Sar4jc@virginia.edu
10512848Sar4jc@virginia.edu/**
10612848Sar4jc@virginia.edu * Initiates all the flash functions: initializes the lookup tables, age of
10712848Sar4jc@virginia.edu * the device, etc. This can only be done once the disk image is known.
10812848Sar4jc@virginia.edu * Thats why it can't be done in the constructor.
10912848Sar4jc@virginia.edu */
11012849Sar4jc@virginia.eduvoid
11112848Sar4jc@virginia.eduFlashDevice::initializeFlash(uint64_t disk_size, uint32_t sector_size)
11212848Sar4jc@virginia.edu{
11312848Sar4jc@virginia.edu    diskSize = disk_size * sector_size;
11412848Sar4jc@virginia.edu    pagesPerBlock = blockSize / pageSize;
11512848Sar4jc@virginia.edu    pagesPerDisk = diskSize / pageSize;
11612848Sar4jc@virginia.edu    blocksPerDisk = diskSize / blockSize;
11712848Sar4jc@virginia.edu
11812848Sar4jc@virginia.edu    /** Sanity information: check flash configuration */
11912848Sar4jc@virginia.edu    DPRINTF(FlashDevice, "diskSize: %d Bytes; %d pages per block, %d pages "
12012848Sar4jc@virginia.edu            "per disk\n", diskSize, pagesPerBlock, pagesPerDisk);
12112848Sar4jc@virginia.edu
12212848Sar4jc@virginia.edu    locationTable.resize(pagesPerDisk);
12313612Sgabeblack@google.com
12412848Sar4jc@virginia.edu    /**Garbage collection related*/
12512849Sar4jc@virginia.edu    blockValidEntries.resize(blocksPerDisk, 0);
12612848Sar4jc@virginia.edu    blockEmptyEntries.resize(blocksPerDisk, pagesPerBlock);
12712848Sar4jc@virginia.edu
12812848Sar4jc@virginia.edu    /**
12912848Sar4jc@virginia.edu     * This is a bitmap. Every bit is a page
13013548Salec.roelke@gmail.com     * unknownPages is a vector of 32 bit integers. If every page was an
13113548Salec.roelke@gmail.com     * integer, the total size would be pagesPerDisk; since we can map one
13213548Salec.roelke@gmail.com     * page per bit we need ceil(pagesPerDisk/32) entries. 32 = 1 << 5 hence
13313548Salec.roelke@gmail.com     * it will do to just shift pagesPerDisk five positions and add one. This
13411723Sar4jc@virginia.edu     * will allocate one integer to many for this data structure in the worst
13512848Sar4jc@virginia.edu     * case.
13611723Sar4jc@virginia.edu     */
13711723Sar4jc@virginia.edu    unknownPages.resize((pagesPerDisk >> 5) + 1, 0xFFFFFFFF);
13812848Sar4jc@virginia.edu
13911723Sar4jc@virginia.edu    for (uint32_t count = 0; count < pagesPerDisk; count++) {
14011723Sar4jc@virginia.edu        //setup lookup table + physical aspects
14112808Srobert.scheffel1@tu-dresden.de
14212808Srobert.scheffel1@tu-dresden.de        if (dataDistribution == Enums::stripe) {
14313547Sar4jc@virginia.edu            locationTable[count].page = count / blocksPerDisk;
14413547Sar4jc@virginia.edu            locationTable[count].block = count % blocksPerDisk;
14513547Sar4jc@virginia.edu
14613547Sar4jc@virginia.edu        } else {
14713547Sar4jc@virginia.edu            locationTable[count].page = count % pagesPerBlock;
14813547Sar4jc@virginia.edu            locationTable[count].block = count / pagesPerBlock;
14913547Sar4jc@virginia.edu        }
15012808Srobert.scheffel1@tu-dresden.de    }
15112808Srobert.scheffel1@tu-dresden.de}
15212808Srobert.scheffel1@tu-dresden.de
15312808Srobert.scheffel1@tu-dresden.deFlashDevice::~FlashDevice()
15412808Srobert.scheffel1@tu-dresden.de{
15511723Sar4jc@virginia.edu    DPRINTF(FlashDevice, "Remove FlashDevice\n");
15612848Sar4jc@virginia.edu}
15711723Sar4jc@virginia.edu
15811723Sar4jc@virginia.edu/**
15911723Sar4jc@virginia.edu * Handles the accesses to the device.
16011723Sar4jc@virginia.edu * The function determines when certain actions are scheduled and schedules
16111723Sar4jc@virginia.edu * an event that uses the callback function on completion of the action.
16211723Sar4jc@virginia.edu */
16312848Sar4jc@virginia.eduvoid
16412136Sar4jc@virginia.eduFlashDevice::accessDevice(uint64_t address, uint32_t amount, Callback *event,
16512136Sar4jc@virginia.edu                          Actions action)
16612136Sar4jc@virginia.edu{
16712136Sar4jc@virginia.edu    DPRINTF(FlashDevice, "Flash calculation for %d bytes in %d pages\n"
16812136Sar4jc@virginia.edu            , amount, pageSize);
16912136Sar4jc@virginia.edu
17012848Sar4jc@virginia.edu    std::vector<Tick> time(numPlanes, 0);
17111723Sar4jc@virginia.edu    uint64_t logic_page_addr = address / pageSize;
17211723Sar4jc@virginia.edu    uint32_t plane_address = 0;
17311723Sar4jc@virginia.edu
17411723Sar4jc@virginia.edu    /**
17511723Sar4jc@virginia.edu     * The access will be broken up in a number of page accesses. The number
17611723Sar4jc@virginia.edu     * of page accesses depends on the amount that needs to be transfered.
17711723Sar4jc@virginia.edu     * The assumption here is that the interface is completely ignorant of
17812848Sar4jc@virginia.edu     * the page size and that this model has to figure out all of the
17911725Sar4jc@virginia.edu     * transaction characteristics.
18011725Sar4jc@virginia.edu     */
18111725Sar4jc@virginia.edu    for (uint32_t count = 0; amount > (count * pageSize); count++) {
18211725Sar4jc@virginia.edu        uint32_t index = (locationTable[logic_page_addr].block *
18311725Sar4jc@virginia.edu                          pagesPerBlock) + (logic_page_addr % pagesPerBlock);
18411725Sar4jc@virginia.edu
18512848Sar4jc@virginia.edu        DPRINTF(FlashDevice, "Index 0x%8x, Block 0x%8x, pages/block %d,"
18611723Sar4jc@virginia.edu                " logic address 0x%8x\n", index,
18711723Sar4jc@virginia.edu                locationTable[logic_page_addr].block, pagesPerBlock,
18811723Sar4jc@virginia.edu                logic_page_addr);
18911723Sar4jc@virginia.edu        DPRINTF(FlashDevice, "Page %d; %d bytes up to this point\n", count,
19011723Sar4jc@virginia.edu                (count * pageSize));
19112848Sar4jc@virginia.edu
19211723Sar4jc@virginia.edu        plane_address = locationTable[logic_page_addr].block & planeMask;
19311877Sbrandon.potter@amd.com
19411877Sbrandon.potter@amd.com        if (action == ActionRead) {
19511723Sar4jc@virginia.edu            //lookup
19612848Sar4jc@virginia.edu            //call accessTimes
19713612Sgabeblack@google.com            time[plane_address] += accessTimes(locationTable[logic_page_addr]
198                                               .block, ActionRead);
199
200            /*stats*/
201            stats.readAccess.sample(logic_page_addr);
202            stats.readLatency.sample(time[plane_address]);
203        } else { //write
204            //lookup
205            //call accessTimes if appropriate, page may be unknown, so lets
206            //give it the benefit of the doubt
207
208            if (getUnknownPages(index))
209                time[plane_address] += accessTimes
210                    (locationTable[logic_page_addr].block, ActionWrite);
211
212            else //A remap is needed
213                time[plane_address] += remap(logic_page_addr);
214
215            /*stats*/
216            stats.writeAccess.sample(logic_page_addr);
217            stats.writeLatency.sample(time[plane_address]);
218        }
219
220        /**
221         * Check if the page is known and used. unknownPages is a bitmap of
222         * all the pages. It tracks wether we can be sure that the
223         * information of this page is taken into acount in the model (is it
224         * considered in blockValidEntries and blockEmptyEntries?). If it has
225         * been used in the past, then it is known.
226         */
227        if (getUnknownPages(index)) {
228            clearUnknownPages(index);
229            --blockEmptyEntries[locationTable[logic_page_addr].block];
230            ++blockValidEntries[locationTable[logic_page_addr].block];
231        }
232
233        stats.fileSystemAccess.sample(address);
234        ++logic_page_addr;
235    }
236
237    /**
238     * previous part of the function found the times spend in different
239     * planes, now lets find the maximum to know when to callback the disk
240     */
241    for (uint32_t count = 0; count < numPlanes; count++){
242        plane_address = (time[plane_address] > time[count]) ? plane_address
243            : count;
244
245        DPRINTF(FlashDevice, "Plane %d is busy for %d ticks\n", count,
246                time[count]);
247
248        if (time[count] != 0) {
249
250            struct CallBackEntry cbe;
251            /**
252             * If there are no events for this plane, then add the current
253             * time to the occupation time; otherwise, plan it after the
254             * last event. If by chance that event is handled in this tick,
255             * then we would still end up with the same result.
256             */
257            if (planeEventQueue[count].empty())
258                cbe.time = time[count] + curTick();
259            else
260                cbe.time = time[count] +
261                           planeEventQueue[count].back().time;
262            cbe.function = NULL;
263            planeEventQueue[count].push_back(cbe);
264
265            DPRINTF(FlashDevice, "scheduled at: %ld\n", cbe.time);
266
267            if (!planeEvent.scheduled())
268                schedule(planeEvent, planeEventQueue[count].back().time);
269            else if (planeEventQueue[count].back().time < planeEvent.when())
270                reschedule(planeEvent,
271                    planeEventQueue[plane_address].back().time, true);
272        }
273    }
274
275    //worst case two plane finish at the same time, each triggers an event
276    //and this callback will be called once. Maybe before the other plane
277    //could execute its event, but in the same tick.
278    planeEventQueue[plane_address].back().function = event;
279    DPRINTF(FlashDevice, "Callback queued for plane %d; %d in queue\n",
280            plane_address, planeEventQueue[plane_address].size());
281    DPRINTF(FlashDevice, "first event @ %d\n", planeEvent.when());
282}
283
284/**
285 * When a plane completes its action, this event is triggered. When a
286 * callback function was associated with that event, it will be called.
287 */
288
289void
290FlashDevice::actionComplete()
291{
292    DPRINTF(FlashDevice, "Plane action completed\n");
293    uint8_t plane_address = 0;
294
295    uint8_t next_event = 0;
296
297    /**Search for a callback that is supposed to happen in this Tick*/
298    for (plane_address = 0; plane_address < numPlanes; plane_address++) {
299        if (!planeEventQueue[plane_address].empty()) {
300            /**
301             * Invariant: All queued events are scheduled in the present
302             *  or future.
303             */
304            assert(planeEventQueue[plane_address].front().time >= curTick());
305
306            if (planeEventQueue[plane_address].front().time == curTick()) {
307                /**
308                 * To ensure that the follow-up action is executed correctly,
309                 * the callback entry first need to be cleared before it can
310                 * be called.
311                 */
312                Callback *temp = planeEventQueue[plane_address].front().
313                                 function;
314                planeEventQueue[plane_address].pop_front();
315
316                /**Found a callback, lets make it happen*/
317                if (temp != NULL) {
318                    DPRINTF(FlashDevice, "Callback, %d\n", plane_address);
319                    temp->process();
320                }
321            }
322        }
323    }
324
325    /** Find when to schedule the planeEvent next */
326    for (plane_address = 0; plane_address < numPlanes; plane_address++) {
327        if (!planeEventQueue[plane_address].empty())
328            if (planeEventQueue[next_event].empty() ||
329                    (planeEventQueue[plane_address].front().time <
330                     planeEventQueue[next_event].front().time))
331                next_event = plane_address;
332    }
333
334    /**Schedule the next plane that will be ready (if any)*/
335    if (!planeEventQueue[next_event].empty()) {
336        DPRINTF(FlashDevice, "Schedule plane: %d\n", plane_address);
337        reschedule(planeEvent, planeEventQueue[next_event].front().time, true);
338    }
339
340    checkDrain();
341
342    DPRINTF(FlashDevice, "returing from flash event\n");
343    DPRINTF(FlashDevice, "first event @ %d\n", planeEvent.when());
344}
345
346/**
347 * Handles the remapping of the pages. It is a (I hope) sensible statistic
348 * approach. asumption: garbage collection happens when a clean is needed
349 * (may become stochastic function).
350 */
351Tick
352FlashDevice::remap(uint64_t logic_page_addr)
353{
354    /**
355     * Are there any empty left in this Block, or do we need to do an erase
356     */
357    if (blockEmptyEntries[locationTable[logic_page_addr].block] > 0) {
358        //just a remap
359        //update tables
360        --blockEmptyEntries[locationTable[logic_page_addr].block];
361        //access to this table won't be sequential anymore
362        locationTable[logic_page_addr].page = pagesPerBlock + 2;
363        //access new block
364        Tick time = accessTimes(locationTable[logic_page_addr].block,
365                                ActionWrite);
366
367        DPRINTF(FlashDevice, "Remap returns %d ticks\n", time);
368        return time;
369
370    } else {
371        //calculate how much time GC would have taken
372        uint32_t block = locationTable[logic_page_addr].block;
373        Tick time = ((GCActivePercentage *
374                       (accessTimes(block, ActionCopy) +
375                        accessTimes(block, ActionErase)))
376                     / 100);
377
378        //use block as the logical start address of the block
379        block = locationTable[logic_page_addr].block * pagesPerBlock;
380
381        //assumption: clean will improve locality
382        for (uint32_t count = 0; count < pagesPerBlock; count++) {
383            assert(block + count < pagesPerDisk);
384            locationTable[block + count].page = (block + count) %
385                pagesPerBlock;
386            ++count;
387        }
388
389        blockEmptyEntries[locationTable[logic_page_addr].block] =
390            pagesPerBlock;
391        /*stats*/
392        ++stats.totalGCActivations;
393
394        DPRINTF(FlashDevice, "Remap with erase action returns %d ticks\n",
395                time);
396
397        return time;
398    }
399
400}
401
402/**
403 * Calculates the accesstime per operation needed
404 */
405Tick
406FlashDevice::accessTimes(uint64_t block, Actions action)
407{
408    Tick time = 0;
409
410    switch(action) {
411      case ActionRead: {
412          /**Just read the page*/
413          time = readLatency;
414      } break;
415
416      case ActionWrite: {
417          /**Write the page, and read the result*/
418          time = writeLatency + readLatency;
419      } break;
420
421      case ActionErase: {
422          /**Erase and check wether it was successfull*/
423          time = eraseLatency + readLatency;
424      } break;
425
426      case ActionCopy: {
427          /**Copy every valid page*/
428          uint32_t validpages = blockValidEntries[block];
429          time = validpages * (readLatency + writeLatency);
430      } break;
431
432      default: break;
433    }
434
435    //Used to determine sequential action.
436    DPRINTF(FlashDevice, "Access returns %d ticks\n", time);
437    return time;
438}
439
440/**
441 * clearUnknownPages. defines that a page is known and used
442 * unknownPages is a bitmap of all the pages. It tracks wether we can be sure
443 * that the information of this page is taken into acount in the model (is it
444 * considered in blockValidEntries and blockEmptyEntries?). If it has been
445 * used in the past, then it is known. But it needs to be tracked to make
446 * decisions about write accesses, and indirectly about copy actions. one
447 * unknownPage entry is a 32 bit integer. So if we have a page index, then
448 * that means that we need entry floor(index/32) (index >> 5) and we need to
449 * select the bit which number is equal to the remainder of index/32
450 * (index%32). The bit is cleared to make sure that we see it as considered
451 * in the future.
452 */
453
454inline
455void
456FlashDevice::clearUnknownPages(uint32_t index)
457{
458    unknownPages[index >> 5] &= ~(0x01 << (index % 32));
459}
460
461/**
462 * getUnknownPages. Verify wether a page is known
463 */
464
465inline
466bool
467FlashDevice::getUnknownPages(uint32_t index)
468{
469    return unknownPages[index >> 5] & (0x01 << (index % 32));
470}
471
472void
473FlashDevice::regStats()
474{
475    using namespace Stats;
476
477    std::string fd_name = name() + ".FlashDevice";
478
479    // Register the stats
480    /** Amount of GC activations*/
481    stats.totalGCActivations
482        .name(fd_name + ".totalGCActivations")
483        .desc("Number of Garbage collector activations")
484        .flags(none);
485
486    /** Histogram of address accesses*/
487    stats.writeAccess
488        .init(2)
489        .name(fd_name + ".writeAccessHist")
490        .desc("Histogram of write addresses")
491        .flags(pdf);
492    stats.readAccess
493        .init(2)
494        .name(fd_name + ".readAccessHist")
495        .desc("Histogram of read addresses")
496        .flags(pdf);
497    stats.fileSystemAccess
498        .init(100)
499        .name(fd_name + ".fileSystemAccessHist")
500        .desc("Histogram of file system accesses")
501        .flags(pdf);
502
503    /** Histogram of access latencies*/
504    stats.writeLatency
505        .init(100)
506        .name(fd_name + ".writeLatencyHist")
507        .desc("Histogram of write latency")
508        .flags(pdf);
509    stats.readLatency
510        .init(100)
511        .name(fd_name + ".readLatencyHist")
512        .desc("Histogram of read latency")
513        .flags(pdf);
514}
515
516/**
517 * Serialize; needed to create checkpoints
518 */
519
520void
521FlashDevice::serialize(CheckpointOut &cp) const
522{
523    SERIALIZE_SCALAR(planeMask);
524
525    SERIALIZE_CONTAINER(unknownPages);
526    SERIALIZE_CONTAINER(blockValidEntries);
527    SERIALIZE_CONTAINER(blockEmptyEntries);
528
529    int location_table_size = locationTable.size();
530    SERIALIZE_SCALAR(location_table_size);
531    for (uint32_t count = 0; count < location_table_size; count++) {
532        paramOut(cp, csprintf("locationTable[%d].page", count),
533                 locationTable[count].page);
534        paramOut(cp, csprintf("locationTable[%d].block", count),
535                 locationTable[count].block);
536    }
537};
538
539/**
540 * Unserialize; needed to restore from checkpoints
541 */
542
543void
544FlashDevice::unserialize(CheckpointIn &cp)
545{
546    UNSERIALIZE_SCALAR(planeMask);
547
548    UNSERIALIZE_CONTAINER(unknownPages);
549    UNSERIALIZE_CONTAINER(blockValidEntries);
550    UNSERIALIZE_CONTAINER(blockEmptyEntries);
551
552    int location_table_size;
553    UNSERIALIZE_SCALAR(location_table_size);
554    locationTable.resize(location_table_size);
555    for (uint32_t count = 0; count < location_table_size; count++) {
556        paramIn(cp, csprintf("locationTable[%d].page", count),
557                locationTable[count].page);
558        paramIn(cp, csprintf("locationTable[%d].block", count),
559                locationTable[count].block);
560    }
561};
562
563/**
564 * Drain; needed to enable checkpoints
565 */
566
567DrainState
568FlashDevice::drain()
569{
570    if (planeEvent.scheduled()) {
571        DPRINTF(Drain, "Flash device is draining...\n");
572        return DrainState::Draining;
573    } else {
574        DPRINTF(Drain, "Flash device in drained state\n");
575        return DrainState::Drained;
576    }
577}
578
579/**
580 * Checkdrain; needed to enable checkpoints
581 */
582
583void
584FlashDevice::checkDrain()
585{
586    if (drainState() != DrainState::Draining)
587        return;
588
589    if (planeEvent.when() > curTick()) {
590        DPRINTF(Drain, "Flash device is still draining\n");
591    } else {
592        DPRINTF(Drain, "Flash device is done draining\n");
593        signalDrainDone();
594    }
595}
596