on 2022-11-10 05:26 AM
First, we must enable SRAM parity via the option bytes. There are multiple ways to do this, but this simplest way is to use STM32CubeProgrammer to program the option bytes via an SWD connection. Connect to the target board, then navigate to the option bytes tab. Under the User Configuration option bytes, uncheck the RAM_PARITY_CHECK box, and click apply. This will enable SRAM parity.
On some MCUs, enabling parity for the SRAM will change the size of the SRAM. This is the case for the example MCU used in this article: the STM32G071RB.
In the reference manual for the part, in the section regarding the embedded SRAM, it shows a table outlining how parity affects the size of SRAM. We need to ensure that the size of SRAM is defined correctly in the linker file. If it is not, the linker file will assign the stack to the end of SRAM assuming it is 36kB, but since it is 32kB now the stack will be assigned to a non-valid address. This will cause a hard fault. In the linker file STM32G071RBTX_FLASH.ld, change the size of the RAM to reflect enabled parity:
/* Memories definition */ MEMORY { /* Commented out old memory defintion of 36kB */ /* RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 36K */ /* Parity changes SRAM size to 32kB */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K }
If you are experiencing a hardfault when enabling parity for SRAM, this could likely be the issue. Be sure to check in the reference manual for your MCU whether parity changes the size of the SRAM or not.
It is necessary to initialize the SRAM in the startup file before any other operations occur. Since the SRAM parity will automatically write the parity bit when an SRAM address is written to, initializing SRAM in startup before anything else occurs will avoid the possibility of reading an SRAM address that hasn’t been initialized and therefore doesn’t have the correct parity bit. In the startup file, startup_stm32g071rbtx.s, add a loop that initializes SRAM to all 0’s. This should be done right at the beginning of Reset_Handler.
Reset_Handler:
/* Start SRAM init */
ldr r1, =0x20000000 // start address of SRAM
ldr r2, =0x20008000 // end address of SRAM
movs r3, #0 // we want to write 0 to all address
initializeSRAM:
str r3, [r1] // store 0 at address
adds r1, r1, #4 // increment to next address
cmp r1, r2 // update flags
bne initializeSRAM // branch if write address equals end of SRAM
/* End SRAM Init */
/* Rest of reset handler continues below */
When a parity error occurs, the processor throws an NMI interrupt. To handle a parity error, the SRAM_ PEF in the SYSCFG_CFGR2 is checked:
As can be seen above, the SRAM_PEF is labeled as a rc_w1. This means it is readable, and it is also cleared by writing 1 to it. Knowing this, we can perform the parity error handling in the NMI_Handler. In the file, stm32g0xx_it.c, we need to add the following code the USER CODE BEGIN NonMaskableInt_IRQn 0 in the NMI_Handler:
void NMI_Handler(void) { /* USER CODE BEGIN NonMaskableInt_IRQn 0 */ /* Read the parity error flag to see if parity error occurred */ if (SYSCFG->CFGR2 & 1<<8) { /* You can handle the error here however you see fit */ /* Clear bit if desired */ SYSCFG->CFGR2 |= 1<<8; } /* USER CODE END NonMaskableInt_IRQn 0 */ /* USER CODE BEGIN NonMaskableInt_IRQn 1 */ while (1) { } /* USER CODE END NonMaskableInt_IRQn 1 */ }
Unfortunately, there is no way to consistently trigger a parity error. The steps to trigger the parity error somewhat regularly using CubeIDE are below. This is solely intended to prove to yourself that the parity error checking is indeed enabled and working as expected.
To make it easier to see when the error occurs, turn on an LED when parity error occurs, and add a while(1) to stop execution in the parity error check:
void NMI_Handler(void) { /* USER CODE BEGIN NonMaskableInt_IRQn 0 */ /* Read the parity error flag to see if parity error occurred */ if (SYSCFG->CFGR2 & 1<<8) { // A parity error has occurred HAL_GPIO_WritePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin, 1); while(1){} } /* USER CODE END NonMaskableInt_IRQn 0 */ /* USER CODE BEGIN NonMaskableInt_IRQn 1 */ while (1) { } /* USER CODE END NonMaskableInt_IRQn 1 */ }
Comment out the assembly code in the startup file that initializes SRAM so that we can induce a parity error by reading uninitialized RAM.
Build the code and start debugging. By default the execution will stop on the first line of main(). Continue execution, then pause. This should have you stopped in the main while(1) loop. While execution is paused, open the memory browser window and type the beginning of SRAM (0x2000 0000) into the browser. Scroll through SRAM in the memory browser until you see some values that are unreadable (????) and interspersed with garbage values:
Now continue execution.
The LED should turn on, indicating a parity error. You can pause debugging to see that program execution is now inside parity error check:
How to enable the SRAM parity bit through code instead of using STM32CubeProgrammer,
I am trying to enable the SRAM parity check, code snippet is as follows:
FLASH_OBProgramInitTypeDef config;
HAL_FLASH_Unlock();
HAL_FLASH_OB_Unlock();
HAL_FLASHEx_OBErase();
HAL_FLASHEx_OBGetConfig(&config);
config.USERConfig = 0x00;
HAL_FLASHEx_OBProgram(&config);
HAL_FLASH_OB_Launch();
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
The code gets stuck at HAL_FLASH_OB_Launch(); and the IAR IDE hangs and exits out of debug mode.