Cortex-M3 / M4 Hard Fault Handler

http://blog.frankvh.com/2011/12/07/cortex-m3-m4-hard-fault-handler/

If you’re seeing a Hard Fault exception on your Cortex M3 or Cortex M4 processor, this handler and information may help. I can’t take credit for it – this code was provided by Joseph Yiu on a few different forums, as well as in his book (Definitive Guide to the ARM Cortex M3). I’m simply providing some assistance on how to install and use it.

Hard Fault Handler Installation

These instructions work for an STM32F2xx or STM32F4xx processor using a GNU-based toolchain (eg Yagarto or Sourcery G++). They should work with other processors and toolchains but may require a small tweak – no doubt your compiler will be pleased to tell you if it’s not happy! As always with programming, the following is not the only way to do it – it’s simply the way I did it. If you want to rearrange things or do things a bit differently then feel free.

Joseph’s hard fault handler is in two pieces – a small piece of assembly, and a small piece of C. You need the processor’s hardfault exception vector to jump to the assembly, and then the assembly code will itself call the C code.

Here’s the assembly code. It extracts the location of the stack frame, then passes it as a pointer to the C code, which is named hard_fault_handler_c.

.syntax unified
.cpu cortex-m3
.thumb
 
.global HardFault_Handler
.extern hard_fault_handler_c
 
HardFault_Handler:
  TST LR, #4
  ITE EQ
  MRSEQ R0, MSP
  MRSNE R0, PSP
  B hard_fault_handler_c

For those of you using the IAR tool chain, the assembly stub must be specified in a modules and section to compile and link properly. Below is the code that I used to apply this example to the IAR tool chain:

MODULE HARDFAULT_MOD
SECTION HARDFAULT_SECT : CODE(2)
THUMB
PUBLIC HardFault_Handler
EXTERN HardFault_Handler_C
HardFault_Handler:
TST LR, #4
ITE EQ
MRSEQ R0, MSP
MRSNE R0, PSP
B HardFault_Handler_C
END
void HardFault_Handler( void )
{
__ASM(“TST LR, #4″);
__ASM(“ITE EQ”);
__ASM(“MRSEQ R0, MSP”);
__ASM(“MRSNE R0, PSP”);
__ASM(“B hard_fault_handler_c”);
}

This assembly needs to be immediately called when the hard fault exception occurs.

If you look at the vectors list, you’ll see something like this:

g_pfnVectors:
  .word  _estack
  .word  Reset_Handler
  .word  NMI_Handler
  .word  HardFault_Handler
  .word  MemManage_Handler
  .word  BusFault_Handler
  .word  UsageFault_Handler
  etc

Given that the name in the vector table is HardFault_Handler, we give the assembler code that name (and declare the name as a global so the linker can find it). If your vector table contains a different name, then change the name of the assembler code to suit.

You need to include this bit of assembler in your build. Just save the assembler code in its own .s file (eg: hardfault.s) and then include it in your build, the same way as your other .s files (like that startup file) are.

Now we need to add the C code. Here it is:

// From Joseph Yiu, minor edits by FVH
// hard fault handler in C,
// with stack frame location as input parameter
// called from HardFault_Handler in file xxx.s
void hard_fault_handler_c (unsigned int * hardfault_args)
{
  unsigned int stacked_r0;
  unsigned int stacked_r1;
  unsigned int stacked_r2;
  unsigned int stacked_r3;
  unsigned int stacked_r12;
  unsigned int stacked_lr;
  unsigned int stacked_pc;
  unsigned int stacked_psr;
 
  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]);
 
  printf ("\n\n[Hard fault handler - all numbers in hex]\n");
  printf ("R0 = %x\n", stacked_r0);
  printf ("R1 = %x\n", stacked_r1);
  printf ("R2 = %x\n", stacked_r2);
  printf ("R3 = %x\n", stacked_r3);
  printf ("R12 = %x\n", stacked_r12);
  printf ("LR [R14] = %x  subroutine call return address\n", stacked_lr);
  printf ("PC [R15] = %x  program counter\n", stacked_pc);
  printf ("PSR = %x\n", stacked_psr);
  printf ("BFAR = %x\n", (*((volatile unsigned long *)(0xE000ED38))));
  printf ("CFSR = %x\n", (*((volatile unsigned long *)(0xE000ED28))));
  printf ("HFSR = %x\n", (*((volatile unsigned long *)(0xE000ED2C))));
  printf ("DFSR = %x\n", (*((volatile unsigned long *)(0xE000ED30))));
  printf ("AFSR = %x\n", (*((volatile unsigned long *)(0xE000ED3C))));
  printf ("SCB_SHCSR = %x\n", SCB->SHCSR);
 
  while (1);
}

This code goes wherever the existing (previous) Hard Fault Handler went. In our example, the vector table pointed to a function called HardFault_Handler. We are replacing that function with the assembler code, so the original HardFault_Handler function needs to be commented out (otherwise we’ll have two functions with the same name). For the STM32F2xx all exception handlers are found in the file: stm32f2xx_it.c So comment out the function HardFault_Handler() from that C file, and paste the C code for Joseph’s hard_fault_handler_c() into the same file.

That’s it. In summary, you commented out the old hard fault handler, and you added in some assembly code and some C code instead. Try building your project and see what happens.

Note that this code will only work if the main stack pointer hasn’t been badly corrupted prior to the hard fault occurring – if the stack pointer is off in never-never land then the C handler may not work. In my experience this has never been a problem.

 

KEIL MDK 4.73

 1 #include <stdio.h>
 2 
 3 __ASM void HardFault_Handler(void)
 4 {
 5   TST lr, #4     // Test for MSP or PSP
 6   ITE EQ
 7   MRSEQ r0, MSP
 8   MRSNE r0, PSP
 9     IMPORT Hard_Fault_Handler
10   B Hard_Fault_Handler
11 }
12 
13 void Hard_Fault_Handler(unsigned int * hardfault_args)
14 {
15   unsigned int stacked_r0;
16   unsigned int stacked_r1;
17   unsigned int stacked_r2;
18   unsigned int stacked_r3;
19   unsigned int stacked_r12;
20   unsigned int stacked_lr;
21   unsigned int stacked_pc;
22   unsigned int stacked_psr;
23  
24   stacked_r0 = ((unsigned long) hardfault_args[0]);
25   stacked_r1 = ((unsigned long) hardfault_args[1]);
26   stacked_r2 = ((unsigned long) hardfault_args[2]);
27   stacked_r3 = ((unsigned long) hardfault_args[3]);
28  
29   stacked_r12 = ((unsigned long) hardfault_args[4]);
30   stacked_lr = ((unsigned long) hardfault_args[5]);
31   stacked_pc = ((unsigned long) hardfault_args[6]);
32   stacked_psr = ((unsigned long) hardfault_args[7]);
33  
34   printf ("\n\n[Hard fault handler - all numbers in hex]\n");
35   printf ("R0 = %x\n", stacked_r0);
36   printf ("R1 = %x\n", stacked_r1);
37   printf ("R2 = %x\n", stacked_r2);
38   printf ("R3 = %x\n", stacked_r3);
39   printf ("R12 = %x\n", stacked_r12);
40   printf ("LR [R14] = %x  subroutine call return address\n", stacked_lr);
41   printf ("PC [R15] = %x  program counter\n", stacked_pc);
42   printf ("PSR = %x\n", stacked_psr);
43   printf ("BFAR = %x\n", (*((volatile unsigned long *)(0xE000ED38))));
44   printf ("CFSR = %x\n", (*((volatile unsigned long *)(0xE000ED28))));
45   printf ("HFSR = %x\n", (*((volatile unsigned long *)(0xE000ED2C))));
46   printf ("DFSR = %x\n", (*((volatile unsigned long *)(0xE000ED30))));
47   printf ("AFSR = %x\n", (*((volatile unsigned long *)(0xE000ED3C))));
48   printf ("SCB_SHCSR = %x\n", SCB->SHCSR);
49  
50   while (1);    
51 }

 

posted @ 2013-04-30 21:57  IAmAProgrammer  阅读(1392)  评论(0编辑  收藏  举报