Very few write cycles in stm32f4

6k views Asked by At

I'm using a STM32F401VCT6U "discovery" board, and I need to provide a way for the user to write addresses in memory at runtime.

I wrote what can be simplified to the following function:

uint8_t Write(uint32_t address, uint8_t* values, uint8_t count) 
{
    uint8_t index;
    for (index = 0; index < count; ++index) {
        if (IS_FLASH_ADDRESS(address+index)) {
            /* flash write */
            FLASH_Unlock();
            if (FLASH_ProgramByte(address+index, values[index]) != FLASH_COMPLETE) {
                  return FLASH_ERROR;
            }
            FLASH_Lock();
        } else {
            /* ram write */
            ((uint8_t*)address)[index] = values[index]
        }
    }
    return NO_ERROR;
}

In the above, address is the base address, values is a buffer of size at least count which contains the bytes to write to memory and count the number of bytes to write.

Now, my problem is the following: when the above function is called with a base address in flash and count=100, it works normally the first few times, writing the passed values buffer to flash. After those first few calls however, I cannot write just any value anymore: I can only reset bits in the values in flash, eg an attempt to write 0xFF to 0x7F will leave 0x7F in the flash, while writing 0xFE to 0x7F will leave 0x7E, and 0x00 to any value will be successful (but no other value will be writable to the address afterwards).

I can still write normally to other addresses in the flash by changing the base address, but again only a few times (two or three calls with count=100).

This behaviour suggests that the maximum write count of the flash has been reached, but I cannot imagine it can be so fast. I'd expect at the very least 10,000 writes before exhaustion. So what am I doing wrong?

3

There are 3 answers

0
dureuill On BEST ANSWER

I have a working and tested solution, but it is rather different from @Ricibob's answer, so I decided to make this an answer.

Since my user can write anywhere in select flash sector, my application cannot handle the responsability of erasing the sector when needed while buffering to RAM only the data that need to be preserved.

As a result, I transferred to my user the responsability of erasing the sector when a write to it doesn't work (this way, the user remains free to use another address in the sector to avoid too many write-erase cycles).

Solution

Basically, I expose a write(uint32_t startAddress, uint8_t count, uint8_t* values) function that has a WRITE_SUCCESSFUL return code and a CANNOT_WRITE_FLASH in case of failure. I also provide my user with a getSector(uint32_t address) function that returns the id, start address and end address of the sector corresponding to the address passed as a parameter. This way, the user knows what range of address is affected by the erase operation. Lastly, I expose an eraseSector(uint8_t sectorID) function that erase the flash sector whose id has been passed as a parameter.

Erase Policy

The policy for a failed write is different from @Ricibob's suggestion of "erase if the value in flash is different of FF", as it is documented in the Flash programming manual that a write will succeed as long as it is only bitreset (which matches the behavior I observed in the question):

Note: Successive write operations are possible without the need of an erase operation when changing bits from ‘1’ to ‘0’. Writing ‘1’ requires a Flash memory erase operation. If an erase and a program operation are requested simultaneously, the erase operation is performed first.

So I use the macro CAN_WRITE(a,b), where a is the original value in flash and b the desired value. The macro is defined as:

!(~a & b)

which works because:

  • the logical not (!) will transform 0 to true and everything else to false, so ~a & b must equal 0 for the macro to be true;
  • any bit at 1 in a is at 0 in ~a, so it will be 0 whatever its value in b is (you can transform a 1 in 1 or 0);
  • if a bit is 0 in a, then it is 1 in ~a, if b equals 1 then ~a & b != 0 and we cannot write, if bequals 0 it's OK (you can transform a 0 to 0 only, not to 1).

List of flash sector in STM32F4

Lastly and for future reference (as it is not that easy to find), the list of sectors of flash in STM32 can be found on page 7 of the Flash programming manual.

3
Ricibob On

You have missunderstood how flash works - it is not for example as straight forward as writing EEPROM. The behaviour you are discribing is normal for flash.
To repeatidly write the same address of flash the whole sector must be first erased using FLASH_EraseSector. Generally any data that needs to preserved during this erase needs to be either buffered in RAM or in another flash sector.
If you are repeatidly writing a small block of data and are worried about flash burnout do to many erase write cycles you would want to write an interface to the flash where each write you move your data along the flash sector to unwriten flash, keeping track of its current offset from the start of sector. Only then when you run out of bytes in the sector would you need to erase and start again at start of sector.

1
Daniel On

ST's "right way" is detailed in AN3969: EEPROM emulation in STM32F40x/STM32F41x microcontrollers

This is more or less the process:

  1. Reserve two Flash pages
  2. Write the latest data to the next available location along with its 'EEPROM address'
  3. When you run out of room on the first page, write all of the latest values to the second page and erase the first
  4. Begin writing values where you left off on page 2
  5. When you run out of room on page 2, repeat on page 1

This is insane, but I didn't come up with it.