@@ -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
730944String EspClass::getSketchMD5 ()
0 commit comments