Increase write endurance on Teensy 4.0

Status
Not open for further replies.

Codepoet

Member
Hello,

I just need to store 32 bit to keep track of a counter. This counter is written relatively often to the eeprom, therefore I would like to increase the write endurance on the T4.
I checked several threads in the forum like https://forum.pjrc.com/threads/57377?p=214566&viewfull=1#post214566 and https://forum.pjrc.com/threads/52433-Teensy-EEPROM-location-is-it-static-regardless-of-program-data but those threads mainly deal with T3.x.

I checked eeprom.c/eeprom.h and the only possible values to tweak are E2END,FLASH_BASEADDR and FLASH_SECTORS. No EEPARTITION or EEESPLIT or similar as stated in the above mentioned threads.
So I'm a bit lost here. How can I increase the write endurance/wear leveling on the T4 when I just need to store 4 bytes?

Thanks in advance
Michael
 
Sorry, I wrote an initial answer, then re-read your post & realized that you are storing a counter. The approach that I initially wrote would work for 4 independent byte values, but would not actually work for your counter. So, look at my next answer instead, which is a much cleaner (as well as more applicable) potential solution to your request !!

Mark J Culross
KD5RXT
 
Last edited:
Hello,

I just need to store 32 bit to keep track of a counter. This counter is written relatively often to the eeprom, therefore I would like to increase the write endurance on the T4.
I checked several threads in the forum like https://forum.pjrc.com/threads/57377?p=214566&viewfull=1#post214566 and https://forum.pjrc.com/threads/52433-Teensy-EEPROM-location-is-it-static-regardless-of-program-data but those threads mainly deal with T3.x.

I checked eeprom.c/eeprom.h and the only possible values to tweak are E2END,FLASH_BASEADDR and FLASH_SECTORS. No EEPARTITION or EEESPLIT or similar as stated in the above mentioned threads.
So I'm a bit lost here. How can I increase the write endurance/wear leveling on the T4 when I just need to store 4 bytes?

Thanks in advance
Michael

Michael:

Here's (a modified version of) an approach that I've used in the past on an Arduino for the exact same (wear leveling) purpose:

1) perform a one-time prep on your EEPROM by writing 0xff to the first four locations of your EEPROM, (optionally) followed by the default/starting value of your counter
2) each time you want to store a new counter, start reading from the very first location in EEPROM until you find four 0xff values in a row (call the location of the first 0xff ALOC for "anchor location")
3) write 0x00 to ALOC
4) write 0xff to ALOC+1 thru ALOC+4, taking care to "wrap" the calculation of where to write these flag values to account for the actual size of the EEPROM
5) write your four byte values to ALOC+5 thru ALOC+8, taking care to "wrap" the calculation of where to write each byte to account for the actual size of the EEPROM

Using this approach, each time you store a new counter, the starting location of the storage of your four bytes will creep its way thru the entire EEPROM. Take note that your counter value must be restricted from ever having a value of 0xffffffff, else that will be mistaken for the flag.

Hope that helps. Good luck & have fun !!

Mark J Culross
KD5RXT
 
Does the T_4.0 have an RTC battery installed? There are a couple of RTC storage bytes that maintain value across shutdown and reset as long as power it maintained.

Those would allow continuous updates and static storage in that case without wear concern. The EEPROM could be used in a fashion like p#3 to spread ongoing writes across all of EEPROM before starting over at some slower rate to minimize the flash erase.

Not sure if ALL of EEPROM should be cleared to 0xff before each pass starts as 0xff is the formatted state. When writing if zero bits can't be set to store the value that forces a 'block' write move. Otherwise as writes progress it could force a rewrite of that block to hold each new value and that may not result in the desired minimal rewrite.

Have not ever looked at the underlying scheme - but it was tested with a sketch by a user in the T_4.0 Beta thread - using the PJRC provided code. Finding that code combined with the PJRC code might offer better insight. Getting blocks formatted before starting would assure only ongoing bits are written
 
You could add a FRAM SPI serial ram nonvolatile storage chip, claiming 100 trillion read/write cycle endurance
 
Teensy 4.0 already does wear leveling. But it also maps certain emulated EEPROM addresses to particular 4K flash sectors, which greatly improves performance for 32 bit writes. You could maximize the wear leveling effect and also keep the performance improvement with a strategy based on the details of how the EEPROM emulation code does this mapping.

The short answer is I'd recommend storing 15 copies of your counter. Put the first copy at address 0, the 2nd copy at address 4, 3rd at 8, and so on. At startup, read all 15 and whichever is largest must be the most recently written. Then as you write new values, always write to the next in the list, of course wrapping back to 0 when you get to the end of the list. This will take advantage of how the EEPROM emulation manages sectors to give you the best possible endurance.

To explain a bit further, internally 15 sectors of the flash memory are used. Each sector maps to 72 bytes of the emulated EEPROM. Sector 0 is used for addresses 0 to 3, then 72 to 75, and 144 to 147, and so on. Sector 1 is used for addresses 4 to 7, 76 to 79, etc. As you write, both the actual data and address offset info is written to the underlying flash sector. So there's no point using EEPROM address past 71 if you only need to store a 4 byte counter. When you write to bytes 0 to 3, it will cause 8 bytes to written into the first sector. Then writing to 4-7 will write 8 bytes into the 2nd sector. This scheme was chosen so that 8, 16 and 32 bit writes can be done with only a single quick write to underlying flash. But use of more EEPROM will tend to spread across sectors, where 60 bytes is the ideal size which perfectly maps to equally wearing across all 15 sectors.

