Writing to MCU flash one byte at a time - Page 1
Forum section: electronics / general

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 09, 2023, 04:27:45 pm
content: I have an existing project where I am using a CH32V003 microcontroller to emulate an I2C EEPROM. The contents of the emulated EEPROM are stored in the MCU's flash. At the moment it is read-only, but I want to add the ability to write to the emulated EEPROM from the system connected as I2C master. This will of course mean that I need to write the received data to the MCU's flash, but I'm not 100% certain on the strategy I should employ, so I'm seeking some advice.To start with, I have ascertained that the I2C master only ever writes the entire contents of the EEPROM (4 kB) at once, but it does so a single byte at a time. This has been verified with a logic analyser capture of the I2C bus. That is, it will perform 4,096 transactions of [slave addr + write flag, 16-bit ROM addr, data byte] in sequential manner for every byte of the EEPROM.However, my microcontroller's flash memory can only be written with a minimum granularity of half-words, 16-bits. The only other mode of writing/erasing is by 64-byte page.First, I have a question about the need for erasure of the flash before writing new data. There is no explicit half-word erasure feature (as there is for pages), so I presume in order to write new single-byte values to the flash, I will need to perform a kind of 'manual' erasure by writing 0xFF to the relevant location, right? Or is it not possible to erase flash memory by simply writing all 1's to it?The procedure I'm thinking of implementing to handle the single-byte writes is as follows:1. Receive the single data byte and the virtual EEPROM address where it is to be written (0x0000 to 0xFFFF).2. Map the virtual address to the actual location in flash memory.3. Adjust that location to the nearest half-word, and determine whether it is the upper or lower byte to be modified.4. Read in the existing half-word of data; if that modified with the new byte value results in unchanged data, do nothing.5. Modify the relevant upper/lower byte in the half-word to be 0xFF and write it back to flash, erasing the byte to be modified.6. Modify the relevant upper/lower byte with the new value, and write the resulting half-word to flash.I will also make sure to set__attribute__((aligned(64)))on thestatic const uint8_tarray holding the virtual EEPROM contents. This will ensure that it is not only aligned to the flash page size, but also has half-word alignment, given the flash page size (64) is a multiple of 2. I think this is important because the MCU documentation says that a bus error will be generated if any non-half-word data is written once the PG flag is set to initiate a half-word write.Am I on the right track? Any gotchas I've failed to see? My fear is that it's not actually possible to do half-word writing without first erasing the containing page, which I of course have no opportunity to do in a feasible manner ahead of time.

author: ataradov
author_rank: Super Contributor
timestamp: October 09, 2023, 04:41:15 pm
content: Those small word writes are only possible into the erased locations. It is impossible to erase them by writing 1's. You will have to erase the whole 64 byte page.If the writes are guaranteed to be sequential, then you can just erase the page when the page boundary is crossed. Then store the first written byte in the SRAM, wait for the second one. When you get the second one - store it in the flash. Repeat until you reach the page boundary (64 bytes).For more efficient operation, I would buffer the whole 64 byte region in the SRAM and write it all at once. This will easily accommodate non-sequential access, you just accept all the writes within the 64-byte region and if a new write is in a different region, you erase/write the current buffer and fill the buffer with the data from the new flash location and update necessary bytes on I2C writes.The only issue is that 64 bytes will be lost on unexpected power interruptions, so you need to periodically flush that data.Note that flash erase/writes may be slow and you would need to check that your master tolerates clock stretching and does not rely on fixed timings.

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: October 09, 2023, 04:51:02 pm
content: Quote from: ataradov on October 09, 2023, 04:41:15 pmIf the host normally writes as a burst then a timeout to trigger a write would be a reasonable approachQuoteIf the MCU can stay running during flash write ( e.g. by locating code in RAM) then you could double-buffer the data to avoid this problemEEPROMs don't typically do clock-stretching so fairly likely that the host doesn't expect it.

author: ataradov
author_rank: Super Contributor
timestamp: October 09, 2023, 04:59:30 pm
content: But eventually data will pile up if the flash is slow and the host is fast.At the same time a sane implementation of the I2C will automatically support clock stretching, you don't actually need to do anything extra. It is likely it will just work. The only place I've seen where clock stretching was a huge issue are optical transceivers. For whatever reason a lot of the hardware they use there is just hard-coded bus toggling without sampling the bus state at all for feedback.

