-
Notifications
You must be signed in to change notification settings - Fork 13.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New flash writing method with offset/memory/size alignment handling #7514
Conversation
I meant different issue: over-read of |
flash_write_puya_buf size is checked against bytesNow, which is enough, since it's aligned to flash sector size: // Good alternative for buffer size is: SPI_FLASH_SEC_SIZE (= 4k)
// Always use a multiple of flash page size (256 bytes)
#define PUYA_BUFFER_SIZE 256 As for spi_flash_read and spi_flash_write - they support |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While it is true that the existing code may read up to 3 bytes past the end of the pointer, this is absolutely safe because no pointer the core can access is anywhere near 3 bytes of the end-of-RAM so there is 0 chance if it causing a HW exception. The heap is ~30K before the end of RAM, and the main app stack pointer will be well beyond it, too.
It's not worth the extra code size and risk of opening up this block to avoid this.
@earlephilhower My commit does not change the over-read, it only changes the over-write, which in my opinion is a valid point. I see that you requested changes to my request, what should I change? |
Thanks for the explanation. I was going thru the original issue and thought the purpose was to not read past end-of-data. This section of code has been very much trouble for us, so honestly what I'd need is a failing and passing valid test case on real XMC hardware with a couple folks' confirmations. XMC just has some strange behavior which has caused something like 3 dot-releases so far, and stuff that looks like it should be fine often isn't when it meets that abomination of a flash chip. |
I have PUYA chip in my Sonoff S20, so I'm verifying each time before creating a pull request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the SDK I think what is in there is correct. Byte-wide spi_read/_write doesn't seem legal.
@devyte, if you're still relaxing, can you eyeball this? Am I missing something? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current code reads bytesAligned, then ANDs bytesAligned, then writes bytesAligned.
I think this is wrong, because the AND loop accesses out-of-bounds, and the result could change the bytes beyond the last one.
This PR reads bytesNow, then ANDs bytesAligned, then writes bytesNow.
I think this is wrong, because of the reason above, and in addition because the spi ops are supposed to be multiple-of-4.
Consider:
Read bytesAligned, then AND bytesNow, then write bytesAligned.
The reading will read into the buffer with potentially more bytes than needed (alignment), then will AND only up until the last valid byte leaving those beyond the last one unchanged, then write back, including the last unchanged bytes.
Of course, handling the last bytes likely need to be done as a special case.
@devyte if (!aligned) {
readLast4BytesFromFlash();
andReadWithData();
writeLast4BytesToFlash();
} |
See my suggested change linked above, it does exactly this (and also does not over-read |
@yumkam It does not handle the generic case |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current state of this PR seems to be technically correct for both PUYA and normal flash chips.
The conclusion of a gitter discussion was:
- The underlying spi_flash_write() and spi_flash_read() calls are documented by Espressif to require size to be a multiple-of-4
- The calls seem to work with size not a multiple-of-4, but it is suspected that the bytes above the last misaligned one will be written either as null or with garbage
- The correctness for allowing size to be not a multiple-of-4 comes at the price of an extra read (unavoidable) and an extra write (may be avoidable)
- An extra write means that the flash life is reduced
Internal discussions are ongoing, this PR should be considered on-hold for now
My additional explanation about flash writes:
|
@drzony So unless they have come up with some revolutionary new type of memory, they must perform an erase somewhere. |
@TD-er |
More like this: |
Ok, so I have confirmed that spi_flash_write self-aligns, this code: uint32_t flash_offset = 0xa0000;
uint32_t wr_buf1[2] = {0x55555555, 0x55555555};
uint32_t wr_buf2[2] = {0xAAAAAAAA, 0xAAAAAAAA};
uint32_t rd_buf1[2] = {0x00000000, 0x00000000};
spi_flash_erase_sector(flash_offset / 0x1000);
spi_flash_write(flash_offset, wr_buf1, 3);
spi_flash_read(flash_offset, rd_buf1, 4);
DEBUG("Read from flash + 0 %08x\n", rd_buf1[0]);
flash_read(flash_offset + 4, rd_buf1, 4);
DEBUG("Read from flash + 4 %08x\n", rd_buf1[0]);
spi_flash_write(flash_offset + 4, wr_buf1, 4);
spi_flash_read(flash_offset + 8, rd_buf1, 4);
DEBUG("Read from flash + 8 %08x\n", rd_buf1[0]);
spi_flash_write(flash_offset + 8, wr_buf2, 4);
spi_flash_read(flash_offset + 4, rd_buf1, 4);
DEBUG("Read from flash + 4 %08x\n", rd_buf1[0]);
spi_flash_read(flash_offset + 8, rd_buf1, 4);
DEBUG("Read from flash + 8 %08x\n", rd_buf1[0]); Produces (on esp12e with "normal" flash and on S20 with PUYA flash): Read from flash + 0 55555555
Read from flash + 4 ffffffff
Read from flash + 8 ffffffff
Read from flash + 4 55555555
Read from flash + 8 aaaaaaaa So always 4 bytes are written |
Next test: uint32_t flash_offset = 0xa0000;
uint32_t wr_buf1[2] = {0x55555555, 0x55555555};
uint32_t wr_buf2[2] = {0xAAAAAAAA, 0xAAAAAAAA};
uint32_t rd_buf1[2] = {0x00000000, 0x00000000};
spi_flash_erase_sector(flash_offset / 0x1000);
spi_flash_write(flash_offset, wr_buf1, 4);
spi_flash_write(flash_offset, wr_buf2, 4);
spi_flash_read(flash_offset, rd_buf1, 4);
DEBUG("Read from flash + 0 %08x\n", rd_buf1[0]); Produces: Read from flash + 0 00000000 On PUYA flash: Read from flash + 0 aaaa2aaa So completely messed up. |
Hmm that |
And the last test for today: uint32_t flash_offset = 0xa0000;
uint32_t wr_buf1[2] = {0xDEADBEEF, 0xDEADBEEF};
uint32_t rd_buf1[2] = {0x00000000, 0x00000000};
uint32_t rd_buf2[2] = {0x00000000, 0x00000000};
spi_flash_erase_sector(flash_offset / 0x1000);
spi_flash_read(flash_offset, rd_buf1, 4);
spi_flash_write(flash_offset, wr_buf1, 4);
spi_flash_read(flash_offset + 4, rd_buf1 + 1, 4);
spi_flash_write(flash_offset + 4, wr_buf1 + 1, 4);
spi_flash_read(flash_offset, rd_buf2, 8);
DEBUG("Read from flash + 0 %08x %08x %08x %08x\n", rd_buf1[0], rd_buf1[1], rd_buf2[0], rd_buf2[1]);
memset(rd_buf1, 0, 8);
memset(rd_buf2, 0, 8);
spi_flash_erase_sector(flash_offset / 0x1000);
spi_flash_read(flash_offset, rd_buf1, 8);
spi_flash_write(flash_offset, wr_buf1, 8);
spi_flash_read(flash_offset, rd_buf2, 8);
DEBUG("Read from flash + 0 %08x %08x %08x %08x\n", rd_buf1[0], rd_buf1[1], rd_buf2[0], rd_buf2[1]); Produces Read from flash + 0 ffffffff ffffffff deadbeef deadbeef
Read from flash + 0 ffffffff ffffffff deadbeef deadbeef On both flashes, so if there is some erasing happening, then it's not affecting other data on flash. |
@TD-er One thing keeps bugging me, what is the reason for the read/write combo on PUYA flashes? |
This commit simplifies the code a bit and fixes a bug that caused wrong number of bytes to be written
Code from my comment above covers that, I'll put that into a test |
@devyte @earlephilhower Can any of you run those tests on the device? I'm using Platformio for development on Windows, so setting up a testing environment will take me some time. |
@drzony I'll give this PR a run thru my system and report back. Thanks for adding tests! |
@drzony, a minor patch is required to get it to build and run:
Once done, I'm seeing a failure on the "Last bytes of page" test:
|
Verbose output:
|
@earlephilhower Thanks for the assist :) Everything should be fixed now |
Still testing on PUYA flash, do not merge yet |
I've managed to run those tests on PUYA device and they passed, so the PR is ready to merge. |
Latest version passes in device tests for me, too:
|
* master: (37 commits) BREAKING: Change return EEPROM.end() to bool (esp8266#7630) BREAKING: Change return type of channel() (esp8266#7656) BREAKING: Change return type of RSSI() (esp8266#7657) Add Copyright notice to Schedule.h (esp8266#7653) MDNS MultiInterface (esp8266#7636) BREAKING: Add Wrong Password wifi status case (esp8266#7652) New flash writing method with offset/memory/size alignment handling (esp8266#7514) Fix error when debug enabled but no port chosen (esp8266#7648) LEAmDNSv2: change a macro name to be independant from LEAmDNS1 (esp8266#7640) Allow test framework to use cores/esp8266/Arduino.h directly (esp8266#7377) Update OTA HTTP Server Header Information (esp8266#7633) Add missing sntp_init/sntp_stop (esp8266#7628) Use direct member initialization instead of ctr initialisation (esp8266#7558) Prevent rewriting Updater_Signing.h if content unchanged (esp8266#7627) Add WiFi Multi to readme.rst Remove stray axtls refs, deprecated compat funcs (esp8266#7626) Pull deprecated axtls link (esp8266#7624) Redesign ESP8266WiFiMulti.[cpp|h] Update README.md (esp8266#7623) Eliminate code duplication by template for printNumber(...)/printFloat(...). ...
Fixes issues raised in #7491 (comment)