on 2024-11-04 10:00 AM - edited on 2024-11-05 12:42 PM by CMYL
This article provides practical steps showing how to inject and check single and double ECC errors in STM32H7 flash memory. Find attached to the article a ECC Flash project for testing purposes.
In the STM32H7 microcontrollers, Flash ECC (Error Correction Code) is implemented to protect the integrity of data stored in nonvolatile flash memory.
It features a SECDED (Single Error Correction, Double Error Detection) to detect and correct single-bit errors and detect double-bit errors within the 256-bit flash memory words.
The ECC functionality is integrated into the flash memory controller and can’t be disabled.
The suggested method consists of two consecutive programming of two 256-bits data injecting either single or double ECC errors. We will guide you to inject and check the flash ECC controller detection.
a. Create a new STM32H7 project using STM32CubeMX and enable the flash global interrupt line of NVIC.
b. In the .IOC file, go to [System Core] → [NVIC] and enable the flash global interrupt as follows:
c. Generate your project (optionally configure the LED I/Os according to your board)
Navigate to [Core] → [Inc] → stm32h7xx_hal_conf.h and add the following code
/* Note that you need to insert this line of code each time the code is regenerated using STM32CubeMX.*/
#define USE_FLASH_ECC 1U
It's important to note that the stm32h7xx_hal_conf.h file doesn't have a 'user code' section to insert customized user code. As a result, if you add the Flash ECC line and then make any changes through the .IOC file, the line will be removed at each project regeneration.
d. When a double bit ECC error occurs in flash, the HardFault handler is called instead of the flash IRQ handler. To get around this, add a check in the HardFault handler to see if a double bit error occurred in flash:
/**
* @brief This function handles Hard fault interrupt.
*/
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
// Check if a double-bit ECC error has occurred
if (FLASH->SR1 & FLASH_SR_DBECCERR)
{
FLASH_IRQHandler();
}
/* USER CODE END HardFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
}
e. To enable flash ECC interrupts, I added the following function in main.c:
void init_flash_ecc()
{
HAL_FLASH_Unlock();
HAL_NVIC_SetPriority(FLASH_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(FLASH_IRQn);
HAL_FLASHEx_EnableEccCorrectionInterrupt();
HAL_FLASHEx_EnableEccDetectionInterrupt();
HAL_FLASH_Lock();
}
f. To define the ECC callbacks, I added the following functions in main.c:
/** Single Error Callback function */
void HAL_FLASHEx_EccCorrectionCallback()
{
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, 1);
}
/** Double Error Callback function */
void HAL_FLASHEx_EccDetectionCallback()
{
HAL_GPIO_WritePin(LD1_GPIO_Port, LD1_Pin, 1);
}
a. In the main.c file, define the global variables and functions to be used by the single error injection test:
The following arrays contain the same data sequences except the last bit which is intentionally modified to inject a single bit error.
/** 256 bit data with single error **/
uint64_t SingleErrorA[4] = { 0xCCCCCCCCCCCCCCCC,
0xCCCCCCCCCCCCCCCC,
0xCCCCCCCCCCCCCCCC,
0xCCCCCCCCCCCCCCC0
};
uint64_t SingleErrorB[4] = { 0xCCCCCCCCCCCCCCCC,
0xCCCCCCCCCCCCCCCC,
0xCCCCCCCCCCCCCCCC,
0xCCCCCCCCCCCCCCC1
};
The flash address to which errors will be injected is 0x0808_0000 defined as follows:
/* Start @ of user Flash area where errors are to be injected */
#define FLASH_USER_START_ADDR (FLASH_BASE + (FLASH_SECTOR_SIZE * 4))
Two consecutive programming operations of the flash at the same address (here FLASH_USER_START_ADDR) will inject a single errors with respect to the above arrays. After reading the flash content from the programmed address the ECC controller will detect the error and redirect the execution to the single error callback function. The single error flag in the FLASH_SR register will be set consequently. The function cause_flash_single_error() implements this single error injection.
/** Function declaration for the injection of a single error test **/
void cause_flash_single_error();
/** variable to read data from flash */
uint64_t readData[4];
/** Function definition to inject a single error test **/
void cause_flash_single_error()
{
HAL_FLASH_Unlock();
/* first programming operation */
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, FLASH_USER_START_ADDR, ((uint32_t) SingleErrorB)) != HAL_OK)
{
Error_Handler();
}
/* second programming operation */
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, FLASH_USER_START_ADDR, ((uint32_t) SingleErrorA)) != HAL_OK)
{
Error_Handler();
}
/* reading the faulty flash programmed address */
for (int i = 0; i < 4; i++)
{
readData[i] = *((uint64_t*) (FLASH_USER_START_ADDR + i * 8)); // Read 64 bits at a time
}
HAL_FLASH_Lock();
}
b. In the main function, call the init_flash_ecc function defined above.
init_flash_ecc();
c. Erase the user flash memory addresses to test ECC errors
static FLASH_EraseInitTypeDef EraseInitStruct;
d. In the main function, add the following code to erase the user sector:
FirstSector = (FLASH_USER_START_ADDR - FLASH_BASE) / FLASH_SECTOR_SIZE;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInitStruct.Banks = FLASH_BANK_1;
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_4;
EraseInitStruct.Sector = FirstSector;
EraseInitStruct.NbSectors = 1 ; /* NbOfSectors;*/
if (HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError) != HAL_OK)
while(1);
e. Debug will trigger the single error correction callback. Set a breakpoint inside the single error callback function and you can see that an ECC single error interrupt happened:
The SNECCERR flag will also be set in the FLASH_SR register to indicate a single error detection correction.
a. In the main.c file, define the global variables and functions to be used by the single error injection test:
The following arrays contain the same data sequences except the last 3-bits which are intentionally modified to inject double bit errors ( C: 1100 vs B: 1011).
/** 256 bit data with double ECC error **/
uint64_t DoubleErrorA[4] = { 0xCCCCCCCCCCCCCCCC,
0xCCCCCCCCCCCCCCCC,
0xCCCCCCCCCCCCCCCC,
0xCCCCCCCCCCCCCCCB
};
uint64_t DoubleErrorB[4] = { 0xCCCCCCCCCCCCCCCC,
0xCCCCCCCCCCCCCCCC,
0xCCCCCCCCCCCCCCCC,
0xCCCCCCCCCCCCCCCC
};
Two consecutive programming operations of the flash at the same address (here FLASH_USER_START_ADDR) will inject double errors with respect to the above arrays. After reading the flash content from the programmed address the ECC controller will detect more than single bit error and will redirect the execution to HardFault_Handler(void). The double error flag in the FLASH_SR register will be set consequently. The function cause_flash_double_error() implements the double error injection sequence.
/** Function declaration for the injection of a double error test **/
void cause_flash_double_error();
/** Function definition for the injection of a double error test **/
void cause_flash_double_error()
{
HAL_FLASH_Unlock();
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, FLASH_USER_START_ADDR, ((uint32_t) DoubleErrorB)) != HAL_OK)
{
Error_Handler();
}
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, FLASH_USER_START_ADDR, ((uint32_t) DoubleErrorA)) != HAL_OK)
{
Error_Handler();
}
for (int i = 0; i < 4; i++)
{
readData[i] = *((uint64_t*) (FLASH_USER_START_ADDR + i * 8)); // Read 64 bits at a time
}
HAL_FLASH_Lock();
}
b. Erase the user flash memory addresses where to test ECC errors (see above)
c. Comment the single error injection test function and call the double error injection function in the main. You may call it after the erase and the init_flash_ecc() functions.
cause_flash_double_error();
d. Debug triggers the Hardfault handler and the double error flag will be set in the FLASH_SR register to indicate a double error detection correction.
The first programming of DoubleErrorB is observed in the debug memory window below:
The second programming of the DoubleErrorA injects a double ECC error which will cause the hardfault.
The flag is also set in the FLASH_SR register as follows:
The debugger is not able to read the content of programmed 256-bit due to the double error detection. Corrupted data will be seen in the memory window at this address:
We would like to sincerely thank our community member @20jmorrison for providing the material for this article. We consider this the very first user contribution to the knowledge base. @CMYL and @Laurids_PETERSEN have provided minor edits to this article. Additionally, we have attached a .zip file for testing purposes of the flash ECC project. You can view the original post from @20jmorrison in our product forums below.
Hi @Laurids_PETERSEN ,
Thank you for the article.
I have few queries on this.
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, DstAddr, ((uint32_t) SingleErrorA)) != HAL_OK)
2. Why would two consecutive programming operations trigger an ECC error ?
>>Two consecutive programming operations of the flash at the same address (here FLASH_USER_START_ADDR) will inject a single errors with respect to the above arrays
3. Am I correct in saying that the user doesn't need to do anything in the event of a single ECC error in flash, unlike in RAM ECC where the application can rewrite the corrected value to the location? Is it true that the core always reads the corrected value from flash in the case of a single-bit ECC error?
4. What is the best practice in the event of a double-bit ECC error in flash? The application note mentions a reset for RAM ECC, as it cannot be recovered. Is the same approach recommended for flash ECC?"