1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00

New flash writing method with offset/memory/size alignment handling (#7514)

* Do not write more data than requested on PUYA flashes

* Always align flash reads/writes to 4 bytes

* fixup! Always align flash reads/writes to 4 bytes

This commit simplifies the code a bit and fixes a bug that caused wrong number of bytes to be
written

* fixup! Always align flash reads/writes to 4 bytes

* fixup! Always align flash reads/writes to 4 bytes

* Check for result before additional read/write

* Add overloads for unaligned reads/writes

* fixup! Add overloads for unaligned reads/writes

* fixup! Add overloads for unaligned reads/writes

* fixup! Add overloads for unaligned reads/writes

* fixup! Add overloads for unaligned reads/writes

* fixup! Add overloads for unaligned reads/writes

* fixup! Add overloads for unaligned reads/writes

* fixup! Add overloads for unaligned reads/writes

* Add tests for flashRead/flashWrite

* fixup! Add overloads for unaligned reads/writes

* fixup! Add tests for flashRead/flashWrite

* fixup! Add tests for flashRead/flashWrite

* fixup! Add overloads for unaligned reads/writes
This commit is contained in:
Drzony 2020-10-15 07:21:41 +02:00 committed by GitHub
parent 200e47fc7b
commit 79ea883fb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 530 additions and 152 deletions

View File

@ -42,11 +42,6 @@ extern struct rst_info resetInfo;
#ifndef PUYA_SUPPORT
#define PUYA_SUPPORT 1
#endif
#ifndef PUYA_BUFFER_SIZE
// Good alternative for buffer size is: SPI_FLASH_SEC_SIZE (= 4k)
// Always use a multiple of flash page size (256 bytes)
#define PUYA_BUFFER_SIZE 256
#endif
/**
* User-defined Literals
@ -668,11 +663,14 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
if (data == nullptr) {
return SPI_FLASH_RESULT_ERR;
}
if (size % 4 != 0) {
return SPI_FLASH_RESULT_ERR;
}
// PUYA flash chips need to read existing data, update in memory and write modified data again.
static uint32_t *flash_write_puya_buf = nullptr;
if (flash_write_puya_buf == nullptr) {
flash_write_puya_buf = (uint32_t*) malloc(PUYA_BUFFER_SIZE);
flash_write_puya_buf = (uint32_t*) malloc(FLASH_PAGE_SIZE);
// No need to ever free this, since the flash chip will never change at runtime.
if (flash_write_puya_buf == nullptr) {
// Memory could not be allocated.
@ -686,45 +684,261 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
uint32_t pos = offset;
while (bytesLeft > 0 && rc == SPI_FLASH_RESULT_OK) {
size_t bytesNow = bytesLeft;
if (bytesNow > PUYA_BUFFER_SIZE) {
bytesNow = PUYA_BUFFER_SIZE;
bytesLeft -= PUYA_BUFFER_SIZE;
if (bytesNow > FLASH_PAGE_SIZE) {
bytesNow = FLASH_PAGE_SIZE;
bytesLeft -= FLASH_PAGE_SIZE;
} else {
bytesLeft = 0;
}
size_t bytesAligned = (bytesNow + 3) & ~3;
rc = spi_flash_read(pos, flash_write_puya_buf, bytesAligned);
rc = spi_flash_read(pos, flash_write_puya_buf, bytesNow);
if (rc != SPI_FLASH_RESULT_OK) {
return rc;
}
for (size_t i = 0; i < bytesAligned / 4; ++i) {
for (size_t i = 0; i < bytesNow / 4; ++i) {
flash_write_puya_buf[i] &= *ptr;
++ptr;
}
rc = spi_flash_write(pos, flash_write_puya_buf, bytesAligned);
rc = spi_flash_write(pos, flash_write_puya_buf, bytesNow);
pos += bytesNow;
}
return rc;
}
#endif
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
bool EspClass::flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount) {
uint32_t alignedAddress = (address & ~3);
uint32_t alignmentOffset = address - alignedAddress;
if (alignedAddress != ((address + byteCount - 1) & ~3)) {
// Only one 4 byte block is supported
return false;
}
#if PUYA_SUPPORT
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
rc = spi_flash_write_puya(offset, data, size);
uint8_t tempData[4] __attribute__((aligned(4)));
if (spi_flash_read(alignedAddress, (uint32_t *)tempData, 4) != SPI_FLASH_RESULT_OK) {
return false;
}
for (size_t i = 0; i < byteCount; i++) {
tempData[i + alignmentOffset] &= value[i];
}
if (spi_flash_write(alignedAddress, (uint32_t *)tempData, 4) != SPI_FLASH_RESULT_OK) {
return false;
}
}
else
#endif
#endif // PUYA_SUPPORT
{
rc = spi_flash_write(offset, data, size);
uint32_t tempData;
if (spi_flash_read(alignedAddress, &tempData, 4) != SPI_FLASH_RESULT_OK) {
return false;
}
memcpy((uint8_t *)&tempData + alignmentOffset, value, byteCount);
if (spi_flash_write(alignedAddress, &tempData, 4) != SPI_FLASH_RESULT_OK) {
return false;
}
}
return true;
}
size_t EspClass::flashWriteUnalignedMemory(uint32_t address, const uint8_t *data, size_t size) {
size_t sizeLeft = (size & ~3);
size_t currentOffset = 0;
// Memory is unaligned, so we need to copy it to an aligned buffer
uint32_t alignedData[FLASH_PAGE_SIZE / sizeof(uint32_t)] __attribute__((aligned(4)));
// Handle page boundary
bool pageBreak = ((address % 4) != 0) && ((address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));
if (pageBreak) {
size_t byteCount = 4 - (address % 4);
if (!flashReplaceBlock(address, data, byteCount)) {
return 0;
}
// We will now have aligned address, so we can cross page boundaries
currentOffset += byteCount;
// Realign size to 4
sizeLeft = (size - byteCount) & ~3;
}
while (sizeLeft) {
size_t willCopy = std::min(sizeLeft, sizeof(alignedData));
memcpy(alignedData, data + currentOffset, willCopy);
// We now have address, data and size aligned to 4 bytes, so we can use aligned write
if (!flashWrite(address + currentOffset, alignedData, willCopy))
{
return 0;
}
sizeLeft -= willCopy;
currentOffset += willCopy;
}
return currentOffset;
}
bool EspClass::flashWritePageBreak(uint32_t address, const uint8_t *data, size_t size) {
if (size > 4) {
return false;
}
size_t pageLeft = FLASH_PAGE_SIZE - (address % FLASH_PAGE_SIZE);
size_t offset = 0;
size_t sizeLeft = size;
if (pageLeft > 3) {
return false;
}
if (!flashReplaceBlock(address, data, pageLeft)) {
return false;
}
offset += pageLeft;
sizeLeft -= pageLeft;
// We replaced last 4-byte block of the page, now we write the remainder in next page
if (!flashReplaceBlock(address + offset, data + offset, sizeLeft)) {
return false;
}
return true;
}
bool EspClass::flashWrite(uint32_t address, const uint32_t *data, size_t size) {
SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + size - 1) / FLASH_PAGE_SIZE));
if ((uintptr_t)data % 4 != 0 || size % 4 != 0 || pageBreak) {
return false;
}
#if PUYA_SUPPORT
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
rc = spi_flash_write_puya(address, const_cast<uint32_t *>(data), size);
}
else
#endif // PUYA_SUPPORT
{
rc = spi_flash_write(address, const_cast<uint32_t *>(data), size);
}
return rc == SPI_FLASH_RESULT_OK;
}
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) {
auto rc = spi_flash_read(offset, (uint32_t*) data, size);
return rc == SPI_FLASH_RESULT_OK;
bool EspClass::flashWrite(uint32_t address, const uint8_t *data, size_t size) {
if (size == 0) {
return true;
}
size_t sizeLeft = size & ~3;
size_t currentOffset = 0;
if (sizeLeft) {
if ((uintptr_t)data % 4 != 0) {
size_t written = flashWriteUnalignedMemory(address, data, size);
if (!written) {
return false;
}
currentOffset += written;
sizeLeft -= written;
} else {
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));
if (pageBreak) {
while (sizeLeft) {
// We cannot cross page boundary, but the write must be 4 byte aligned,
// so this is the maximum amount we can write
size_t pageBoundary = (FLASH_PAGE_SIZE - ((address + currentOffset) % FLASH_PAGE_SIZE)) & ~3;
if (sizeLeft > pageBoundary) {
// Aligned write up to page boundary
if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), pageBoundary)) {
return false;
}
currentOffset += pageBoundary;
sizeLeft -= pageBoundary;
// Cross the page boundary
if (!flashWritePageBreak(address + currentOffset, data + currentOffset, 4)) {
return false;
}
currentOffset += 4;
sizeLeft -= 4;
} else {
// We do not cross page boundary
if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), sizeLeft)) {
return false;
}
currentOffset += sizeLeft;
sizeLeft = 0;
}
}
} else {
// Pointer is properly aligned and write does not cross page boundary,
// so use aligned write
if (!flashWrite(address, (uint32_t *)data, sizeLeft)) {
return false;
}
currentOffset = sizeLeft;
sizeLeft = 0;
}
}
}
sizeLeft = size - currentOffset;
if (sizeLeft > 0) {
// Size was not aligned, so we have some bytes left to write, we also need to recheck for
// page boundary crossing
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));
if (pageBreak) {
// Cross the page boundary
if (!flashWritePageBreak(address + currentOffset, data + currentOffset, sizeLeft)) {
return false;
}
} else {
// Just write partial block
flashReplaceBlock(address + currentOffset, data + currentOffset, sizeLeft);
}
}
return true;
}
bool EspClass::flashRead(uint32_t address, uint8_t *data, size_t size) {
size_t sizeAligned = size & ~3;
size_t currentOffset = 0;
if ((uintptr_t)data % 4 != 0) {
uint32_t alignedData[FLASH_PAGE_SIZE / sizeof(uint32_t)] __attribute__((aligned(4)));
size_t sizeLeft = sizeAligned;
while (sizeLeft) {
size_t willCopy = std::min(sizeLeft, sizeof(alignedData));
// We read to our aligned buffer and then copy to data
if (!flashRead(address + currentOffset, alignedData, willCopy))
{
return false;
}
memcpy(data + currentOffset, alignedData, willCopy);
sizeLeft -= willCopy;
currentOffset += willCopy;
}
} else {
// Pointer is properly aligned, so use aligned read
if (!flashRead(address, (uint32_t *)data, sizeAligned)) {
return false;
}
currentOffset = sizeAligned;
}
if (currentOffset < size) {
uint32_t tempData;
if (spi_flash_read(address + currentOffset, &tempData, 4) != SPI_FLASH_RESULT_OK) {
return false;
}
memcpy((uint8_t *)data + currentOffset, &tempData, size - currentOffset);
}
return true;
}
bool EspClass::flashRead(uint32_t address, uint32_t *data, size_t size) {
if ((uintptr_t)data % 4 != 0 || size % 4 != 0) {
return false;
}
return (spi_flash_read(address, data, size) == SPI_FLASH_RESULT_OK);
}
String EspClass::getSketchMD5()

View File

@ -150,8 +150,48 @@ class EspClass {
bool checkFlashCRC();
bool flashEraseSector(uint32_t sector);
bool flashWrite(uint32_t offset, uint32_t *data, size_t size);
bool flashRead(uint32_t offset, uint32_t *data, size_t size);
/**
* @brief Write @a size bytes from @a data to flash at @a address
* This overload requires @a data and @a size to be always 4 byte aligned and
* @a address to be 4 byte aligned if the write crossess page boundary,
* but guarantees no overhead (except on PUYA flashes)
* @param address address on flash where write should start, 4 byte alignment is conditional
* @param data input buffer, must be 4-byte aligned
* @param size amount of data, must be a multiple of 4
* @return bool result of operation
* @retval true success
* @retval false failure to write to flash or incorrect alignment of params
*/
bool flashWrite(uint32_t address, const uint32_t *data, size_t size);
/**
* @brief Write @a size bytes from @a data to flash at @a address
* This overload handles all misalignment cases
* @param address address on flash where write should start
* @param data input buffer, passing unaligned memory will cause significant stack usage
* @param size amount of data, passing not multiple of 4 will cause additional reads and writes
* @return bool result of operation
*/
bool flashWrite(uint32_t address, const uint8_t *data, size_t size);
/**
* @brief Read @a size bytes to @a data to flash at @a address
* This overload requires @a data and @a size to be 4 byte aligned
* @param address address on flash where read should start
* @param data input buffer, must be 4-byte aligned
* @param size amount of data, must be a multiple of 4
* @return bool result of operation
* @retval true success
* @retval false failure to read from flash or incorrect alignment of params
*/
bool flashRead(uint32_t address, uint32_t *data, size_t size);
/**
* @brief Read @a size bytes to @a data to flash at @a address
* This overload handles all misalignment cases
* @param address address on flash where read should start
* @param data input buffer, passing unaligned memory will cause significant stack usage
* @param size amount of data, passing not multiple of 4 will cause additional read
* @return bool result of operation
*/
bool flashRead(uint32_t address, uint8_t *data, size_t size);
uint32_t getSketchSize();
String getSketchMD5();
@ -175,6 +215,40 @@ class EspClass {
#else
uint32_t getCycleCount();
#endif // !defined(CORE_MOCK)
private:
/**
* @brief Replaces @a byteCount bytes of a 4 byte block on flash
*
* @param address flash address
* @param value buffer with data
* @param byteCount number of bytes to replace
* @return bool result of operation
* @retval true success
* @retval false failed to read/write or invalid args
*/
bool flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount);
/**
* @brief Write up to @a size bytes from @a data to flash at @a address
* This function takes case of unaligned memory acces by copying @a data to a temporary buffer,
* it also takes care of page boundary crossing see @a flashWritePageBreak as to why it's done.
* Less than @a size bytes may be written, due to 4 byte alignment requirement of spi_flash_write
* @param address address on flash where write should start
* @param data input buffer
* @param size amount of data
* @return size_t amount of data written, 0 on failure
*/
size_t flashWriteUnalignedMemory(uint32_t address, const uint8_t *data, size_t size);
/**
* @brief Splits up to 4 bytes into 4 byte blocks and writes them to flash
* We need this since spi_flash_write cannot handle writing over a page boundary with unaligned offset
* i.e. spi_flash_write(254, data, 4) will fail, also we cannot write less bytes as in
* spi_flash_write(254, data, 2) since it will be extended internally to 4 bytes and fail
* @param address start of write
* @param data data to be written
* @param size amount of data, must be < 4
* @return bool result of operation
*/
bool flashWritePageBreak(uint32_t address, const uint8_t *data, size_t size);
};
extern EspClass ESP;

