Skip to content

Commit a83c0cd

Browse files
committed
break pages
1 parent 0778980 commit a83c0cd

File tree

2 files changed

+137
-186
lines changed

2 files changed

+137
-186
lines changed

cores/esp8266/Esp.cpp

Lines changed: 132 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,48 @@ bool EspClass::flashEraseSector(uint32_t sector) {
673673
return rc == 0;
674674
}
675675

676+
// Adapted from the old version of `flash_hal_write()` (before 3.0.0), which was used for SPIFFS to allow
677+
// writing from both unaligned u8 buffers and to an unaligned offset on flash.
678+
// Updated version re-uses some of the code from RTOS, replacing individual methods for block & page
679+
// writes with just a single one
680+
// https://github.com/espressif/ESP8266_RTOS_SDK/blob/master/components/spi_flash/src/spi_flash.c
681+
682+
// Wrapper around spi_flash_write, handling offset + size crossing page boundaries
683+
// Note that we expect that both `offset` and `data` are properly aligned
684+
static SpiFlashOpResult spi_flash_write_page_break(uint32_t offset, uint32_t* data, size_t size) {
685+
static constexpr uint32_t PageSize { FLASH_PAGE_SIZE };
686+
size_t page_size = PageSize - (offset % PageSize);
687+
688+
// most common case, we don't cross a page and simply write the data
689+
if (size < page_size) {
690+
return spi_flash_write(offset, data, size);
691+
}
692+
693+
// otherwise, write the initial part and continue writing breaking each page interval
694+
SpiFlashOpResult result = SPI_FLASH_RESULT_ERR;
695+
if ((result = spi_flash_write(offset, data, page_size)) != SPI_FLASH_RESULT_OK) {
696+
return result;
697+
}
698+
699+
uint32_t page_offset = PageSize;
700+
for (uint32_t page = 0; page < (size - page_size) / PageSize; ++page) {
701+
if ((result = spi_flash_write(offset + page_offset, data + (page_offset >> 2), PageSize)) != SPI_FLASH_RESULT_OK) {
702+
return result;
703+
}
704+
705+
page_offset += PageSize;
706+
}
707+
708+
if ((offset + page_offset) < (offset + size)) {
709+
return spi_flash_write(offset + page_offset, data + (page_offset >> 2), size - page_offset);
710+
}
711+
712+
return SPI_FLASH_RESULT_OK;
713+
}
714+
676715
#if PUYA_SUPPORT
716+
// Special wrapper for spi_flash_write *only for PUYA flash chips*
717+
// Already handles paging, could be used as a `spi_flash_write_page_break` replacement
677718
static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) {
678719
if (data == nullptr) {
679720
return SPI_FLASH_RESULT_ERR;
@@ -720,195 +761,127 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
720761
}
721762
#endif
722763

723-
bool EspClass::flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount) {
724-
uint32_t alignedAddress = (address & ~3);
725-
uint32_t alignmentOffset = address - alignedAddress;
764+
static constexpr uint32_t Alignment { 4 };
726765

727-
if (alignedAddress != ((address + byteCount - 1) & ~3)) {
728-
// Only one 4 byte block is supported
729-
return false;
730-
}
731-
#if PUYA_SUPPORT
732-
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
733-
uint8_t tempData[4] __attribute__((aligned(4)));
734-
if (spi_flash_read(alignedAddress, (uint32_t *)tempData, 4) != SPI_FLASH_RESULT_OK) {
735-
return false;
736-
}
737-
for (size_t i = 0; i < byteCount; i++) {
738-
tempData[i + alignmentOffset] &= value[i];
739-
}
740-
if (spi_flash_write(alignedAddress, (uint32_t *)tempData, 4) != SPI_FLASH_RESULT_OK) {
741-
return false;
742-
}
743-
}
744-
else
745-
#endif // PUYA_SUPPORT
746-
{
747-
uint32_t tempData;
748-
if (spi_flash_read(alignedAddress, &tempData, 4) != SPI_FLASH_RESULT_OK) {
749-
return false;
750-
}
751-
memcpy((uint8_t *)&tempData + alignmentOffset, value, byteCount);
752-
if (spi_flash_write(alignedAddress, &tempData, 4) != SPI_FLASH_RESULT_OK) {
753-
return false;
754-
}
755-
}
756-
return true;
766+
static uint32_t alignAddress(uint32_t address) {
767+
static constexpr uint32_t Mask { Alignment - 1 };
768+
return (address + Mask) & ~Mask;
769+
}
770+
771+
static uint32_t alignBeforeAddress(uint32_t address) {
772+
return alignAddress(address) - Alignment;
773+
}
774+
775+
static bool isAlignedAddress(uint32_t address) {
776+
return (address & (Alignment - 1)) == 0;
777+
}
778+
779+
static bool isAlignedSize(size_t size) {
780+
return (size & (Alignment - 1)) == 0;
781+
}
782+
783+
static bool isAlignedPointer(const uint8_t* ptr) {
784+
return isAlignedAddress(reinterpret_cast<uint32_t>(ptr));
757785
}
758786

