Skip to content

Commit 33bf962

Browse files
authored
Merge branch 'master' into devyte-copyright
2 parents 16c93f8 + 9003b02 commit 33bf962

File tree

22 files changed

+741
-337
lines changed

22 files changed

+741
-337
lines changed

cores/esp8266/Esp.cpp

Lines changed: 234 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,6 @@ extern struct rst_info resetInfo;
4242
#ifndef PUYA_SUPPORT
4343
#define PUYA_SUPPORT 1
4444
#endif
45-
#ifndef PUYA_BUFFER_SIZE
46-
// Good alternative for buffer size is: SPI_FLASH_SEC_SIZE (= 4k)
47-
// Always use a multiple of flash page size (256 bytes)
48-
#define PUYA_BUFFER_SIZE 256
49-
#endif
5045

5146
/**
5247
* User-defined Literals
@@ -668,11 +663,14 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
668663
if (data == nullptr) {
669664
return SPI_FLASH_RESULT_ERR;
670665
}
666+
if (size % 4 != 0) {
667+
return SPI_FLASH_RESULT_ERR;
668+
}
671669
// PUYA flash chips need to read existing data, update in memory and write modified data again.
672670
static uint32_t *flash_write_puya_buf = nullptr;
673671

674672
if (flash_write_puya_buf == nullptr) {
675-
flash_write_puya_buf = (uint32_t*) malloc(PUYA_BUFFER_SIZE);
673+
flash_write_puya_buf = (uint32_t*) malloc(FLASH_PAGE_SIZE);
676674
// No need to ever free this, since the flash chip will never change at runtime.
677675
if (flash_write_puya_buf == nullptr) {
678676
// Memory could not be allocated.
@@ -686,45 +684,261 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
686684
uint32_t pos = offset;
687685
while (bytesLeft > 0 && rc == SPI_FLASH_RESULT_OK) {
688686
size_t bytesNow = bytesLeft;
689-
if (bytesNow > PUYA_BUFFER_SIZE) {
690-
bytesNow = PUYA_BUFFER_SIZE;
691-
bytesLeft -= PUYA_BUFFER_SIZE;
687+
if (bytesNow > FLASH_PAGE_SIZE) {
688+
bytesNow = FLASH_PAGE_SIZE;
689+
bytesLeft -= FLASH_PAGE_SIZE;
692690
} else {
693691
bytesLeft = 0;
694692
}
695-
size_t bytesAligned = (bytesNow + 3) & ~3;
696-
rc = spi_flash_read(pos, flash_write_puya_buf, bytesAligned);
693+
rc = spi_flash_read(pos, flash_write_puya_buf, bytesNow);
697694
if (rc != SPI_FLASH_RESULT_OK) {
698695
return rc;
699696
}
700-
for (size_t i = 0; i < bytesAligned / 4; ++i) {
697+
for (size_t i = 0; i < bytesNow / 4; ++i) {
701698
flash_write_puya_buf[i] &= *ptr;
702699
++ptr;
703700
}
704-
rc = spi_flash_write(pos, flash_write_puya_buf, bytesAligned);
701+
rc = spi_flash_write(pos, flash_write_puya_buf, bytesNow);
705702
pos += bytesNow;
706703
}
707704
return rc;
708705
}
709706
#endif
710707

711-
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
708+
bool EspClass::flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount) {
709+
uint32_t alignedAddress = (address & ~3);
710+
uint32_t alignmentOffset = address - alignedAddress;
711+
712+
if (alignedAddress != ((address + byteCount - 1) & ~3)) {
713+
// Only one 4 byte block is supported
714+
return false;
715+
}
716+
#if PUYA_SUPPORT
717+
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
718+
uint8_t tempData[4] __attribute__((aligned(4)));
719+
if (spi_flash_read(alignedAddress, (uint32_t *)tempData, 4) != SPI_FLASH_RESULT_OK) {
720+
return false;
721+
}
722+
for (size_t i = 0; i < byteCount; i++) {
723+
tempData[i + alignmentOffset] &= value[i];
724+
}
725+
if (spi_flash_write(alignedAddress, (uint32_t *)tempData, 4) != SPI_FLASH_RESULT_OK) {
726+
return false;
727+
}
728+
}
729+
else
730+
#endif // PUYA_SUPPORT
731+
{
732+
uint32_t tempData;
733+
if (spi_flash_read(alignedAddress, &tempData, 4) != SPI_FLASH_RESULT_OK) {
734+
return false;
735+
}
736+
memcpy((uint8_t *)&tempData + alignmentOffset, value, byteCount);
737+
if (spi_flash_write(alignedAddress, &tempData, 4) != SPI_FLASH_RESULT_OK) {
738+
return false;
739+
}
740+
}
741+
return true;
742+
}
743+
744+
size_t EspClass::flashWriteUnalignedMemory(uint32_t address, const uint8_t *data, size_t size) {
745+
size_t sizeLeft = (size & ~3);
746+
size_t currentOffset = 0;
747+
// Memory is unaligned, so we need to copy it to an aligned buffer
748+
uint32_t alignedData[FLASH_PAGE_SIZE / sizeof(uint32_t)] __attribute__((aligned(4)));
749+
// Handle page boundary
750+
bool pageBreak = ((address % 4) != 0) && ((address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));
751+
752+
if (pageBreak) {
753+
size_t byteCount = 4 - (address % 4);
754+
755+
if (!flashReplaceBlock(address, data, byteCount)) {
756+
return 0;
757+
}
758+
// We will now have aligned address, so we can cross page boundaries
759+
currentOffset += byteCount;
760+
// Realign size to 4
761+
sizeLeft = (size - byteCount) & ~3;
762+
}
763+
764+
while (sizeLeft) {
765+
size_t willCopy = std::min(sizeLeft, sizeof(alignedData));
766+
memcpy(alignedData, data + currentOffset, willCopy);
767+
// We now have address, data and size aligned to 4 bytes, so we can use aligned write
768+
if (!flashWrite(address + currentOffset, alignedData, willCopy))
769+
{
770+
return 0;
771+
}
772+
sizeLeft -= willCopy;
773+
currentOffset += willCopy;
774+
}
775+
776+
return currentOffset;
777+
}
778+
779+
bool EspClass::flashWritePageBreak(uint32_t address, const uint8_t *data, size_t size) {
780+
if (size > 4) {
781+
return false;
782+
}
783+
size_t pageLeft = FLASH_PAGE_SIZE - (address % FLASH_PAGE_SIZE);
784+
size_t offset = 0;
785+
size_t sizeLeft = size;
786+
if (pageLeft > 3) {
787+
return false;
788+
}
789+
790+
if (!flashReplaceBlock(address, data, pageLeft)) {
791+
return false;
792+
}
793+
offset += pageLeft;
794+
sizeLeft -= pageLeft;
795+
// We replaced last 4-byte block of the page, now we write the remainder in next page
796+
if (!flashReplaceBlock(address + offset, data + offset, sizeLeft)) {
797+
return false;
798+
}
799+
return true;
800+
}
801+
802+
bool EspClass::flashWrite(uint32_t address, const uint32_t *data, size_t size) {
712803
SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
804+
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + size - 1) / FLASH_PAGE_SIZE));
805+
806+
if ((uintptr_t)data % 4 != 0 || size % 4 != 0 || pageBreak) {
807+
return false;
808+
}
713809
#if PUYA_SUPPORT
714810
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
715-
rc = spi_flash_write_puya(offset, data, size);
811+
rc = spi_flash_write_puya(address, const_cast<uint32_t *>(data), size);
716812
}
717813
else
718-
#endif
814+
#endif // PUYA_SUPPORT
719815
{
720-
rc = spi_flash_write(offset, data, size);
816+
rc = spi_flash_write(address, const_cast<uint32_t *>(data), size);
721817
}
722818
return rc == SPI_FLASH_RESULT_OK;
723819
}
724820

725-
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) {
726-
auto rc = spi_flash_read(offset, (uint32_t*) data, size);
727-
return rc == SPI_FLASH_RESULT_OK;
821+
bool EspClass::flashWrite(uint32_t address, const uint8_t *data, size_t size) {
822+
if (size == 0) {
823+
return true;
824+
}
825+
826+
size_t sizeLeft = size & ~3;
827+
size_t currentOffset = 0;
828+
829+
if (sizeLeft) {
830+
if ((uintptr_t)data % 4 != 0) {
831+
size_t written = flashWriteUnalignedMemory(address, data, size);
832+
if (!written) {
833+
return false;
834+
}
835+
currentOffset += written;
836+
sizeLeft -= written;
837+
} else {
838+
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));
839+
840+
if (pageBreak) {
841+
while (sizeLeft) {
842+
// We cannot cross page boundary, but the write must be 4 byte aligned,
843+
// so this is the maximum amount we can write
844+
size_t pageBoundary = (FLASH_PAGE_SIZE - ((address + currentOffset) % FLASH_PAGE_SIZE)) & ~3;
845+
846+
if (sizeLeft > pageBoundary) {
847+
// Aligned write up to page boundary
848+
if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), pageBoundary)) {
849+
return false;
850+
}
851+
currentOffset += pageBoundary;
852+
sizeLeft -= pageBoundary;
853+
// Cross the page boundary
854+
if (!flashWritePageBreak(address + currentOffset, data + currentOffset, 4)) {
855+
return false;
856+
}
857+
currentOffset += 4;
858+
sizeLeft -= 4;
859+
} else {
860+
// We do not cross page boundary
861+
if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), sizeLeft)) {
862+
return false;
863+
}
864+
currentOffset += sizeLeft;
865+
sizeLeft = 0;
866+
}
867+
}
868+
} else {
869+
// Pointer is properly aligned and write does not cross page boundary,
870+
// so use aligned write
871+
if (!flashWrite(address, (uint32_t *)data, sizeLeft)) {
872+
return false;
873+
}
874+
currentOffset = sizeLeft;
875+
sizeLeft = 0;
876+
}
877+
}
878+
}
879+
sizeLeft = size - currentOffset;
880+
if (sizeLeft > 0) {
881+
// Size was not aligned, so we have some bytes left to write, we also need to recheck for
882+
// page boundary crossing
883+
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));
884+
885+
if (pageBreak) {
886+
// Cross the page boundary
887+
if (!flashWritePageBreak(address + currentOffset, data + currentOffset, sizeLeft)) {
888+
return false;
889+
}
890+
} else {
891+
// Just write partial block
892+
flashReplaceBlock(address + currentOffset, data + currentOffset, sizeLeft);
893+
}
894+
}
895+
896+
return true;
897+
}
898+
899+
bool EspClass::flashRead(uint32_t address, uint8_t *data, size_t size) {
900+
size_t sizeAligned = size & ~3;
901+
size_t currentOffset = 0;
902+
903+
if ((uintptr_t)data % 4 != 0) {
904+
uint32_t alignedData[FLASH_PAGE_SIZE / sizeof(uint32_t)] __attribute__((aligned(4)));
905+
size_t sizeLeft = sizeAligned;
906+
907+
while (sizeLeft) {
908+
size_t willCopy = std::min(sizeLeft, sizeof(alignedData));
909+
// We read to our aligned buffer and then copy to data
910+
if (!flashRead(address + currentOffset, alignedData, willCopy))
911+
{
912+
return false;
913+
}
914+
memcpy(data + currentOffset, alignedData, willCopy);
915+
sizeLeft -= willCopy;
916+
currentOffset += willCopy;
917+
}
918+
} else {
919+
// Pointer is properly aligned, so use aligned read
920+
if (!flashRead(address, (uint32_t *)data, sizeAligned)) {
921+
return false;
922+
}
923+
currentOffset = sizeAligned;
924+
}
925+
926+
if (currentOffset < size) {
927+
uint32_t tempData;
928+
if (spi_flash_read(address + currentOffset, &tempData, 4) != SPI_FLASH_RESULT_OK) {
929+
return false;
930+
}
931+
memcpy((uint8_t *)data + currentOffset, &tempData, size - currentOffset);
932+
}
933+
934+
return true;
935+
}
936+
937+
bool EspClass::flashRead(uint32_t address, uint32_t *data, size_t size) {
938+
if ((uintptr_t)data % 4 != 0 || size % 4 != 0) {
939+
return false;
940+
}
941+
return (spi_flash_read(address, data, size) == SPI_FLASH_RESULT_OK);
728942
}
729943

730944
String EspClass::getSketchMD5()

cores/esp8266/Esp.h

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,48 @@ class EspClass {
150150
bool checkFlashCRC();
151151

152152
bool flashEraseSector(uint32_t sector);
153-
bool flashWrite(uint32_t offset, uint32_t *data, size_t size);
154-
bool flashRead(uint32_t offset, uint32_t *data, size_t size);
153+
/**
154+
* @brief Write @a size bytes from @a data to flash at @a address
155+
* This overload requires @a data and @a size to be always 4 byte aligned and
156+
* @a address to be 4 byte aligned if the write crossess page boundary,
157+
* but guarantees no overhead (except on PUYA flashes)
158+
* @param address address on flash where write should start, 4 byte alignment is conditional
159+
* @param data input buffer, must be 4-byte aligned
160+
* @param size amount of data, must be a multiple of 4
161+
* @return bool result of operation
162+
* @retval true success
163+
* @retval false failure to write to flash or incorrect alignment of params
164+
*/
165+
bool flashWrite(uint32_t address, const uint32_t *data, size_t size);
166+
/**
167+
* @brief Write @a size bytes from @a data to flash at @a address
168+
* This overload handles all misalignment cases
169+
* @param address address on flash where write should start
170+
* @param data input buffer, passing unaligned memory will cause significant stack usage
171+
* @param size amount of data, passing not multiple of 4 will cause additional reads and writes
172+
* @return bool result of operation
173+
*/
174+
bool flashWrite(uint32_t address, const uint8_t *data, size_t size);
175+
/**
176+
* @brief Read @a size bytes to @a data to flash at @a address
177+
* This overload requires @a data and @a size to be 4 byte aligned
178+
* @param address address on flash where read should start
179+
* @param data input buffer, must be 4-byte aligned
180+
* @param size amount of data, must be a multiple of 4
181+
* @return bool result of operation
182+
* @retval true success
183+
* @retval false failure to read from flash or incorrect alignment of params
184+
*/
185+
bool flashRead(uint32_t address, uint32_t *data, size_t size);
186+
/**
187+
* @brief Read @a size bytes to @a data to flash at @a address
188+
* This overload handles all misalignment cases
189+
* @param address address on flash where read should start
190+
* @param data input buffer, passing unaligned memory will cause significant stack usage
191+
* @param size amount of data, passing not multiple of 4 will cause additional read
192+
* @return bool result of operation
193+
*/
194+
bool flashRead(uint32_t address, uint8_t *data, size_t size);
155195

