Teensy LC Increase EEPROM Size

Status
Not open for further replies.
Have you tried the edit of E2END in eeprom.h, as suggested in msg #20?

For a little more detail, when you find that file, here is the code you'll see:

Code:
#if defined(__MK20DX128__) || defined(__MK20DX256__)
  #define E2END 0x7FF
#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)
  #define E2END 0xFFF
#elif defined(__MKL26Z64__)
  #define E2END 0x7F
#else
  #define E2END 0
#endif

For Teensy LC, the default is 0x7F, meaning you get 128 bytes. The maximum you can change this into (and still expect it to actually work) is 0xFE, for 254 bytes.

All you have to do is find this file and edit it to change that number. Arduino will automatically notice the change and start using it for the next upload. Easy stuff.

Well, except perhaps finding this file. If you have a Macintosh, the trick is to control-click Arduino and "show package contexts". Then you can access inside Arduino. Look in Contents/Java/hardware/teensy/avr/cores/teensy3/avr for this file. If using Windows, the default install location is C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\avr. Don't let all those "avr" names fool you... they're only named "avr" for compatibility with the huge amount of software designed for Arduino that assumes all boards are AVR chips. If using Linux, hopefully you know where you extracted Arduino! Inside that folder, look for hardware/teensy/avr/cores/teensy3/avr.

You'll also see suggestions on this thread to edit EEPROM_SIZE in eeprom.c. That is unnecessary with the modern code. Long ago, the size was indeed controlled by EEPROM_SIZE. It still is internally, but the modern code automatically sets EEPROM_SIZE, based on what you configure with E2END. Here's what you'll find in eeprom.c.

Code:
#elif defined(KINETISL)

#define EEPROM_SIZE (E2END+1)

If you do look at eeprom.c, please keep in mind this file has 2 sections. EEPROM is done very differently in Teensy LC than it is on the Teensy 3.x boards. Generally I would not recommend editing eeprom.c unless you have fairly advanced programming skills. Especially do not mess with the code having the comment "// with great power comes great responsibility...." ;)

Editing eeprom.h to merely increase E2END is pretty simple. Just don't go over the maximum of 0xFE for Teensy LC. That's the most which can be supported using the existing EEPROM emulation code. Doing any more would require completely redesigning that code.
 
Maybe understanding why the max limit is 254, and why the default is lower might help? I don't believe I've ever written about this... so here goes. But the clear message for Teensy LC is this: 254 is the max. Don't define E2END higher than 0xFE.

Teensy LC lacks hardware support for EEPROM emulation, as the Teensy 3.x boards have with "FlexRAM" and "FlexNVM" (who at NXP/Freescale makes up these names anyway?) So the EEPROM emulation is done using a chunk of the flash memory and some crafty code. Again, I want to emphasize a warning... this code is tricky stuff. Even with this explanation, I do not recommend editing it unless you have pretty advanced device-driver level programming & hardware troubleshooting skills.

On Teensy LC, the flash memory can be "programmed" in small 4 byte sizes. It can be "erased" in much larger 1024 byte blocks. Erasing a block causes the all the bits in those 1024 bytes to be changed back to 1s. The programming operation can only change 1s into 0s. Once a bit is changed to 0, the only way to get it back to 1 to the erase it and all the others in that 1024 byte block. The other important property of the hardware is you can program the same 4 bytes more than once, but only bits that are still 1s can be changed into 0s.

While all flash memory works pretty much this way, different chips have differing hardware limitations and special gotchas. For example, the SAMD chips used on Arduino Zero, MKR and many of Adafruit's boards have other special limitations. This code could probably appear to work on those chips, but would likely have long-term problems. If anyone finds this message by search and want to apply it to any other chip, I want to emphasize the delicate nature and careful optimization needed to truly make this work on any chip. It's not casual beginner-level stuff.

