ARM Cortex-M HardFault
Wer kennt das nicht... Da läuft die Software seit Stunden oder gar Tagen/Wochen und dann steht das Ganze plötzlich im HardFault... :-(
Was kann da besser helfen, als ein kleiner Assembler Schnipsel im startup-code, der einem wenigstens die failed PC-Adresse und die Rückkehraddresse (zum Aufrufer) heraus holt.
Sollten die lokalen Variablen dort nicht durch die hier verwendeten Register überschrieben sein, kann man den Fehler somit schon recht gut mit einem angeschlossenen JTAG-Debugger identifizieren...
Cortex-M0 | Cortex-M3/4 |
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
MOVS R5, #4
MOV R6, LR
TST R5, R6
BEQ HardFault_M
MRS R5, PSP
B HardFault_reg
HardFault_M
MRS R5, MSP
HardFault_reg
LDR R6, [R5, #24] ; R6 is the failed PC-address
LDR R7, [R5, #20] ; R7 is the failed LR-address
B .
ENDP
|
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
TST LR, #4
ITE EQ
MRSEQ R10, MSP
MRSNE R10, PSP
LDR R11, [R10, #24] ; R11 is the failed PC-address
LDR R12, [R10, #20] ; R12 is the failed LR-address
B .
ENDP
|
Cortex-M23 (ARMv8M baseline) | Cortex-M33 (ARMv8M mainline) |
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
MOVS R5, #0x40
MOV R6, LR
TST R5, R6
BEQ HardFault_ns
HardFault_s
MOVS R5, #4
MOV R6, LR
TST R5, R6
BEQ HardFault_s_M
MRS R5, PSP
B HardFault_reg
HardFault_s_M
MRS R5, MSP
B HardFault_reg
HardFault_ns
MOVS R5, #2
MRS R5, CONTROL_NS
TST R5, R6
BEQ HardFault_ns_M
MRS R5, PSP_NS
B HardFault_reg
HardFault_ns_M
MRS R5, MSP_NS
HardFault_reg
LDR R6, [R5, #24] ; R6 is the failed PC-address
LDR R7, [R5, #20] ; R7 is the failed LR-address
B .
ENDP
|
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
TST LR, #0x40
BEQ HardFault_ns
HardFault_s
TST LR, #0x04
ITE EQ
MRSEQ R10, MSP
MRSNE R10, PSP
B HardFault_reg
HardFault_ns
MRS R10, CONTROL_NS
TST R10, #2
ITE EQ
MRSEQ R10, MSP_NS
MRSNE R10, PSP_NS
HardFault_reg
LDR R11, [R10, #24] ; R11 is the failed PC-address
LDR R12, [R10, #20] ; R12 is the failed LR-address
B .
ENDP
|
Wenn diese beiden Register nicht ausreichen um den Fehler zu finden oder eine detail-Implementierung zu Laufzeit benötigt wird, sollte ein umfassender Handler wie der Nachfolgende implementiert werden.
Cortex-M0 | Cortex-M3/4 |
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
MOVS R0, #4
MOV R1, LR
TST R0, R1
BEQ HardFault_M
MRS R0, PSP
B HardFault_reg
HardFault_M
MRS R0, MSP
HardFault_reg
B HardFault_HandlerC
ENDP
|
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
TST LR, #4
ITE EQ
MRSEQ R0, MSP
MRSNE R0, PSP
B HardFault_HandlerC
ENDP
|
void HardFault_HandlerC(unsigned int *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))) ;
// --> print or handle the information-set
__asm("BKPT #0\n") ; // Break into the debugger
}
|
Für den Cortex-M23 und Cortex-M33 existiert eine Datei "HardFault.c" im CMSIS 5.0.0 die dieses übernimmt.
Ansonsten gibt es natürlich viele weitere Quellen im Internet, die sich mit diesem Thema beschäftigen....
Auf die gleiche Weise kann man die Quelle eines Interruptes, der auf dem DefaultHandler sitzt weil er eigentlich nicht verwendet wird, herausfinden.
Cortex-M |
Default_Handler\
PROC
EXPORT Default_Handler [WEAK]
LDR R7, =0xE000ED04 ; ICSR
LDR R7, [R7, #0]
UXTB R7, R7 ; R7 is the number of the executing interrupt
B .
ENDP
|