How to Analyze HardFault on ARM Cortex-M: Step-by-Step Guide
To analyze a
HardFault on ARM Cortex-M, first capture the fault stack frame from the processor's stack pointer at the time of the fault. Then, read the HardFault Status Register (HFSR) and related fault status registers like Configurable Fault Status Register (CFSR) to identify the cause. Using these, you can pinpoint the instruction and reason that triggered the fault.Syntax
The key registers and steps to analyze a HardFault are:
HFSR: HardFault Status Register, indicates fault type.CFSR: Configurable Fault Status Register, shows precise fault cause.Fault Stack Frame: Contains CPU registers saved automatically on fault.Stack Pointer (SP): Points to the fault stack frame.
Use these to decode the fault and find the faulting instruction address.
c
volatile uint32_t *HFSR = (uint32_t *)0xE000ED2C; volatile uint32_t *CFSR = (uint32_t *)0xE000ED28; // Example: Read fault status uint32_t hfsr_value = *HFSR; uint32_t cfsr_value = *CFSR;
Example
This example shows how to extract the stacked registers from the fault stack frame inside the HardFault handler to find the program counter (PC) at fault.
c
void HardFault_Handler(void) { __asm volatile ( "tst lr, #4 \n" // Test EXC_RETURN bit 2 to find stack used "ite eq \n" "mrseq r0, msp \n" // If 0, use MSP "mrsne r0, psp \n" // Else use PSP "b HardFault_Handler_C \n" ); } void HardFault_Handler_C(uint32_t *stack_frame) { uint32_t r0 = stack_frame[0]; uint32_t r1 = stack_frame[1]; uint32_t r2 = stack_frame[2]; uint32_t r3 = stack_frame[3]; uint32_t r12 = stack_frame[4]; uint32_t lr = stack_frame[5]; uint32_t pc = stack_frame[6]; uint32_t psr = stack_frame[7]; // Now pc holds the address of the instruction that caused the fault // You can inspect or log these values for debugging while(1); // Halt here for debugger }
Common Pitfalls
Common mistakes when analyzing HardFaults include:
- Not checking which stack pointer (MSP or PSP) was used at fault, leading to wrong stack frame data.
- Ignoring the
HFSRandCFSRregisters that provide fault cause details. - Failing to enable fault handlers and system exceptions in the NVIC and SCB.
- Not using a debugger to inspect the faulting
PCaddress and source code.
Always verify the stack pointer source and read fault status registers for accurate analysis.
c
/* Wrong: Assuming MSP always used */ void HardFault_Handler(void) { uint32_t *stack_frame = (uint32_t *)__get_MSP(); // This may be wrong if PSP was used } /* Right: Check EXC_RETURN to select stack pointer */ void HardFault_Handler(void) { __asm volatile ( "tst lr, #4 \n" "ite eq \n" "mrseq r0, msp \n" "mrsne r0, psp \n" "b HardFault_Handler_C \n" ); }
Quick Reference
Summary tips for HardFault analysis:
- Check
HFSRat0xE000ED2Cfor HardFault cause. - Check
CFSRat0xE000ED28for precise fault details. - Extract stacked registers from the correct stack pointer (MSP or PSP).
- Use debugger to find faulting instruction address (
PCin stack frame). - Enable fault handlers and system exceptions in system control block.
Key Takeaways
Always extract the fault stack frame using the correct stack pointer (MSP or PSP) based on EXC_RETURN.
Read the HardFault Status Register (HFSR) and Configurable Fault Status Register (CFSR) to identify fault causes.
The program counter (PC) in the fault stack frame shows the exact instruction that caused the fault.
Enable system fault handlers and use a debugger to inspect fault registers and stack for effective analysis.
Common mistakes include assuming the wrong stack pointer and ignoring fault status registers.