View File

@ -236,7 +236,7 @@ bool UpdaterClass::end(bool evenIfRemaining){
DEBUG_UPDATER.printf_P(PSTR("[Updater] Adjusted binsize: %d\n"), binSize);
#endif
// Calculate the MD5 and hash using proper size
uint8_t buff[128];
uint8_t buff[128] __attribute__((aligned(4)));
for(int i = 0; i < binSize; i += sizeof(buff)) {
ESP.flashRead(_startAddress + i, (uint32_t *)buff, sizeof(buff));
size_t read = std::min((int)sizeof(buff), binSize - i);
@ -255,7 +255,7 @@ bool UpdaterClass::end(bool evenIfRemaining){
_reset();
return false;
}
ESP.flashRead(_startAddress + binSize, (uint32_t *)sig, sigLen);
ESP.flashRead(_startAddress + binSize, sig, sigLen);
#ifdef DEBUG_UPDATER
DEBUG_UPDATER.printf_P(PSTR("[Updater] Received Signature:"));
for (size_t i=0; i<sigLen; i++) {
@ -357,7 +357,7 @@ bool UpdaterClass::_writeBuffer(){
if (eraseResult) {
if(!_async) yield();
writeResult = ESP.flashWrite(_currentAddress, (uint32_t*) _buffer, _bufferLen);
writeResult = ESP.flashWrite(_currentAddress, _buffer, _bufferLen);
} else { // if erase was unsuccessful
_currentAddress = (_startAddress + _size);
_setError(UPDATE_ERROR_ERASE);
@ -435,7 +435,7 @@ bool UpdaterClass::_verifyHeader(uint8_t data) {
bool UpdaterClass::_verifyEnd() {
if(_command == U_FLASH) {
uint8_t buf[4];
uint8_t buf[4] __attribute__((aligned(4)));
if(!ESP.flashRead(_startAddress, (uint32_t *) &buf[0], 4)) {
_currentAddress = (_startAddress);
_setError(UPDATE_ERROR_READ);

View File

@ -28,141 +28,27 @@ extern "C" {
#include "c_types.h"
#include "spi_flash.h"
}
/*
spi_flash_read function requires flash address to be aligned on word boundary.
We take care of this by reading first and last words separately and memcpy
relevant bytes into result buffer.
alignment: 012301230123012301230123
bytes requested: -------***********------
read directly: --------xxxxxxxx--------
read pre: ----aaaa----------------
read post: ----------------bbbb----
alignedBegin: ^
alignedEnd: ^
*/
int32_t flash_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
optimistic_yield(10000);
uint32_t result = FLASH_HAL_OK;
uint32_t alignedBegin = (addr + 3) & (~3);
uint32_t alignedEnd = (addr + size) & (~3);
if (alignedEnd < alignedBegin) {
alignedEnd = alignedBegin;
}
if (addr < alignedBegin) {
uint32_t nb = alignedBegin - addr;
uint32_t tmp;
if (!ESP.flashRead(alignedBegin - 4, &tmp, 4)) {
DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return FLASH_HAL_READ_ERROR;
}
memcpy(dst, ((uint8_t*) &tmp) + 4 - nb, nb);
}
if (alignedEnd != alignedBegin) {
if (!ESP.flashRead(alignedBegin, (uint32_t*) (dst + alignedBegin - addr),
alignedEnd - alignedBegin)) {
DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
// We use flashRead overload that handles proper alignment
if (ESP.flashRead(addr, dst, size)) {
return FLASH_HAL_OK;
} else {
return FLASH_HAL_READ_ERROR;
}
}
if (addr + size > alignedEnd) {
uint32_t nb = addr + size - alignedEnd;
uint32_t tmp;
if (!ESP.flashRead(alignedEnd, &tmp, 4)) {
DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return FLASH_HAL_READ_ERROR;
}
memcpy(dst + size - nb, &tmp, nb);
}
return result;
}
/*
Like spi_flash_read, spi_flash_write has a requirement for flash address to be
aligned. However it also requires RAM address to be aligned as it reads data
in 32-bit words. Flash address (mis-)alignment is handled much the same way
as for reads, but for RAM alignment we have to copy data into a temporary
buffer. The size of this buffer is a tradeoff between number of writes required
and amount of stack required. This is chosen to be 512 bytes here, but might
be adjusted in the future if there are good reasons to do so.
*/
static const int UNALIGNED_WRITE_BUFFER_SIZE = 512;
int32_t flash_hal_write(uint32_t addr, uint32_t size, const uint8_t *src) {
optimistic_yield(10000);
uint32_t alignedBegin = (addr + 3) & (~3);
uint32_t alignedEnd = (addr + size) & (~3);
if (alignedEnd < alignedBegin) {
alignedEnd = alignedBegin;
}
if (addr < alignedBegin) {
uint32_t ofs = alignedBegin - addr;
uint32_t nb = (size < ofs) ? size : ofs;
uint8_t tmp[4] __attribute__((aligned(4))) = {0xff, 0xff, 0xff, 0xff};
memcpy(tmp + 4 - ofs, src, nb);
if (!ESP.flashWrite(alignedBegin - 4, (uint32_t*) tmp, 4)) {
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return FLASH_HAL_WRITE_ERROR;
}
}
if (alignedEnd != alignedBegin) {
uint32_t* srcLeftover = (uint32_t*) (src + alignedBegin - addr);
uint32_t srcAlign = ((uint32_t) srcLeftover) & 3;
if (!srcAlign) {
if (!ESP.flashWrite(alignedBegin, (uint32_t*) srcLeftover,
alignedEnd - alignedBegin)) {
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return FLASH_HAL_WRITE_ERROR;
}
}
else {
uint8_t buf[UNALIGNED_WRITE_BUFFER_SIZE];
for (uint32_t sizeLeft = alignedEnd - alignedBegin; sizeLeft; ) {
size_t willCopy = std::min(sizeLeft, sizeof(buf));
memcpy(buf, srcLeftover, willCopy);
if (!ESP.flashWrite(alignedBegin, (uint32_t*) buf, willCopy)) {
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return FLASH_HAL_WRITE_ERROR;
}
sizeLeft -= willCopy;
srcLeftover += willCopy;
alignedBegin += willCopy;
}
}
}
if (addr + size > alignedEnd) {
uint32_t nb = addr + size - alignedEnd;
uint32_t tmp = 0xffffffff;
memcpy(&tmp, src + size - nb, nb);
if (!ESP.flashWrite(alignedEnd, &tmp, 4)) {
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return FLASH_HAL_WRITE_ERROR;
}
}
// We use flashWrite overload that handles proper alignment
if (ESP.flashWrite(addr, src, size)) {
return FLASH_HAL_OK;
} else {
return FLASH_HAL_WRITE_ERROR;
}
}
int32_t flash_hal_erase(uint32_t addr, uint32_t size) {

View File

@ -0,0 +1,187 @@
#include <BSTest.h>
#include <ESP8266WiFi.h>
BS_ENV_DECLARE();
void preinit() {
// (no C++ in function)
// disable wifi
ESP8266WiFiClass::preinitWiFiOff();
}
void setup()
{
Serial.begin(115200);
BS_RUN(Serial);
}
bool pretest()
{
return true;
}
bool compareBuffers(uint32_t *first, uint32_t *second, size_t offset, size_t len)
{
uint8_t *firstBytes = (uint8_t *)first;
uint8_t *secondBytes = (uint8_t *)second;
for (size_t i = offset; i < offset + len; i++)
{
if (firstBytes[i] != secondBytes[i])
{
Serial.printf("Compare fail @ %u\n", i);
for (size_t j = i & ~3; j < (i & ~3) + 4; j++)
{
Serial.printf("%02x ", firstBytes[j]);
}
Serial.println();
for (size_t j = i & ~3; j < (i & ~3) + 4; j++)
{
Serial.printf("%02x ", secondBytes[j]);
}
Serial.println();
return false;
}
}
return true;
}
bool testFlash(uint32_t start_offset, uint8_t data_offset, size_t amount)
{
static uint32_t *write_buffer = (uint32_t *)malloc(4096);
static uint32_t *read_buffer = (uint32_t *)malloc(4096);
for (uint32_t i = 0; i < 1024; i++)
{
write_buffer[i] = (i + 100) * 33;
read_buffer[i] = 0xAAAAAAAA;
}
Serial.println("---------------------------------------------------");
ESP.flashEraseSector(start_offset / 0x1000);
Serial.printf("Testing %d bytes @ %08x + %d\n", amount, start_offset, data_offset);
unsigned long start = micros();
if (!ESP.flashWrite(start_offset, (uint8_t *)write_buffer + data_offset, amount))
{
Serial.printf("Write fail\n");
return false;
}
if (!ESP.flashRead(start_offset, (uint8_t *)read_buffer + data_offset, amount))
{
Serial.printf("Read fail\n");
return false;
}
if (!compareBuffers(write_buffer, read_buffer, data_offset, amount))
{
return false;
}
Serial.printf("Write took %lu us\n", micros() - start);
return true;
}
// Columns in test case names are as following:
// 1. Offset -> +o (4 byte aligned), -o (unaligned)
// 2. Memory pointer -> +m (4 byte aligned), -m (unaligned)
// 3. Size -> +s (4 byte ), -s (unaligned)
// 4. Number of pages crossed -> np
// Aligned offset
// Aligned memory
// Aligned size
TEST_CASE("|+o|+m|+s|0p|", "[spi_flash]")
{
CHECK(testFlash(0xa0000, 0, 100));
}
TEST_CASE("|+o|+m|+s|1p|", "[spi_flash]")
{
CHECK(testFlash(0xa0000, 0, 512));
}
// Unaligned size
TEST_CASE("|+o|+m|-s|0p|", "[spi_flash]")
{
CHECK(testFlash(0xa0000, 0, 101));
}
TEST_CASE("|+o|+m|-s|2p|", "[spi_flash]")
{
CHECK(testFlash(0xa0000, 0, 515));
}
// Unaligned memory
// Aligned size
TEST_CASE("|+o|-m|+s|0|", "[spi_flash]")
{
CHECK(testFlash(0xa0000, 1, 100));
}
TEST_CASE("|+o|-m|+s|1p|", "[spi_flash]")
{
CHECK(testFlash(0xa0000, 3, 512));
}
// Unaligned size
TEST_CASE("|+o|-m|-s|0p|", "[spi_flash]")
{
CHECK(testFlash(0xa0000, 2, 101));
}
TEST_CASE("|+o|-m|-s|2p|", "[spi_flash]")
{
CHECK(testFlash(0xa0000, 1, 515));
}
// Unaligned offset
// Aligned memory
// Aligned size
TEST_CASE("|-o|+m|+s|0p|", "[spi_flash]")
{
CHECK(testFlash(0xa0001, 0, 100));
}
TEST_CASE("|-o|+m|+s|1p|", "[spi_flash]")
{
CHECK(testFlash(0xa0001, 0, 260));
}
// Unaligned size
TEST_CASE("|-o|+m|-s|0p|", "[spi_flash]")
{
CHECK(testFlash(0xa0001, 0, 105));
}
TEST_CASE("|-o|+m|-s|1p|", "[spi_flash]")
{
CHECK(testFlash(0xa0001, 0, 271));
}
// Unaligned memory
// Aligned size
TEST_CASE("|-o|-m|+s|0p|", "[spi_flash]")
{
CHECK(testFlash(0xa0001, 1, 100));
}
TEST_CASE("|-o|-m|+s|1p|", "[spi_flash]")
{
CHECK(testFlash(0xa0001, 2, 260));
}
// Unaligned size
TEST_CASE("|-o|-m|-s|0p|", "[spi_flash]")
{
CHECK(testFlash(0xa0001, 3, 105));
}
TEST_CASE("|-o|-m|-s|1p|", "[spi_flash]")
{
CHECK(testFlash(0xa0001, 1, 271));
}
TEST_CASE("Last bytes of page", "[spi_flash]")
{
CHECK(testFlash(0xa0000 + 255, 0, 1));
CHECK(testFlash(0xa0000 + 255, 1, 1));
CHECK(testFlash(0xa0000 + 254, 0, 2));
CHECK(testFlash(0xa0000 + 254, 1, 2));
CHECK(testFlash(0xa0000 + 253, 0, 3));
CHECK(testFlash(0xa0000 + 253, 1, 3));
}
TEST_CASE("Unaligned page cross only", "[spi_flash]")
{
CHECK(testFlash(0xa0000 + 254, 0, 3));
CHECK(testFlash(0xa0000 + 254, 1, 3));
CHECK(testFlash(0xa0000 + 255, 0, 2));
CHECK(testFlash(0xa0000 + 255, 1, 2));
}
void loop ()
{
}

View File

@ -159,7 +159,15 @@ FlashMode_t EspClass::magicFlashChipMode(uint8_t byte)
return FM_DOUT;
}
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size)
bool EspClass::flashWrite(uint32_t offset, const uint32_t *data, size_t size)
{
(void)offset;
(void)data;
(void)size;
return true;
}
bool EspClass::flashWrite(uint32_t offset, const uint8_t *data, size_t size)
{
(void)offset;
(void)data;
@ -175,6 +183,14 @@ bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size)
return true;
}
bool EspClass::flashRead(uint32_t offset, uint8_t *data, size_t size)
{
(void)offset;
(void)data;
(void)size;
return true;
}
uint32_t EspClass::magicFlashChipSize(uint8_t byte) {
switch(byte & 0x0F) {
case 0x0: // 4 Mbit (512KB)

View File

@ -7,6 +7,7 @@
#define FLASH_SECTOR_SIZE 0x1000
#define FLASH_BLOCK_SIZE 0x10000
#define FLASH_PAGE_SIZE 0x100
#define APP_START_OFFSET 0x1000
//pulled this define from spi_flash.h for reuse in the Arduino core without pulling in a bunch of other stuff