To emulate EEPROM, the code writes 2 bytes into the flash for each byte you write to the emulated EEPROM. The first byte is the emulated address and the second byte is your actual data. As you write a byte over and over, many duplicate copies can accumulate in the flash. When you read, the code searches for the *last* copy, which is the most recent you wrote. The search needs to know when it's reached the end. Likewise, which writing the emulation code needs to know which portion of the flash hasn't yet been written. This is done with address=255, the erased all-1s state of the flash.

Eventually the portion of the flash dedicated to EEPROM emulation will fill up. To make more space, a large block of 1024 bytes needs to be erased. The emulation code does this by temporarily using an array in RAM which is large enough to hold all the emulated EEPROM bytes. The size of this array is the main reason why the default emulated size is kept fairly small. 128 bytes is a trade-off, hopefully enough for many users, but (hopefully) not too much to allocate an array on the stack in a chip which has only a total of 8K RAM. The erase code fills this array with 255 first, then scans through the used flash to find all the EEPROM bytes you've stored. One subtle point is you might actually store the number 255. If so, the result still works out properly in the end. After erasing, all the non-255 bytes in the RAM buffer are written back to the freshly erased memory. Since the block is fairly large, even if you've increased the E2END limit, all 254 can still fit into only half of that block when writing both their address and data. If the data happens to be 255, nothing is written at all. Later, the read code will fail to find anything, and returns 255 as the default.