author: DavidAlfa
author_rank: Super Contributor
timestamp: October 09, 2023, 05:45:35 pm
content: If you're totally sure it writes all the bytes sequentally (Otherwise this code will cause plenty of trouble!), simply use a buffer and write the entire page when done.A 64-byte buffer, two variables to store the virtual page and the buffer index, and done!If the master addresses each byte individually, just check the page it belongs to.Otherwise, the memory address should be incremented automatically after each write following the eeprom protocol.As said, using i2c clock stretching should prevent any problems.Probably the host checks the busy bit, but some chinese crap will simply use a fixed delay and check nothing!Whenever it crosses the page boundary, erase the current page, store the buffer and increase the page.Something like this. Thought this is very simplified and you might need to check more conditions.Code:uint8_t ee_buffer; // Page bufferuint8_t ee_page; // Stores the buffer page addressuint8_t ee_count; // Stores the bytes writtenvoid writeEeprom(uint8_t data, uint16_t addr){uint8_t page = addr / 64;uint8_t index = addr % 64;if(ee_page == page) { // Address belongs to current pageee_buffer = data; // Store byte in bufferee_count++; // Increase countif(index == 63){ // Last page byte ?page++; // Increase page}}if(ee_page != page) // New page ?if(ee_count){ // Did we receive data previously or is this a random address change?if(ee_count != 64({ // We should have received 64 bytes// Only a partial write! Existing flash data will get overwritten with random data!}EraseFlashPage(ee_page); // Erase flash pageWriteFlashPage(ee_page, ee_buffer, 64; // Write new data (Page address, data pointer, data size)ee_page = page; // Update page addressee_count = 0; // Reset count}if(index!=63){ // We didn't reach end of page, so this was a random address changeee_buffer = data; // Store byte in bufferee_count++; // Increase count}}}

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 09, 2023, 05:57:53 pm
content: Quote from: ataradov on October 09, 2023, 04:41:15 pmAh, I was worried that would be the case.Quote from: ataradov on October 09, 2023, 04:41:15 pmI'm not too worried about the speed of erasing and writing. Firstly, the MCU's page erase or write time is max of 3.1ms, whereas a typical 24C32 EEPROM takes 5 ms. Secondly, the I2C master does ACK polling when waiting for the write to complete, so it shouldn't actually care how long it takes (assuming I don't let it exceed some kind of overall timeout or retry count). What I mean by ACK polling is that it tries to write the next byte almost immediately after the end of the previous transaction, but if it receives NACK from the slave address transmission, it wait about 1500 us and tries again - repeating until successfully ACK-ed. I've actually already experimented with some bare-bones code to handle writes but with a dummy delay of about 4 ms instead of the flash writing and it's fine with that.I think, yes, I can buffer a whole 64-byte page, and when either the last byte of a page is received or a byte in a different page is received, I'll erase and then write the entire page.

author: AVI-crak
author_rank: Regular Contributor
timestamp: October 09, 2023, 06:38:40 pm
content: Planned aging.I have a Chinese lab power supply, every time the power button is pressed - it writes a new mode of operation to the MCU flash memory in EEPROM emulation mode. So the life of the lab power supply is directly related to the intensity of use.Don't repeat the Chinese mistakes, use FRAM. Otherwise you will suffer from night hiccups.

author: ataradov
author_rank: Super Contributor
timestamp: October 09, 2023, 06:49:31 pm
content: Flash has more write/erase cycles than a power button you press. There is no chance you will power cycle the power supply 100K times (or even 10K times, which is usually the lowest endurance number).Flash wear is not a real concern, mechanical things will break faster.And if there is a chance that you will exceed the cycle limit, wear leveling will get you significant life extension. Using external ICs is a waste of time and money.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 09, 2023, 07:03:18 pm
content: This is not that kind of scenario. The EEPROM serves as a kind of configuration ROM for the master. The master doesn't normally do any writing to it. The only situation in which it does so is during what could be described as a firmware update, which will be very infrequent, if it ever occurs at all.