759787
size_t EspClass::flashWriteUnalignedMemory(uint32_t address, const uint8_t *data, size_t size) {
760-
size_t sizeLeft = (size & ~3);
761-
size_t currentOffset = 0;
762-
// Memory is unaligned, so we need to copy it to an aligned buffer
763-
uint32_t alignedData[FLASH_PAGE_SIZE / sizeof(uint32_t)] __attribute__((aligned(4)));
764-
// Handle page boundary
765-
bool pageBreak = ((address % 4) != 0) && ((address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));
788+
auto flash_write = [&](uint32_t address, uint8_t* data, size_t size) {
789+
return flashWrite(address, reinterpret_cast<uint32_t*>(data), size);
790+
};
791+
792+
auto flash_read = [](uint32_t address, uint8_t* data, size_t size) {
793+
return spi_flash_read(address, reinterpret_cast<uint32_t*>(data), size) == SPI_FLASH_RESULT_OK;
794+
};
795+
796+
alignas(alignof(uint32_t)) uint8_t buf[FLASH_PAGE_SIZE];
766797

767-
if (pageBreak) {
768-
size_t byteCount = 4 - (address % 4);
798+
size_t written = 0;
769799

770-
if (!flashReplaceBlock(address, data, byteCount)) {
800+
if (!isAlignedAddress(address)) {
801+
auto r_addr = alignBeforeAddress(address);
802+
auto c_off = address - r_addr;
803+
auto wbytes = Alignment - c_off;
804+
805+
wbytes = std::min(wbytes, size);
806+
807+
if (spi_flash_read(r_addr, reinterpret_cast<uint32_t*>(&buf[0]), Alignment) != SPI_FLASH_RESULT_OK) {
771808
return 0;
772809
}
773-
// We will now have aligned address, so we can cross page boundaries
774-
currentOffset += byteCount;
775-
// Realign size to 4
776-
sizeLeft = (size - byteCount) & ~3;
777-
}
778810

779-
while (sizeLeft) {
780-
size_t willCopy = std::min(sizeLeft, sizeof(alignedData));
781-
memcpy(alignedData, data + currentOffset, willCopy);
782-
// We now have address, data and size aligned to 4 bytes, so we can use aligned write
783-
if (!flashWrite(address + currentOffset, alignedData, willCopy))
784-
{
785-
return 0;
811+
#if PUYA_SUPPORT
812+
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
813+
for (size_t i = 0; i < wbytes ; ++i) {
814+
buf[c_off + i] &= data[i];
815+
}
816+
} else {
817+
#endif
818+
memcpy(&buf[c_off], data, wbytes);
819+
#if PUYA_SUPPORT
786820
}
787-
sizeLeft -= willCopy;
788-
currentOffset += willCopy;
789-
}
821+
#endif
790822

791-
return currentOffset;
792-
}
823+
if (spi_flash_write(r_addr, reinterpret_cast<uint32_t*>(&buf[0]), Alignment) != SPI_FLASH_RESULT_OK) {
824+
return wbytes;
825+
}
793826

794-
bool EspClass::flashWritePageBreak(uint32_t address, const uint8_t *data, size_t size) {
795-
if (size > 4) {
796-
return false;
797-
}
798-
size_t pageLeft = FLASH_PAGE_SIZE - (address % FLASH_PAGE_SIZE);
799-
size_t offset = 0;
800-
size_t sizeLeft = size;
801-
if (pageLeft > 3) {
802-
return false;
827+
address += wbytes;
828+
data += wbytes;
829+
written += wbytes;
830+
size -= wbytes;
803831
}
804832

805-
if (!flashReplaceBlock(address, data, pageLeft)) {
806-
return false;
807-
}
808-
offset += pageLeft;
809-
sizeLeft -= pageLeft;
810-
// We replaced last 4-byte block of the page, now we write the remainder in next page
811-
if (!flashReplaceBlock(address + offset, data + offset, sizeLeft)) {
812-
return false;
833+
while (size > 0) {
834+
auto len = std::min(size, sizeof(buf));
835+
auto wlen = alignAddress(len);
836+
837+
if (wlen != len) {
838+
auto l_b = wlen - Alignment;
839+
if (!flash_read(address + l_b, &buf[l_b], Alignment)) {
840+
return written;
841+
}
842+
}
843+
844+
memcpy(&buf[0], data, len);
845+
if (!flash_write(address, &buf[0], wlen)) {
846+
return written;
847+
}
848+
849+
address += len;
850+
data += len;
851+
written += len;
852+
size -= len;
813853
}
814-
return true;
854+
855+
return written;
815856
}
816857

817858
bool EspClass::flashWrite(uint32_t address, const uint32_t *data, size_t size) {
818-
SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
819-
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + size - 1) / FLASH_PAGE_SIZE));
820-
821-
if ((uintptr_t)data % 4 != 0 || size % 4 != 0 || pageBreak) {
822-
return false;
823-
}
859+
SpiFlashOpResult result;
824860
#if PUYA_SUPPORT
825861
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
826-
rc = spi_flash_write_puya(address, const_cast<uint32_t *>(data), size);
862+
result = spi_flash_write_puya(address, const_cast<uint32_t *>(data), size);
827863
}
828864
else
829-
#endif // PUYA_SUPPORT
865+
#endif
830866
{
831-
rc = spi_flash_write(address, const_cast<uint32_t *>(data), size);
867+
result = spi_flash_write_page_break(address, const_cast<uint32_t *>(data), size);
832868
}
833-
return rc == SPI_FLASH_RESULT_OK;
869+
return result == SPI_FLASH_RESULT_OK;
834870
}
835871

