ARM Cortex-M HardFault



Who does not know ... As the software runs for hours or even days / weeks and then the whole thing is suddenly in HardFault... :-(

What can help better as than a small assembler snippets in the startup code, that get out the failed PC address and the return address (to the caller).
If the local variables there are not overwritten by the registers used here, the error can be identify quite well with an attached JTAG debugger ...


Cortex-M0Cortex-M3/4
HardFault_Handler\ PROC EXPORT HardFault_Handler [WEAK] MOVS R5, #4 MOV R6, LR TST R5, R6 BEQ HardFault_M MRS R5, PSP B HardFault_reg HardFault_M MRS R5, MSP HardFault_reg LDR R6, [R5, #24] ; R6 is the failed PC-address LDR R7, [R5, #20] ; R7 is the failed LR-address B . ENDP

HardFault_Handler\ PROC EXPORT HardFault_Handler [WEAK] TST LR, #4 ITE EQ MRSEQ R10, MSP MRSNE R10, PSP LDR R11, [R10, #24] ; R11 is the failed PC-address LDR R12, [R10, #20] ; R12 is the failed LR-address B . ENDP


Cortex-M23 (ARMv8M baseline)Cortex-M33 (ARMv8M mainline)
HardFault_Handler\ PROC EXPORT HardFault_Handler [WEAK] MOVS R5, #0x40 MOV R6, LR TST R5, R6 BEQ HardFault_ns HardFault_s MOVS R5, #4 MOV R6, LR TST R5, R6 BEQ HardFault_s_M MRS R5, PSP B HardFault_reg HardFault_s_M MRS R5, MSP B HardFault_reg HardFault_ns MOVS R5, #2 MRS R5, CONTROL_NS TST R5, R6 BEQ HardFault_ns_M MRS R5, PSP_NS B HardFault_reg HardFault_ns_M MRS R5, MSP_NS HardFault_reg LDR R6, [R5, #24] ; R6 is the failed PC-address LDR R7, [R5, #20] ; R7 is the failed LR-address B . ENDP

HardFault_Handler\ PROC EXPORT HardFault_Handler [WEAK] TST LR, #0x40 BEQ HardFault_ns HardFault_s TST LR, #0x04 ITE EQ MRSEQ R10, MSP MRSNE R10, PSP B HardFault_reg HardFault_ns MRS R10, CONTROL_NS TST R10, #2 ITE EQ MRSEQ R10, MSP_NS MRSNE R10, PSP_NS HardFault_reg LDR R11, [R10, #24] ; R11 is the failed PC-address LDR R12, [R10, #20] ; R12 is the failed LR-address B . ENDP




If these two registers are not sufficient to find the error or a detailed implementation required at run-time,, a comprehensive handler should be implemented as the following.

Cortex-M0Cortex-M3/4
HardFault_Handler\ PROC EXPORT HardFault_Handler [WEAK] MOVS R0, #4 MOV R1, LR TST R0, R1 BEQ HardFault_M MRS R0, PSP B HardFault_reg HardFault_M MRS R0, MSP HardFault_reg B HardFault_HandlerC ENDP

HardFault_Handler\ PROC EXPORT HardFault_Handler [WEAK] TST LR, #4 ITE EQ MRSEQ R0, MSP MRSNE R0, PSP B HardFault_HandlerC ENDP

void HardFault_HandlerC(unsigned int *hardfault_args) { volatile unsigned long stacked_r0 ; volatile unsigned long stacked_r1 ; volatile unsigned long stacked_r2 ; volatile unsigned long stacked_r3 ; volatile unsigned long stacked_r12 ; volatile unsigned long stacked_lr ; volatile unsigned long stacked_pc ; volatile unsigned long stacked_psr ; volatile unsigned long _CFSR ; volatile unsigned long _HFSR ; volatile unsigned long _DFSR ; volatile unsigned long _AFSR ; volatile unsigned long _BFAR ; volatile unsigned long _MMAR ; 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]) ; // Configurable Fault Status Register // Consists of MMSR, BFSR and UFSR _CFSR = (*((volatile unsigned long *)(0xE000ED28))) ; // Hard Fault Status Register _HFSR = (*((volatile unsigned long *)(0xE000ED2C))) ; // Debug Fault Status Register _DFSR = (*((volatile unsigned long *)(0xE000ED30))) ; // Auxiliary Fault Status Register _AFSR = (*((volatile unsigned long *)(0xE000ED3C))) ; // Read the Fault Address Registers. These may not contain valid values. // Check BFARVALID/MMARVALID to see if they are valid values // MemManage Fault Address Register _MMAR = (*((volatile unsigned long *)(0xE000ED34))) ; // Bus Fault Address Register _BFAR = (*((volatile unsigned long *)(0xE000ED38))) ; // --> print or handle the information-set __asm("BKPT #0\n") ; // Break into the debugger }



For the Cortex-M23 and Cortex-M33 a file "HardFault.c" exist in CMSIS 5.0.0 that does this.




Otherwise, there are of course many other sources on the internet that deal with this topic....


In the same way you can see / find out the source of an interrupt, who sits on the DefaultHandler because it is not actually used.

Cortex-M
Default_Handler\ PROC EXPORT Default_Handler [WEAK] LDR R7, =0xE000ED04 ; ICSR LDR R7, [R7, #0] UXTB R7, R7 ; R7 is the number of the executing interrupt B . ENDP