/*
Unlike Cortex-M3, on Cortex-M0 it is not possible to access the debug
registers to find out if a debug probe (like LPC-link) is attached. Only
debug tools can access these registers.
At the "bottom layer" of the C library, semihosting is implemented by a
"BKPT 0xAB" instruction. When the debug tools are not connected, this
instruction triggers a hard fault - and the default hard fault handler
just contains an infinite loop. This is why your application appears to
hang.
But what we can do instead, is provide a hard fault handler which
instead looks to see what the instruction that caused the hard fault was
- and if it was a "BKPT 0xAB", then it instead returns back to the user
application.
--- CodeRed support June 2011.
*/
void HardFault_HandlerC(unsigned long *hardfault_args);
// Use the 'naked' attribute so that C stacking is not used.
__attribute__((naked)) void HardFault_Handler(void){
/*
* Get the stack pointer that was in use (MSP or PSP), depending on
* current operating mode, and pass it as the parameter to the C handler.
* This function will never return EXCEPT if it was an unhandled
* debugger breakpoint (which happens if no debug probe is attached).
*/
__asm(".syntax unified\n"
// Check which stack is in use
"MOVS R0, #4 \n"
"MOV R1, LR \n"
"TST R0, R1 \n"
"BEQ _MSP \n"
"MRS R0, PSP \n"
"B _checkFaultInstruction \n"
"_MSP: \n"
"MRS R0, MSP \n"
"_checkFaultInstruction: \n"
// Load the instruction that triggered hard fault
"LDR R1,[R0,#24] \n" // R1 = address of instruction that caused fault
"LDRH R2,[r1] \n"
// Semihosting instruction is "BKPT 0xAB" (0xBEAB)
"LDR R3,=0xBEAB \n"
"CMP R2,R3 \n"
// If it is NOT a BKT 0xAB, jump to the terminal hardfault handler
"BNE HardFault_HandlerC \n"
// Was semihosting instruction, so adjust location to
// return to by 1 instruction (2 bytes), then exit function
"ADDS R1,#2 \n"
"STR R1,[R0,#24] \n"
"BX LR \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
}
|