2022-06-27 03:24:07 -04:00
|
|
|
/*
|
|
|
|
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
2016-08-17 11:08:22 -04:00
|
|
|
#ifndef spi_flash_emulation_h
|
|
|
|
#define spi_flash_emulation_h
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <cassert>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <random>
|
2022-06-27 03:24:07 -04:00
|
|
|
#include "spi_flash_mmap.h"
|
2016-08-17 11:08:22 -04:00
|
|
|
#include "catch.hpp"
|
|
|
|
|
|
|
|
class SpiFlashEmulator;
|
|
|
|
|
|
|
|
void spi_flash_emulator_set(SpiFlashEmulator*);
|
|
|
|
|
|
|
|
class SpiFlashEmulator
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SpiFlashEmulator(size_t sectorCount) : mUpperSectorBound(sectorCount)
|
|
|
|
{
|
|
|
|
mData.resize(sectorCount * SPI_FLASH_SEC_SIZE / 4, 0xffffffff);
|
2020-03-11 06:28:30 -04:00
|
|
|
mEraseCnt.resize(sectorCount);
|
2016-08-17 11:08:22 -04:00
|
|
|
spi_flash_emulator_set(this);
|
|
|
|
}
|
|
|
|
|
2018-04-18 20:18:05 -04:00
|
|
|
SpiFlashEmulator(const char *filename)
|
|
|
|
{
|
|
|
|
load(filename);
|
2024-03-28 04:14:05 -04:00
|
|
|
// At least one page should be free, hence we create mData of size of 2 sectors.
|
2018-04-02 06:44:59 -04:00
|
|
|
mData.resize(mData.size() + SPI_FLASH_SEC_SIZE / 4, 0xffffffff);
|
|
|
|
mUpperSectorBound = mData.size() * 4 / SPI_FLASH_SEC_SIZE;
|
2018-04-18 20:18:05 -04:00
|
|
|
spi_flash_emulator_set(this);
|
|
|
|
}
|
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
~SpiFlashEmulator()
|
|
|
|
{
|
|
|
|
spi_flash_emulator_set(nullptr);
|
|
|
|
}
|
|
|
|
|
2016-10-21 05:28:50 -04:00
|
|
|
bool read(uint32_t* dest, size_t srcAddr, size_t size) const
|
2016-08-17 11:08:22 -04:00
|
|
|
{
|
|
|
|
if (srcAddr % 4 != 0 ||
|
|
|
|
size % 4 != 0 ||
|
|
|
|
srcAddr + size > mData.size() * 4) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
copy(begin(mData) + srcAddr / 4, begin(mData) + (srcAddr + size) / 4, dest);
|
|
|
|
|
|
|
|
++mReadOps;
|
|
|
|
mReadBytes += size;
|
|
|
|
mTotalTime += getReadOpTime(static_cast<uint32_t>(size));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-10-21 05:28:50 -04:00
|
|
|
bool write(size_t dstAddr, const uint32_t* src, size_t size)
|
2016-08-17 11:08:22 -04:00
|
|
|
{
|
|
|
|
uint32_t sectorNumber = dstAddr/SPI_FLASH_SEC_SIZE;
|
|
|
|
if (sectorNumber < mLowerSectorBound || sectorNumber >= mUpperSectorBound) {
|
|
|
|
WARN("invalid flash operation detected: erase sector=" << sectorNumber);
|
|
|
|
return false;
|
|
|
|
}
|
2020-11-10 02:40:01 -05:00
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
if (dstAddr % 4 != 0 ||
|
|
|
|
size % 4 != 0 ||
|
|
|
|
dstAddr + size > mData.size() * 4) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-11-10 02:40:01 -05:00
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
for (size_t i = 0; i < size / 4; ++i) {
|
2016-10-31 07:17:25 -04:00
|
|
|
if (mFailCountdown != SIZE_MAX && mFailCountdown-- == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
uint32_t sv = src[i];
|
|
|
|
size_t pos = dstAddr / 4 + i;
|
|
|
|
uint32_t& dv = mData[pos];
|
|
|
|
|
|
|
|
if (((~dv) & sv) != 0) { // are we trying to set some 0 bits to 1?
|
|
|
|
WARN("invalid flash operation detected: dst=" << dstAddr << " size=" << size << " i=" << i);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
dv = sv;
|
|
|
|
}
|
|
|
|
++mWriteOps;
|
|
|
|
mWriteBytes += size;
|
|
|
|
mTotalTime += getWriteOpTime(static_cast<uint32_t>(size));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-10-21 05:28:50 -04:00
|
|
|
bool erase(size_t sectorNumber)
|
2016-08-17 11:08:22 -04:00
|
|
|
{
|
|
|
|
size_t offset = sectorNumber * SPI_FLASH_SEC_SIZE / 4;
|
|
|
|
if (offset > mData.size()) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-11-10 02:40:01 -05:00
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
if (sectorNumber < mLowerSectorBound || sectorNumber >= mUpperSectorBound) {
|
|
|
|
WARN("invalid flash operation detected: erase sector=" << sectorNumber);
|
|
|
|
return false;
|
|
|
|
}
|
2020-11-10 02:40:01 -05:00
|
|
|
|
2016-08-22 02:16:21 -04:00
|
|
|
if (mFailCountdown != SIZE_MAX && mFailCountdown-- == 0) {
|
2016-08-22 00:11:32 -04:00
|
|
|
return false;
|
|
|
|
}
|
2016-08-17 11:08:22 -04:00
|
|
|
|
|
|
|
std::fill_n(begin(mData) + offset, SPI_FLASH_SEC_SIZE / 4, 0xffffffff);
|
|
|
|
|
|
|
|
++mEraseOps;
|
2020-03-11 06:28:30 -04:00
|
|
|
mEraseCnt[sectorNumber]++;
|
2016-08-17 11:08:22 -04:00
|
|
|
mTotalTime += getEraseOpTime();
|
|
|
|
return true;
|
|
|
|
}
|
2020-11-10 02:40:01 -05:00
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
void randomize(uint32_t seed)
|
|
|
|
{
|
|
|
|
std::random_device rd;
|
|
|
|
std::mt19937 gen(rd());
|
|
|
|
gen.seed(seed);
|
|
|
|
std::generate_n(mData.data(), mData.size(), gen);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t size() const
|
|
|
|
{
|
|
|
|
return mData.size() * 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint32_t* words() const
|
|
|
|
{
|
|
|
|
return mData.data();
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint8_t* bytes() const
|
|
|
|
{
|
|
|
|
return reinterpret_cast<const uint8_t*>(mData.data());
|
|
|
|
}
|
2020-11-10 02:40:01 -05:00
|
|
|
|
2016-10-31 07:48:28 -04:00
|
|
|
void load(const char* filename)
|
|
|
|
{
|
|
|
|
FILE* f = fopen(filename, "rb");
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
|
|
off_t size = ftell(f);
|
|
|
|
assert(size % SPI_FLASH_SEC_SIZE == 0);
|
2017-09-18 10:43:03 -04:00
|
|
|
mData.resize(size / sizeof(uint32_t));
|
2016-10-31 07:48:28 -04:00
|
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
auto s = fread(mData.data(), SPI_FLASH_SEC_SIZE, size / SPI_FLASH_SEC_SIZE, f);
|
|
|
|
assert(s == static_cast<size_t>(size / SPI_FLASH_SEC_SIZE));
|
2017-09-18 10:43:03 -04:00
|
|
|
fclose(f);
|
|
|
|
}
|
2020-11-10 02:40:01 -05:00
|
|
|
|
2017-09-18 10:43:03 -04:00
|
|
|
void save(const char* filename)
|
|
|
|
{
|
|
|
|
FILE* f = fopen(filename, "wb");
|
|
|
|
auto n_sectors = mData.size() * sizeof(uint32_t) / SPI_FLASH_SEC_SIZE;
|
|
|
|
auto s = fwrite(mData.data(), SPI_FLASH_SEC_SIZE, n_sectors, f);
|
|
|
|
assert(s == n_sectors);
|
|
|
|
fclose(f);
|
2016-10-31 07:48:28 -04:00
|
|
|
}
|
2016-08-17 11:08:22 -04:00
|
|
|
|
|
|
|
void clearStats()
|
|
|
|
{
|
|
|
|
mReadBytes = 0;
|
|
|
|
mWriteBytes = 0;
|
|
|
|
mEraseOps = 0;
|
|
|
|
mReadOps = 0;
|
|
|
|
mWriteOps = 0;
|
|
|
|
mTotalTime = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t getReadOps() const
|
|
|
|
{
|
|
|
|
return mReadOps;
|
|
|
|
}
|
|
|
|
size_t getWriteOps() const
|
|
|
|
{
|
|
|
|
return mWriteOps;
|
|
|
|
}
|
|
|
|
size_t getEraseOps() const
|
|
|
|
{
|
|
|
|
return mEraseOps;
|
|
|
|
}
|
|
|
|
size_t getReadBytes() const
|
|
|
|
{
|
|
|
|
return mReadBytes;
|
|
|
|
}
|
|
|
|
size_t getWriteBytes() const
|
|
|
|
{
|
|
|
|
return mWriteBytes;
|
|
|
|
}
|
|
|
|
size_t getTotalTime() const
|
|
|
|
{
|
|
|
|
return mTotalTime;
|
|
|
|
}
|
2020-11-10 02:40:01 -05:00
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
void setBounds(uint32_t lowerSector, uint32_t upperSector) {
|
|
|
|
mLowerSectorBound = lowerSector;
|
|
|
|
mUpperSectorBound = upperSector;
|
|
|
|
}
|
2020-11-10 02:40:01 -05:00
|
|
|
|
2016-08-22 00:11:32 -04:00
|
|
|
void failAfter(uint32_t count) {
|
|
|
|
mFailCountdown = count;
|
|
|
|
}
|
2016-08-17 11:08:22 -04:00
|
|
|
|
2020-03-11 06:28:30 -04:00
|
|
|
size_t getSectorEraseCount(uint32_t sector) const {
|
|
|
|
return mEraseCnt[sector];
|
|
|
|
}
|
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
protected:
|
|
|
|
static size_t getReadOpTime(uint32_t bytes);
|
|
|
|
static size_t getWriteOpTime(uint32_t bytes);
|
|
|
|
static size_t getEraseOpTime();
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<uint32_t> mData;
|
2020-03-11 06:28:30 -04:00
|
|
|
std::vector<uint32_t> mEraseCnt;
|
2016-08-17 11:08:22 -04:00
|
|
|
|
|
|
|
mutable size_t mReadOps = 0;
|
|
|
|
mutable size_t mWriteOps = 0;
|
|
|
|
mutable size_t mReadBytes = 0;
|
|
|
|
mutable size_t mWriteBytes = 0;
|
|
|
|
mutable size_t mEraseOps = 0;
|
|
|
|
mutable size_t mTotalTime = 0;
|
|
|
|
size_t mLowerSectorBound = 0;
|
|
|
|
size_t mUpperSectorBound = 0;
|
2020-11-10 02:40:01 -05:00
|
|
|
|
2016-08-22 02:16:21 -04:00
|
|
|
size_t mFailCountdown = SIZE_MAX;
|
2016-08-17 11:08:22 -04:00
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* spi_flash_emulation_h */
|