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);
}
This must have been a hardware problem/defective chip. When I run the above code on another board it works fine.