AnsweredAssumed Answered

Debugging a Hard Fault

Question asked by LPCware Support on Mar 31, 2016

Displaying hard fault information in the debugger

 

If you are getting a hard fault when executing your code under the debugger, the VECTPC pseudo-register tells you the PC of your application when the Hard Fault occurred. By clicking on the VECTPC register in the Register View, additional information is displayed, including the PC that caused the fault, and its matching source file/location (if available) and various Fault registers indicating the exact cause of the Fault, as shown in the screenshot below.

 

Possible reasons for a Hard Fault include:

 

  • Trying to read or write to an on-chip peripheral that is powered down or not being clocked
  • Stack corruption - for example, overwriting the stack with data
  • Calling a function pointer with an invalid address

 

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. You will then see VECTPC displayed.For further information on fault handling, review the following ARM Documentation:

 

 

 

Extending the hard fault handler

 

It is possible to provide an extended hard fault handler in your application, 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.

__attribute__((naked))

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.

Outcomes