@@ -673,7 +673,48 @@ bool EspClass::flashEraseSector(uint32_t sector) {
673
673
return rc == 0 ;
674
674
}
675
675
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
+
676
715
#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
677
718
static SpiFlashOpResult spi_flash_write_puya (uint32_t offset, uint32_t *data, size_t size) {
678
719
if (data == nullptr ) {
679
720
return SPI_FLASH_RESULT_ERR;
@@ -720,195 +761,127 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
720
761
}
721
762
#endif
722
763
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 };
726
765
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));
757
785
}
758
786
759
787
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];
766
797
767
- if (pageBreak) {
768
- size_t byteCount = 4 - (address % 4 );
798
+ size_t written = 0 ;
769
799
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) {
771
808
return 0 ;
772
809
}
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
- }
778
810
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
786
820
}
787
- sizeLeft -= willCopy;
788
- currentOffset += willCopy;
789
- }
821
+ #endif
790
822
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
+ }
793
826
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;
803
831
}
804
832
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;
813
853
}
814
- return true ;
854
+
855
+ return written;
815
856
}
816
857
817
858
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;
824
860
#if PUYA_SUPPORT
825
861
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);
827
863
}
828
864
else
829
- #endif // PUYA_SUPPORT
865
+ #endif
830
866
{
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);
832
868
}
833
- return rc == SPI_FLASH_RESULT_OK;
869
+ return result == SPI_FLASH_RESULT_OK;
834
870
}
835
871
836
872
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;
908
879
}
880
+
881
+ return flashWrite (address, reinterpret_cast <const uint32_t *>(data), size) == size;
909
882
}
910
883
911
- return true ;
884
+ return false ;
912
885
}
913
886
914
887
bool EspClass::flashRead (uint32_t address, uint8_t *data, size_t size) {
0 commit comments