Teensy 4 "EEPROM" blocking for flash sector erase

blahfoo

Active member
The T4 and 4.1's EEPROM emulation using Flash ROM has to sometimes erase a sector. This takes 0.045 to 0.400 seconds to do and the code is completely blocking- interrupts are disabled during this time. (eeprom.c in \hardware\teensy\avr\cores\teensy4, code below.)

I don't see what would influence the Winbond serial flash chip towards the 0.4 second end of that spec, but I have to assume the worst unless I hear otherwise. (P.90 on the datasheet referenced in this thread: https://forum.pjrc.com/index.php?threads/teensy-4-1-eeprom-endurance.72280/).

Moreover, some scenarios where you write more than one byte of data will result in multiples of these, though unlikely, it appears possible for the CPU to disappear for most of 3-25 seconds (all 63 sectors touched). (Possibly triggering a watchdog timer restart BTW, as I am using it?)

In my application, blocking for more than one ms is fatal to the purpose of the product, so writing to this flash is usually not permissible. Yet I need NV storage (not on the SD card - that's being used and might not always be there). And I don't want to add more parts.

So my question is, for an application where there is only one user of the flash, and that can be written to wait while everything else continues, is there a reason I shouldn't write a modified version of eeprom.c to not block so long, or not at all? Such here: eepromemu_flash_erase_sector()

Thanks if you have a chance to comment on this,
-Phil

// erase a 4K sector - From eeprom.c
void eepromemu_flash_erase_sector(void *addr)
{
__disable_irq();
FLEXSPI_LUTKEY = FLEXSPI_LUTKEY_VALUE;
FLEXSPI_LUTCR = FLEXSPI_LUTCR_UNLOCK;
FLEXSPI_LUT60 = LUT0(CMD_SDR, PINS1, 0x06); // 06 = write enable
FLEXSPI_LUT61 = 0;
FLEXSPI_LUT62 = 0;
FLEXSPI_LUT63 = 0;
FLEXSPI_IPCR0 = 0;
FLEXSPI_IPCR1 = FLEXSPI_IPCR1_ISEQID(15);
FLEXSPI_IPCMD = FLEXSPI_IPCMD_TRG;
arm_dcache_delete((void *)((uint32_t)addr & 0xFFFFF000), 4096); // purge data from cache
while (!(FLEXSPI_INTR & FLEXSPI_INTR_IPCMDDONE)) ; // wait
FLEXSPI_INTR = FLEXSPI_INTR_IPCMDDONE;
FLEXSPI_LUT60 = LUT0(CMD_SDR, PINS1, 0x20) | LUT1(ADDR_SDR, PINS1, 24); // 20 = sector erase
FLEXSPI_IPCR0 = (uint32_t)addr & 0x00FFF000;
FLEXSPI_IPCR1 = FLEXSPI_IPCR1_ISEQID(15);
FLEXSPI_IPCMD = FLEXSPI_IPCMD_TRG;
while (!(FLEXSPI_INTR & FLEXSPI_INTR_IPCMDDONE)) ; // wait
FLEXSPI_INTR = FLEXSPI_INTR_IPCMDDONE;
flash_wait();
}

static void flash_wait()
{
FLEXSPI_LUT60 = LUT0(CMD_SDR, PINS1, 0x05) | LUT1(READ_SDR, PINS1, 1); // 05 = read status
FLEXSPI_LUT61 = 0;
uint8_t status;
do {
FLEXSPI_IPRXFCR = FLEXSPI_IPRXFCR_CLRIPRXF; // clear rx fifo
FLEXSPI_IPCR0 = 0;
FLEXSPI_IPCR1 = FLEXSPI_IPCR1_ISEQID(15) | FLEXSPI_IPCR1_IDATSZ(1);
FLEXSPI_IPCMD = FLEXSPI_IPCMD_TRG;
while (!(FLEXSPI_INTR & FLEXSPI_INTR_IPCMDDONE)) {;}
FLEXSPI_INTR = FLEXSPI_INTR_IPCMDDONE;
asm("":::"memory");
status = *(uint8_t *)&FLEXSPI_RFDR0;
} while (status & 1);
FLEXSPI_MCR0 |= FLEXSPI_MCR0_SWRESET; // purge stale data from FlexSPI's AHB FIFO
while (FLEXSPI_MCR0 & FLEXSPI_MCR0_SWRESET) ; // wait
__enable_irq();
}
 
I don't see what would influence the Winbond serial flash chip towards the 0.4 second end of that spec,

The maximum spec is probably conservative, covering a wide temperature range and possible variation on manufacturing of the chips.

But it's also well know that flash memory write and erase becomes slower with use, so you could probably expect to see closer to the maximum after 100,000 erase-write cycles.


is there a reason I shouldn't write a modified version of eeprom.c to not block so long

Yes, of course there's a reason. The code wouldn't have been written this way for no reason.

If any code tries to execute from flash memory and has a cache miss needing the FlexSPI controller to fetch from the actual chip, wrong data will be read and your program will crash.
 
Thanks Paul for the first answer. I've been testing and so far it's faster than the fast end of the spec, if I read the address scattering code right. I can get it to go away for a good chunk of a second for multiple sector erases, but nothing like the spec. But I shouldn't trust that for the reasons you mention. Interestingly, one can easily make it go away for long enough that the serial terminal connection goes away and reconnects a number of seconds later. (Windows end, probably, I'll drop the test routines here if anyone's interested).

On the second point, as I said, there would be only one user unless there's something going on I don't understand (quite possible). Correct me, as (mentioned) no other use specifically uses the EEPROM emulation, I am assuming the only teensyduino use of the flash is to load the code/initialized vars into RAM on startup (or restart)? Why otherwise would code run from flash?
 
On the second point, as I said, there would be only one user unless there's something going on I don't understand (quite possible). Correct me, as (mentioned) no other use specifically uses the EEPROM emulation, I am assuming the only teensyduino use of the flash is to load the code/initialized vars into RAM on startup (or restart)? Why otherwise would code run from flash?
Any user code can be marked with the FLASHMEM attribute so that it runs from FLASH. This is helpful in memory constrained systems where the total amount of runnable code does not fit into RAM1
 
Last edited:
We do indeed have functions defined with FLASHMEM in libraries. Over time more can be expected. It just makes good sense to allocate code that isn't performance critical into only flash memory.
 
Thanks, so if I make sure that all code I'm using fits in (and is running from) RAM1, then I am safe to write a private version of the EEPROM write code? I'm pretty much only running things that are performance critical on this one. Of course it would get all the testingz, but preferably only once. Teensyduino or whatnot isn't going to swap locations of things currently in RAM, into FLASHMEM in the future? Or perhaps I just go on a hunt for the FLASHMEM define and kill it?

Latest testing is that if I confine my stuff to one sector (68 bytes available, plenty) the read times are fine (<.2ms). The max write time is ~3mS however. This gadget has about 0.5ms to save some parts from short circuits.
-Phil
 
Back
Top