Debugging Embedded Systems Part 2: Fault handling and diagnosis

Posted on July 28, 2016

This article is the second in a series on the new debug features in SOMNIUM DRT. The first one was about the live expression view.

Debugging is difficult, especially when unexpected errors cause the program to behave unpredictably, such as writing to an invalid area of memory or executing an illegal instruction. The ARM architecture includes an exception mechanism to trap such faults. This is a powerful feature but it can be difficult to understand the information available.

SOMNIUM DRT includes features to support debugging embedded systems. One of these is a Fault Diagnosis tool that presents the fault information in a human-readable format. I have written briefly about the Fault Diagnosis view in DRT before. This article goes into a bit more detail about how to make the best use of it.


SOMNIUM DRT

SOMNIUM DRT is is a set of development tools for ARM Cortex-M based devices such as SMART devices from Atmel, Kinetis and LPC devices from NXP, and STM32 devices from STMicroelectronics. It is fully compatible with industry-standard tools such as the GNU toolchain and Eclipse IDE. DRT uses our patented techniques to produce highly optimized code by exploiting information about the embedded processor, the memory system and other peripherals to deliver improved performance, lower code size and reduced energy use.


Fault Diagnosis

ARM processors raise an exception when a fault occurs. If the exception handler triggers a breakpoint, then you can use this when debugging to find errors in the program. In a live system, the exception handler could attempt to correct for the cause of the error and then restart execution.

There are four types of faults that are detected by Cortex-M3 and Cortex-M4 processors. These trap errors such as illegal memory accesses or attempting to execute non-existent instructions.

  • Bus Fault: An error during instruction fetch or data read/write. The amount of information available about the fault depends on whether the fault is "precise" or "imprecise". Precise bus faults are caused by the last instruction to execute. For example, data read faults are precise because the instruction cannot complete until the data has been read. In this case more detail about the instruction that caused the fault will be displayed. Imprecise bus faults are caused by an instruction that completed several cycles earlier and so the exact instruction that caused the problem is no longer known.
  • Memory Management Fault: For example, attempting to execute an instruction from a non-executable area of memory.
  • Usage Fault: An exception that occurs because of a fault related to instruction execution such as attempting to execute and undefined instruction.
  • Hard Fault: A hard fault is normally caused by one of the other types of fault occurring when the appropriate fault handler cannot be executed for some reason. In this case, the Fault Diagnosis view will display the original cause of the fault, when possible.


In the case of the Cortex-M0 processor, only hard faults are available.

To make best use of the Fault Diagnosis view, you should make sure that your program is set up to catch any faults when they occur. There are two steps to this:

1. Enable all relevant fault handlers

2. Add a breakpoint to the fault handler(s) to stop execution immediately

There are various ways to ensure a breakpoint occurs when a fault handler is triggered. Perhaps the simplest, for occasional use, is to add a breakpoint to the default exception handler code. This is defined in the file startup_ device.S

A more flexible approach is to define your own exception handler. For example:

void __attribute__((naked)) HardFault_Handler(void)

{

__asm__("bkpt 0");

while (1);

}

The names of the other fault handlers are: MemManage_Handler, BusFault_Handler, and UsageFault_Handler. Using this approach allows you to quickly see what type of error occurred. You can also use this as the basis for a more complete exception handler that attempts to continue execution after the fault.

The next step is to enable all the necessary fault handlers.

Note that only the hard-fault handler is enabled by default. Also, fault handling is not enabled by default for for unaligned memory accesses and divide by zero errors, Therefore you should make sure that the exception handlers and fault types you need are enabled. To enable exception handlers for all the above, you can add the following code where you do most of your initialization (for example, at the start of the main() function):

// Enable all exception handlers

SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk

| SCB_SHCSR_BUSFAULTENA_Msk

| SCB_SHCSR_USGFAULTENA_Msk;

// Enable divide by zero and unaligned access faults

SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk

| SCB_CCR_UNALIGN_TRP_Msk;


The hardware records information about the cause of the fault in various system registers. Different levels of detail are available depending on the architecture and the type of the fault.

The DRT Fault Diagnosis tool will automatically extract this information, decode it and display it in a readable form, relating it to the program source code (when possible). This is much easier than looking at register values, decoding bit-fields and referring to the ARM architecture manual.

The Fault Diagnosis view will display the source of the error (including the file name and line number, when available). You can display the source or the disassembly view of the code that caused the error, and see the values of the main registers when the error occurred.

For more information on the information displayed, see the DRT Reference Manual.

A free trial of DRT is available. DRT is able to automatically import projects from other development tools, making it simple to migrate your projects to DRT so you can quickly see the benefits for yourself. Download your free trial today and try out these powerful debug features for yourself.

x

Download a free trial of SOMNIUM DRT

   Got a question?