Developing A Generic Hard Fault Handler For ARM Cortex M3/M4
Developing A Generic Hard Fault Handler For ARM Cortex M3/M4
Developing A Generic Hard Fault Handler For ARM Cortex M3/M4
Developing a
Generic Hard Fault handler
for
ARM Cortex-M3/Cortex-M4
Niall Cooling
Feabhas Limited
www.feabhas.com
int main(void)
{
int a = 10;
int b = 0;
int c;
…
c = div(a, b);
…
1
16/09/2013
• The CCR is part of the Cortex-M's System Control Block (SCB) and
controls entry trapping of divide by zero and unaligned accesses among
other things.
SCB->CCR |= 0x10;
0x1c-0x28 Reserved
0x2c SVCall System Service Call (SVC) – used for RTOS entry calls
0x3c SysTick
0x40 IRQ0 First Device Specific IRQ (0-239)
…. More IRQs
2
16/09/2013
CMSIS-Core
• Cortex™-M3 & Cortex™-M0
CMSIS
Include
_Template_Vendor
Silicon Device
vendor
Source compiler
Device
ARMArch.h
system_ARMArch.h
ARM ARM Arch e.g. system_ARMCM3.h
e.g. ARMCM3
Source compiler startup_ARMArch.s
system_ARMArch.c
e.g. system_ARMCM3.c
3
16/09/2013
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
4
16/09/2013
Weak linkage
This function attribute is a GNU compiler extension that is supported by the ARM
compiler.
int main(void)
{
simple();
}
void simple(void)
{
printf("overridden in: %s\n", __FILE__);
}
5
16/09/2013
void HardFault_Handler(void)
{
while(1);
}
• Inserting the BKPT (breakpoint) ARM instruction in our code will cause the
processor to enter debug state.
• The immediate following the opcode normally doesn't matter (but always
check) except it shouldn't be 0xAB (which is used for semihosting).
#include "ARMCM3.h"
void HardFault_Handler(void)
{
__ASM volatile("BKPT #01");
while(1);
}
6
16/09/2013
7
16/09/2013
NVIC ETM
1-240 Interrupts Instruction Trace
8-256 Priorities Cortex-M3
SYSTICK CPU Core (5-pins)
Trace Port
TPIU
Trace Port Serial-Wire
Viewer
DAP
Memory (1-pin)
JTAG/SWD ITM
Protection
Instrumentation
Unit
Trace
FPB DWT
BKPT Data Trace
Bus Matrix
Code Buses System Bus
to Flash to Stack SRAM
to Code SRAM to Peripherals
– Timestamps Protection
Instrumentation
Unit
Trace ROM
tables
FPB DWT
BKPT APB
Data
Trace i/f
Bus Matrix
Code Buses System Bus
to Flash to Stack SRAM
to Code SRAM to Peripherals
8
16/09/2013
void HardFault_Handler(void)
{
static char msg[80];
printErrorMsg("In Hard Fault Handler\n");
sprintf(msg, "SCB->HFSR = 0x%08x\n", SCB->HFSR);
printErrorMsg(msg);
__ASM volatile("BKPT #01");
while(1);
}
9
16/09/2013
By examining the HFSR bit configuration, we can see that the FORCED bit is set.
0 1 0 0
When this bit is set to 1, the HardFault handler must read the other fault status
registers to find the cause of the fault.
10
16/09/2013
So given what we know to date, our basic Fault Handler can be updated to:
• check if the FORCED bit is set ( if ((SCB->HFSR & (1 << 30)) != 0) )
• and if so, print out the contents of the CFSR
void HardFault_Handler(void)
{
static char msg[80];
printErrorMsg("In Hard Fault Handler\n");
sprintf(msg, "SCB->HFSR = 0x%08x\n", SCB->HFSR);
printErrorMsg(msg);
if ((SCB->HFSR & (1 << 30)) != 0) {
printErrorMsg("Forced Hard Fault\n");
sprintf(msg, "SCB->CFSR = 0x%08x\n", SCB->CFSR );
printErrorMsg(msg);
}
__ASM volatile("BKPT #01");
while(1);
}
CFSR Output
11
16/09/2013
The bit configuration of the UFSR is shown below, and unsurprisingly the output
shows that bit 9 (DIVBYZERO) is set.
1 0 0 0 0 0
We can now extend the HardFault handler to mask the top half of the CFSR, and if not
zero then further report on those flags, as in:
12
16/09/2013
Register dump
• One final thing we can do as part of any fault handler is to dump out
known register contents as they were at the time of the exception.
• One really useful feature of the Cortex-M architecture is that a core set of
registers are automatically stacked (by the hardware) as part of the
exception handling mechanism.
13
16/09/2013
r12
• On Cortex-M3/M4, in parallel, the ISR address is prefetched on the
instruction bus
r13/sp – ISR ready to start executing as soon as stack push is complete
r14/lr
r15/pc
xPSR
14
16/09/2013
r0
r1 Result(s) from function
Arguments into function
otherwise usable as scratch r2
r3
r4
r5
r6
Register variables r7
Must be preserved r8
r9
r10
r11
15
16/09/2013
• Based on AAPCS rules, we know that the parameter label (stack) will map
onto register r0.
16
16/09/2013
New HardFault_Handler
• We now implement the actual HardFault_Handler.
• This function simply copies the current Main Stack Pointer (MSP) into r0
and then branches to our renamed Hard_Fault_Handler (this is based on
ARM/Keil syntax):
StackDump function
• Finally we implement a function to dump the stack values based on their relative offset
17
16/09/2013
Examining the output, we can see that the program counter (pc) is reported as being
the value 0x00000272, giving us the opcode generating the fault.
Tracing the PC
If we disassemble the image using the command:
By trawling through the listing (listing.txt) we can see the SDIV instruction at
offending line (note also r2 contains 10 and r1 the offending 0).
.text
div
0x00000270: 4602 MOV r2,r0
0x00000272: fb92f0f1 SDIV r0,r2,r1
0x00000276: 4770 BX lr
.text
18
16/09/2013
Power-up/
Reset Thread Mode Handler Mode
Exception
exit
Exception
exit
CONTROL[0] = 1
User Thread Mode Exception
Power-up/
Reset
Thread Mode Handler Mode
Exception
exit
19
16/09/2013
Power-up/
Reset
Thread Mode Handler Mode
Exception Exception
exit
20
16/09/2013
Final Notes
• The initial model for fault handling can be found in Joseph Yiu's excellent
book “The Definitive Guide to the ARM Cortex-M3”
• The code shown was built using the ARM/Keil MDK-ARM Version 4.60
development environment (a 32Kb size limited evaluation is available from
the Keil website)
• The code, deliberately, has not been refactored to remove the hard-coded
(magic) values.
Thank You
Niall Cooling
Feabhas Limited
5 Lowesden Works
Lambourn Woodlands
Hungerford
Berks. RG17 7RY
UK
+44 1488 73050
www.feabhas.com
niall.cooling(at)feabhas.com
@feabhas
blog.feabhas.com
uk.linkedin.com/in/nscooling
21