cancel
Showing results for 
Search instead for 
Did you mean: 

Why does my STM32H743VI processor throw a hard fault exception when I place my HAL usart.o variables in SRAM1,SRAM2 or SRAM3, but it runs perfectly when the variables are placed in AXI RAM?

Garnett.Robert
Senior III

I am using an STM32H743VI processor with uVision V5. When I assign the usart.o ZI data to SRAM1, 2 or 3 the project crashes with a hard fault. If I leave out the specification for the usart.o section it gets placed in the AXI ram and the code runs fine. The code snippet for the scatter file is shown below:

  VARS_SRAM2 0x300265C0 0x00001A40
  {
	usart.o (+RW +ZI)  
  }
 
 
  RW_AXI	0x24000000 0x000080000  
  {
	main.o  (POWER_RECDS)
	.ANY (+RW +ZI)	
  }

The usart.c file (MXCube Generated) is shown below:

void MX_USART2_UART_Init(void)
{
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 57600;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_DMADISABLEONERROR_INIT;
  huart2.AdvancedInit.DMADisableonRxError = UART_ADVFEATURE_DMA_ENABLEONRXERROR;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart2, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart2, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
 
}
 
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
 
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART2)
  {
  /* USER CODE BEGIN USART2_MspInit 0 */
 
  /* USER CODE END USART2_MspInit 0 */
    /* USART2 clock enable */
    __HAL_RCC_USART2_CLK_ENABLE();
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    /**USART2 GPIO Configuration    
    PD5     ------> USART2_TX
    PD6     ------> USART2_RX 
    */
    GPIO_InitStruct.Pin = PD6_GPSMsg_UART2_Rx_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
 
    /* USART2 DMA Init */
    /* USART2_RX Init */
    hdma_usart2_rx.Instance = DMA2_Stream2;
    hdma_usart2_rx.Init.Request = DMA_REQUEST_USART2_RX;
    hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart2_rx.Init.Mode = DMA_NORMAL;
    hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_usart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
    {
      Error_Handler();
    }
 
   __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx);
											
													
    /* USART2 interrupt Init */
    HAL_NVIC_SetPriority(USART2_IRQn, PRIO_USART2, 0);
    HAL_NVIC_EnableIRQ(USART2_IRQn); 
 
...

The hard fault occurs because the DMA link address of the usart structure gets set to an address 0x189C0947 which of course doesn't exist so that when usart receive command is issued later in the program the driver tries to access this address and the hard fault is generated.

Other HAL code modules such as the dac.o initialization code appear to run OK when assigned to these RAM areas. In these cases I am not using DMA so no linkDMA is issued.

I didn't think there were any restrictions on using these three RAM areas for ordinary code variables, but maybe I am wrong.

The reason I wish to use these RAM areas is to keep the AXI ram as free as I can so I can use the large continuous space of the AXI ram for large arrays. These arrays will be used for high speed snapshots of realtime process data from the ADC's.

I have attached a couple of uVision Watch Window screen shots of the huart structure in the working and not working states and the program code for the project.

Any thoughts would be appreciated.

8 REPLIES 8
Garnett.Robert
Senior III

ADDITIONAL - INFO

I can put all of the variabless into SRAM4 and the project works fine.

RJG

thomfischer
Senior

try to add in SystemClock_Config()

...

/* Enable D2 domain SRAM3 Clock (0x30040000 AXI)*/

 __HAL_RCC_D2SRAM1_CLK_ENABLE();

 __HAL_RCC_D2SRAM2_CLK_ENABLE();

 __HAL_RCC_D2SRAM3_CLK_ENABLE();

...

Enable the clocks either in SystemInit() or startup.s

Examples of the assembler code have been posted to other threads.​

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Garnett.Robert
Senior III

Hi Guys,

Thanks for your prompt replies.

Yes I had thought about the RAM clocks, but when I checked I had enabled these three RAMS, but then I commented out these entries because ADC 1 and ADC 2 were writing valid to RAMs 2 & 3 in double buffer DMA mode. I have set the ADC's up at 1.65 volt which gives about 32700 counts and this is what I am getting with the RAMS turned off. Each scan is a little different and the values are all slightly different so I believe the data. I commented out the entries on the assumption that the RAM was enabled in the start-up code somewhere because the ADC's were working with RAM2 and RAM3.

I have now enabled all three rams but am now getting a hard fault due to memory misalignment which I should be able to sort out with a bit of jiggery pokery with the disassembly and scatter file.

Does anyone have an explanation of how the ADC's are able to write to these RAMS and I am able to read from them with the clocks?

Very strange.

I have attached a screenshot movie showing the RAMS off and the memory changeing on each scan cycle (3 per second) for those non-believers among you.

I must be missing something, but I cannot work out what.

Best regards

Rob

Watch some of the linker scripts supplied by ST set the initial stack to odd addresses like 0x2003FFFF when 0x20040000 is the correct address.

The Cortex-M cores don't like unaligned addressing for LDRD/STRD, frequently used for reading doubles. LDRM/STRM (multiple) would also be problematic.

In Keil I'm using this for my Hard Fault Handler, I implement the STDIO wrappers to output via a USART or SWD, and comment out the handler in the stm32xyz_it.c

startup.s

HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler
 
                ; Determine correct stack
 
                TST     lr, #4
                ITE     EQ
                MRSEQ   R0, MSP         ; Read MSP (Main)
                MRSNE   R0, PSP         ; Read PSP (Process)
 
                MOV     R1, R4          ; Registers R4-R6
                MOV     R2, R5
                MOV     R3, R6          ; sourcer32@gmail.com
 
                EXTERN  hard_fault_handler_c
                B       hard_fault_handler_c
 
                ENDP ; HardFault_Handler

main.c

void hard_fault_handler_c(unsigned int * hardfault_args, unsigned int r4, unsigned int r5, unsigned int r6)
{
  printf("\n[Hard Fault]\n"); // After Joseph Yiu
 
  printf("r0 = %08X, r1 = %08X, r2 = %08X, r3 = %08X\n",
    hardfault_args[0], hardfault_args[1], hardfault_args[2], hardfault_args[3]);
  printf("r4 = %08X, r5 = %08X, r6 = %08X, sp = %08X\n",
    r4, r5, r6, (unsigned int)&hardfault_args[8]);
  printf("r12= %08X, lr = %08X, pc = %08X, psr= %08X\n",
    hardfault_args[4], hardfault_args[5], hardfault_args[6], hardfault_args[7]);
 
	if (__CORTEX_M >= 3)
		printf("bfar=%08X, cfsr=%08X, hfsr=%08X, dfsr=%08X, afsr=%08X\n",
			*((volatile unsigned int *)(0xE000ED38)),
			*((volatile unsigned int *)(0xE000ED28)),
			*((volatile unsigned int *)(0xE000ED2C)),
			*((volatile unsigned int *)(0xE000ED30)),
			*((volatile unsigned int *)(0xE000ED3C)) );
 
  while(1);
}

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Garnett.Robert
Senior III

Hi Clive

Thanks for that I will have another look at the issue.

Best regards

Rob

Garnett.Robert
Senior III

Additional

I'm using a double for Julian date in the satellite clock synchronization task so I think you have diagnosed my problem.

I'm using Joseph Yiu's hard fault handler, but with Segger RTT as the output port for the print statements. I implemented the assembler code in the interrupt handler file viz:

__asm void NMI_Handler(void)
{
	TST    LR, #4
	ITE    EQ
	MRSEQ  R0, MSP
	MRSNE  R0, PSP
	MOV    R1, LR
	B      __cpp(HardFault_Handler_C)
}	

This seems a little different from the code you posted. Should I update my code to reflect yours?

My hard fault handler:

#if HARDFAULT_HANDLER == 1
 
void HardFault_Handler_C(unsigned long * svc_args, unsigned int lr_value);
 
/* HardFault handler wrapper in assembly language.
   It extracts the location of stack frame and passes it to the handler written
   in C as a pointer. We also extract the LR value as second parameter. 
	 Joseph Yiu */
 
unsigned long stacked_r0;
unsigned long stacked_r1;
unsigned long stacked_r2;
unsigned long stacked_r3;
unsigned long stacked_r12;
unsigned long stacked_lr;
unsigned long stacked_pc;
unsigned long stacked_psr;
unsigned long cfsr;
unsigned long bus_fault_address;
unsigned long memmanage_fault_address; 
 
// HardFault handler in C, with stack frame location and LR value extracted
// from the assembly wrapper as input parameters
void HardFault_Handler_C(unsigned long * hardfault_args, unsigned int lr_value)
{
  bus_fault_address       = SCB->BFAR;
  memmanage_fault_address = SCB->MMFAR;
  cfsr                    = SCB->CFSR;
 
  stacked_r0  = ((unsigned long) hardfault_args[0]);
  stacked_r1  = ((unsigned long) hardfault_args[1]);
  stacked_r2  = ((unsigned long) hardfault_args[2]);
  stacked_r3  = ((unsigned long) hardfault_args[3]);
  stacked_r12 = ((unsigned long) hardfault_args[4]);
  stacked_lr  = ((unsigned long) hardfault_args[5]);
  stacked_pc  = ((unsigned long) hardfault_args[6]);
  stacked_psr = ((unsigned long) hardfault_args[7]);
 
	#if USE_RTT_VIEWER == 1 && PRINT_HARD_FAULTS == 1
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_YELLOW"[HardFault]\n");
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE"- Stack frame:\n"); 
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" R0  = %x\n", stacked_r0);
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" R1  = %x\n", stacked_r1);
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" R2  = %x\n", stacked_r2);
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" R3  = %x\n", stacked_r3);
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" R12 = %x\n", stacked_r12);
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" LR  = %x\n", stacked_lr);
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_RED" PC  = %x\n", stacked_pc);
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" PSR = %x\n", stacked_psr);
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE"- FSR/FAR:\n");  
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" CFSR = %x\n", cfsr);
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" HFSR = %x\n", SCB->HFSR);
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" DFSR = %x\n", SCB->DFSR);
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" AFSR = %x\n", SCB->AFSR);
		if (cfsr & 0x0080) SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" MMFAR = %x\n", memmanage_fault_address);
		if (cfsr & 0x8000) SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" BFAR = %x\n", bus_fault_address);
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE"- Misc\n"); 
		SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_WHITE" LR/EXC_RETURN= %x\n", lr_value);
	#endif
 	
  while(1)
	{
		
	}	; // endless loop
}
#endif

Rob