author: Sacodepatatas
author_rank: Regular Contributor
timestamp: October 10, 2023, 12:15:43 am
content: Quote from: AVI-crak on October 09, 2023, 06:38:40 pmYou can cook the MCU that has the worn out flash in an oven at 200 degrees celsius for about one or two hours, for the flash to self-heal. Or just store that PSU for 10 years for the same thing.

author: ataradov
author_rank: Super Contributor
timestamp: October 10, 2023, 12:20:31 am
content: Quote from: Sacodepatatas on October 10, 2023, 12:15:43 amThis is not 100% correct. Baking may eliminate some issues, but it will not do anything to the damage done by write/erase cycles. Also, two hours is not even close to being enough. It is usually a week-long process to get real results.

author: Sacodepatatas
author_rank: Regular Contributor
timestamp: October 10, 2023, 12:41:15 am
content: Quote from: ataradov on October 10, 2023, 12:20:31 amThe paper i read some time back regarding self-healing flash concept stated that flash chips can be recovered from program/erase wear out cycles and they chose 200º C for accelerated recovering (TBH, they just did simulations, not real experiments). Sincerely as long as i can remember i haven't had any worn out memory or mcu so i can't try fixing any flash in this manner.https://www.usenix.org/legacy/events/hotstorage11/tech/final_files/Wu.pdf

author: ataradov
author_rank: Super Contributor
timestamp: October 10, 2023, 12:51:26 am
content: There may be a better effect on the high density SSDs, which are already pushed to the edge of barely working as it is. MCU flash is very relaxed in that respect. Quote from the paper:QuoteSo, they can potentially recover the least likely and least impactful defects. Those may still affect the SSD flash, but they are not that important to the MCU flash.In MCUs baking may help eliminate trapped stray charges, which accumulate over time even if there are no flash writes. But even if that helps, it just means the device has latent defect. Normal devices should not need this.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 10, 2023, 09:23:57 am
content: Quote from: ataradov on October 09, 2023, 04:41:15 pmI've just had a thought about this. I'm wondering whether it's possible that the first byte doesn't need to be stored at all. Am I correct in thinking that because flash memory bits can only be written from a '1' to a '0', would it not be possible to just write the first byte of a half-word as 0xNNFF (where 'NN' is the new value), then the second byte of the same half-word as 0xFFNN, and the 0xFF will not cause the existing value of the first byte to be changed?

author: cv007
author_rank: Frequent Contributor
timestamp: October 10, 2023, 10:27:57 am
content: Typically you can, and most likely you can in this case. The exceptions may be where the mcu generates error correction info for the flash, where that info would get corrupt by doing this kind of thing.QuoteA half word is 0xHHLL, where the low byte is LL and is located before HH, so I think you have it backwards-rx data 0x12, addr 0x00 -> write 0x00 as u16 0xFF12rx data 0x34, addr 0x01 -> write 0x00 as u16 0x34FFrx data 0x56, addr 0x02 -> write 0x02 as u16 0xFF56rx data 0x78, addr 0x03 -> write 0x02 as u16 0x78FFsomething like-flash_write( addr & ~1, (addr & 1) ? ((u16)byte<<8 )+0xFF : 0xFF+byte );

author: Sacodepatatas
author_rank: Regular Contributor
timestamp: October 10, 2023, 10:56:32 am
content: Quote from: cv007 on October 10, 2023, 10:27:57 amThat drove me crazy when using the last 2KB page of the (undocumented) flash in a STM32G030 for emulating an EEPROM, which has a granularity o 64bit(data)+8bit(crc). I just needed to store a few bytes for each record so what i did is erasing once, then write 256 times before next erase. The problem arised when I statically stored some test data to that last page. I thought that writing an array of 0xFF values at the end of the flash wouldn't be an issue because those bytes were already 0xFF after all due to a previous erase. Then my code was failing when attempted to store new data to that page (but it worked if such page was erased before the first record was stored). Then i checked the datasheet and realized that the erasing algorythm sets the (hidden) CRC as 0xFF but storing an all 0xFF double word was setting such CRC with any value other than 0xFF and hence the flash controller fails when attempt to modify the content. As a curiosity, an all 0 write on a previously written location will work because the generated CRC is also 0 (that is, any 1 bit of the CRC is reset to 0). I wonder what is the CRC polinomial used for the error correction algorythm in that part.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 10, 2023, 11:02:28 am
content: Quote from: cv007 on October 10, 2023, 10:27:57 amI'm not sure whether the CH32V003's flash has ECC, I'll have to check the docs. If it does, this technique won't work?Quote from: cv007 on October 10, 2023, 10:27:57 amSorry, I was talking in terms of physical order of the bytes in the flash, not little-endianness.But yes, I know what you mean - for a LE system I'd need to mask the first byte with C integer value of 0xNNFF.

