cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L4xx (and maybe others) EEPROM emulation library bug with single-bank flash mode

DAlbe.3
Senior

When used on STM32L4xx platforms (and possibly others), with flash configured for single-bank mode, the EEPROM emulation library (https://www.st.com/en/embedded-software/x-cube-eeprom.html) miscalculates flash sectors which causes silent erase failures.

Background:
The EEPROM emulation library uses flash on the target to emulate EEPROM. When porting to a new target, the emulated EEPROM must be given a place in the flash memory map; this is done by setting START_PAGE_ADDRESS in eeprom_emul_conf.h. In my case, I am using a 512K flash processor (STM32L471) with the flash memory configured as a single contiguous bank so I set the emulated EEPROM start page address to to 0x0806E000 (flash page 220).

The Bug:
A subtle, silent failure first manifests when the emulated EEPROM is initialized in EE_Init(). This calls Porting/STM32L4/flash_interface.c: Page_Erase() which calls GetBankNumber(). That function should consider whether flash is configured in single or dual bank mode, but simply assumes dual-bank mode resulting in the wrong sector(s) being erased.

Core/eeprom_emul.h defines:

 

#define PAGE(__ADDRESS__) (uint32_t)((((__ADDRESS__) - FLASH_BASE) % BANK_SIZE) / FLASH_PAGE_SIZE)

 

  • BANK_SIZE is defined as FLASH_BANK_SIZE from HAL which is defined as (FLASH_SIZE >> 1) so it works out to 256K on my 512K processor
  • Note that this limits the calculated page numbers to the first half of flash.

The reverse macro tries to fix this by adding BANK_SIZE if the START_PAGE_ADDRESS is higher than BANK_SIZE:

 

#define PAGE_ADDRESS(__PAGE__) (uint32_t)(FLASH_BASE + (__PAGE__) * PAGE_SIZE + ((START_PAGE_ADDRESS - FLASH_BASE) / BANK_SIZE) * BANK_SIZE)

 

Although that works for most cases, it doesn't fix a subtle problem when Core/eeprom_emul.c calls PageErase(page, 1U) in Porting/STM32L4/flash_interface.c and passes in the page number to erase. PageErase() uses the reversing macro to calculate the address. This gets back to the correct address, but then GetBankNumber() is called to determine which bank that address is in and determines that it is in Bank 2. The actual flash erase operation then tries to erase sector 92 in bank 2 instead of sector 220 in bank 1. This works out to a page in the second 512K of flash...which doesn't exist on this processor (at least not officially) and as a result, the erase sort of "works", but not on the appropriate sector.

The Solution:
The real issue is that BANK_SIZE (which evaluates to FLASH_BANK_SIZE defined in HAL) needs to evaluate to FLASH_SIZE when in single-bank mode. I think the fix is to:

  • Modify the definition of BANK_SIZE in Porting/STM32L4/flash_interface.h to:

 

#define BANK_SIZE ((FLASH->OPTR & FLASH_OPTR_DUALBANK)?FLASH_BANK_SIZE:FLASH_SIZE)​
  • Modify function GetBankNumber() in Porting/STM32L4/flash_interface.c to then use BANK_SIZE instead of FLASH_BANK_SIZE

 

This fixed the issue for me.

1 REPLY 1
STOne-32
ST Employee

Dear @DAlbe.3 ,

Thank you very much for the valuable contribution and providing the issue and solution . We will look and take the actions for fix ! Much appreciated .

Ciao 

STOne-32