cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G0: Flash SR register have "sticky" error bits PGAERR (5) and PGSERR (7)

JLemi
Associate III

I always get errors when trying to erase a flash page on STM32G0. I traced through the HAL code, and the error is caused by the fact that the FLASH->SR register almost always has two error bits set, PGAERR and PGSERR.

So before spending more time on erasing the flash page, I first tried to simply clear the error bits from the FLASH->SR register.

In the attached file, you can see the execution log at the end.

When the function "erasePageAt" is called, we can clearly see that:

  • At the beginning of the execution, the FLASH->SR errors were 000000A0 (bits 5 and 7 set).
  • After clearing the FLASH->SR errors, they became 00000000 as expected.
  • BUT just few cycles after, at the very start of the "lock" function which is called next, we can see that the FLASH->SR errors are back to 000000A0...!

I built my test code to do retries when unlocking and locking the flash fail, and I wait on two flash busy conditions between steps. Please note that I wrote my own waiting function because the library function FLASH_WaitForLastOperation was *always* failing due to these two sticky error bits.

How can these two error bits suddenly appear when no operation is conducted on the flash in the meantime? It is as if they were spontanneously appearing.

Finally, what can cause the hard fault in the HAL_FLASH_Lock function? We can see that when the method was called, the flash was unlocked (bit 31 unset). Very strange.

Many thanks !

JL

2 REPLIES 2
JLemi
Associate III

I came up with the following code for the STM32G0 to load and save a structure named "EepromConfig" to the flash memory. I totally bypassed the HAL libraries. I post the code here in case it could help others. This code allows the emulation of eeprom with flash memory. Links to some other code that I used as a starting point are provided.

The code for watchdogPlatform_reset is (it prevents resetting the watchdog too early)):

void watchdogPlatform_reset() {

 uint32_t counter = hwwdg.Instance->CR & WWDG_CR_T;

 uint32_t window = hwwdg.Instance->CFR & WWDG_CFR_W;

 if (counter <= window)

  HAL_WWDG_Refresh(&hwwdg);

}

// SEE: https://community.st.com/s/question/0D50X0000C22D4a/stm32g070-flash-cfgbsy-flag-issue-report-in-generated-code

// SEE: https://os.mbed.com/questions/69101/Is-there-a-way-to-store-variables-in-a-n/

#include "General.h"

#include "Eeprom.h"

#include "Error.h"

// ***************************************************************************************************************************

// NOTE: It is expected in the following code that the struct EepromConfig has at least 8 bytes of useless padding at the end.

// ***************************************************************************************************************************

// Size of the Eeprom configuration structure, maybe with few padding bytes stripped at the end. This size is a multiple of 8.

#define EEPROM_CONFIG_NB_BYTES_USED (sizeof(struct EepromConfig) & ~7)

#define EEPROM_NB_PAGES ((EEPROM_CONFIG_NB_BYTES_USED + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE)

#define EEPROM_NB_BYTES (EEPROM_NB_PAGES * FLASH_PAGE_SIZE)

#define ALIGNED(x) ((x % FLASH_PAGE_SIZE) == 0)

// Reserve some space in flash memory for the "eeprom".

// We use the # at the end of the text section name to prevent a linker warning.

uint8_t eepromStorage[EEPROM_NB_BYTES]

 __attribute__ ((section (".text#"), used))

 __attribute__ ((aligned (FLASH_PAGE_SIZE)));

static void unlock_flash() {

 __disable_irq();

#if MODEL_HAS_WATCHDOG()

 watchdogPlatform_reset();

#endif

 while (FLASH->SR & (FLASH_SR_BSY1))

  continue;

 FLASH->KEYR = FLASH_KEY1;

 FLASH->KEYR = FLASH_KEY2;

}

static void lock_flash() {

 FLASH->CR |= FLASH_CR_LOCK;

 __enable_irq();

}