156196
uint32_t getSketchSize();
157197
String getSketchMD5();
@@ -175,6 +215,40 @@ class EspClass {
175215
#else
176216
uint32_t getCycleCount();
177217
#endif // !defined(CORE_MOCK)
218+
private:
219+
/**
220+
* @brief Replaces @a byteCount bytes of a 4 byte block on flash
221+
*
222+
* @param address flash address
223+
* @param value buffer with data
224+
* @param byteCount number of bytes to replace
225+
* @return bool result of operation
226+
* @retval true success
227+
* @retval false failed to read/write or invalid args
228+
*/
229+
bool flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount);
230+
/**
231+
* @brief Write up to @a size bytes from @a data to flash at @a address
232+
* This function takes case of unaligned memory acces by copying @a data to a temporary buffer,
233+
* it also takes care of page boundary crossing see @a flashWritePageBreak as to why it's done.
234+
* Less than @a size bytes may be written, due to 4 byte alignment requirement of spi_flash_write
235+
* @param address address on flash where write should start
236+
* @param data input buffer
237+
* @param size amount of data
238+
* @return size_t amount of data written, 0 on failure
239+
*/
240+
size_t flashWriteUnalignedMemory(uint32_t address, const uint8_t *data, size_t size);
241+
/**
242+
* @brief Splits up to 4 bytes into 4 byte blocks and writes them to flash
243+
* We need this since spi_flash_write cannot handle writing over a page boundary with unaligned offset
244+
* i.e. spi_flash_write(254, data, 4) will fail, also we cannot write less bytes as in
245+
* spi_flash_write(254, data, 2) since it will be extended internally to 4 bytes and fail
246+
* @param address start of write
247+
* @param data data to be written
248+
* @param size amount of data, must be < 4
249+
* @return bool result of operation
250+
*/
251+
bool flashWritePageBreak(uint32_t address, const uint8_t *data, size_t size);
178252
};
179253

180254
extern EspClass ESP;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy