I've just spent two days debugging a chibios project that seemed to hang after a few seconds. Turns out that I was getting an "unhandled exception". I ended up editing stuff into "vectors.c" to grab some state and put it in variables that the debugger can print. Once I recognized where the "lr" register was pointing, i.e. seeing where it crashed, it took less than 10 seconds to know what the problem was and a further 60 seconds to actually fix the problem.
This is what I added:
Code: Select all
__asm (" \
.section .text.Reset_Handler \n \
.weak HardFault_Handler \n\
.type HardFault_Handler, %function \n\
HardFault_Handler: \n\
movs r0,#4 \n\
movs r1, lr \n\
tst r0, r1 \n\
beq _MSP \n\
mrs r0, psp \n\
b _HALT \n\
_MSP: \n\
mrs r0, msp \n\
_HALT: \n\
ldr r1,[r0,#20] \n\
b hard_fault_handler_c \n\
bkpt #0 \n\
\n\
.size HardFault_Handler, .-HardFault_Handler\n\
\
");
extern void HardFault_Handler(void);
struct dbg {
unsigned long stacked_r0 ;
unsigned long stacked_r1 ;
unsigned long stacked_r2 ;
unsigned long stacked_r3 ;
unsigned long stacked_r12 ;
unsigned long stacked_lr ;
unsigned long stacked_pc ;
unsigned long stacked_psr ;
unsigned long _CFSR ;
unsigned long _HFSR ;
unsigned long _DFSR ;
unsigned long _AFSR ;
unsigned long _BFAR ;
unsigned long _MMAR ;
NVIC_Type *nvic;
SCB_Type *scb;
};
static volatile struct dbg d;
void hard_fault_handler_c(unsigned long *hardfault_args)
{
d.stacked_r0 = ((unsigned long)hardfault_args[0]) ;
d.stacked_r1 = ((unsigned long)hardfault_args[1]) ;
d.stacked_r2 = ((unsigned long)hardfault_args[2]) ;
d.stacked_r3 = ((unsigned long)hardfault_args[3]) ;
d.stacked_r12 = ((unsigned long)hardfault_args[4]) ;
d.stacked_lr = ((unsigned long)hardfault_args[5]) ;
d.stacked_pc = ((unsigned long)hardfault_args[6]) ;
d.stacked_psr = ((unsigned long)hardfault_args[7]) ;
d.nvic = NVIC;
d.scb = SCB;
// Configurable Fault Status Register
// Consists of MMSR, BFSR and UFSR
d._CFSR = (*((volatile unsigned long *)(0xE000ED28))) ;
// Hard Fault Status Register
d._HFSR = (*((volatile unsigned long *)(0xE000ED2C))) ;
// Debug Fault Status Register
d._DFSR = (*((volatile unsigned long *)(0xE000ED30))) ;
// Auxiliary Fault Status Register
d._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
d._MMAR = (*((volatile unsigned long *)(0xE000ED34))) ;
// Bus Fault Address Register
d._BFAR = (*((volatile unsigned long *)(0xE000ED38))) ;
__asm("BKPT #0\n") ; // Break into the debugger
}
It is not perfect yet. As is: this doesn't seem to work with LTO enabled. And the CFSR etc registers ... might not be available on my M0 processor (STM32F030) as they all end up being zero. And probably somewhere the source of the interrupt/exception should be findable, but I have not been able to find it yet. (I'm really not friends with the ARM documentation site. So in the cortex M0 docs it says to look in the ARM_V6 manual for further information. Well.... they could make that a link. Or easily find-able. But docs for bunches of other arm processor variants are in the menu, but not V6. )