Subtile modification of the CFI memory to bring back u-boot compatibility : - Ignoring AMD_RESET_CMD (0xf0) - Increasing CFIQueryTable size to have 4 Erase Block Region Information (3 are just empty) Change-Id: I49e7a78a89a46b1298f04132559debafdeddb8ef Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/49570 Reviewed-by: Bobby R. Bruce <bbruce@ucdavis.edu> Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com> Maintainer: Bobby R. Bruce <bbruce@ucdavis.edu> Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com> Tested-by: kokoro <noreply+kokoro@google.com>
405 lines
12 KiB
C++
405 lines
12 KiB
C++
/*
|
|
* Copyright (c) 2012-2013, 2021 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.
|
|
*
|
|
* Copyright (c) 2001-2005 The Regents of The University of Michigan
|
|
* All rights reserved.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef __MEM_FLASH_MEM_HH__
|
|
#define __MEM_FLASH_MEM_HH__
|
|
|
|
#include "mem/abstract_mem.hh"
|
|
#include "params/CfiMemory.hh"
|
|
|
|
namespace gem5
|
|
{
|
|
|
|
namespace memory
|
|
{
|
|
|
|
/**
|
|
* CfiMemory: This is modelling a flash memory adhering to the
|
|
* Common Flash Interface (CFI):
|
|
*
|
|
* JEDEC JESD68.01
|
|
* JEDEC JEP137B
|
|
* Intel Application Note 646
|
|
*
|
|
* This is as of now a pure functional model of a flash controller:
|
|
* no timing/power information has been encoded in it and it is therefore
|
|
* not representive of a real device. Some voltage/timing values have
|
|
* nevertheless been encoded in the CFI table.
|
|
* This is just a requirement from the CFI specification: guest software
|
|
* might query those entries, but they are not reflected in gem5 statistics.
|
|
*
|
|
* The model is meant to be used to allow execution of flash drivers
|
|
* (e.g. UEFI firmware storing EFI variables in non volatile memory)
|
|
*/
|
|
class CfiMemory : public AbstractMemory
|
|
{
|
|
private:
|
|
enum class CfiCommand
|
|
{
|
|
NO_CMD = 0,
|
|
LOCK_BLOCK = 0x1,
|
|
ERASE_BLOCK_SETUP = 0x20,
|
|
WORD_PROGRAM = 0x40,
|
|
CLEAR_STATUS_REG = 0x50,
|
|
LOCK_BLOCK_SETUP = 0x60,
|
|
READ_STATUS_REG = 0x70,
|
|
READ_DEVICE_ID = 0x90,
|
|
READ_CFI_QUERY = 0x98,
|
|
BUFFERED_PROGRAM_SETUP = 0xE8,
|
|
BUFFERED_PROGRAM_CONFIRM = 0xD0,
|
|
BLOCK_ERASE_CONFIRM = 0xD0,
|
|
UNLOCK_BLOCK = 0xD0,
|
|
AMD_RESET=0xF0,
|
|
READ_ARRAY = 0xFF,
|
|
/** This is not a real command, but it is used by the internal
|
|
* model only to represent the 2nd write cycle state for a buffered
|
|
* program (when the buffer size is supplied) */
|
|
BUFFER_SIZE_READ,
|
|
};
|
|
|
|
/** Possible in the status register */
|
|
static const uint8_t STATUS_ERASE_ERROR = 0x30;
|
|
static const uint8_t STATUS_LOCK_ERROR = 0x12;
|
|
static const uint8_t STATUS_READY = 0x80;
|
|
static const uint8_t STATUS_PROGRAM_LOCK_BIT = 0x10;
|
|
|
|
/** Metadata about the erase blocks in flash */
|
|
struct BlockData : public Serializable
|
|
{
|
|
BlockData(const CfiMemory &_parent, ssize_t number, ssize_t size)
|
|
: Serializable(), locked(number, false), blockSize(size),
|
|
parent(_parent)
|
|
{}
|
|
|
|
/**
|
|
* Return true if the block pointed by the block_address
|
|
* parameter is locked
|
|
*
|
|
* @params block_address address of the erase block in flash
|
|
* memory: first block starts ad address 0x0
|
|
* @return true if block is locked
|
|
*/
|
|
bool isLocked(Addr block_address) const;
|
|
|
|
/**
|
|
* Lock the block pointed by the block_address
|
|
* parameter
|
|
*
|
|
* @params block_address address of the erase block in flash
|
|
* memory: first block starts ad address 0x0
|
|
*/
|
|
void lock(Addr block_address);
|
|
|
|
/**
|
|
* Unlock the block pointed by the block_address
|
|
* parameter
|
|
*
|
|
* @params block_address address of the erase block in flash
|
|
* memory: first block starts ad address 0x0
|
|
*/
|
|
void unlock(Addr block_address);
|
|
|
|
/** Erase a single block. The address of the block
|
|
* is supplied by the packet address.
|
|
*
|
|
* @params pkt memory packet targeting the erase block
|
|
*/
|
|
void erase(PacketPtr pkt);
|
|
|
|
/** Number of erase blocks in flash memory */
|
|
ssize_t number() const { return locked.size(); }
|
|
|
|
/** Size in bytes of a single erase block */
|
|
ssize_t size() const { return blockSize; }
|
|
|
|
private: // Serializable
|
|
void serialize(CheckpointOut &cp) const override;
|
|
|
|
void unserialize(CheckpointIn &cp) override;
|
|
|
|
private:
|
|
uint32_t blockIdx(Addr block_address) const;
|
|
|
|
// Per block flag. True if the block is locked
|
|
std::vector<bool> locked;
|
|
|
|
// Size of the block in bytes
|
|
const ssize_t blockSize;
|
|
|
|
const CfiMemory &parent;
|
|
};
|
|
|
|
/**
|
|
* Word Buffer used by the BUFFERED PROGRAM command
|
|
* to write (program) chunks of words to flash
|
|
*/
|
|
struct ProgramBuffer : public Serializable
|
|
{
|
|
public:
|
|
// program buffer max size = 32 words
|
|
static const ssize_t MAX_BUFFER_SIZE = 32 * 4;
|
|
|
|
ProgramBuffer(const CfiMemory &_parent)
|
|
: Serializable(), parent(_parent)
|
|
{}
|
|
|
|
/**
|
|
* Start buffering
|
|
* @param buffer_size new size (in bytes) of the program buffer
|
|
*/
|
|
void setup(ssize_t buffer_size);
|
|
|
|
/**
|
|
* Write data into the buffer. If the buffer is full, the
|
|
* method will return true, meaning it's time to write
|
|
* back the buffer into memory
|
|
*
|
|
* @params flash_address address in flash (relative to the start)
|
|
* @params data_ptr pointer to the data
|
|
* @params size number of bytes to be written to the buffer
|
|
*
|
|
* @return true if buffer needs to be written back to flash
|
|
*/
|
|
bool write(Addr flash_address, void *data_ptr, ssize_t size);
|
|
|
|
bool writeback();
|
|
|
|
private:
|
|
void serialize(CheckpointOut &cp) const override;
|
|
|
|
void unserialize(CheckpointIn &cp) override;
|
|
|
|
private:
|
|
// program buffer
|
|
std::vector<uint8_t> buffer;
|
|
|
|
// Number of bytes written in the buffer
|
|
ssize_t bytesWritten = 0;
|
|
|
|
// Pointing to the latest written word in the buffer
|
|
Addr blockPointer = 0;
|
|
|
|
const CfiMemory &parent;
|
|
};
|
|
|
|
/**
|
|
* A deferred packet stores a packet along with its scheduled
|
|
* transmission time
|
|
*/
|
|
class DeferredPacket
|
|
{
|
|
|
|
public:
|
|
|
|
const Tick tick;
|
|
const PacketPtr pkt;
|
|
|
|
DeferredPacket(PacketPtr _pkt, Tick _tick) : tick(_tick), pkt(_pkt)
|
|
{ }
|
|
};
|
|
|
|
class MemoryPort : public ResponsePort
|
|
{
|
|
private:
|
|
CfiMemory& mem;
|
|
|
|
public:
|
|
MemoryPort(const std::string& _name, CfiMemory& _memory);
|
|
|
|
protected:
|
|
Tick recvAtomic(PacketPtr pkt) override;
|
|
Tick recvAtomicBackdoor(
|
|
PacketPtr pkt, MemBackdoorPtr &_backdoor) override;
|
|
void recvFunctional(PacketPtr pkt) override;
|
|
bool recvTimingReq(PacketPtr pkt) override;
|
|
void recvRespRetry() override;
|
|
AddrRangeList getAddrRanges() const override;
|
|
};
|
|
|
|
MemoryPort port;
|
|
|
|
/**
|
|
* Latency from that a request is accepted until the response is
|
|
* ready to be sent.
|
|
*/
|
|
const Tick latency;
|
|
|
|
/**
|
|
* Fudge factor added to the latency.
|
|
*/
|
|
const Tick latency_var;
|
|
|
|
/**
|
|
* Internal (unbounded) storage to mimic the delay caused by the
|
|
* actual memory access. Note that this is where the packet spends
|
|
* the memory latency.
|
|
*/
|
|
std::list<DeferredPacket> packetQueue;
|
|
|
|
/**
|
|
* Bandwidth in ticks per byte. The regulation affects the
|
|
* acceptance rate of requests and the queueing takes place after
|
|
* the regulation.
|
|
*/
|
|
const double bandwidth;
|
|
|
|
/**
|
|
* Track the state of the memory as either idle or busy, no need
|
|
* for an enum with only two states.
|
|
*/
|
|
bool isBusy;
|
|
|
|
/**
|
|
* Remember if we have to retry an outstanding request that
|
|
* arrived while we were busy.
|
|
*/
|
|
bool retryReq;
|
|
|
|
/**
|
|
* Remember if we failed to send a response and are awaiting a
|
|
* retry. This is only used as a check.
|
|
*/
|
|
bool retryResp;
|
|
|
|
/**
|
|
* Release the memory after being busy and send a retry if a
|
|
* request was rejected in the meanwhile.
|
|
*/
|
|
void release();
|
|
|
|
EventFunctionWrapper releaseEvent;
|
|
|
|
/**
|
|
* Dequeue a packet from our internal packet queue and move it to
|
|
* the port where it will be sent as soon as possible.
|
|
*/
|
|
void dequeue();
|
|
|
|
EventFunctionWrapper dequeueEvent;
|
|
|
|
/**
|
|
* Detemine the latency.
|
|
*
|
|
* @return the latency seen by the current packet
|
|
*/
|
|
Tick getLatency() const;
|
|
|
|
/**
|
|
* Upstream caches need this packet until true is returned, so
|
|
* hold it for deletion until a subsequent call
|
|
*/
|
|
std::unique_ptr<Packet> pendingDelete;
|
|
|
|
const uint8_t numberOfChips;
|
|
|
|
const uint16_t vendorID;
|
|
const uint16_t deviceID;
|
|
const uint16_t bankWidth;
|
|
|
|
/** Previous command (issued in the previous write cycle) */
|
|
CfiCommand readState;
|
|
CfiCommand writeState;
|
|
|
|
uint8_t statusRegister;
|
|
|
|
BlockData blocks;
|
|
|
|
ProgramBuffer programBuffer;
|
|
|
|
uint8_t cfiQueryTable[61];
|
|
|
|
public:
|
|
CfiMemory(const CfiMemoryParams &p);
|
|
|
|
DrainState drain() override;
|
|
|
|
Port &getPort(const std::string &if_name,
|
|
PortID idx=InvalidPortID) override;
|
|
void init() override;
|
|
|
|
void serialize(CheckpointOut &cp) const override;
|
|
void unserialize(CheckpointIn &cp) override;
|
|
|
|
protected:
|
|
Tick recvAtomic(PacketPtr pkt);
|
|
Tick recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr &_backdoor);
|
|
void recvFunctional(PacketPtr pkt);
|
|
bool recvTimingReq(PacketPtr pkt);
|
|
void recvRespRetry();
|
|
|
|
/** Make a read/write access to the CFI Memory */
|
|
void cfiAccess(PacketPtr pkt);
|
|
|
|
/** Write request to the CFI Memory */
|
|
void write(PacketPtr pkt);
|
|
|
|
/** Read request to the CFI Memory */
|
|
void read(PacketPtr pkt);
|
|
|
|
/**
|
|
* Helper function to read the device identifier after the
|
|
* read state machine is put in the CfiCommand::READ_DEVICE_ID
|
|
* mode.
|
|
*
|
|
* @param flash_address: The flash address LSBits encode the
|
|
* the information the software is trying
|
|
* to read
|
|
*/
|
|
uint64_t readDeviceID(Addr flash_address) const;
|
|
|
|
/**
|
|
* Service a new command issued to the flash device
|
|
*
|
|
* @param command: new command issued to the flash device
|
|
*/
|
|
void handleCommand(CfiCommand command);
|
|
|
|
/** Return the selected entry in the CFI table
|
|
*
|
|
* @param addr: offset in the CFI table
|
|
*/
|
|
uint64_t cfiQuery(Addr addr);
|
|
};
|
|
|
|
} // namespace memory
|
|
} // namespace gem5
|
|
|
|
#endif
|