OVMS3-idf/components/nvs_flash/test/spi_flash_emulation.h
Ivan Grokhotkov d9cdc7de58 nvs_flash: don't allow more operation to be done on page in PageState::INVALID
Currently a restart is required to recover a page from invalid state.
The long-term solution is to detect such a condition and recover automatically (without a restart). This will be implemented in a separate change set.
2016-10-31 19:48:28 +08:00

224 lines
5.7 KiB
C++

// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef spi_flash_emulation_h
#define spi_flash_emulation_h
#include <vector>
#include <cassert>
#include <algorithm>
#include <random>
#include "esp_spi_flash.h"
#include "catch.hpp"
using std::copy;
using std::begin;
using std::end;
using std::fill_n;
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);
spi_flash_emulator_set(this);
}
~SpiFlashEmulator()
{
spi_flash_emulator_set(nullptr);
}
bool read(uint32_t* dest, size_t srcAddr, size_t size) const
{
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;
}
bool write(size_t dstAddr, const uint32_t* src, size_t size)
{
uint32_t sectorNumber = dstAddr/SPI_FLASH_SEC_SIZE;
if (sectorNumber < mLowerSectorBound || sectorNumber >= mUpperSectorBound) {
WARN("invalid flash operation detected: erase sector=" << sectorNumber);
return false;
}
if (dstAddr % 4 != 0 ||
size % 4 != 0 ||
dstAddr + size > mData.size() * 4) {
return false;
}
for (size_t i = 0; i < size / 4; ++i) {
if (mFailCountdown != SIZE_MAX && mFailCountdown-- == 0) {
return false;
}
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;
}
bool erase(size_t sectorNumber)
{
size_t offset = sectorNumber * SPI_FLASH_SEC_SIZE / 4;
if (offset > mData.size()) {
return false;
}
if (sectorNumber < mLowerSectorBound || sectorNumber >= mUpperSectorBound) {
WARN("invalid flash operation detected: erase sector=" << sectorNumber);
return false;
}
if (mFailCountdown != SIZE_MAX && mFailCountdown-- == 0) {
return false;
}
std::fill_n(begin(mData) + offset, SPI_FLASH_SEC_SIZE / 4, 0xffffffff);
++mEraseOps;
mTotalTime += getEraseOpTime();
return true;
}
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());
}
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);
mData.resize(size);
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));
}
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;
}
void setBounds(uint32_t lowerSector, uint32_t upperSector) {
mLowerSectorBound = lowerSector;
mUpperSectorBound = upperSector;
}
void failAfter(uint32_t count) {
mFailCountdown = count;
}
protected:
static size_t getReadOpTime(uint32_t bytes);
static size_t getWriteOpTime(uint32_t bytes);
static size_t getEraseOpTime();
std::vector<uint32_t> mData;
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;
size_t mFailCountdown = SIZE_MAX;
};
#endif /* spi_flash_emulation_h */