Teensy 4.1 EEPROM Endurance

nwplouff

Member
After spending a long time puzzling over the wear leveling code for emulated EEPROM on the Teensy 4.1, I think I finally understand how it works, but it makes me question whether the PJRC specification of 100,000 write cycles per emulated EE address is far too low.

Per eeprom.c code, 4284 EE addresses (E2END + 1) are assigned evenly across 63 flash memory sectors (FLASH_SECTORS). Each sector is 2048 words, where each word is one index byte (derived from the EE address) and one data byte. Each successive write to an EE address, if its value has changed, stores its index and the new data value to the next unused word in the flash sector. So, there can be many copies of EE data written to the same EE address, but only the value at the highest physical address in the sector is valid. When the sector is full, the software erases the physical flash sector and writes all the valid EE index/data combinations previously stored in that sector, starting at the beginning of the sector. Is this right so far?

4284 EE addresses divided by 63 sectors gives 68 EE addresses per sector. Assuming for a moment that writes are distributed evenly, then a sector will be full after 2048/68 writes per EE address, or 30 plus a fraction on average. So, the endurance of emulated EEPROM should be roughly 30 times the endurance of the underlying flash memory.

The endurance of the Winbond W25Q64JV serial flash IC is not given in the chip specification. However, an Infineon application note says the Winbond chip, like all its close competitiors, has a minimum endurance of 100,000 program/erase cycles. Does this mean the actual endurance of emulated EE is around 3 million cycles? Or am I missing some important point?
 
Seems about right. Paul has made prior posts and during the T_4.0 Beta one tester dug into the code and wrote a test to decode and verify tracking with a sketch to verify function to reliably act as designed.

The 100K cycle expectation comes from individual expected writes on the underlying hardware. And as noted the underlying code indeed further distributes and cycles the writes into one to many mapping groups where the presented available bytes is a small part of the actual allocated area over which the actual storage operates. With prior T_3.x family having on chip IIRC such mapping was built in.
 
Last edited:
Winbond does claim "Min 100K Program-Erase cycles per sector" in their datasheet for the flash memory chip.

winbond.png

The effect of wear leveling depends on the specific write usage. Your analysis sounds about right for the case of repeatedly fully writing the entire emulated address space. Other usage patterns where less of the emulated address space is written might get more wear leveling benefit.
 
Wondering: Expected that write/commits done before any write call returns?

Doing a PUT of 48 Byte structure - might cross two sectors:
> Assume it is boundary checked and both committed before return?

With a for loop of 12 changed 4 byte values using write - to the same addresses as above:
> Assume each write is committed before return?

So using structured PUT would be better for group data updates?
 
Winbond does claim "Min 100K Program-Erase cycles per sector" in their datasheet for the flash memory chip.

Paul Stoffregen, thanks, I sit corrected. That's the problem with going directly to the electrical specs section.

The effect of wear leveling depends on the specific write usage. Your analysis sounds about right for the case of repeatedly fully writing the entire emulated address space. Other usage patterns where less of the emulated address space is written might get more wear leveling benefit.

Okay, here's some further speculation. Each flash memory sector has a lifetime pool of write cycles P, so

P = 10^5 writes/word * 2048 words/sector ≈ 2.05 x 10^8 or 205 million writes/sector​

So for the simplest case, where n EE addresses are in use (n ≤ 68) and each is written to the same number of times, the endurance E of each virtual EE address is

E = P/n​

For my example of all 68 addresses being used, E = 2.05 x 10^8 / 68 = 3.01 x 10 ^6, or just a hair over 3 million writes per EE address. And if n is smaller than 68, E gets larger.

What about the case where some EE locations are written a lot more frequently than others? Let's say there are m "frequent write" and n "seldom write" EE addresses. Comparing two locations, let's call the ratio of frequent to seldom writes the frequency f. Then

Eseldom = P / ((f * m) + n)
Efrequent = f * Eseldom

For example, in one sector, one EE byte gets written every minute, and 67 EE bytes get written once per hour, so m = 1, f = 60 (minutes/hr), and n = 67. Doing the math, the endurance of the "seldom" bytes is 1.61 million writes each. If the number of frequent locations increases to 8, and the "seldom" bytes are reduced to 60, then the "seldom" endurance goes down to 380,000 writes per EE byte, and the "frequent" endurance is 22.8 million writes. At this rate, the flash memory sector would wear out in 43 years.

What frequency does it take for "seldom" EE bytes to have an endurance of only 100,000? For 1 "frequent" byte, f = 1983. For 8 "frequent" bytes, f = 249. If the example changed to 8 "frequent" bytes written every 15 seconds and 60 "seldom" bytes written every hour, then the flash sector would wear out in only 11 years.

None of this takes into account the fact that after 2047 writes to the same flash sector, all the valid data gets rewritten, i.e. up to 68 words in flash, following a flash sector erase. I think this would reduce the size of the write cycle pool by up to 3.3%, depending on how many of the 68 EEPROM addresses assigned to the flash sector are occupied.

This is a simplistic analysis, but to me it says that every EE address in a physical sector "drains" the pool of lifetime write cycles at a different rate. High-frequency writes, especially if there are several in EE addresses assigned to the same flash sector, can take the lion's share of the lifetime pool (higher endurance) at the expense of the low-frequency writes (reduced endurance). It also says to me, because the EEPROM emulation stores "chunks" of four EE addresses in the same flash sector, i.e. EE 0-3 in sector 0, EE 4-7 in sector 1, etc., that the most frequently written 252 bytes should be assigned to sequential EEPROM addresses, to spread them more evenly across the 63 flash sectors.

I think with some planning, the lifetime of Teensy 4.1 emulated EEPROM can be much higher than 100,000 writes per byte. For real applications, though, the calculations get more complicated.
 
So PUT with all desired data at once minimizes the completed writes, versus a series of writes.

@defragster, no. The methods attached to the EEPROM structure, for example when EEPROM.put() is called, in turn make calls to eeprom_write_block() and eeprom_write_byte() to do the writes to physical memory.

In the Teensy 4.1 board package, the key sequence in eeprom.c is:

Code:
void eeprom_write_block(const void *buf, void *addr, uint32_t len)
{
	uint8_t *p = (uint8_t *)addr;
	const uint8_t *src = (const uint8_t *)buf;
	while (len--) {
		eeprom_write_byte(p++, *src++);
	}
}

So, in the current implementation of emulated EEPROM, all writes are done one data byte at a time. (Each data byte is stored as a 16-bit word in flash memory.)

The source code for the EEPROM class can be found at https://github.com/PaulStoffregen/EEPROM and for eeprom.c at https://github.com/PaulStoffregen/cores/tree/master/teensy4.
 
Back
Top