2626#include " MD5Builder.h"
2727#include " umm_malloc/umm_malloc.h"
2828#include " cont.h"
29+
2930#include " coredecls.h"
31+ #include < pgmspace.h>
3032
3133extern " C" {
3234#include " user_interface.h"
@@ -40,11 +42,6 @@ extern struct rst_info resetInfo;
4042#ifndef PUYA_SUPPORT
4143 #define PUYA_SUPPORT 1
4244#endif
43- #ifndef PUYA_BUFFER_SIZE
44- // Good alternative for buffer size is: SPI_FLASH_SEC_SIZE (= 4k)
45- // Always use a multiple of flash page size (256 bytes)
46- #define PUYA_BUFFER_SIZE 256
47- #endif
4845
4946/* *
5047 * User-defined Literals
@@ -264,13 +261,6 @@ uint8_t EspClass::getBootMode(void)
264261 return system_get_boot_mode ();
265262}
266263
267- #ifndef F_CPU
268- uint8_t EspClass::getCpuFreqMHz (void )
269- {
270- return system_get_cpu_freq ();
271- }
272- #endif
273-
274264uint32_t EspClass::getFlashChipId (void )
275265{
276266 static uint32_t flash_chip_id = 0 ;
@@ -673,11 +663,14 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
673663 if (data == nullptr ) {
674664 return SPI_FLASH_RESULT_ERR;
675665 }
666+ if (size % 4 != 0 ) {
667+ return SPI_FLASH_RESULT_ERR;
668+ }
676669 // PUYA flash chips need to read existing data, update in memory and write modified data again.
677670 static uint32_t *flash_write_puya_buf = nullptr ;
678671
679672 if (flash_write_puya_buf == nullptr ) {
680- flash_write_puya_buf = (uint32_t *) malloc (PUYA_BUFFER_SIZE );
673+ flash_write_puya_buf = (uint32_t *) malloc (FLASH_PAGE_SIZE );
681674 // No need to ever free this, since the flash chip will never change at runtime.
682675 if (flash_write_puya_buf == nullptr ) {
683676 // Memory could not be allocated.
@@ -691,45 +684,261 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
691684 uint32_t pos = offset;
692685 while (bytesLeft > 0 && rc == SPI_FLASH_RESULT_OK) {
693686 size_t bytesNow = bytesLeft;
694- if (bytesNow > PUYA_BUFFER_SIZE ) {
695- bytesNow = PUYA_BUFFER_SIZE ;
696- bytesLeft -= PUYA_BUFFER_SIZE ;
687+ if (bytesNow > FLASH_PAGE_SIZE ) {
688+ bytesNow = FLASH_PAGE_SIZE ;
689+ bytesLeft -= FLASH_PAGE_SIZE ;
697690 } else {
698691 bytesLeft = 0 ;
699692 }
700- size_t bytesAligned = (bytesNow + 3 ) & ~3 ;
701- rc = spi_flash_read (pos, flash_write_puya_buf, bytesAligned);
693+ rc = spi_flash_read (pos, flash_write_puya_buf, bytesNow);
702694 if (rc != SPI_FLASH_RESULT_OK) {
703695 return rc;
704696 }
705- for (size_t i = 0 ; i < bytesAligned / 4 ; ++i) {
697+ for (size_t i = 0 ; i < bytesNow / 4 ; ++i) {
706698 flash_write_puya_buf[i] &= *ptr;
707699 ++ptr;
708700 }
709- rc = spi_flash_write (pos, flash_write_puya_buf, bytesAligned );
701+ rc = spi_flash_write (pos, flash_write_puya_buf, bytesNow );
710702 pos += bytesNow;
711703 }
712704 return rc;
713705}
714706#endif
715707
716- 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) {
717803 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+ }
718809#if PUYA_SUPPORT
719810 if (getFlashChipVendorId () == SPI_FLASH_VENDOR_PUYA) {
720- rc = spi_flash_write_puya (offset, data, size);
811+ rc = spi_flash_write_puya (address, const_cast < uint32_t *>( data) , size);
721812 }
722813 else
723- #endif
814+ #endif // PUYA_SUPPORT
724815 {
725- rc = spi_flash_write (offset, data, size);
816+ rc = spi_flash_write (address, const_cast < uint32_t *>( data) , size);
726817 }
727818 return rc == SPI_FLASH_RESULT_OK;
728819}
729820
730- bool EspClass::flashRead (uint32_t offset, uint32_t *data, size_t size) {
731- auto rc = spi_flash_read (offset, (uint32_t *) data, size);
732- 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);
733942}
734943
735944String EspClass::getSketchMD5 ()
@@ -740,17 +949,17 @@ String EspClass::getSketchMD5()
740949 }
741950 uint32_t lengthLeft = getSketchSize ();
742951 const size_t bufSize = 512 ;
743- std::unique_ptr<uint8_t []> buf (new uint8_t [bufSize]);
952+ std::unique_ptr<uint8_t []> buf (new (std::nothrow) uint8_t [bufSize]);
744953 uint32_t offset = 0 ;
745954 if (!buf.get ()) {
746- return String () ;
955+ return emptyString ;
747956 }
748957 MD5Builder md5;
749958 md5.begin ();
750959 while ( lengthLeft > 0 ) {
751960 size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize;
752961 if (!flashRead (offset, reinterpret_cast <uint32_t *>(buf.get ()), (readBytes + 3 ) & ~3 )) {
753- return String () ;
962+ return emptyString ;
754963 }
755964 md5.add (buf.get (), readBytes);
756965 lengthLeft -= readBytes;
0 commit comments