836872
bool EspClass::flashWrite(uint32_t address, const uint8_t *data, size_t size) {
837-
if (size == 0) {
838-
return true;
839-
}
840-
841-
size_t sizeLeft = size & ~3;
842-
size_t currentOffset = 0;
843-
844-
if (sizeLeft) {
845-
if ((uintptr_t)data % 4 != 0) {
846-
size_t written = flashWriteUnalignedMemory(address, data, size);
847-
if (!written) {
848-
return false;
849-
}
850-
currentOffset += written;
851-
sizeLeft -= written;
852-
} else {
853-
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));
854-
855-
if (pageBreak) {
856-
while (sizeLeft) {
857-
// We cannot cross page boundary, but the write must be 4 byte aligned,
858-
// so this is the maximum amount we can write
859-
size_t pageBoundary = (FLASH_PAGE_SIZE - ((address + currentOffset) % FLASH_PAGE_SIZE)) & ~3;
860-
861-
if (sizeLeft > pageBoundary) {
862-
// Aligned write up to page boundary
863-
if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), pageBoundary)) {
864-
return false;
865-
}
866-
currentOffset += pageBoundary;
867-
sizeLeft -= pageBoundary;
868-
// Cross the page boundary
869-
if (!flashWritePageBreak(address + currentOffset, data + currentOffset, 4)) {
870-
return false;
871-
}
872-
currentOffset += 4;
873-
sizeLeft -= 4;
874-
} else {
875-
// We do not cross page boundary
876-
if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), sizeLeft)) {
877-
return false;
878-
}
879-
currentOffset += sizeLeft;
880-
sizeLeft = 0;
881-
}
882-
}
883-
} else {
884-
// Pointer is properly aligned and write does not cross page boundary,
885-
// so use aligned write
886-
if (!flashWrite(address, (uint32_t *)data, sizeLeft)) {
887-
return false;
888-
}
889-
currentOffset = sizeLeft;
890-
sizeLeft = 0;
891-
}
892-
}
893-
}
894-
sizeLeft = size - currentOffset;
895-
if (sizeLeft > 0) {
896-
// Size was not aligned, so we have some bytes left to write, we also need to recheck for
897-
// page boundary crossing
898-
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));
899-
900-
if (pageBreak) {
901-
// Cross the page boundary
902-
if (!flashWritePageBreak(address + currentOffset, data + currentOffset, sizeLeft)) {
903-
return false;
904-
}
905-
} else {
906-
// Just write partial block
907-
flashReplaceBlock(address + currentOffset, data + currentOffset, sizeLeft);
873+
if (data && size) {
874+
if (!isAlignedAddress(address)
875+
|| !isAlignedPointer(data)
876+
|| !isAlignedSize(size))
877+
{
878+
return flashWriteUnalignedMemory(address, data, size) == size;
908879
}
880+
881+
return flashWrite(address, reinterpret_cast<const uint32_t *>(data), size) == size;
909882
}
910883

911-
return true;
884+
return false;
912885
}
913886