author: cv007
author_rank: Frequent Contributor
timestamp: October 10, 2023, 11:16:31 am
content: QuoteMost likely not, but I somehow doubt this mcu has that kind of thing. Even if it does, you then just hang on to the first byte and write when you get the second. Probably do that anyway as it will cut the programming time roughly in half (half the time for something to go wrong).What happens is those error correction bits need programming, too. When you do a 'double write', you will also end up with different error correction data for each write, and the second write has a high probability that the error correction bits will need at least 1 bit to go from a 0 to a 1.

author: DavidAlfa
author_rank: Super Contributor
timestamp: October 10, 2023, 11:27:11 am
content: Last time I tried this I got an exception, the Flash peripheral checked the word to be erased before writing.Check the manual...or try!

author: tszaboo
author_rank: Super Contributor
timestamp: October 10, 2023, 11:36:29 am
content: STM32 MCUs for Arduino have EEPROM emulation libraries. STMDuino. It takes care of the Erase and write operations, and provides byte-wide write. It's reasonably well tested. You could port that library for example.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 10, 2023, 01:17:14 pm
content: After searching the docs (datasheet and reference manual), I can't find any mention of the flash having error checking or CRC. So I have to assume it doesn't, because they would probably mention it as a feature if it did.

author: DavidAlfa
author_rank: Super Contributor
timestamp: October 10, 2023, 01:30:27 pm
content: Stop wondering and get into it!Can I write to a non-erased word? Make a small program and try it!I would understand if it was a huge task to find this out. But it will likely take less than 20 lines of code.- Erase a page.- Write 0xFFAA to 1st page byte -> Should work as expected.- Write 0x55AA to 1st page byte -> What will happen?But if you say the write is sequential the buffer will do it fine with the 64byte erase/write fast programming.

author: eutectique
author_rank: Frequent Contributor
timestamp: October 10, 2023, 02:10:03 pm
content: Quote from: DavidAlfa on October 10, 2023, 01:30:27 pmCareful here. For example, NXP explicitly prohibits doing this for their S32K14x devices:QuoteRTFM before you proceed.

author: peter-h
author_rank: Super Contributor
timestamp: October 10, 2023, 02:11:16 pm
content: Also don't forget to do a pre-read and proceed with any writing only if the data (any of the 64 bytes in your case) has actually changed.You may well find that most of the time the data does not change, and the writing will be dramatically faster.In the project I am doing now I am getting a 100x flash write speedup on firmware updates; a 500k+ file in which just changing some variable initialisation values doesn't change the other bytes.A pre-read also helps protect against a runaway program loop. Often the same data is written over and over...

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 10, 2023, 11:35:20 pm
content: Yep, good firmware updaters (for instance) check which parts require writing and which do not.As flash memory is usually written by pages of some kind, the usual is to: read the current page, check if it's different from the new data for this page, if so write the page with new data, go to the next page, rinse and repeat. You can use other similar schemes as appropriate.OTOH, rarely written areas of the flash memory may run a higher risk of having data retention issues over a long period of time? So it might be a good idea to re-write the same pages even when not changed, every N updates, or something like this.

author: rhodges
author_rank: Frequent Contributor
timestamp: October 11, 2023, 04:11:43 pm
content: You have 16k of flash, maybe you can set aside 8k for your emulated EEPROM and use a half-word for each byte?By the way, I enjoy reading these posts on the CH32V003, as I am also working with it. And last night I started on my flash library, so this is timely.Would anyone else be interested in a thread (or threads) to use as a journal or blog working with this chip? I am duplicating my STM8 ecosystem to CH32V003, and it would be nice to have a place to chat. For example, I just wrote a small library for the WS2812B LED strings using SPI and DMA. Before that, I wrote a 32-bit, 8 digit binary and decimal library with inline assembly. And others.