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:
parent
200e47fc7b
commit
79ea883fb3
@ -42,11 +42,6 @@ extern struct rst_info resetInfo;
|
|||||||
#ifndef PUYA_SUPPORT
|
#ifndef PUYA_SUPPORT
|
||||||
#define PUYA_SUPPORT 1
|
#define PUYA_SUPPORT 1
|
||||||
#endif
|
#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
|
* User-defined Literals
|
||||||
@ -668,11 +663,14 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
|
|||||||
if (data == nullptr) {
|
if (data == nullptr) {
|
||||||
return SPI_FLASH_RESULT_ERR;
|
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.
|
// PUYA flash chips need to read existing data, update in memory and write modified data again.
|
||||||
static uint32_t *flash_write_puya_buf = nullptr;
|
static uint32_t *flash_write_puya_buf = nullptr;
|
||||||
|
|
||||||
if (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.
|
// No need to ever free this, since the flash chip will never change at runtime.
|
||||||
if (flash_write_puya_buf == nullptr) {
|
if (flash_write_puya_buf == nullptr) {
|
||||||
// Memory could not be allocated.
|
// 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;
|
uint32_t pos = offset;
|
||||||
while (bytesLeft > 0 && rc == SPI_FLASH_RESULT_OK) {
|
while (bytesLeft > 0 && rc == SPI_FLASH_RESULT_OK) {
|
||||||
size_t bytesNow = bytesLeft;
|
size_t bytesNow = bytesLeft;
|
||||||
if (bytesNow > PUYA_BUFFER_SIZE) {
|
if (bytesNow > FLASH_PAGE_SIZE) {
|
||||||
bytesNow = PUYA_BUFFER_SIZE;
|
bytesNow = FLASH_PAGE_SIZE;
|
||||||
bytesLeft -= PUYA_BUFFER_SIZE;
|
bytesLeft -= FLASH_PAGE_SIZE;
|
||||||
} else {
|
} else {
|
||||||
bytesLeft = 0;
|
bytesLeft = 0;
|
||||||
}
|
}
|
||||||
size_t bytesAligned = (bytesNow + 3) & ~3;
|
rc = spi_flash_read(pos, flash_write_puya_buf, bytesNow);
|
||||||
rc = spi_flash_read(pos, flash_write_puya_buf, bytesAligned);
|
|
||||||
if (rc != SPI_FLASH_RESULT_OK) {
|
if (rc != SPI_FLASH_RESULT_OK) {
|
||||||
return rc;
|
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;
|
flash_write_puya_buf[i] &= *ptr;
|
||||||
++ptr;
|
++ptr;
|
||||||
}
|
}
|
||||||
rc = spi_flash_write(pos, flash_write_puya_buf, bytesAligned);
|
rc = spi_flash_write(pos, flash_write_puya_buf, bytesNow);
|
||||||
pos += bytesNow;
|
pos += bytesNow;
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
|
bool EspClass::flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount) {
|
||||||
SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
|
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 PUYA_SUPPORT
|
||||||
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
|
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
|
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;
|
return rc == SPI_FLASH_RESULT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) {
|
bool EspClass::flashWrite(uint32_t address, const uint8_t *data, size_t size) {
|
||||||
auto rc = spi_flash_read(offset, (uint32_t*) data, size);
|
if (size == 0) {
|
||||||
return rc == SPI_FLASH_RESULT_OK;
|
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()
|
String EspClass::getSketchMD5()
|
||||||
|
@ -150,8 +150,48 @@ class EspClass {
|
|||||||
bool checkFlashCRC();
|
bool checkFlashCRC();
|
||||||
|
|
||||||
bool flashEraseSector(uint32_t sector);
|
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();
|
uint32_t getSketchSize();
|
||||||
String getSketchMD5();
|
String getSketchMD5();
|
||||||
@ -175,6 +215,40 @@ class EspClass {
|
|||||||
#else
|
#else
|
||||||
uint32_t getCycleCount();
|
uint32_t getCycleCount();
|
||||||
#endif // !defined(CORE_MOCK)
|
#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;
|
extern EspClass ESP;
|
||||||
|
@ -236,7 +236,7 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
|||||||
DEBUG_UPDATER.printf_P(PSTR("[Updater] Adjusted binsize: %d\n"), binSize);
|
DEBUG_UPDATER.printf_P(PSTR("[Updater] Adjusted binsize: %d\n"), binSize);
|
||||||
#endif
|
#endif
|
||||||
// Calculate the MD5 and hash using proper size
|
// 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)) {
|
for(int i = 0; i < binSize; i += sizeof(buff)) {
|
||||||
ESP.flashRead(_startAddress + i, (uint32_t *)buff, sizeof(buff));
|
ESP.flashRead(_startAddress + i, (uint32_t *)buff, sizeof(buff));
|
||||||
size_t read = std::min((int)sizeof(buff), binSize - i);
|
size_t read = std::min((int)sizeof(buff), binSize - i);
|
||||||
@ -255,7 +255,7 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
|||||||
_reset();
|
_reset();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ESP.flashRead(_startAddress + binSize, (uint32_t *)sig, sigLen);
|
ESP.flashRead(_startAddress + binSize, sig, sigLen);
|
||||||
#ifdef DEBUG_UPDATER
|
#ifdef DEBUG_UPDATER
|
||||||
DEBUG_UPDATER.printf_P(PSTR("[Updater] Received Signature:"));
|
DEBUG_UPDATER.printf_P(PSTR("[Updater] Received Signature:"));
|
||||||
for (size_t i=0; i<sigLen; i++) {
|
for (size_t i=0; i<sigLen; i++) {
|
||||||
@ -357,7 +357,7 @@ bool UpdaterClass::_writeBuffer(){
|
|||||||
|
|
||||||
if (eraseResult) {
|
if (eraseResult) {
|
||||||
if(!_async) yield();
|
if(!_async) yield();
|
||||||
writeResult = ESP.flashWrite(_currentAddress, (uint32_t*) _buffer, _bufferLen);
|
writeResult = ESP.flashWrite(_currentAddress, _buffer, _bufferLen);
|
||||||
} else { // if erase was unsuccessful
|
} else { // if erase was unsuccessful
|
||||||
_currentAddress = (_startAddress + _size);
|
_currentAddress = (_startAddress + _size);
|
||||||
_setError(UPDATE_ERROR_ERASE);
|
_setError(UPDATE_ERROR_ERASE);
|
||||||
@ -435,7 +435,7 @@ bool UpdaterClass::_verifyHeader(uint8_t data) {
|
|||||||
bool UpdaterClass::_verifyEnd() {
|
bool UpdaterClass::_verifyEnd() {
|
||||||
if(_command == U_FLASH) {
|
if(_command == U_FLASH) {
|
||||||
|
|
||||||
uint8_t buf[4];
|
uint8_t buf[4] __attribute__((aligned(4)));
|
||||||
if(!ESP.flashRead(_startAddress, (uint32_t *) &buf[0], 4)) {
|
if(!ESP.flashRead(_startAddress, (uint32_t *) &buf[0], 4)) {
|
||||||
_currentAddress = (_startAddress);
|
_currentAddress = (_startAddress);
|
||||||
_setError(UPDATE_ERROR_READ);
|
_setError(UPDATE_ERROR_READ);
|
||||||
|
@ -28,141 +28,27 @@ extern "C" {
|
|||||||
#include "c_types.h"
|
#include "c_types.h"
|
||||||
#include "spi_flash.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) {
|
int32_t flash_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
|
||||||
optimistic_yield(10000);
|
optimistic_yield(10000);
|
||||||
|
|
||||||
uint32_t result = FLASH_HAL_OK;
|
// We use flashRead overload that handles proper alignment
|
||||||
uint32_t alignedBegin = (addr + 3) & (~3);
|
if (ESP.flashRead(addr, dst, size)) {
|
||||||
uint32_t alignedEnd = (addr + size) & (~3);
|
return FLASH_HAL_OK;
|
||||||
if (alignedEnd < alignedBegin) {
|
} else {
|
||||||
alignedEnd = alignedBegin;
|
return FLASH_HAL_READ_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
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) {
|
int32_t flash_hal_write(uint32_t addr, uint32_t size, const uint8_t *src) {
|
||||||
optimistic_yield(10000);
|
optimistic_yield(10000);
|
||||||
|
|
||||||
uint32_t alignedBegin = (addr + 3) & (~3);
|
// We use flashWrite overload that handles proper alignment
|
||||||
uint32_t alignedEnd = (addr + size) & (~3);
|
if (ESP.flashWrite(addr, src, size)) {
|
||||||
if (alignedEnd < alignedBegin) {
|
return FLASH_HAL_OK;
|
||||||
alignedEnd = alignedBegin;
|
} else {
|
||||||
|
return FLASH_HAL_WRITE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return FLASH_HAL_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t flash_hal_erase(uint32_t addr, uint32_t size) {
|
int32_t flash_hal_erase(uint32_t addr, uint32_t size) {
|
||||||
|
187
tests/device/test_spi_flash/test_spi_flash.ino
Normal file
187
tests/device/test_spi_flash/test_spi_flash.ino
Normal 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 ()
|
||||||
|
{
|
||||||
|
}
|
@ -159,7 +159,15 @@ FlashMode_t EspClass::magicFlashChipMode(uint8_t byte)
|
|||||||
return FM_DOUT;
|
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)offset;
|
||||||
(void)data;
|
(void)data;
|
||||||
@ -175,6 +183,14 @@ bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size)
|
|||||||
return true;
|
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) {
|
uint32_t EspClass::magicFlashChipSize(uint8_t byte) {
|
||||||
switch(byte & 0x0F) {
|
switch(byte & 0x0F) {
|
||||||
case 0x0: // 4 Mbit (512KB)
|
case 0x0: // 4 Mbit (512KB)
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#define FLASH_SECTOR_SIZE 0x1000
|
#define FLASH_SECTOR_SIZE 0x1000
|
||||||
#define FLASH_BLOCK_SIZE 0x10000
|
#define FLASH_BLOCK_SIZE 0x10000
|
||||||
|
#define FLASH_PAGE_SIZE 0x100
|
||||||
#define APP_START_OFFSET 0x1000
|
#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
|
//pulled this define from spi_flash.h for reuse in the Arduino core without pulling in a bunch of other stuff
|
||||||
|
Loading…
x
Reference in New Issue
Block a user