914887
bool EspClass::flashRead(uint32_t address, uint8_t *data, size_t size) {

cores/esp8266/Esp.h

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,11 @@ class EspClass {
103103

104104
static void reset();
105105
static void restart();
106-
/**
107-
* @brief When calling this method the ESP8266 reboots into the UART download mode without
108-
* the need of any external wiring. This is the same mode which can also be entered by
109-
* pulling GPIO0=low, GPIO2=high, GPIO15=low and resetting the ESP8266.
110-
*/
106+
/**
107+
* @brief When calling this method the ESP8266 reboots into the UART download mode without
108+
* the need of any external wiring. This is the same mode which can also be entered by
109+
* pulling GPIO0=low, GPIO2=high, GPIO15=low and resetting the ESP8266.
110+
*/
111111
[[noreturn]] static void rebootIntoUartDownloadMode();
112112

113113
static uint16_t getVcc();
@@ -252,17 +252,6 @@ class EspClass {
252252
*/
253253
static void resetHeap();
254254
private:
255-
/**
256-
* @brief Replaces @a byteCount bytes of a 4 byte block on flash
257-
*
258-
* @param address flash address
259-
* @param value buffer with data
260-
* @param byteCount number of bytes to replace
261-
* @return bool result of operation
262-
* @retval true success
263-
* @retval false failed to read/write or invalid args
264-
*/
265-
static bool flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount);
266255
/**
267256
* @brief Write up to @a size bytes from @a data to flash at @a address
268257
* This function takes case of unaligned memory access by copying @a data to a temporary buffer,
@@ -274,17 +263,6 @@ class EspClass {
274263
* @return size_t amount of data written, 0 on failure
275264
*/
276265
static size_t flashWriteUnalignedMemory(uint32_t address, const uint8_t *data, size_t size);
277-
/**
278-
* @brief Splits up to 4 bytes into 4 byte blocks and writes them to flash
279-
* We need this since spi_flash_write cannot handle writing over a page boundary with unaligned offset
280-
* i.e. spi_flash_write(254, data, 4) will fail, also we cannot write less bytes as in
281-
* spi_flash_write(254, data, 2) since it will be extended internally to 4 bytes and fail
282-
* @param address start of write
283-
* @param data data to be written
284-
* @param size amount of data, must be < 4
285-
* @return bool result of operation
286-
*/
287-
static bool flashWritePageBreak(uint32_t address, const uint8_t *data, size_t size);
288266
};
289267

290268
extern EspClass ESP;

0 commit comments

Comments
 (0)