As far as endurance improvement is concerned, each 32 bit write you perform will write 8 of 4096 bytes in each sector. That uses 1/512th of its endurance. Winbond specifies the W25Q16 chip at 100K write endurance. So the wear leveling will give 51.2 million writes of your 32 bit counter until you reach Winbond's spec, assuming you use this approach with 15 copies of your counter.

If you will need to write more than 51.2 million times over the life of your board, you really should consider using the RTC's (NXP called it "SNVS") memory with a coin cell or large capacitor backup, or add one of those FRAM chips which gives incredible write endurance.

You could also try adding an early warning detection of power loss. Usually this involves having circuitry which can monitor the power supply input voltage, so you can recognize power loss before the power supply's capacitors discharge.... with enough time to allow you to reliably back up the counter to EEPROM.
 
Use Magnetoresistive RAM (MRAM).
MRAM has unlimited endurance and infinite Read/Write cycles; FRAM Reads are destructive and eventually lead to wear-out.

Everspin MRAM is available in a functional equivalent or drop-in replacement for most FRAM devices.
Immediate (<1ns) Power-off with no loss of data.
Unlimited read and write cycle endurance.
Truly Asynchronous SRAM compatible speeds and cycle times.
20-Year data retention with no cycling dependence.
No wear-out concerns.

https://www.digikey.com/product-detail/en/everspin-technologies-inc/MR25H256MDC/819-1048-ND/4021226
 
Last edited:
Teensy 4.0 already does wear leveling. But it also maps certain emulated EEPROM addresses to particular 4K flash sectors, which greatly improves performance for 32 bit writes. You could maximize the wear leveling effect and also keep the performance improvement with a strategy based on the details of how the EEPROM emulation code does this mapping.

The short answer is I'd recommend storing 15 copies of your counter. Put the first copy at address 0, the 2nd copy at address 4, 3rd at 8, and so on. At startup, read all 15 and whichever is largest must be the most recently written. Then as you write new values, always write to the next in the list, of course wrapping back to 0 when you get to the end of the list. This will take advantage of how the EEPROM emulation manages sectors to give you the best possible endurance.

To explain a bit further, internally 15 sectors of the flash memory are used. Each sector maps to 72 bytes of the emulated EEPROM. Sector 0 is used for addresses 0 to 3, then 72 to 75, and 144 to 147, and so on. Sector 1 is used for addresses 4 to 7, 76 to 79, etc. As you write, both the actual data and address offset info is written to the underlying flash sector. So there's no point using EEPROM address past 71 if you only need to store a 4 byte counter. When you write to bytes 0 to 3, it will cause 8 bytes to written into the first sector. Then writing to 4-7 will write 8 bytes into the 2nd sector. This scheme was chosen so that 8, 16 and 32 bit writes can be done with only a single quick write to underlying flash. But use of more EEPROM will tend to spread across sectors, where 60 bytes is the ideal size which perfectly maps to equally wearing across all 15 sectors.

As far as endurance improvement is concerned, each 32 bit write you perform will write 8 of 4096 bytes in each sector. That uses 1/512th of its endurance. Winbond specifies the W25Q16 chip at 100K write endurance. So the wear leveling will give 51.2 million writes of your 32 bit counter until you reach Winbond's spec, assuming you use this approach with 15 copies of your counter.

If you will need to write more than 51.2 million times over the life of your board, you really should consider using the RTC's (NXP called it "SNVS") memory with a coin cell or large capacitor backup, or add one of those FRAM chips which gives incredible write endurance.

You could also try adding an early warning detection of power loss. Usually this involves having circuitry which can monitor the power supply input voltage, so you can recognize power loss before the power supply's capacitors discharge.... with enough time to allow you to reliably back up the counter to EEPROM.

Thanks Paul, exactly what I was searching for. I already knew the Teensy does wear leveling but thought there is a possibility to somehow shrink the space in benefit of increased wear leveling. But this approach is as good. Only difference is that my counter can also decrease, so I cannot check for the highest counter.
You write that 15 sectors are mapped to the addresses 4 byte wise. What about the addresses 60 to 71? What are they mapped to?

If I find it still not enough write cycles I will consider using FRAM/MRAM. But my space is very limited so I think I have to stick with the EEProm/Flash approach.

Thanks
Michael
 
In that case write both your counter and another counter (or timestamp) that always increases, so that you can
tell which is the latest position written.
 
You write that 15 sectors are mapped to the addresses 4 byte wise. What about the addresses 60 to 71? What are they mapped to?

Addresses 60 to 63 are mapped to the first flash sector, just like addresses 0 to 4 are. When you write a 32 bit number to either location, 8 bytes are actually written into the flash sector. 4 of those 8 bytes are your data. The other 4 are address info. So when you write to locations 60-63, those other 4 bytes are written differently than if had written to locations 0-3.

When you read from emulated EEPROM, the emulation code immediately narrows the search to just one 4K sector, and then it must read those other 4 bytes to find the most recent copy of your data within the sector.

That is why you should use exactly 15 copies of your counter in locations 0 to 59, and why there is no point to using any more beyond location 59. This isn't anything fundamental about how the flash or processor works... it's simply how I wrote the EEPROM emulation code. I considered other schemes which could have distributed wear across all 15 sectors (in a worst case only 1 sector gets all the wear while the other 14 go unsed), but they would have required more address data per byte. I also considered ways using less address info, and a variety of ideas to improve performance for various writing patterns. Ultimately I chose this approach which gives pretty good wear leveling for most of the common usage cases, and optimizes speed for the common usage of writing up to 32 bit numbers.
 
If I understood it correctly it can be visualized like follows (?)

Teensy4_WearLevelling.jpg

Thanks
Michael
 
Status
Not open for further replies.
Back
Top