So conceptually, the emulation scheme is pretty simple. Not necessarily fast though (NXP/Freescale's "FlexNVM" usually a totally different approach which is optimized for performance). Reading the emulated memory requires a slow linear search to find the most recently written copy. Writing is fairly quick (in the dozens of microseconds range), but because it's using the same physical flash as your program, interrupts are disabled and a busy loop running in RAM does the waiting. The thing that's *really* slow is the block erase when the flash has filled up with too many duplicate copies from all your writing activity. The erase disables interrupts too, and takes much longer... in the milliseconds range.

Despite all this complexity and fairly simple-but-slow linear search, the final result is pretty good EEPROM compatibility for most Arduino programs. As you perform many writes, the activity is wear leveled over the range of flash used.

While theoretically someone could craft similar code to use more than 8 bits for the address field, doing so would become pretty complicated. More emulated flash also means the buffer in RAM to hold data during an erase operation needs to be bigger. Or perhaps a strategy where the emulated EEPROM is segmented into different flash regions could reduce the need for a larger RAM buffer, but that would also divide the wear leveling effect. No matter how the data is managed, the physical properties and specs of the flash controller need to be carefully considered.

Hopefully this makes the 254 byte limit clear and gives you a better understanding of what's really happening when you use the emulated EEPROM on Teensy LC. Feel free to edit eeprom.h and change E2END anywhere between 0x01 to 0xFE. Smaller numbers have the advantage of using less RAM during the erase step.
 
On Teensy LC, the flash wear leveling depends only on the total number of byte size writes you perform. Each 1 byte write uses 2 bytes of the 2K flash reserved for the EEPROM emulation. Hopefully it's clear from this explanation that the addresses you choose within the emulated spare don't matter. Neither does the amount of emulated memory. Only the total number of writes matter.

For every 1024 writes, you will fully cycle the flash through 1 program/erase operation. So if we expect the flash to endure at least 10,000 write cycles, then this emulation gives you an endurance of ~10 million total 1-byte writes. While that seems like a big number, it's not as impressive if your write activity is large. For example, if you write all 128 emulated bytes, you get 80,000 of those full-eeprom writes before reaching 10,000 cycles on the flash. If you double the E2END size and your write activity is to write all 254 bytes, then you get only about 40,000 total endurance. But if your write activity follows the more typical usage of writing just a few bytes at a time, then the wear leveling works extremely well.

On Teensy 3.x, the wear leveling approach used by NXP/Freescale's FlexNVM is very different. It is optimized for excellent performance, avoiding a slow linear search (and much of the work accelerated by hardware). With FlexNVM, the total emulated size does matter. This stuff is well documented in their reference manual and data sheet. One of the reasons you might wish to edit eeprom.h on Teensy 3.x is the *reduce* the emulated size, which greatly increases endurance.

FlexNVM also manages data internally as 16 bit numbers, so you gain the full endurance only if you write 16 or 32 bits at a time. Writing single bytes on Teensy 3.x still works fine, but wears the flash out twice as fast.

Unlike Teensy LC where we use the same flash as your program, FlexNVM on Teensy 3.x uses a dedicated block of flash which doesn't cause your program to stall while it's writing or erasing. You do end up having to wait if you want to write multiple bytes or words, but while you wait your program keeps running and interrupts can be serviced.

Just to get all the EEPROM details into this thread, there's also a special consideration on Teensy 3.6 if running faster than 120 MHz. The FlexNVM doesn't work at those speeds. Teensy 3.6 needs to momentarily drop its CPU speed in half to write to EEPROM. Usually this is a non-issue, but it can cause little audio glitches on the audio library or data loss on Serial1 & Serial2 (the other 4 ports and most peripherals run from a different/slower clock). Still, if using Teensy 3.6 at its full speed, best to come up with a special plan for EEPROM usage so it can be done at a time when the CPU speed change isn't disruptive.
 
On Teensy LC, the flash wear leveling depends only on the total number of byte size writes you perform. Each 1 byte write uses 2 bytes of the 2K flash reserved for the EEPROM emulation. Hopefully it's clear from this explanation that the addresses you choose within the emulated spare don't matter. Neither does the amount of emulated memory. Only the total number of writes matter.

For every 1024 writes, you will fully cycle the flash through 1 program/erase operation. So if we expect the flash to endure at least 10,000 write cycles, then this emulation gives you an endurance of ~10 million total 1-byte writes. While that seems like a big number, it's not as impressive if your write activity is large. For example, if you write all 128 emulated bytes, you get 80,000 of those full-eeprom writes before reaching 10,000 cycles on the flash. If you double the E2END size and your write activity is to write all 254 bytes, then you get only about 40,000 total endurance. But if your write activity follows the more typical usage of writing just a few bytes at a time, then the wear leveling works extremely well.

On Teensy 3.x, the wear leveling approach used by NXP/Freescale's FlexNVM is very different. It is optimized for excellent performance, avoiding a slow linear search (and much of the work accelerated by hardware). With FlexNVM, the total emulated size does matter. This stuff is well documented in their reference manual and data sheet. One of the reasons you might wish to edit eeprom.h on Teensy 3.x is the *reduce* the emulated size, which greatly increases endurance.

FlexNVM also manages data internally as 16 bit numbers, so you gain the full endurance only if you write 16 or 32 bits at a time. Writing single bytes on Teensy 3.x still works fine, but wears the flash out twice as fast.

Unlike Teensy LC where we use the same flash as your program, FlexNVM on Teensy 3.x uses a dedicated block of flash which doesn't cause your program to stall while it's writing or erasing. You do end up having to wait if you want to write multiple bytes or words, but while you wait your program keeps running and interrupts can be serviced.

Just to get all the EEPROM details into this thread, there's also a special consideration on Teensy 3.6 if running faster than 120 MHz. The FlexNVM doesn't work at those speeds. Teensy 3.6 needs to momentarily drop its CPU speed in half to write to EEPROM. Usually this is a non-issue, but it can cause little audio glitches on the audio library or data loss on Serial1 & Serial2 (the other 4 ports and most peripherals run from a different/slower clock). Still, if using Teensy 3.6 at its full speed, best to come up with a special plan for EEPROM usage so it can be done at a time when the CPU speed change isn't disruptive.

Paul, you are an extremely intelligent and dedicated individual. Thanks for the detailed answer!
 
Just out of curiosity, does the EEPROM emulation code preclude one from using the Teensy LC with an external EEPROM chip & using i2c with wire?
 
Sure, you can use external EEPROM chips with the Wire library.

Remember the SDA & SCL signals need real pullup resistors when using Teensy LC.
 
Status
Not open for further replies.
Back
Top