diff options
-rw-r--r-- | src/dev/arm/AbstractNVM.py | 46 | ||||
-rw-r--r-- | src/dev/arm/FlashDevice.py | 74 | ||||
-rw-r--r-- | src/dev/arm/SConscript | 4 | ||||
-rw-r--r-- | src/dev/arm/abstract_nvm.hh | 109 | ||||
-rw-r--r-- | src/dev/arm/flash_device.cc | 630 | ||||
-rw-r--r-- | src/dev/arm/flash_device.hh | 202 |
6 files changed, 1065 insertions, 0 deletions
diff --git a/src/dev/arm/AbstractNVM.py b/src/dev/arm/AbstractNVM.py new file mode 100644 index 000000000..2a50e76c3 --- /dev/null +++ b/src/dev/arm/AbstractNVM.py @@ -0,0 +1,46 @@ +# Copyright (c) 2013-2015 ARM Limited +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Rene de Jong +# + +from m5.params import * +from m5.proxy import * +from m5.SimObject import SimObject + +class AbstractNVM(SimObject): + type = 'AbstractNVM' + abstract = True + cxx_header = "dev/arm/abstract_nvm.hh" diff --git a/src/dev/arm/FlashDevice.py b/src/dev/arm/FlashDevice.py new file mode 100644 index 000000000..ed3b9d04d --- /dev/null +++ b/src/dev/arm/FlashDevice.py @@ -0,0 +1,74 @@ +# Copyright (c) 2013-2015 ARM Limited +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Rene de Jong +# + +from m5.params import * +from m5.proxy import * +from AbstractNVM import * + +#Distribution of the data. +#sequential: sequential (address n+1 is likely to be on the same plane as n) +#Random: @TODO Not yet implemented +#stripe: striping over all the planes +class DataDistribution(Enum): vals = ['sequential', 'stripe'] + +class FlashDevice(AbstractNVM): + type = 'FlashDevice' + cxx_header = "dev/arm/flash_device.hh" + # default blocksize is 128 kB.This seems to be the most common size in + # mobile devices (not the image blocksize) + blk_size = Param.MemorySize("128kB", "Size of one disk block") + # disk page size is 2 kB. This is the most commonly used page size in + # flash devices + page_size = Param.MemorySize("2kB", "Size of one disk page") + # There are many GC flavours. It is impossible to cover them all; this + # parameter enables the approximation of different GC algorithms + GC_active = Param.Percent(50, "Percentage of the time (in whole numbers) \ + that the GC is activated if a block is full") + # Access latencies. Different devices will have different latencies, but + # the latencies will be around the default values. + read_lat = Param.Latency("25us", "Read Latency") + write_lat = Param.Latency("200us", "Write Latency") + erase_lat = Param.Latency("1500us", "Erase Delay") + # Number of planes ought to be a power of two according to ONFI standard + num_planes = Param.UInt32(1, "Number of planes per die") + # Data distribution. Default is none. It is adviced to switch to stripe + # when more than one plane is used. + data_distribution = Param.DataDistribution('sequential', "Distribution \ + of the data in the adress table; Stripe needed for multiple\ + planes; otherwise use: sequential") + diff --git a/src/dev/arm/SConscript b/src/dev/arm/SConscript index 7ed85a587..09ee7b4e5 100644 --- a/src/dev/arm/SConscript +++ b/src/dev/arm/SConscript @@ -40,6 +40,8 @@ Import('*') if env['TARGET_ISA'] == 'arm': + SimObject('AbstractNVM.py') + SimObject('FlashDevice.py') SimObject('Gic.py') SimObject('RealView.py') SimObject('EnergyCtrl.py') @@ -48,6 +50,7 @@ if env['TARGET_ISA'] == 'arm': Source('amba_device.cc') Source('amba_fake.cc') Source('base_gic.cc') + Source('flash_device.cc') Source('generic_timer.cc') Source('gic_pl390.cc') Source('gic_v2m.cc') @@ -64,6 +67,7 @@ if env['TARGET_ISA'] == 'arm': Source('energy_ctrl.cc') DebugFlag('AMBA') + DebugFlag('FlashDevice') DebugFlag('HDLcd') DebugFlag('PL111') DebugFlag('GICV2M') diff --git a/src/dev/arm/abstract_nvm.hh b/src/dev/arm/abstract_nvm.hh new file mode 100644 index 000000000..0882df215 --- /dev/null +++ b/src/dev/arm/abstract_nvm.hh @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013-2015 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Rene de Jong + */ + +#ifndef __DEV_ARM_ABSTRACT_NVM_HH__ +#define __DEV_ARM_ABSTRACT_NVM_HH__ + +#include "base/callback.hh" +#include "params/AbstractNVM.hh" +#include "sim/sim_object.hh" + +/** + * This is an interface between the disk interface (which will handle the disk + * data transactions) and the timing model. The timing model only takes care + * of calculating the appropriate delay to the disk, and calling back a + * function when the action has completed. All the other associated actions + * (such as getting data from A to B) should be taken care of by the disk + * interface. + */ +class AbstractNVM : public SimObject +{ + + public: + AbstractNVM(const AbstractNVMParams* p): SimObject(p) {}; + virtual ~AbstractNVM() {}; + + /** + * Initialize Memory. + * This function is used to set the memory device dimensions to the + * dimensions that it controls. For instance, One can imagine that the + * memory is one disk, e.g. the /data partition of Android, which means + * that the data handling part will have an image of /data. On the other + * hand one might want to set up a Raid like configuration, without + * wanting to create multiple disk images. In that case one can choose to + * devide the image over multiple memory devices in any way he wants + * (i.e. higher layers can implement some division based on logical + * addresses, or intelligent file system interpretation analysis; to + * effectively devide the disk over the devices; enabling object oriented + * storage devices). + * Moving this function outside of the constructor allows you the + * flexibility to make this decision once the image is loaded. + * + * @param disk_size disksize in sectors; value can be obtained from the + * disk image + * @param sector_size size of one sector in bytes; value is defined in + * disk_image.hh + */ + virtual void initializeMemory(uint64_t disk_size, uint32_t sector_size) = + 0; + + /** + * Access functions + * Access function to simulate a read/write access to the memory. Once + * the action has completed, the Callback event should be called. Putting + * a NULL pointer as callback is valid syntax, and should result in the + * simulation of the access, but with no callback to the higher layer. + * This may be used to occupy the device, such that next actions will be + * delayed. The read/write function will schedule the incoming requests + * on a first come first serve basis. + * + * @param address The logical address to a location in the Non-volatile + * memory. + * @param amount The amount of data transfered from the NVM in bytes + * @param event A pointer to a callback function that will perform the + * actions taken by the disk controller on successfull completion of the + * data transfer between the disk and the disk controller. + */ + virtual void readMemory(uint64_t address, uint32_t amount, + Callback *event) = 0; + virtual void writeMemory(uint64_t address, uint32_t amount, + Callback *event) = 0; +}; + +#endif //__DEV_ARM_ABSTRACT_NVM_HH__ diff --git a/src/dev/arm/flash_device.cc b/src/dev/arm/flash_device.cc new file mode 100644 index 000000000..eaa839178 --- /dev/null +++ b/src/dev/arm/flash_device.cc @@ -0,0 +1,630 @@ +/* + * Copyright (c) 2013-2015 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Rene de Jong + */ + +/** @file + * This simplistic flash model is designed to model managed SLC NAND flash. + * This device will need an interface module (such as NVMe or UFS); Note that + * this model only calculates the delay and does not perform the actual + * transaction. + * + * To access the memory, use either readMemory or writeMemory. This will + * schedule an event at the tick where the action will finish. If a callback + * has been given as argument then that function will be called on completion + * of that event. Note that this does not guarantee that there are no other + * actions pending in the flash device. + * + * IMPORTANT: number of planes should be a power of 2. + */ + +#include "dev/arm/flash_device.hh" + +#include "debug/Drain.hh" + +/** + * Create this device + */ + +FlashDevice* +FlashDeviceParams::create() +{ + return new FlashDevice(this); +} + + +/** + * Flash Device constructor and destructor + */ + +FlashDevice::FlashDevice(const FlashDeviceParams* p): + AbstractNVM(p), + diskSize(0), + blockSize(p->blk_size), + pageSize(p->page_size), + GCActivePercentage(p->GC_active), + readLatency(p->read_lat), + writeLatency(p->write_lat), + eraseLatency(p->erase_lat), + dataDistribution(p->data_distribution), + numPlanes(p->num_planes), + pagesPerBlock(0), + pagesPerDisk(0), + blocksPerDisk(0), + planeMask(numPlanes - 1), + drainManager(NULL), + planeEventQueue(numPlanes), + planeEvent(this) +{ + + /* + * Let 'a' be a power of two of n bits, written such that a-n is the msb + * and a-0 is the lsb. Since it is a power of two, only one bit (a-x, + * with 0 <= x <= n) is set. If we subtract one from this number the bits + * a-(x-1) to a-0 are set and all the other bits are cleared. Hence a + * bitwise AND with those two numbers results in an integer with all bits + * cleared. + */ + if(numPlanes & planeMask) + fatal("Number of planes is not a power of 2 in flash device.\n"); +} + +/** + * Initiates all the flash functions: initializes the lookup tables, age of + * the device, etc. This can only be done once the disk image is known. + * Thats why it can't be done in the constructor. + */ +void +FlashDevice::initializeFlash(uint64_t disk_size, uint32_t sector_size) +{ + diskSize = disk_size * sector_size; + pagesPerBlock = blockSize / pageSize; + pagesPerDisk = diskSize / pageSize; + blocksPerDisk = diskSize / blockSize; + + /** Sanity information: check flash configuration */ + DPRINTF(FlashDevice, "diskSize: %d Bytes; %d pages per block, %d pages " + "per disk\n", diskSize, pagesPerBlock, pagesPerDisk); + + locationTable.resize(pagesPerDisk); + + /**Garbage collection related*/ + blockValidEntries.resize(blocksPerDisk, 0); + blockEmptyEntries.resize(blocksPerDisk, pagesPerBlock); + + /** + * This is a bitmap. Every bit is a page + * unknownPages is a vector of 32 bit integers. If every page was an + * integer, the total size would be pagesPerDisk; since we can map one + * page per bit we need ceil(pagesPerDisk/32) entries. 32 = 1 << 5 hence + * it will do to just shift pagesPerDisk five positions and add one. This + * will allocate one integer to many for this data structure in the worst + * case. + */ + unknownPages.resize((pagesPerDisk >> 5) + 1, 0xFFFFFFFF); + + for (uint32_t count = 0; count < pagesPerDisk; count++) { + //setup lookup table + physical aspects + + if (dataDistribution == Enums::stripe) { + locationTable[count].page = count / blocksPerDisk; + locationTable[count].block = count % blocksPerDisk; + + } else { + locationTable[count].page = count % pagesPerBlock; + locationTable[count].block = count / pagesPerBlock; + } + } +} + +FlashDevice::~FlashDevice() +{ + DPRINTF(FlashDevice, "Remove FlashDevice\n"); +} + +/** + * Handles the accesses to the device. + * The function determines when certain actions are scheduled and schedules + * an event that uses the callback function on completion of the action. + */ +void +FlashDevice::accessDevice(uint64_t address, uint32_t amount, Callback *event, + Actions action) +{ + DPRINTF(FlashDevice, "Flash calculation for %d bytes in %d pages\n" + , amount, pageSize); + + std::vector<Tick> time(numPlanes, 0); + uint64_t logic_page_addr = address / pageSize; + uint32_t plane_address = 0; + + /** + * The access will be broken up in a number of page accesses. The number + * of page accesses depends on the amount that needs to be transfered. + * The assumption here is that the interface is completely ignorant of + * the page size and that this model has to figure out all of the + * transaction characteristics. + */ + for (uint32_t count = 0; amount > (count * pageSize); count++) { + uint32_t index = (locationTable[logic_page_addr].block * + pagesPerBlock) + (logic_page_addr % pagesPerBlock); + + DPRINTF(FlashDevice, "Index 0x%8x, Block 0x%8x, pages/block %d," + " logic address 0x%8x\n", index, + locationTable[logic_page_addr].block, pagesPerBlock, + logic_page_addr); + DPRINTF(FlashDevice, "Page %d; %d bytes up to this point\n", count, + (count * pageSize)); + + plane_address = locationTable[logic_page_addr].block & planeMask; + + if (action == ActionRead) { + //lookup + //call accessTimes + time[plane_address] += accessTimes(locationTable[logic_page_addr] + .block, ActionRead); + + /*stats*/ + stats.readAccess.sample(logic_page_addr); + stats.readLatency.sample(time[plane_address]); + } else { //write + //lookup + //call accessTimes if appropriate, page may be unknown, so lets + //give it the benefit of the doubt + + if (getUnknownPages(index)) + time[plane_address] += accessTimes + (locationTable[logic_page_addr].block, ActionWrite); + + else //A remap is needed + time[plane_address] += remap(logic_page_addr); + + /*stats*/ + stats.writeAccess.sample(logic_page_addr); + stats.writeLatency.sample(time[plane_address]); + } + + /** + * Check if the page is known and used. unknownPages is a bitmap of + * all the pages. It tracks wether we can be sure that the + * information of this page is taken into acount in the model (is it + * considered in blockValidEntries and blockEmptyEntries?). If it has + * been used in the past, then it is known. + */ + if (getUnknownPages(index)) { + clearUnknownPages(index); + --blockEmptyEntries[locationTable[logic_page_addr].block]; + ++blockValidEntries[locationTable[logic_page_addr].block]; + } + + stats.fileSystemAccess.sample(address); + ++logic_page_addr; + } + + /** + * previous part of the function found the times spend in different + * planes, now lets find the maximum to know when to callback the disk + */ + for (uint32_t count = 0; count < numPlanes; count++){ + plane_address = (time[plane_address] > time[count]) ? plane_address + : count; + + DPRINTF(FlashDevice, "Plane %d is busy for %d ticks\n", count, + time[count]); + + if (time[count] != 0) { + + struct CallBackEntry cbe; + /** + * If there are no events for this plane, then add the current + * time to the occupation time; otherwise, plan it after the + * last event. If by chance that event is handled in this tick, + * then we would still end up with the same result. + */ + if (planeEventQueue[count].empty()) + cbe.time = time[count] + curTick(); + else + cbe.time = time[count] + + planeEventQueue[count].back().time; + cbe.function = NULL; + planeEventQueue[count].push_back(cbe); + + DPRINTF(FlashDevice, "scheduled at: %ld\n", cbe.time); + + if (!planeEvent.scheduled()) + schedule(planeEvent, planeEventQueue[count].back().time); + else if (planeEventQueue[count].back().time < planeEvent.when()) + reschedule(planeEvent, + planeEventQueue[plane_address].back().time, true); + } + } + + //worst case two plane finish at the same time, each triggers an event + //and this callback will be called once. Maybe before the other plane + //could execute its event, but in the same tick. + planeEventQueue[plane_address].back().function = event; + DPRINTF(FlashDevice, "Callback queued for plane %d; %d in queue\n", + plane_address, planeEventQueue[plane_address].size()); + DPRINTF(FlashDevice, "first event @ %d\n", planeEvent.when()); +} + +/** + * When a plane completes its action, this event is triggered. When a + * callback function was associated with that event, it will be called. + */ + +void +FlashDevice::actionComplete() +{ + DPRINTF(FlashDevice, "Plane action completed\n"); + uint8_t plane_address = 0; + + uint8_t next_event = 0; + + /**Search for a callback that is supposed to happen in this Tick*/ + for (plane_address = 0; plane_address < numPlanes; plane_address++) { + if (!planeEventQueue[plane_address].empty()) { + /** + * Invariant: All queued events are scheduled in the present + * or future. + */ + assert(planeEventQueue[plane_address].front().time >= curTick()); + + if (planeEventQueue[plane_address].front().time == curTick()) { + /** + * To ensure that the follow-up action is executed correctly, + * the callback entry first need to be cleared before it can + * be called. + */ + Callback *temp = planeEventQueue[plane_address].front(). + function; + planeEventQueue[plane_address].pop_front(); + + /**Found a callback, lets make it happen*/ + if (temp != NULL) { + DPRINTF(FlashDevice, "Callback, %d\n", plane_address); + temp->process(); + } + } + } + } + + /** Find when to schedule the planeEvent next */ + for (plane_address = 0; plane_address < numPlanes; plane_address++) { + if (!planeEventQueue[plane_address].empty()) + if (planeEventQueue[next_event].empty() || + (planeEventQueue[plane_address].front().time < + planeEventQueue[next_event].front().time)) + next_event = plane_address; + } + + /**Schedule the next plane that will be ready (if any)*/ + if (!planeEventQueue[next_event].empty()) { + DPRINTF(FlashDevice, "Schedule plane: %d\n", plane_address); + reschedule(planeEvent, planeEventQueue[next_event].front().time, true); + } + + checkDrain(); + + DPRINTF(FlashDevice, "returing from flash event\n"); + DPRINTF(FlashDevice, "first event @ %d\n", planeEvent.when()); +} + +/** + * Handles the remapping of the pages. It is a (I hope) sensible statistic + * approach. asumption: garbage collection happens when a clean is needed + * (may become stochastic function). + */ +Tick +FlashDevice::remap(uint64_t logic_page_addr) +{ + /** + * Are there any empty left in this Block, or do we need to do an erase + */ + if (blockEmptyEntries[locationTable[logic_page_addr].block] > 0) { + //just a remap + //update tables + --blockEmptyEntries[locationTable[logic_page_addr].block]; + //access to this table won't be sequential anymore + locationTable[logic_page_addr].page = pagesPerBlock + 2; + //access new block + Tick time = accessTimes(locationTable[logic_page_addr].block, + ActionWrite); + + DPRINTF(FlashDevice, "Remap returns %d ticks\n", time); + return time; + + } else { + //calculate how much time GC would have taken + uint32_t block = locationTable[logic_page_addr].block; + Tick time = ((GCActivePercentage * + (accessTimes(block, ActionCopy) + + accessTimes(block, ActionErase))) + / 100); + + //use block as the logical start address of the block + block = locationTable[logic_page_addr].block * pagesPerBlock; + + //assumption: clean will improve locality + for (uint32_t count = 0; count < pageSize; count++) { + locationTable[block + count].page = (block + count) % + pagesPerBlock; + ++count; + } + + blockEmptyEntries[locationTable[logic_page_addr].block] = + pagesPerBlock; + /*stats*/ + ++stats.totalGCActivations; + + DPRINTF(FlashDevice, "Remap with erase action returns %d ticks\n", + time); + + return time; + } + +} + +/** + * Calculates the accesstime per operation needed + */ +Tick +FlashDevice::accessTimes(uint64_t block, Actions action) +{ + Tick time = 0; + + switch(action) { + case ActionRead: { + /**Just read the page*/ + time = readLatency; + } break; + + case ActionWrite: { + /**Write the page, and read the result*/ + time = writeLatency + readLatency; + } break; + + case ActionErase: { + /**Erase and check wether it was successfull*/ + time = eraseLatency + readLatency; + } break; + + case ActionCopy: { + /**Copy every valid page*/ + uint32_t validpages = blockValidEntries[block]; + time = validpages * (readLatency + writeLatency); + } break; + + default: break; + } + + //Used to determine sequential action. + DPRINTF(FlashDevice, "Access returns %d ticks\n", time); + return time; +} + +/** + * clearUnknownPages. defines that a page is known and used + * unknownPages is a bitmap of all the pages. It tracks wether we can be sure + * that the information of this page is taken into acount in the model (is it + * considered in blockValidEntries and blockEmptyEntries?). If it has been + * used in the past, then it is known. But it needs to be tracked to make + * decisions about write accesses, and indirectly about copy actions. one + * unknownPage entry is a 32 bit integer. So if we have a page index, then + * that means that we need entry floor(index/32) (index >> 5) and we need to + * select the bit which number is equal to the remainder of index/32 + * (index%32). The bit is cleared to make sure that we see it as considered + * in the future. + */ + +inline +void +FlashDevice::clearUnknownPages(uint32_t index) +{ + unknownPages[index >> 5] &= ~(0x01 << (index % 32)); +} + +/** + * getUnknownPages. Verify wether a page is known + */ + +inline +bool +FlashDevice::getUnknownPages(uint32_t index) +{ + return unknownPages[index >> 5] & (0x01 << (index % 32)); +} + +void +FlashDevice::regStats() +{ + using namespace Stats; + + std::string fd_name = name() + ".FlashDevice"; + + // Register the stats + /** Amount of GC activations*/ + stats.totalGCActivations + .name(fd_name + ".totalGCActivations") + .desc("Number of Garbage collector activations") + .flags(none); + + /** Histogram of address accesses*/ + stats.writeAccess + .init(2) + .name(fd_name + ".writeAccessHist") + .desc("Histogram of write addresses") + .flags(pdf); + stats.readAccess + .init(2) + .name(fd_name + ".readAccessHist") + .desc("Histogram of read addresses") + .flags(pdf); + stats.fileSystemAccess + .init(100) + .name(fd_name + ".fileSystemAccessHist") + .desc("Histogram of file system accesses") + .flags(pdf); + + /** Histogram of access latencies*/ + stats.writeLatency + .init(100) + .name(fd_name + ".writeLatencyHist") + .desc("Histogram of write latency") + .flags(pdf); + stats.readLatency + .init(100) + .name(fd_name + ".readLatencyHist") + .desc("Histogram of read latency") + .flags(pdf); +} + +/** + * Serialize; needed to create checkpoints + */ + +void +FlashDevice::serialize(std::ostream &os) +{ + SERIALIZE_SCALAR(planeMask); + + int unknown_pages_size = unknownPages.size(); + SERIALIZE_SCALAR(unknown_pages_size); + for (uint32_t count = 0; count < unknownPages.size(); count++) + SERIALIZE_SCALAR(unknownPages[count]); + + int location_table_size = locationTable.size(); + SERIALIZE_SCALAR(location_table_size); + for (uint32_t count = 0; count < location_table_size; count++) { + SERIALIZE_SCALAR(locationTable[count].page); + SERIALIZE_SCALAR(locationTable[count].block); + } + + int block_valid_entries_size = blockValidEntries.size(); + SERIALIZE_SCALAR(block_valid_entries_size); + for (uint32_t count = 0; count < blockValidEntries.size(); count++) + SERIALIZE_SCALAR(blockValidEntries[count]); + + int block_empty_entries_size = blockEmptyEntries.size(); + SERIALIZE_SCALAR(block_empty_entries_size); + for (uint32_t count = 0; count < blockEmptyEntries.size(); count++) + SERIALIZE_SCALAR(blockEmptyEntries[count]); + +}; + +/** + * Unserialize; needed to restore from checkpoints + */ + +void +FlashDevice::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(planeMask); + + int unknown_pages_size; + UNSERIALIZE_SCALAR(unknown_pages_size); + unknownPages.resize(unknown_pages_size); + for (uint32_t count = 0; count < unknown_pages_size; count++) + UNSERIALIZE_SCALAR(unknownPages[count]); + + int location_table_size; + UNSERIALIZE_SCALAR(location_table_size); + locationTable.resize(location_table_size); + for (uint32_t count = 0; count < location_table_size; count++) { + UNSERIALIZE_SCALAR(locationTable[count].page); + UNSERIALIZE_SCALAR(locationTable[count].block); + } + + int block_valid_entries_size; + UNSERIALIZE_SCALAR(block_valid_entries_size); + blockValidEntries.resize(block_valid_entries_size); + for (uint32_t count = 0; count < block_valid_entries_size; count++) + UNSERIALIZE_SCALAR(blockValidEntries[count]); + + int block_empty_entries_size; + UNSERIALIZE_SCALAR(block_empty_entries_size); + blockEmptyEntries.resize(block_empty_entries_size); + for (uint32_t count = 0; count < block_empty_entries_size; count++) + UNSERIALIZE_SCALAR(blockEmptyEntries[count]); + +}; + +/** + * Drain; needed to enable checkpoints + */ + +unsigned int +FlashDevice::drain(DrainManager *dm) +{ + unsigned int count = 0; + + if (planeEvent.scheduled()) { + count = 1; + drainManager = dm; + } else { + DPRINTF(Drain, "Flash device in drained state\n"); + } + + if (count) { + DPRINTF(Drain, "Flash device is draining...\n"); + setDrainState(Drainable::Draining); + } else { + DPRINTF(Drain, "Flash device drained\n"); + setDrainState(Drainable::Drained); + } + return count; +} + +/** + * Checkdrain; needed to enable checkpoints + */ + +void +FlashDevice::checkDrain() +{ + if (drainManager == NULL) { + return; + } + + if (planeEvent.when() > curTick()) { + DPRINTF(Drain, "Flash device is still draining\n"); + } else { + DPRINTF(Drain, "Flash device is done draining\n"); + drainManager->signalDrainDone(); + drainManager = NULL; + } +} diff --git a/src/dev/arm/flash_device.hh b/src/dev/arm/flash_device.hh new file mode 100644 index 000000000..8dc4ba04a --- /dev/null +++ b/src/dev/arm/flash_device.hh @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2013-2015 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Rene de Jong + */ +#ifndef __DEV_ARM_FLASH_DEVICE_HH__ +#define __DEV_ARM_FLASH_DEVICE_HH__ + +#include <deque> + +#include "base/statistics.hh" +#include "debug/FlashDevice.hh" +#include "dev/arm/abstract_nvm.hh" +#include "enums/DataDistribution.hh" +#include "params/FlashDevice.hh" +#include "sim/serialize.hh" + +/** + * Flash Device model + * The Flash Device model is a timing model for a NAND flash device. + * It doesn't tranfer any data + */ +class FlashDevice : public AbstractNVM +{ + public: + + /** Initialize functions*/ + FlashDevice(const FlashDeviceParams*); + ~FlashDevice(); + + /** Checkpoint functions*/ + unsigned int drain(DrainManager *dm); + void checkDrain(); + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + + private: + /** Defines the possible actions to the flash*/ + enum Actions { + ActionRead, + ActionWrite, + ActionErase, + /** + * A copy involves taking all the used pages from a block and store + * it in another + */ + ActionCopy + }; + + /** Every logical address maps to a physical block and a physical page*/ + struct PageMapEntry { + uint32_t page; + uint32_t block; + }; + + struct CallBackEntry { + Tick time; + Callback *function; + }; + + struct FlashDeviceStats { + /** Amount of GC activations*/ + Stats::Scalar totalGCActivations; + + /** Histogram of address accesses*/ + Stats::Histogram writeAccess; + Stats::Histogram readAccess; + Stats::Histogram fileSystemAccess; + + /** Histogram of access latencies*/ + Stats::Histogram writeLatency; + Stats::Histogram readLatency; + }; + + /** Device access functions Inherrited from AbstractNVM*/ + virtual void initializeMemory(uint64_t disk_size, uint32_t sector_size) + { + initializeFlash(disk_size, sector_size); + } + + virtual void readMemory(uint64_t address, uint32_t amount, + Callback *event) + { + accessDevice(address, amount, event, ActionRead); + } + virtual void writeMemory(uint64_t address, uint32_t amount, + Callback *event) + { + accessDevice(address, amount, event, ActionWrite); + } + + /**Initialization function; called when all disk specifics are known*/ + void initializeFlash(uint64_t disk_size, uint32_t sector_size); + + /**Flash action function*/ + void accessDevice(uint64_t address, uint32_t amount, Callback *event, + Actions action); + + /** Event rescheduler*/ + void actionComplete(); + + /** FTL functionality */ + Tick remap(uint64_t logic_page_addr); + + /** Access time calculator*/ + Tick accessTimes(uint64_t address, Actions accesstype); + + /** Function to indicate that a page is known*/ + void clearUnknownPages(uint32_t index); + + /** Function to test if a page is known*/ + bool getUnknownPages(uint32_t index); + + /**Stats register function*/ + void regStats(); + + /** Disk sizes in bytes */ + uint64_t diskSize; + const uint32_t blockSize; + const uint32_t pageSize; + + /** Garbage collection algorithm emulator */ + const uint32_t GCActivePercentage; + + /** Access latencies */ + const Tick readLatency; + const Tick writeLatency; + const Tick eraseLatency; + + /** Flash organization */ + const Enums::DataDistribution dataDistribution; + const uint32_t numPlanes; + + /** RequestHandler stats */ + struct FlashDeviceStats stats; + + /** Disk dimensions in pages and blocks */ + uint32_t pagesPerBlock; + uint32_t pagesPerDisk; + uint32_t blocksPerDisk; + + uint32_t planeMask; + + /** + * drain manager + * Needed to be able to implement checkpoint functionality + */ + + DrainManager *drainManager; + + /** + * when the disk is first started we are unsure of the number of + * used pages, this variable will help determining what we do know. + */ + std::vector<uint32_t> unknownPages; + /** address to logic place has a block and a page field*/ + std::vector<struct PageMapEntry> locationTable; + /** number of valid entries per block*/ + std::vector<uint32_t> blockValidEntries; + /** number of empty entries*/ + std::vector<uint32_t> blockEmptyEntries; + + /**This vector of queues keeps track of all the callbacks per plane*/ + std::vector<std::deque<struct CallBackEntry> > planeEventQueue; + + /** Completion event */ + EventWrapper<FlashDevice, &FlashDevice::actionComplete> planeEvent; +}; +#endif //__DEV_ARM_FLASH_DEVICE_HH__ |