cancel
Showing results for 
Search instead for 
Did you mean: 

A solution to resolve and exit from the BusFault triggered by am Flash memory double-fault ECC

robertj.stuart9
Associate

I have spent a bit of time looking for a solution on how-to deal with BusFault caused by a flash-read ECC event. This is what I would like to share with others in the absence of finding an online solution myself.

void fv_flash_eec_test(void)

{

   volatile uint8_t      u8_i;

   volatile uint32_t      u32_addr, u32a_rd[8], u32a_wr[8];

   // enable BusFault interrupt to prevent escalation to a HardFault

   NVIC_SetPriority(BusFault_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));

   NVIC_EnableIRQ(BusFault_IRQn);

   // enable interrupt

   LL_HANDLER_EnableFault(LL_HANDLER_FAULT_BUS);

   u32_addr         = ADDR_FLASH_SECTOR_4_BANK1;

   fu8_flash_erase_sector(FLASH_SECTOR_4, 1);

   // no fault triggered

   for (u8_i = 0; u8_i < 8; u8_i++)

   {

      u32a_rd[u8_i]         = *(volatile uint32_t *) (u32_addr + (sizeof(uint32_t) * u8_i));

   }

   memset(&u32a_wr[0], 0x23, sizeof(uint32_t) * 8);

   HAL_FLASHEx_Unlock_Bank1();

   HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, u32_addr, (uint32_t) &u32a_wr[0]);

   HAL_FLASHEx_Lock_Bank1();

   // no fault triggered

   for (u8_i = 0; u8_i < 8; u8_i++)

   {

      u32a_rd[u8_i]         = *(volatile uint32_t *) (u32_addr + (sizeof(uint32_t) * u8_i));

   }

   // corrupt a 32bit segment

   u32a_wr[3]         = 0xD4;

   HAL_FLASHEx_Unlock_Bank1();

   HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, u32_addr, (uint32_t) &u32a_wr[0]);

   HAL_FLASHEx_Lock_Bank1();

   // this will trigger a BusFault

   for (u8_i = 0; u8_i < 8; u8_i++)

   {

      u32a_rd[u8_i]         = *(volatile uint32_t *) (u32_addr + (sizeof(uint32_t) * u8_i));

   }

   while (1) {}

}

/**

 * @brief This function handles Pre-fetch fault, memory access fault.

 */

void BusFault_Handler(void)

{

   // check for a double ecc fault

   if (FLASH->SR1 & FLASH_SR_DBECCERR)

   {

      // clear flag

      FLASH->CCR1      |= FLASH_CCR_CLR_DBECCERR;

      // flash programming is done in block of 256-bits (8 x uint32)

      uint32_t   u32_addr = SCB->BFAR;

      uint32_t   u32a_wr[8] = {0};

      // place on a 256-bit boundary

      u32_addr         = (u32_addr >> 5) << 5;

      // overwrite all data to zero - this will remove the ecc fault and clear the interrupt

      HAL_FLASHEx_Unlock_Bank1();

      HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, u32_addr, (uint32_t) &u32a_wr[0]);

      HAL_FLASHEx_Lock_Bank1();

   }

   // clear any pending interrupts

   NVIC_ClearPendingIRQ(BusFault_IRQn);

}

1 REPLY 1
Bob S
Principal

Overwriting FLASH in a fault handler sounds dodgy/dangerous to me. And can you really write into flash that has already been written to without erasing it first? Some flash you can change "1" bits to "0" bits, but I didn't think flash with built-in ECC allowed that. You don't say which CPU you are targeting, so we can't verify that. It doesn't look like a G4xx series, as that has ECC on 8 byte blocks and a double ECC failure asserts NMI not a bus fault.

And it looks like you are presuming the error is only triggered on FLASH used as data. What happens if you get a (perhaps momentary) ECC error from program memory? Boom - there goes your code. Not to mention what happens if the fault is somehow triggered during code that is writing to flash (is this a dual-bank device with "read-while-write" capability?).