Several reasons may lead to a hard fault, for example, calling a function pointer with an invalid address, stack overflow, trying to read or write to an on-chip peripheral that is powered down or not being clocked, accessing non-mapped memory etc. It is often difficult to find out what caused the hard fault, fortunately the cortex-M stores some information in ARM fault status register, the hardware automatically pushes several CPU register on the stack before entering the Hard Fault Handler. This document below shows a way to debug a Hard Fault in MQX.
For Hard Fault, we can hook our exception handler into the vector table. we will find vector table in the vector.c source file in the BSP library. By default, isr is configured to DEFAULT_VECTOR, which is the _int_kernel_isr() function of the MQX dispatcher. We cam put our own function, like void my_kernel_isr (void), to vector table offset 0x0c.
Inside my_kernel_isr(), we can get the interrupt stack frame, CPU register content at the time of exception and use directly the my_kernel_isr - just remove the static keyword, as we need the my_kernel_isr to be global, as it is referenced from vectors.c and it will be implemented in the application, main.c, for example.
Now, put breakpoint to this my_kernel_isr and see the registers read by prvGetRegistersFromStack(). Typically at most interest would by "lr" and "pc". For example, "lr" would have an address of an instruction that a return from a function call would return to.
By looking into the .map file or using the debugger, we will know, which function has been executed by the CPU when exception occurred.
Hook our own kernel isr into the vector table
In file vectors.c, the default ISR for Hard_Fault is DEFAULT_VECTOR
Now we modified it to my_kernel_isr, so when hard fault happens, my_kernel_isr will be executed.
Read the register value
The following code is for CodeWarrior/Kinetis Design Studio to a C module, for IAR/KEIL, we need to write the same function as an assembler source file.
void my_kernel_isr( void ) __attribute__( ( naked ) );
/* The fault handler implementation calls a function called
static void my_kernel_isr(void)
" tst lr, #4 \n"
" ite eq \n"
" mrseq r0, msp \n"
" mrsne r0, psp \n"
" ldr r1, [r0, #24] \n"
" ldr r2, handler2_address_const \n"
" bx r2 \n"
" handler2_address_const: .word prvGetRegistersFromStack \n"
void prvGetRegistersFromStack( uint32_t *pulFaultStackAddress )
/* These are volatile to try and prevent the compiler/linker optimising them
away as the variables never actually get used. If the debugger won't show the
values of the variables, make them global my moving their declaration outside
of this function. */
volatile uint32_t r0;
volatile uint32_t r1;
volatile uint32_t r2;
volatile uint32_t r3;
volatile uint32_t r12;
volatile uint32_t lr; /* Link register. */
volatile uint32_t pc; /* Program counter. */
volatile uint32_t psr;/* Program status register. */
r0 = pulFaultStackAddress[ 0 ];
r1 = pulFaultStackAddress[ 1 ];
r2 = pulFaultStackAddress[ 2 ];
r3 = pulFaultStackAddress[ 3 ];
r12 = pulFaultStackAddress[ 4 ];
lr = pulFaultStackAddress[ 5 ];
pc = pulFaultStackAddress[ 6 ];
psr = pulFaultStackAddress[ 7 ];
/* When the following line is hit, the variables contain the register values. */
for( ;; );
Notes about Cortex M4 registers:
MQX sets active stack pointer to PSP in boot.s
The core automatically switches the active stack pointer to MSP in handler mode and switches back to PSP with exception return
Compare with .map file
we need to find out which function executes while hard fault occurs.
To determine application , Use trace debugging tool (DSTREAM, ULINK-PRO, J-Trace, Lauterbach uTrace for Cortex-M …) or enable Kernel log and explore its content with MQX TAD
#define MQX_KERNEL_LOGGING 1
/* Enable kernel log */
_klog_control(KLOG_ENABLED | KLOG_CONTEXT_ENABLED |
KLOG_INTERRUPTS_ENABLED| KLOG_FUNCTIONS_ENABLED | KLOG_INTERRUPT_FUNCTIONS |
KLOG_MEMORY_FUNCTIONS | KLOG_PARTITION_FUNCTIONS, TRUE);
By this we only find, which function causes the problem. Next, we will have to think why this occurs, and give more application specific debugging effort to find the root cause.