static void wait_and_check_error(void) {

 // Wait until done.

 while (FLASH->SR & FLASH_SR_BSY1)

  continue;

 //Check for errors.

 uint32_t cc = ERROR_CODE_NO_ERROR;

 if (FLASH->SR & FLASH_SR_WRPERR)

  cc = ERROR_CODE_FLASH_WRPERR;

 else if (FLASH->SR & FLASH_SR_PGAERR)

  cc = ERROR_CODE_FLASH_PGAERR;

 else if (FLASH->SR & FLASH_SR_PGSERR)

  cc = ERROR_CODE_FLASH_PGSERR;

 if (cc != ERROR_CODE_NO_ERROR)

  error_fatal(cc);

}

static void erasePage(void *dest) {

 uint32_t pageNumber = ((uint32_t) dest - FLASH_BASE) / FLASH_PAGE_SIZE;

 unlock_flash();

 // Clear current errors.

 FLASH->SR = FLASH_SR_WRPERR | FLASH_SR_PGAERR | FLASH_SR_PGSERR;

 // Run command.

 FLASH->CR = FLASH_CR_PER | (pageNumber << FLASH_CR_PNB_Pos);

 FLASH->CR |= FLASH_CR_STRT;

 wait_and_check_error();

 // Clear command.

 FLASH->CR = 0;

 lock_flash();

}

static void copyToFlash(void *dest, void *src, size_t sizeInBytes) {

 unlock_flash();

 // Set programming mode.

 SET_BIT(FLASH->CR, FLASH_CR_PG);

 uint32_t *pDest = (uint32_t*) dest;

 uint32_t *pSrc = (uint32_t*) src;

 // Two uint32_t will be written to flash for each loop iteration. i.e. 8 bytes written per iteration.

 for (uint32_t i = 0, n = sizeInBytes / 8; i < n; i++) {

  /* Barrier to ensure programming is performed in 2 steps, in right order (independently of compiler optimization behavior) */

  __ISB();

  /* Program first word. */

  *pDest++ = *pSrc++;

  /* Barrier to ensure programming is performed in 2 steps, in right order (independently of compiler optimization behavior) */

  __ISB();

  /* Program second word. */

  *pDest++ = *pSrc++;

  wait_and_check_error();

#if MODEL_HAS_WATCHDOG()

  watchdogPlatform_reset();

#endif

 }

 // Clear programming mode.

 FLASH->CR = 0;

 lock_flash();

}

static void programPage(void *dest, void *src, size_t sizeInBytes) {

 erasePage(dest);

 copyToFlash(dest, src, sizeInBytes);

}

static void checkAlignment() {

 if (!ALIGNED((uint32_t) eepromStorage) || !ALIGNED(sizeof eepromStorage))

  error_fatal(ERROR_CODE_FLASH_NOT_ALIGNED);

}

// eeprom_cfg must be aligned on a 4 byte boundary.

void eeprom_platform_save(EepromConfig &eeprom_cfg) {

 checkAlignment();

 char *pSrc = (char *) &eeprom_cfg;

 char *pDest = (char *) eepromStorage;

 uint32_t toGo = EEPROM_CONFIG_NB_BYTES_USED;

 for (uint32_t i = 0; i < EEPROM_NB_PAGES; i++, pSrc += FLASH_PAGE_SIZE, pDest += FLASH_PAGE_SIZE) {

  uint16_t thisTime = (toGo > FLASH_PAGE_SIZE) ? FLASH_PAGE_SIZE : toGo;

  toGo -= thisTime;

  uint8_t same = memcmp(pSrc, pDest, thisTime) == 0;

  if (!same)

   programPage(pDest, pSrc, thisTime);

 }

}

// eeprom_cfg must be aligned on a 4 byte boundary.

void eeprom_platform_load(EepromConfig &eeprom_cfg) {

 checkAlignment();

 uint32_t *pSrc = (uint32_t *) eepromStorage;

 uint32_t *pDest = (uint32_t *) &eeprom_cfg;

 // Load the structure by chunks of 32 bits.

 for (uint32_t i = 0, n = EEPROM_CONFIG_NB_BYTES_USED / sizeof (uint32_t); i < n; i++)

  *pDest++ = *pSrc++;

}

yIzumi
Associate II

Hi Jim, i'm having the same problem on my STM32G03 personal dedicated board,did you manage to fix it? i simply can't use the ST dedicated MIDDLEWARE for eeprom emulation because of this error.