Search code examples
stm32microcontrollerflash-memory

When writing to flash on a STM32G031 Page erase enable occasionally causes hardfault


When writing a value to flash on a STM32G031 Page erase enable occasionally causes hardfault.

Some times it works ok, some times it faults on the page erase enable line

FLASH->CR |= FLASH_CR_PER;

Anyone have any idea what is wrong? As far as I can see I do it as per the datasheet (RM0444 p75-76)

There are no obvious errors in SR or CR. The value of SR=0x40000 == CFGBSY and CR = 0x40000078 == PNB = 15 and OPTLOCK = 1

void FLASH_WriteValue(uint16_t val) {
uint32_t old_primask = __get_PRIMASK();
__disable_irq();

/*
After reset, write into the FLASH control register (FLASH_CR) is not allowed so as to
protect the Flash memory against possible unwanted operations due, for example, to
electric disturbances. The following sequence unlocks these registers:
1. Write KEY1 = 0x4567 0123 in the FLASH key register (FLASH_KEYR)
2. Write KEY2 = 0xCDEF 89AB in the FLASH key register (FLASH_KEYR).
Any wrong sequence locks the FLASH_CR registers until the next system reset. In the case of a wrong key sequence, a bus error is detected and a Hard Fault interrupt is generated.
The FLASH_CR registers can be locked again by software by setting the LOCK bit in one of
these registers.
Note:The FLASH_CR register cannot be written when the BSY1 bit of the FLASH status register (FLASH_SR) is set. Any attempt to write to this register with the BSY1 bit set causes the
AHB bus to stall until the BSY1 bit is cleared.
*/

// Wait for the flash memory to be ready
while ((FLASH->SR & FLASH_SR_BSY1) != 0) { __asm("nop"); }
//if (!waitFlag(&(FLASH->SR ), FLASH_SR_BSY1, BIT_RESET, 100)) { __asm("bkpt"); }

// Unlock the flash memory for programming
FLASH->KEYR = 0x45670123;
FLASH->KEYR = 0xCDEF89AB;

/*

To erase a page (2Kbytes), follow the procedure below:
1.Check that no Flash memory operation is ongoing by checking the BSY1 bit of the
FLASH status register (FLASH_SR).
2. Check and clear all error programming flags due to a previous programming. If not,
PGSERR is set.
3. Set the PER bit and select the page to erase (PNB) in the FLASH control register (FLASH_CR).
4. Set the STRT bit of the FLASH control register (FLASH_CR).
5. Wait until the BSY1 bit of the FLASH status register (FLASH_SR) is cleared.

*/

// Wait for the flash memory to be ready
while ((FLASH->SR & FLASH_SR_BSY1) != 0) { __asm("nop"); }
//if (!waitFlag(&(FLASH->SR ), FLASH_SR_BSY1, BIT_RESET, 100)) { __asm("bkpt"); }

// Clear all error flags
if ((FLASH->SR & FLASH_SR_OPTVERR) ==  FLASH_SR_OPTVERR ) { FLASH->SR |= FLASH_SR_OPTVERR; }
if ((FLASH->SR & FLASH_SR_RDERR)   ==  FLASH_SR_RDERR   ) { FLASH->SR |= FLASH_SR_RDERR  ; }
if ((FLASH->SR & FLASH_SR_FASTERR) ==  FLASH_SR_FASTERR ) { FLASH->SR |= FLASH_SR_FASTERR; }
if ((FLASH->SR & FLASH_SR_MISERR)  ==  FLASH_SR_MISERR  ) { FLASH->SR |= FLASH_SR_MISERR ; }
if ((FLASH->SR & FLASH_SR_PGSERR)  ==  FLASH_SR_PGSERR  ) { FLASH->SR |= FLASH_SR_PGSERR ; }
if ((FLASH->SR & FLASH_SR_SIZERR)  ==  FLASH_SR_SIZERR  ) { FLASH->SR |= FLASH_SR_SIZERR ; }
if ((FLASH->SR & FLASH_SR_PGAERR)  ==  FLASH_SR_PGAERR  ) { FLASH->SR |= FLASH_SR_PGAERR ; }
if ((FLASH->SR & FLASH_SR_WRPERR)  ==  FLASH_SR_WRPERR  ) { FLASH->SR |= FLASH_SR_WRPERR ; }
if ((FLASH->SR & FLASH_SR_PROGERR) ==  FLASH_SR_PROGERR ) { FLASH->SR |= FLASH_SR_PROGERR; }
if ((FLASH->SR & FLASH_SR_OPERR)   ==  FLASH_SR_OPERR   ) { FLASH->SR |= FLASH_SR_OPERR  ; }


while ((FLASH->SR & FLASH_SR_BSY1) != 0) { __asm("nop"); }
//if (!waitFlag(&(FLASH->SR ), FLASH_SR_BSY1, BIT_RESET, 100)) { __asm("bkpt"); }

// Page erase enable
FLASH->CR |= FLASH_CR_PER;

// Set selected page to page 15
FLASH->CR &= ~FLASH_CR_PNB;
FLASH->CR |= (0xF << FLASH_CR_PNB_Pos);

// Start erase procedure
FLASH->CR |= FLASH_CR_STRT;

// Wait for it to finish
while ((FLASH->SR & FLASH_SR_BSY1) != 0) { __asm("nop"); }
//if (!waitFlag(&(FLASH->SR ), FLASH_SR_BSY1, BIT_RESET, 100)) { __asm("bkpt"); }

// Disable page erase
FLASH->CR &= ~FLASH_CR_PER;

/*

The Flash memory programming sequence in standard mode is as follows:
1.Check that no Main Flash memory operation is ongoing by checking the BSY1 bit of the
FLASH status register (FLASH_SR)..
2. Check and clear all error programming flags due to a previous programming. If not,
PGSERR is set.
3. Set the PG bit of the FLASH control register (FLASH_CR).
4. Perform the data write operation at the desired memory address, inside Main memory
block or OTP area. Only double word (64 bits) can be programmed.
a) Write a first word in an address aligned with double word
b) Write the second word.
5. Wait until the BSY1 bit of the FLASH status register (FLASH_SR) is cleared.
6. Check that EOP flag of the FLASH status register (FLASH_SR) is set (programming
operation succeeded), and clear it by software.
7. Clear the PG bit of the FLASH control register (FLASH_CR) if there no more
programming request anymore.

*/

while ((FLASH->SR & FLASH_SR_BSY1) != 0) { __asm("nop"); }
//if (!waitFlag(&(FLASH->SR ), FLASH_SR_BSY1, BIT_RESET, 100)) { __asm("bkpt"); }

// Clear all error flags
if ((FLASH->SR & FLASH_SR_OPTVERR) ==  FLASH_SR_OPTVERR ) { FLASH->SR |= FLASH_SR_OPTVERR; }
if ((FLASH->SR & FLASH_SR_RDERR)   ==  FLASH_SR_RDERR   ) { FLASH->SR |= FLASH_SR_RDERR  ; }
if ((FLASH->SR & FLASH_SR_FASTERR) ==  FLASH_SR_FASTERR ) { FLASH->SR |= FLASH_SR_FASTERR; }
if ((FLASH->SR & FLASH_SR_MISERR)  ==  FLASH_SR_MISERR  ) { FLASH->SR |= FLASH_SR_MISERR ; }
if ((FLASH->SR & FLASH_SR_PGSERR)  ==  FLASH_SR_PGSERR  ) { FLASH->SR |= FLASH_SR_PGSERR ; }
if ((FLASH->SR & FLASH_SR_SIZERR)  ==  FLASH_SR_SIZERR  ) { FLASH->SR |= FLASH_SR_SIZERR ; }
if ((FLASH->SR & FLASH_SR_PGAERR)  ==  FLASH_SR_PGAERR  ) { FLASH->SR |= FLASH_SR_PGAERR ; }
if ((FLASH->SR & FLASH_SR_WRPERR)  ==  FLASH_SR_WRPERR  ) { FLASH->SR |= FLASH_SR_WRPERR ; }
if ((FLASH->SR & FLASH_SR_PROGERR) ==  FLASH_SR_PROGERR ) { FLASH->SR |= FLASH_SR_PROGERR; }
if ((FLASH->SR & FLASH_SR_OPERR)   ==  FLASH_SR_OPERR   ) { FLASH->SR |= FLASH_SR_OPERR  ; }

while ((FLASH->SR & FLASH_SR_BSY1) != 0) { __asm("nop"); }
//if (!waitFlag(&(FLASH->SR ), FLASH_SR_BSY1, BIT_RESET, 100)) { __asm("bkpt"); }

// Write the value to the flash memory
uint32_t address = 0x08007800;
uint32_t data = val;
uint32_t dataPadding = 0;
FLASH->CR |= FLASH_CR_PG;

*(volatile uint32_t*)address = data;
*(volatile uint32_t*)(address + 4) = dataPadding;

while ((FLASH->SR & FLASH_SR_BSY1) != 0) { __asm("nop"); }
//if (!waitFlag(&(FLASH->SR ), FLASH_SR_BSY1, BIT_RESET, 100)) { __asm("bkpt"); }

FLASH->CR &= ~FLASH_CR_PG;

// Lock the flash memory
FLASH->CR |= FLASH_CR_LOCK;

__set_PRIMASK (old_primask);

}


Solution

  • This must have been a hardware problem/defective chip. When I run the above code on another board it works fine.