--- /dev/null
+# 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")
+
--- /dev/null
+/*
+ * 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__
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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__