**** Advance Notice ****

This site will be closed on 31 December 2015,

Important Information for users of NXP LPCXpresso

This site is for users of Code Red branded products.

NXP LPCXpresso users should visit the LPCXpresso FAQ's for up-to-date information relevant to that product.

Debugging a hard fault (Cortex-M4/M3/M0+/M0 based processors)

If you are getting a hard fault when executing your code under the debugger, then you can tell where in your sources this is being caused by looking at the VECTPC pseudo-register that is displayed in the Core Register view when the hard fault is trapped. If you hover your mouse over VECTPC, it will do a lookup on the address and the tooltip will show you where in your code that is.

Alternatively you can match the address show in VECTPC up to the code as shown in a disassembly of the .axf file. Information on how to create a disassembly can be found in the FAQ Disassembling objects and executables.

Hopefully from there, you should be able to find and fix the cause of the fault.

Possible reasons include:

In some circumstances, a hard fault might be caused early on during the initialisation of the system before the breakpoint on main() is hit. This can mean that the fault is triggered before the debugger can take action to display VECTPC.

If this happens, try setting a breakpoint in the startup code (ie inside cr_startup_lpcXX.c, say on the call to SystemInit() ) - this might then allow your code to load without the hard fault being triggered. You should then be able to single step / run until the cause of the hard fault is hit. Hopefully you will then see VECTPC displayed.

Extending the hard fault handler

Alternatively it is possible to provide an extended hard fault handler, for example to provide more detailed information as to the state of the MCU when the hard fault condition is encountered. The below code shows an example of how this can be done.


 * HardFault_HandlerAsm:
 * Alternative Hard Fault handler to help debug the reason for a fault.
 * To use, edit the vector table to reference this function in the HardFault vector
 * This code is suitable for Cortex-M3 and Cortex-M0 cores

// Use the 'naked' attribute so that C stacking is not used.
void HardFault_HandlerAsm(void){
         * Get the appropriate stack pointer, depending on our mode,
         * and use it as the parameter to the C handler. This function
         * will never return

        __asm(  ".syntax unified\n"
                        "MOVS   R0, #4  \n"
                        "MOV    R1, LR  \n"
                        "TST    R0, R1  \n"
                        "BEQ    _MSP    \n"
                        "MRS    R0, PSP \n"
                        "B      HardFault_HandlerC      \n"
                "_MSP:  \n"
                        "MRS    R0, MSP \n"
                        "B      HardFault_HandlerC      \n"
                ".syntax divided\n") ;

 * HardFaultHandler_C:
 * This is called from the HardFault_HandlerAsm with a pointer the Fault stack
 * as the parameter. We can then read the values from the stack and place them
 * into local variables for ease of reading.
 * We then read the various Fault Status and Address Registers to help decode
 * cause of the fault.
 * The function ends with a BKPT instruction to force control back into the debugger
void HardFault_HandlerC(unsigned long *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))) ;

        __asm("BKPT #0\n") ; // Break into the debugger


Note that depending upon tools version and target MCU, you may encounter an assembler error when building the above code along the lines of:

Error: instruction not supported in Thumb16 mode -- `adds r4,r4,#12'

If you do, then deleting the ".syntax unified\n" and ".syntax divided\n" in the inline assembler of the function HardFault_HandlerAsm() should solve the problem.

DebugHardFault (last edited 2013-02-21 08:42:33 by CrSupportAb)