110801Srene.dejong@arm.com/* 210801Srene.dejong@arm.com * Copyright (c) 2013-2015 ARM Limited 310801Srene.dejong@arm.com * All rights reserved 410801Srene.dejong@arm.com * 510801Srene.dejong@arm.com * The license below extends only to copyright in the software and shall 610801Srene.dejong@arm.com * not be construed as granting a license to any other intellectual 710801Srene.dejong@arm.com * property including but not limited to intellectual property relating 810801Srene.dejong@arm.com * to a hardware implementation of the functionality of the software 910801Srene.dejong@arm.com * licensed hereunder. You may use the software subject to the license 1010801Srene.dejong@arm.com * terms below provided that you ensure that this notice is replicated 1110801Srene.dejong@arm.com * unmodified and in its entirety in all distributions of the software, 1210801Srene.dejong@arm.com * modified or unmodified, in source code or in binary form. 1310801Srene.dejong@arm.com * 1410801Srene.dejong@arm.com * Redistribution and use in source and binary forms, with or without 1510801Srene.dejong@arm.com * modification, are permitted provided that the following conditions are 1610801Srene.dejong@arm.com * met: redistributions of source code must retain the above copyright 1710801Srene.dejong@arm.com * notice, this list of conditions and the following disclaimer; 1810801Srene.dejong@arm.com * redistributions in binary form must reproduce the above copyright 1910801Srene.dejong@arm.com * notice, this list of conditions and the following disclaimer in the 2010801Srene.dejong@arm.com * documentation and/or other materials provided with the distribution; 2110801Srene.dejong@arm.com * neither the name of the copyright holders nor the names of its 2210801Srene.dejong@arm.com * contributors may be used to endorse or promote products derived from 2310801Srene.dejong@arm.com * this software without specific prior written permission. 2410801Srene.dejong@arm.com * 2510801Srene.dejong@arm.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2610801Srene.dejong@arm.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2710801Srene.dejong@arm.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2810801Srene.dejong@arm.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2910801Srene.dejong@arm.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3010801Srene.dejong@arm.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3110801Srene.dejong@arm.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3210801Srene.dejong@arm.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3310801Srene.dejong@arm.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3410801Srene.dejong@arm.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3510801Srene.dejong@arm.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3610801Srene.dejong@arm.com * 3710801Srene.dejong@arm.com * Authors: Rene de Jong 3810801Srene.dejong@arm.com */ 3910801Srene.dejong@arm.com 4010801Srene.dejong@arm.com/** @file 4110801Srene.dejong@arm.com * This simplistic flash model is designed to model managed SLC NAND flash. 4210801Srene.dejong@arm.com * This device will need an interface module (such as NVMe or UFS); Note that 4310801Srene.dejong@arm.com * this model only calculates the delay and does not perform the actual 4410801Srene.dejong@arm.com * transaction. 4510801Srene.dejong@arm.com * 4610801Srene.dejong@arm.com * To access the memory, use either readMemory or writeMemory. This will 4710801Srene.dejong@arm.com * schedule an event at the tick where the action will finish. If a callback 4810801Srene.dejong@arm.com * has been given as argument then that function will be called on completion 4910801Srene.dejong@arm.com * of that event. Note that this does not guarantee that there are no other 5010801Srene.dejong@arm.com * actions pending in the flash device. 5110801Srene.dejong@arm.com * 5210801Srene.dejong@arm.com * IMPORTANT: number of planes should be a power of 2. 5310801Srene.dejong@arm.com */ 5410801Srene.dejong@arm.com 5510801Srene.dejong@arm.com#include "dev/arm/flash_device.hh" 5610801Srene.dejong@arm.com 5711800Sbrandon.potter@amd.com#include "base/trace.hh" 5810801Srene.dejong@arm.com#include "debug/Drain.hh" 5910801Srene.dejong@arm.com 6010801Srene.dejong@arm.com/** 6110801Srene.dejong@arm.com * Create this device 6210801Srene.dejong@arm.com */ 6310801Srene.dejong@arm.com 6410801Srene.dejong@arm.comFlashDevice* 6510801Srene.dejong@arm.comFlashDeviceParams::create() 6610801Srene.dejong@arm.com{ 6710801Srene.dejong@arm.com return new FlashDevice(this); 6810801Srene.dejong@arm.com} 6910801Srene.dejong@arm.com 7010801Srene.dejong@arm.com 7110801Srene.dejong@arm.com/** 7210801Srene.dejong@arm.com * Flash Device constructor and destructor 7310801Srene.dejong@arm.com */ 7410801Srene.dejong@arm.com 7510801Srene.dejong@arm.comFlashDevice::FlashDevice(const FlashDeviceParams* p): 7610801Srene.dejong@arm.com AbstractNVM(p), 7710801Srene.dejong@arm.com diskSize(0), 7810801Srene.dejong@arm.com blockSize(p->blk_size), 7910801Srene.dejong@arm.com pageSize(p->page_size), 8010801Srene.dejong@arm.com GCActivePercentage(p->GC_active), 8110801Srene.dejong@arm.com readLatency(p->read_lat), 8210801Srene.dejong@arm.com writeLatency(p->write_lat), 8310801Srene.dejong@arm.com eraseLatency(p->erase_lat), 8410801Srene.dejong@arm.com dataDistribution(p->data_distribution), 8510801Srene.dejong@arm.com numPlanes(p->num_planes), 8610801Srene.dejong@arm.com pagesPerBlock(0), 8710801Srene.dejong@arm.com pagesPerDisk(0), 8810801Srene.dejong@arm.com blocksPerDisk(0), 8910801Srene.dejong@arm.com planeMask(numPlanes - 1), 9010801Srene.dejong@arm.com planeEventQueue(numPlanes), 9112086Sspwilson2@wisc.edu planeEvent([this]{ actionComplete(); }, name()) 9210801Srene.dejong@arm.com{ 9310801Srene.dejong@arm.com 9410801Srene.dejong@arm.com /* 9510801Srene.dejong@arm.com * Let 'a' be a power of two of n bits, written such that a-n is the msb 9610801Srene.dejong@arm.com * and a-0 is the lsb. Since it is a power of two, only one bit (a-x, 9710801Srene.dejong@arm.com * with 0 <= x <= n) is set. If we subtract one from this number the bits 9810801Srene.dejong@arm.com * a-(x-1) to a-0 are set and all the other bits are cleared. Hence a 9910801Srene.dejong@arm.com * bitwise AND with those two numbers results in an integer with all bits 10010801Srene.dejong@arm.com * cleared. 10110801Srene.dejong@arm.com */ 10211321Ssteve.reinhardt@amd.com if (numPlanes & planeMask) 10310801Srene.dejong@arm.com fatal("Number of planes is not a power of 2 in flash device.\n"); 10410801Srene.dejong@arm.com} 10510801Srene.dejong@arm.com 10610801Srene.dejong@arm.com/** 10710801Srene.dejong@arm.com * Initiates all the flash functions: initializes the lookup tables, age of 10810801Srene.dejong@arm.com * the device, etc. This can only be done once the disk image is known. 10910801Srene.dejong@arm.com * Thats why it can't be done in the constructor. 11010801Srene.dejong@arm.com */ 11110801Srene.dejong@arm.comvoid 11210801Srene.dejong@arm.comFlashDevice::initializeFlash(uint64_t disk_size, uint32_t sector_size) 11310801Srene.dejong@arm.com{ 11410801Srene.dejong@arm.com diskSize = disk_size * sector_size; 11510801Srene.dejong@arm.com pagesPerBlock = blockSize / pageSize; 11610801Srene.dejong@arm.com pagesPerDisk = diskSize / pageSize; 11710801Srene.dejong@arm.com blocksPerDisk = diskSize / blockSize; 11810801Srene.dejong@arm.com 11910801Srene.dejong@arm.com /** Sanity information: check flash configuration */ 12010801Srene.dejong@arm.com DPRINTF(FlashDevice, "diskSize: %d Bytes; %d pages per block, %d pages " 12110801Srene.dejong@arm.com "per disk\n", diskSize, pagesPerBlock, pagesPerDisk); 12210801Srene.dejong@arm.com 12310801Srene.dejong@arm.com locationTable.resize(pagesPerDisk); 12410801Srene.dejong@arm.com 12510801Srene.dejong@arm.com /**Garbage collection related*/ 12610801Srene.dejong@arm.com blockValidEntries.resize(blocksPerDisk, 0); 12710801Srene.dejong@arm.com blockEmptyEntries.resize(blocksPerDisk, pagesPerBlock); 12810801Srene.dejong@arm.com 12910801Srene.dejong@arm.com /** 13010801Srene.dejong@arm.com * This is a bitmap. Every bit is a page 13110801Srene.dejong@arm.com * unknownPages is a vector of 32 bit integers. If every page was an 13210801Srene.dejong@arm.com * integer, the total size would be pagesPerDisk; since we can map one 13310801Srene.dejong@arm.com * page per bit we need ceil(pagesPerDisk/32) entries. 32 = 1 << 5 hence 13410801Srene.dejong@arm.com * it will do to just shift pagesPerDisk five positions and add one. This 13510801Srene.dejong@arm.com * will allocate one integer to many for this data structure in the worst 13610801Srene.dejong@arm.com * case. 13710801Srene.dejong@arm.com */ 13810801Srene.dejong@arm.com unknownPages.resize((pagesPerDisk >> 5) + 1, 0xFFFFFFFF); 13910801Srene.dejong@arm.com 14010801Srene.dejong@arm.com for (uint32_t count = 0; count < pagesPerDisk; count++) { 14110801Srene.dejong@arm.com //setup lookup table + physical aspects 14210801Srene.dejong@arm.com 14310801Srene.dejong@arm.com if (dataDistribution == Enums::stripe) { 14410801Srene.dejong@arm.com locationTable[count].page = count / blocksPerDisk; 14510801Srene.dejong@arm.com locationTable[count].block = count % blocksPerDisk; 14610801Srene.dejong@arm.com 14710801Srene.dejong@arm.com } else { 14810801Srene.dejong@arm.com locationTable[count].page = count % pagesPerBlock; 14910801Srene.dejong@arm.com locationTable[count].block = count / pagesPerBlock; 15010801Srene.dejong@arm.com } 15110801Srene.dejong@arm.com } 15210801Srene.dejong@arm.com} 15310801Srene.dejong@arm.com 15410801Srene.dejong@arm.comFlashDevice::~FlashDevice() 15510801Srene.dejong@arm.com{ 15610801Srene.dejong@arm.com DPRINTF(FlashDevice, "Remove FlashDevice\n"); 15710801Srene.dejong@arm.com} 15810801Srene.dejong@arm.com 15910801Srene.dejong@arm.com/** 16010801Srene.dejong@arm.com * Handles the accesses to the device. 16110801Srene.dejong@arm.com * The function determines when certain actions are scheduled and schedules 16210801Srene.dejong@arm.com * an event that uses the callback function on completion of the action. 16310801Srene.dejong@arm.com */ 16410801Srene.dejong@arm.comvoid 16510801Srene.dejong@arm.comFlashDevice::accessDevice(uint64_t address, uint32_t amount, Callback *event, 16610801Srene.dejong@arm.com Actions action) 16710801Srene.dejong@arm.com{ 16810801Srene.dejong@arm.com DPRINTF(FlashDevice, "Flash calculation for %d bytes in %d pages\n" 16910801Srene.dejong@arm.com , amount, pageSize); 17010801Srene.dejong@arm.com 17110801Srene.dejong@arm.com std::vector<Tick> time(numPlanes, 0); 17210801Srene.dejong@arm.com uint64_t logic_page_addr = address / pageSize; 17310801Srene.dejong@arm.com uint32_t plane_address = 0; 17410801Srene.dejong@arm.com 17510801Srene.dejong@arm.com /** 17610801Srene.dejong@arm.com * The access will be broken up in a number of page accesses. The number 17710801Srene.dejong@arm.com * of page accesses depends on the amount that needs to be transfered. 17810801Srene.dejong@arm.com * The assumption here is that the interface is completely ignorant of 17910801Srene.dejong@arm.com * the page size and that this model has to figure out all of the 18010801Srene.dejong@arm.com * transaction characteristics. 18110801Srene.dejong@arm.com */ 18210801Srene.dejong@arm.com for (uint32_t count = 0; amount > (count * pageSize); count++) { 18310801Srene.dejong@arm.com uint32_t index = (locationTable[logic_page_addr].block * 18410801Srene.dejong@arm.com pagesPerBlock) + (logic_page_addr % pagesPerBlock); 18510801Srene.dejong@arm.com 18610801Srene.dejong@arm.com DPRINTF(FlashDevice, "Index 0x%8x, Block 0x%8x, pages/block %d," 18710801Srene.dejong@arm.com " logic address 0x%8x\n", index, 18810801Srene.dejong@arm.com locationTable[logic_page_addr].block, pagesPerBlock, 18910801Srene.dejong@arm.com logic_page_addr); 19010801Srene.dejong@arm.com DPRINTF(FlashDevice, "Page %d; %d bytes up to this point\n", count, 19110801Srene.dejong@arm.com (count * pageSize)); 19210801Srene.dejong@arm.com 19310801Srene.dejong@arm.com plane_address = locationTable[logic_page_addr].block & planeMask; 19410801Srene.dejong@arm.com 19510801Srene.dejong@arm.com if (action == ActionRead) { 19610801Srene.dejong@arm.com //lookup 19710801Srene.dejong@arm.com //call accessTimes 19810801Srene.dejong@arm.com time[plane_address] += accessTimes(locationTable[logic_page_addr] 19910801Srene.dejong@arm.com .block, ActionRead); 20010801Srene.dejong@arm.com 20110801Srene.dejong@arm.com /*stats*/ 20210801Srene.dejong@arm.com stats.readAccess.sample(logic_page_addr); 20310801Srene.dejong@arm.com stats.readLatency.sample(time[plane_address]); 20410801Srene.dejong@arm.com } else { //write 20510801Srene.dejong@arm.com //lookup 20610801Srene.dejong@arm.com //call accessTimes if appropriate, page may be unknown, so lets 20710801Srene.dejong@arm.com //give it the benefit of the doubt 20810801Srene.dejong@arm.com 20910801Srene.dejong@arm.com if (getUnknownPages(index)) 21010801Srene.dejong@arm.com time[plane_address] += accessTimes 21110801Srene.dejong@arm.com (locationTable[logic_page_addr].block, ActionWrite); 21210801Srene.dejong@arm.com 21310801Srene.dejong@arm.com else //A remap is needed 21410801Srene.dejong@arm.com time[plane_address] += remap(logic_page_addr); 21510801Srene.dejong@arm.com 21610801Srene.dejong@arm.com /*stats*/ 21710801Srene.dejong@arm.com stats.writeAccess.sample(logic_page_addr); 21810801Srene.dejong@arm.com stats.writeLatency.sample(time[plane_address]); 21910801Srene.dejong@arm.com } 22010801Srene.dejong@arm.com 22110801Srene.dejong@arm.com /** 22210801Srene.dejong@arm.com * Check if the page is known and used. unknownPages is a bitmap of 22310801Srene.dejong@arm.com * all the pages. It tracks wether we can be sure that the 22410801Srene.dejong@arm.com * information of this page is taken into acount in the model (is it 22510801Srene.dejong@arm.com * considered in blockValidEntries and blockEmptyEntries?). If it has 22610801Srene.dejong@arm.com * been used in the past, then it is known. 22710801Srene.dejong@arm.com */ 22810801Srene.dejong@arm.com if (getUnknownPages(index)) { 22910801Srene.dejong@arm.com clearUnknownPages(index); 23010801Srene.dejong@arm.com --blockEmptyEntries[locationTable[logic_page_addr].block]; 23110801Srene.dejong@arm.com ++blockValidEntries[locationTable[logic_page_addr].block]; 23210801Srene.dejong@arm.com } 23310801Srene.dejong@arm.com 23410801Srene.dejong@arm.com stats.fileSystemAccess.sample(address); 23510801Srene.dejong@arm.com ++logic_page_addr; 23610801Srene.dejong@arm.com } 23710801Srene.dejong@arm.com 23810801Srene.dejong@arm.com /** 23910801Srene.dejong@arm.com * previous part of the function found the times spend in different 24010801Srene.dejong@arm.com * planes, now lets find the maximum to know when to callback the disk 24110801Srene.dejong@arm.com */ 24210801Srene.dejong@arm.com for (uint32_t count = 0; count < numPlanes; count++){ 24310801Srene.dejong@arm.com plane_address = (time[plane_address] > time[count]) ? plane_address 24410801Srene.dejong@arm.com : count; 24510801Srene.dejong@arm.com 24610801Srene.dejong@arm.com DPRINTF(FlashDevice, "Plane %d is busy for %d ticks\n", count, 24710801Srene.dejong@arm.com time[count]); 24810801Srene.dejong@arm.com 24911321Ssteve.reinhardt@amd.com if (time[count] != 0) { 25010801Srene.dejong@arm.com 25110801Srene.dejong@arm.com struct CallBackEntry cbe; 25210801Srene.dejong@arm.com /** 25310801Srene.dejong@arm.com * If there are no events for this plane, then add the current 25410801Srene.dejong@arm.com * time to the occupation time; otherwise, plan it after the 25510801Srene.dejong@arm.com * last event. If by chance that event is handled in this tick, 25610801Srene.dejong@arm.com * then we would still end up with the same result. 25710801Srene.dejong@arm.com */ 25810801Srene.dejong@arm.com if (planeEventQueue[count].empty()) 25910801Srene.dejong@arm.com cbe.time = time[count] + curTick(); 26010801Srene.dejong@arm.com else 26110801Srene.dejong@arm.com cbe.time = time[count] + 26210801Srene.dejong@arm.com planeEventQueue[count].back().time; 26310801Srene.dejong@arm.com cbe.function = NULL; 26410801Srene.dejong@arm.com planeEventQueue[count].push_back(cbe); 26510801Srene.dejong@arm.com 26610801Srene.dejong@arm.com DPRINTF(FlashDevice, "scheduled at: %ld\n", cbe.time); 26710801Srene.dejong@arm.com 26810801Srene.dejong@arm.com if (!planeEvent.scheduled()) 26910801Srene.dejong@arm.com schedule(planeEvent, planeEventQueue[count].back().time); 27010801Srene.dejong@arm.com else if (planeEventQueue[count].back().time < planeEvent.when()) 27110801Srene.dejong@arm.com reschedule(planeEvent, 27210801Srene.dejong@arm.com planeEventQueue[plane_address].back().time, true); 27310801Srene.dejong@arm.com } 27410801Srene.dejong@arm.com } 27510801Srene.dejong@arm.com 27610801Srene.dejong@arm.com //worst case two plane finish at the same time, each triggers an event 27710801Srene.dejong@arm.com //and this callback will be called once. Maybe before the other plane 27810801Srene.dejong@arm.com //could execute its event, but in the same tick. 27910801Srene.dejong@arm.com planeEventQueue[plane_address].back().function = event; 28010801Srene.dejong@arm.com DPRINTF(FlashDevice, "Callback queued for plane %d; %d in queue\n", 28110801Srene.dejong@arm.com plane_address, planeEventQueue[plane_address].size()); 28210801Srene.dejong@arm.com DPRINTF(FlashDevice, "first event @ %d\n", planeEvent.when()); 28310801Srene.dejong@arm.com} 28410801Srene.dejong@arm.com 28510801Srene.dejong@arm.com/** 28610801Srene.dejong@arm.com * When a plane completes its action, this event is triggered. When a 28710801Srene.dejong@arm.com * callback function was associated with that event, it will be called. 28810801Srene.dejong@arm.com */ 28910801Srene.dejong@arm.com 29010801Srene.dejong@arm.comvoid 29110801Srene.dejong@arm.comFlashDevice::actionComplete() 29210801Srene.dejong@arm.com{ 29310801Srene.dejong@arm.com DPRINTF(FlashDevice, "Plane action completed\n"); 29410801Srene.dejong@arm.com uint8_t plane_address = 0; 29510801Srene.dejong@arm.com 29610801Srene.dejong@arm.com uint8_t next_event = 0; 29710801Srene.dejong@arm.com 29810801Srene.dejong@arm.com /**Search for a callback that is supposed to happen in this Tick*/ 29910801Srene.dejong@arm.com for (plane_address = 0; plane_address < numPlanes; plane_address++) { 30010801Srene.dejong@arm.com if (!planeEventQueue[plane_address].empty()) { 30110801Srene.dejong@arm.com /** 30210801Srene.dejong@arm.com * Invariant: All queued events are scheduled in the present 30310801Srene.dejong@arm.com * or future. 30410801Srene.dejong@arm.com */ 30510801Srene.dejong@arm.com assert(planeEventQueue[plane_address].front().time >= curTick()); 30610801Srene.dejong@arm.com 30710801Srene.dejong@arm.com if (planeEventQueue[plane_address].front().time == curTick()) { 30810801Srene.dejong@arm.com /** 30910801Srene.dejong@arm.com * To ensure that the follow-up action is executed correctly, 31010801Srene.dejong@arm.com * the callback entry first need to be cleared before it can 31110801Srene.dejong@arm.com * be called. 31210801Srene.dejong@arm.com */ 31310801Srene.dejong@arm.com Callback *temp = planeEventQueue[plane_address].front(). 31410801Srene.dejong@arm.com function; 31510801Srene.dejong@arm.com planeEventQueue[plane_address].pop_front(); 31610801Srene.dejong@arm.com 31710801Srene.dejong@arm.com /**Found a callback, lets make it happen*/ 31810801Srene.dejong@arm.com if (temp != NULL) { 31910801Srene.dejong@arm.com DPRINTF(FlashDevice, "Callback, %d\n", plane_address); 32010801Srene.dejong@arm.com temp->process(); 32110801Srene.dejong@arm.com } 32210801Srene.dejong@arm.com } 32310801Srene.dejong@arm.com } 32410801Srene.dejong@arm.com } 32510801Srene.dejong@arm.com 32610801Srene.dejong@arm.com /** Find when to schedule the planeEvent next */ 32710801Srene.dejong@arm.com for (plane_address = 0; plane_address < numPlanes; plane_address++) { 32810801Srene.dejong@arm.com if (!planeEventQueue[plane_address].empty()) 32910801Srene.dejong@arm.com if (planeEventQueue[next_event].empty() || 33010801Srene.dejong@arm.com (planeEventQueue[plane_address].front().time < 33110801Srene.dejong@arm.com planeEventQueue[next_event].front().time)) 33210801Srene.dejong@arm.com next_event = plane_address; 33310801Srene.dejong@arm.com } 33410801Srene.dejong@arm.com 33510801Srene.dejong@arm.com /**Schedule the next plane that will be ready (if any)*/ 33610801Srene.dejong@arm.com if (!planeEventQueue[next_event].empty()) { 33710801Srene.dejong@arm.com DPRINTF(FlashDevice, "Schedule plane: %d\n", plane_address); 33810801Srene.dejong@arm.com reschedule(planeEvent, planeEventQueue[next_event].front().time, true); 33910801Srene.dejong@arm.com } 34010801Srene.dejong@arm.com 34110801Srene.dejong@arm.com checkDrain(); 34210801Srene.dejong@arm.com 34310801Srene.dejong@arm.com DPRINTF(FlashDevice, "returing from flash event\n"); 34410801Srene.dejong@arm.com DPRINTF(FlashDevice, "first event @ %d\n", planeEvent.when()); 34510801Srene.dejong@arm.com} 34610801Srene.dejong@arm.com 34710801Srene.dejong@arm.com/** 34810801Srene.dejong@arm.com * Handles the remapping of the pages. It is a (I hope) sensible statistic 34910801Srene.dejong@arm.com * approach. asumption: garbage collection happens when a clean is needed 35010801Srene.dejong@arm.com * (may become stochastic function). 35110801Srene.dejong@arm.com */ 35210801Srene.dejong@arm.comTick 35310801Srene.dejong@arm.comFlashDevice::remap(uint64_t logic_page_addr) 35410801Srene.dejong@arm.com{ 35510801Srene.dejong@arm.com /** 35610801Srene.dejong@arm.com * Are there any empty left in this Block, or do we need to do an erase 35710801Srene.dejong@arm.com */ 35810801Srene.dejong@arm.com if (blockEmptyEntries[locationTable[logic_page_addr].block] > 0) { 35910801Srene.dejong@arm.com //just a remap 36010801Srene.dejong@arm.com //update tables 36110801Srene.dejong@arm.com --blockEmptyEntries[locationTable[logic_page_addr].block]; 36210801Srene.dejong@arm.com //access to this table won't be sequential anymore 36310801Srene.dejong@arm.com locationTable[logic_page_addr].page = pagesPerBlock + 2; 36410801Srene.dejong@arm.com //access new block 36510801Srene.dejong@arm.com Tick time = accessTimes(locationTable[logic_page_addr].block, 36610801Srene.dejong@arm.com ActionWrite); 36710801Srene.dejong@arm.com 36810801Srene.dejong@arm.com DPRINTF(FlashDevice, "Remap returns %d ticks\n", time); 36910801Srene.dejong@arm.com return time; 37010801Srene.dejong@arm.com 37110801Srene.dejong@arm.com } else { 37210801Srene.dejong@arm.com //calculate how much time GC would have taken 37310801Srene.dejong@arm.com uint32_t block = locationTable[logic_page_addr].block; 37410801Srene.dejong@arm.com Tick time = ((GCActivePercentage * 37510801Srene.dejong@arm.com (accessTimes(block, ActionCopy) + 37610801Srene.dejong@arm.com accessTimes(block, ActionErase))) 37710801Srene.dejong@arm.com / 100); 37810801Srene.dejong@arm.com 37910801Srene.dejong@arm.com //use block as the logical start address of the block 38010801Srene.dejong@arm.com block = locationTable[logic_page_addr].block * pagesPerBlock; 38110801Srene.dejong@arm.com 38210801Srene.dejong@arm.com //assumption: clean will improve locality 38311180Ssascha.bischoff@ARM.com for (uint32_t count = 0; count < pagesPerBlock; count++) { 38411180Ssascha.bischoff@ARM.com assert(block + count < pagesPerDisk); 38510801Srene.dejong@arm.com locationTable[block + count].page = (block + count) % 38610801Srene.dejong@arm.com pagesPerBlock; 38710801Srene.dejong@arm.com } 38810801Srene.dejong@arm.com 38910801Srene.dejong@arm.com blockEmptyEntries[locationTable[logic_page_addr].block] = 39010801Srene.dejong@arm.com pagesPerBlock; 39110801Srene.dejong@arm.com /*stats*/ 39210801Srene.dejong@arm.com ++stats.totalGCActivations; 39310801Srene.dejong@arm.com 39410801Srene.dejong@arm.com DPRINTF(FlashDevice, "Remap with erase action returns %d ticks\n", 39510801Srene.dejong@arm.com time); 39610801Srene.dejong@arm.com 39710801Srene.dejong@arm.com return time; 39810801Srene.dejong@arm.com } 39910801Srene.dejong@arm.com 40010801Srene.dejong@arm.com} 40110801Srene.dejong@arm.com 40210801Srene.dejong@arm.com/** 40310801Srene.dejong@arm.com * Calculates the accesstime per operation needed 40410801Srene.dejong@arm.com */ 40510801Srene.dejong@arm.comTick 40610801Srene.dejong@arm.comFlashDevice::accessTimes(uint64_t block, Actions action) 40710801Srene.dejong@arm.com{ 40810801Srene.dejong@arm.com Tick time = 0; 40910801Srene.dejong@arm.com 41010801Srene.dejong@arm.com switch(action) { 41110801Srene.dejong@arm.com case ActionRead: { 41210801Srene.dejong@arm.com /**Just read the page*/ 41310801Srene.dejong@arm.com time = readLatency; 41410801Srene.dejong@arm.com } break; 41510801Srene.dejong@arm.com 41610801Srene.dejong@arm.com case ActionWrite: { 41710801Srene.dejong@arm.com /**Write the page, and read the result*/ 41810801Srene.dejong@arm.com time = writeLatency + readLatency; 41910801Srene.dejong@arm.com } break; 42010801Srene.dejong@arm.com 42110801Srene.dejong@arm.com case ActionErase: { 42210801Srene.dejong@arm.com /**Erase and check wether it was successfull*/ 42310801Srene.dejong@arm.com time = eraseLatency + readLatency; 42410801Srene.dejong@arm.com } break; 42510801Srene.dejong@arm.com 42610801Srene.dejong@arm.com case ActionCopy: { 42710801Srene.dejong@arm.com /**Copy every valid page*/ 42810801Srene.dejong@arm.com uint32_t validpages = blockValidEntries[block]; 42910801Srene.dejong@arm.com time = validpages * (readLatency + writeLatency); 43010801Srene.dejong@arm.com } break; 43110801Srene.dejong@arm.com 43210801Srene.dejong@arm.com default: break; 43310801Srene.dejong@arm.com } 43410801Srene.dejong@arm.com 43510801Srene.dejong@arm.com //Used to determine sequential action. 43610801Srene.dejong@arm.com DPRINTF(FlashDevice, "Access returns %d ticks\n", time); 43710801Srene.dejong@arm.com return time; 43810801Srene.dejong@arm.com} 43910801Srene.dejong@arm.com 44010801Srene.dejong@arm.com/** 44110801Srene.dejong@arm.com * clearUnknownPages. defines that a page is known and used 44210801Srene.dejong@arm.com * unknownPages is a bitmap of all the pages. It tracks wether we can be sure 44310801Srene.dejong@arm.com * that the information of this page is taken into acount in the model (is it 44410801Srene.dejong@arm.com * considered in blockValidEntries and blockEmptyEntries?). If it has been 44510801Srene.dejong@arm.com * used in the past, then it is known. But it needs to be tracked to make 44610801Srene.dejong@arm.com * decisions about write accesses, and indirectly about copy actions. one 44710801Srene.dejong@arm.com * unknownPage entry is a 32 bit integer. So if we have a page index, then 44810801Srene.dejong@arm.com * that means that we need entry floor(index/32) (index >> 5) and we need to 44910801Srene.dejong@arm.com * select the bit which number is equal to the remainder of index/32 45010801Srene.dejong@arm.com * (index%32). The bit is cleared to make sure that we see it as considered 45110801Srene.dejong@arm.com * in the future. 45210801Srene.dejong@arm.com */ 45310801Srene.dejong@arm.com 45410801Srene.dejong@arm.cominline 45510801Srene.dejong@arm.comvoid 45610801Srene.dejong@arm.comFlashDevice::clearUnknownPages(uint32_t index) 45710801Srene.dejong@arm.com{ 45810801Srene.dejong@arm.com unknownPages[index >> 5] &= ~(0x01 << (index % 32)); 45910801Srene.dejong@arm.com} 46010801Srene.dejong@arm.com 46110801Srene.dejong@arm.com/** 46210801Srene.dejong@arm.com * getUnknownPages. Verify wether a page is known 46310801Srene.dejong@arm.com */ 46410801Srene.dejong@arm.com 46510801Srene.dejong@arm.cominline 46610801Srene.dejong@arm.combool 46710801Srene.dejong@arm.comFlashDevice::getUnknownPages(uint32_t index) 46810801Srene.dejong@arm.com{ 46910801Srene.dejong@arm.com return unknownPages[index >> 5] & (0x01 << (index % 32)); 47010801Srene.dejong@arm.com} 47110801Srene.dejong@arm.com 47210801Srene.dejong@arm.comvoid 47310801Srene.dejong@arm.comFlashDevice::regStats() 47410801Srene.dejong@arm.com{ 47511523Sdavid.guillen@arm.com AbstractNVM::regStats(); 47611523Sdavid.guillen@arm.com 47710801Srene.dejong@arm.com using namespace Stats; 47810801Srene.dejong@arm.com 47910801Srene.dejong@arm.com std::string fd_name = name() + ".FlashDevice"; 48010801Srene.dejong@arm.com 48110801Srene.dejong@arm.com // Register the stats 48210801Srene.dejong@arm.com /** Amount of GC activations*/ 48310801Srene.dejong@arm.com stats.totalGCActivations 48410801Srene.dejong@arm.com .name(fd_name + ".totalGCActivations") 48510801Srene.dejong@arm.com .desc("Number of Garbage collector activations") 48610801Srene.dejong@arm.com .flags(none); 48710801Srene.dejong@arm.com 48810801Srene.dejong@arm.com /** Histogram of address accesses*/ 48910801Srene.dejong@arm.com stats.writeAccess 49010801Srene.dejong@arm.com .init(2) 49110801Srene.dejong@arm.com .name(fd_name + ".writeAccessHist") 49210801Srene.dejong@arm.com .desc("Histogram of write addresses") 49310801Srene.dejong@arm.com .flags(pdf); 49410801Srene.dejong@arm.com stats.readAccess 49510801Srene.dejong@arm.com .init(2) 49610801Srene.dejong@arm.com .name(fd_name + ".readAccessHist") 49710801Srene.dejong@arm.com .desc("Histogram of read addresses") 49810801Srene.dejong@arm.com .flags(pdf); 49910801Srene.dejong@arm.com stats.fileSystemAccess 50010801Srene.dejong@arm.com .init(100) 50110801Srene.dejong@arm.com .name(fd_name + ".fileSystemAccessHist") 50210801Srene.dejong@arm.com .desc("Histogram of file system accesses") 50310801Srene.dejong@arm.com .flags(pdf); 50410801Srene.dejong@arm.com 50510801Srene.dejong@arm.com /** Histogram of access latencies*/ 50610801Srene.dejong@arm.com stats.writeLatency 50710801Srene.dejong@arm.com .init(100) 50810801Srene.dejong@arm.com .name(fd_name + ".writeLatencyHist") 50910801Srene.dejong@arm.com .desc("Histogram of write latency") 51010801Srene.dejong@arm.com .flags(pdf); 51110801Srene.dejong@arm.com stats.readLatency 51210801Srene.dejong@arm.com .init(100) 51310801Srene.dejong@arm.com .name(fd_name + ".readLatencyHist") 51410801Srene.dejong@arm.com .desc("Histogram of read latency") 51510801Srene.dejong@arm.com .flags(pdf); 51610801Srene.dejong@arm.com} 51710801Srene.dejong@arm.com 51810801Srene.dejong@arm.com/** 51910801Srene.dejong@arm.com * Serialize; needed to create checkpoints 52010801Srene.dejong@arm.com */ 52110801Srene.dejong@arm.com 52210801Srene.dejong@arm.comvoid 52310905Sandreas.sandberg@arm.comFlashDevice::serialize(CheckpointOut &cp) const 52410801Srene.dejong@arm.com{ 52510801Srene.dejong@arm.com SERIALIZE_SCALAR(planeMask); 52610801Srene.dejong@arm.com 52711226SGeoffrey.Blake@arm.com SERIALIZE_CONTAINER(unknownPages); 52811226SGeoffrey.Blake@arm.com SERIALIZE_CONTAINER(blockValidEntries); 52911226SGeoffrey.Blake@arm.com SERIALIZE_CONTAINER(blockEmptyEntries); 53010801Srene.dejong@arm.com 53110801Srene.dejong@arm.com int location_table_size = locationTable.size(); 53210801Srene.dejong@arm.com SERIALIZE_SCALAR(location_table_size); 53310801Srene.dejong@arm.com for (uint32_t count = 0; count < location_table_size; count++) { 53411226SGeoffrey.Blake@arm.com paramOut(cp, csprintf("locationTable[%d].page", count), 53511226SGeoffrey.Blake@arm.com locationTable[count].page); 53611226SGeoffrey.Blake@arm.com paramOut(cp, csprintf("locationTable[%d].block", count), 53711226SGeoffrey.Blake@arm.com locationTable[count].block); 53811226SGeoffrey.Blake@arm.com } 53910801Srene.dejong@arm.com}; 54010801Srene.dejong@arm.com 54110801Srene.dejong@arm.com/** 54210801Srene.dejong@arm.com * Unserialize; needed to restore from checkpoints 54310801Srene.dejong@arm.com */ 54410801Srene.dejong@arm.com 54510801Srene.dejong@arm.comvoid 54610905Sandreas.sandberg@arm.comFlashDevice::unserialize(CheckpointIn &cp) 54710801Srene.dejong@arm.com{ 54810801Srene.dejong@arm.com UNSERIALIZE_SCALAR(planeMask); 54910801Srene.dejong@arm.com 55011226SGeoffrey.Blake@arm.com UNSERIALIZE_CONTAINER(unknownPages); 55111226SGeoffrey.Blake@arm.com UNSERIALIZE_CONTAINER(blockValidEntries); 55211226SGeoffrey.Blake@arm.com UNSERIALIZE_CONTAINER(blockEmptyEntries); 55310801Srene.dejong@arm.com 55410801Srene.dejong@arm.com int location_table_size; 55510801Srene.dejong@arm.com UNSERIALIZE_SCALAR(location_table_size); 55610801Srene.dejong@arm.com locationTable.resize(location_table_size); 55710801Srene.dejong@arm.com for (uint32_t count = 0; count < location_table_size; count++) { 55811226SGeoffrey.Blake@arm.com paramIn(cp, csprintf("locationTable[%d].page", count), 55911226SGeoffrey.Blake@arm.com locationTable[count].page); 56011226SGeoffrey.Blake@arm.com paramIn(cp, csprintf("locationTable[%d].block", count), 56111226SGeoffrey.Blake@arm.com locationTable[count].block); 56211226SGeoffrey.Blake@arm.com } 56310801Srene.dejong@arm.com}; 56410801Srene.dejong@arm.com 56510801Srene.dejong@arm.com/** 56610801Srene.dejong@arm.com * Drain; needed to enable checkpoints 56710801Srene.dejong@arm.com */ 56810801Srene.dejong@arm.com 56910913Sandreas.sandberg@arm.comDrainState 57010913Sandreas.sandberg@arm.comFlashDevice::drain() 57110801Srene.dejong@arm.com{ 57210801Srene.dejong@arm.com if (planeEvent.scheduled()) { 57310913Sandreas.sandberg@arm.com DPRINTF(Drain, "Flash device is draining...\n"); 57410913Sandreas.sandberg@arm.com return DrainState::Draining; 57510801Srene.dejong@arm.com } else { 57610801Srene.dejong@arm.com DPRINTF(Drain, "Flash device in drained state\n"); 57710913Sandreas.sandberg@arm.com return DrainState::Drained; 57810801Srene.dejong@arm.com } 57910801Srene.dejong@arm.com} 58010801Srene.dejong@arm.com 58110801Srene.dejong@arm.com/** 58210801Srene.dejong@arm.com * Checkdrain; needed to enable checkpoints 58310801Srene.dejong@arm.com */ 58410801Srene.dejong@arm.com 58510801Srene.dejong@arm.comvoid 58610801Srene.dejong@arm.comFlashDevice::checkDrain() 58710801Srene.dejong@arm.com{ 58811179Ssascha.bischoff@ARM.com if (drainState() != DrainState::Draining) 58910801Srene.dejong@arm.com return; 59010801Srene.dejong@arm.com 59110801Srene.dejong@arm.com if (planeEvent.when() > curTick()) { 59210801Srene.dejong@arm.com DPRINTF(Drain, "Flash device is still draining\n"); 59310801Srene.dejong@arm.com } else { 59410801Srene.dejong@arm.com DPRINTF(Drain, "Flash device is done draining\n"); 59510913Sandreas.sandberg@arm.com signalDrainDone(); 59610801Srene.dejong@arm.com } 59710801Srene.dejong@arm.com} 598