on 2024-11-04 10:00 AM
This article provides a step-by-step method to test the ECC single and double errors in the STM32H7 series. Find attached to the article a ECC RAM project for testing purposes.
In STM32H7 microcontrollers, RAM ECC (Error Correction Code) is designed to protect the integrity of data stored in volatile memory. It features Single Error Correction (SEC) and Double Error Detection (DED).
On the STM32H7 series, ECC is implemented on various SRAM regions, such as AXI-SRAM, ITCM-RAM, and DTCM-RAM. The ECC computes on data words, with a data width of 64-bit for AXI-SRAM and ITCM-RAM, and 32-bit for other volatile memories. The ECC can’t be turned off in most STM32H7 series microcontrollers, except for STM32H7Rx/H7Sx lines.
In the STM32H7 series, except the STM32H7R/S series, there are no dedicated bits at user level to control the injection of ECC errors in SRAM memories. A control method is provided in other STM32 series such as STM32H5, STM32U5, or lines such as STM32H7R/S.
To emulate an ECC error in the STM32H7 series, you need to read from an uninitialized memory after reset.
Note: The H7R/S series provides dedicated bits and a method to control the injection of ECC errors in SRAM memories.
The goal is to read from uninitialized SRAM sectors and enable the ECC monitoring interrupt bits to detect Single Error Correction and Double Error Detection.
In practice, the D1-AXI SRAM is used, but you can modify or add other memories. We’ll keep the D1-AXI SRAM of STM32H723 uninitialized hoping to maintain random data at startup. You can reset the board without powering-off or check with other memories if the ECC detection isn’t seen at first.
/* Enable monitor notifications */
/* ECC single error notification and ECC double error notification */
if (HAL_RAMECC_EnableNotification(&hramecc1_m1, (RAMECC_IT_MONITOR_SINGLEERR_R | RAMECC_IT_MONITOR_DOUBLEERR_R))!= HAL_OK)
{
Error_Handler();
}
/* Start Monitor : Enable latching failing information
Failing information : * Failing address
* Failing Data Low
* Failing Data High
* Hamming bits injected
*/
if (HAL_RAMECC_StartMonitor(&hramecc1_m1) != HAL_OK)
{
Error_Handler();
}
b. At the bottom, add the following lines to enable the RAM ECC IRQ:
HAL_NVIC_SetPriority(ECC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ECC_IRQn);
c. The entire MX_RAMECC_Init function after modifications is as follows:
/**
* @brief RAMECC Initialization Function
* None
* @retval None
*/
static void MX_RAMECC_Init(void)
{
/** Initialize RAMECC1 M1 : AXI SRAM
*/
hramecc1_m1.Instance = RAMECC1_Monitor1;
if (HAL_RAMECC_Init(&hramecc1_m1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN RAMECC_Init 2 */
/* Enable monitor notifications */
/* ECC single error notification and ECC double error notification */
if (HAL_RAMECC_EnableNotification(&hramecc1_m1, (RAMECC_IT_MONITOR_SINGLEERR_R | RAMECC_IT_MONITOR_DOUBLEERR_R))!= HAL_OK)
{
Error_Handler();
}
/* Start Monitor : Enable latching failing information
Failing information : * Failing address
* Failing Data Low
* Failing Data High
* Hamming bits injected
*/
if (HAL_RAMECC_StartMonitor(&hramecc1_m1) != HAL_OK)
{
Error_Handler();
}
HAL_NVIC_SetPriority(ECC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ECC_IRQn);
/* USER CODE END RAMECC_Init 2 */
}
d. Add the following callback in main.c:
/**
* @brief Single or double ECC error detected callback.
* hramecc : RAMECC handle
* @retval None
*/
void HAL_RAMECC_DetectErrorCallback(RAMECC_HandleTypeDef *hramecc)
{
FAR = HAL_RAMECC_GetFailingAddress(hramecc);
if ((HAL_RAMECC_GetRAMECCError(hramecc) & HAL_RAMECC_SINGLEERROR_DETECTED) != 0U) {
HAL_GPIO_WritePin(LD1_GPIO_Port, LD1_Pin, 1);
}
if ((HAL_RAMECC_GetRAMECCError(hramecc) & HAL_RAMECC_DOUBLEERROR_DETECTED) != 0U) {
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, 1);
}
hramecc->RAMECCErrorCode = HAL_RAMECC_NO_ERROR;
HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, 1);
}
e. Add a function ECC_IRQHandler in the main.c or it.c files to enable the IRQ handler and to pass the callback to the respective ECC handle. If more than one memory is tested, check the flag for each enabled RAM ECC monitor.
void ECC_IRQHandler(void)
{
HAL_RAMECC_IRQHandler(&hramecc1_m1);
}
a. In the main.c file, define the start and end addresses of the SRAM under test (SRAM_AXI memory)
/* USER CODE BEGIN PTD */
RAMECC_HandleTypeDef hramecc1_m1;
#define SRAM_AXI_START (uint32_t*)0x24000000 //512kb
#define SRAM_AXI_END (uint32_t*)0x2407FFFF
/* USER CODE END PTD */
b. In main() read the content of the SRAM_AXI incrementally. This will raise ECC errors when the memory isn’t initialized after startup
uint32_t* mem_pointer4 = (uint32_t*)SRAM_AXI_START;
while(1){
CurrentData = *(mem_pointer4++);
if(mem_pointer4 >= (volatile uint32_t*)SRAM_AXI_END){
break;
}
}
c. The entire main() function for the test is as follows:
/* USER CODE BEGIN PTD */
RAMECC_HandleTypeDef hramecc1_m1;
#define SRAM_AXI_START (uint32_t*)0x24000000 //512kb
#define SRAM_AXI_END (uint32_t*)0x2407FFFF
/* USER CODE END PTD */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_RAMECC_Init();
/* USER CODE BEGIN 2 */
while(1)
{
CurrentData = *(mem_pointer4++);
if(mem_pointer4 >= (volatile uint32_t*)SRAM_AXI_END)
{
break;
}
}
/* USER CODE END 2 */
while(1);
}
d. Run the test and set breakpoints into the callback to check if an ECC error occurred or not. You’ll be redirected to the callback function when ECC errors occurred as follows:
e. You can check the flags of the SR and the failing address registers (FAR) as well:
The material of this article is provided thanks to the community members @snmunters and @20jmorrison of the following posts: