The purpose of this Technical Note is to show how HardFault errors can be debugged using IAR Embedded Workbench for ARM.
Different scenarios are described using a number of examples, see below.
HardFault is the generic fault that exists for all classes of fault that cannot be handled by any of the other exception mechanisms. Typically, HardFault is used for unrecoverable system failures.
In this example, the CPU clock on a Cortex-M3 board has been set to a very high frequency. This leads to HardFault exceptions at "random" places, at instructions that are normally valid.
See the screenshot below. Using the "Call Stack" window, it is possible to see which line of code that was executed when the HardFault occurred.
Using the Register window, the NVIC:CFSR (Configurable Fault Status Register) shows that an "Imprecise data access error" has occurred. (BusFault Status Register, BFSR). IMPRECISERR = 1 = Imprecise data access error has occurred.
Since the error is "imprecise", we do not get the address of the offending data access. In this specific case, it is very difficult to find the actual reason for the problem, since it has to do with a CPU running at an incorrect frequency.
This example shows how to "catch" division by zero errors, by enabling the "DIV_0_TRP" bit in the CCR register. Using the Call Stack window, you will see the source code line where the invalid division occurred. Looking at the Register window, you will find that the NVIC:CFSR flag "DIVBYZERO" is set. See the screenshot below.
In this example, invalid memory is accessed. Using the Call Stack window, you will find where the illegal access was made. Using the Register window, the NVIC:CFSR flags show a "PRECISERR". A precise data access error has occurred, and the processor has written the faulting address to the BFAR register. See the screenshots below.
In this example, an invalid function pointer is called. Using the Register window, the NVIC:CFSR flags show: UNDEFINSTR = 1 = The processor has attempted to execute an undefined instruction.
Using the Call Stack window, you will find from where the illegal instruction was called. Now, there are several ways to continue:
1. Place a breakpoint at the illegal instruction, and re-run the program. When you get to the breakpoint, just use the Call Stack window to get to the calling function.
2. Using the Register window, looking at the CPU:LR register, you will find where the previous call was made. Use the address from the LR register in the Disassembly window, and "Go to" that address. This is where the previous call was made. See screenshots below.
To make it easier to identify exactly what type of HardFault the application has encountered, there
is a debugger macro available in recent versions of IAR Embedded Workbench for ARM. The macro file
is located in the installation directory under arm\config\debugger\ARM\vector_catch.mac
Load the macro via the View > Macros > Macro Registration window. When a HardFault is triggered, the macro will produce useful output in the Debug Log window. See the screenshot below.
Note that the information in the Register window may look different from the screenshots above, depending on what type of Cortex-M device you are using.
If you have complex code in the Fault Handlers, it might be a good idea to set a breakpoint early in the handler, so that registers and buffers do not lose any vital information when continuing. By setting a breakpoint, execution will stop immediately when the fault handler is called.
The above information does not cover how to use Trace to debug the problem. Please see our articles on Trace in the Resources section of www.iar.com.
See chapter "Fault types" in the "Cortex-M3 Devices Generic User Guide".
All product names are trademarks or registered trademarks of their respective owners.