3. Hardfaults Session¶
This section provides a brief description of hardfaults on Cortex-M0 processors. It also describes the tools that can be used to deal with system faults.
It also explains how the SDK handles system faults and demonstrates a real use case of a hardfault, including all the steps that need to be followed to handle the fault.
3.1. Introduction¶
In ARM processors, when a program goes wrong and the processor detects the cause that made the device to fail, an exception is raised. On the Cortex-M0 processor integrated in the DA1468x family of devices, there is only one exception type that handles hardfaults. This is named “the hardfault handler”. There are many reasons for a fault to occur, such as accessing invalid memory address during bus transaction or attempting to generate an unaligned memory access.
The way the SDK handles the various system faults depends on whether the application is built in development or production mode.
- In development mode (enabled by default), the SDK stores the system status in a predefined retained location in memory (SySRAM) and then adds an infinite loop. This allows the developer to attach a debugger, extract all the information stored in that memory area, and eventually identify the reason for the fault.
- In production mode, adding an infinite loop is not practical as it would require the user to get involved with
debugging and recovery of the system. Instead, the system status is stored in a dedicated retained area (
hard_fault_info
) in SySRAM memory and a system reset is triggered so that the device starts its execution from start.
Note
When in production mode and before executing the main application tasks, it can be determined whether or
not a reset derives from a system fault by examining the information stored during a hardfault exception.
For validity purposes, the first entry in the memory area, where the system status has been stored, should be
0xBADC0FFE
. This entry can be considered a special flag to indicate that the data that follows is valid.
3.2. Manually Triggering a Hardfault¶
The following real use case demonstrates triggering a hardfault. The next section demonstrates how to identify the cause of the fault.
- Make a copy of the freertos_retarget sample code found in the SDK of the DA1468x family of devices. For information on how to create a new project, see Create a New Project in the Starting a Project tutorial.
- In the
main.c
source file of the newly created application, insert the following code which will trigger a hardfault. The function tries to access an invalid memory address, which triggers an exception to be issued when executed.
/* This is an invalid memory address - outside the recognized memory boundaries */
#define INVALID_ADDRESS 0x99999999UL;
void trigger_hardfault(void);
void trigger_hardfault(void)
{
/* Declare a pointer that points to an invalid memory address */
uint8_t *p = (uint8_t *) INVALID_ADDRESS;
/* Try to access that invalid memory address */
*p = 0x50;
}
- In the main task of the application, that is
prvTemplateTask
, call thetrigger_hardfault()
function somewhere within its main loop, for example:
/* Place this task in the blocked state until it is time to run again.
The block time is specified in ticks, the constant used converts ticks
to ms. While in the blocked state, this task will not consume any CPU
time. */
vTaskDelayUntil( &xNextWakeTime, mainCOUNTER_FREQUENCY_MS );
/* Trigger a hardfault deliberately! */
trigger_hardfault();
test_counter++;
- Optionally, to enable debugging messages on the serial console, add the following macro definition in the
config/custom_config_qspi.h
header file.
/* Enable hardfault debugging messages */
#define VERBOSE_HARDFAULT (1)
- Build the project in Debug mode (for example, in the case of DA14681 SoC select the
DA14681-01-Debug-QSPI
build scheme) and burn the generated image to the chip.
Note
Debug mode is preferred over production mode when a debugging session is to be performed, as stepping the code is a more straightforward task. In production mode, the source code is built using optimizations, thus making tracing more complex.
- Press the K2 button on Pro DevKit. This starts the chip executing its firmware. After a while, the hardfault will be triggered.
3.3. Dealing with a Hardfault¶
This section provides the steps to identify the cause of a hardfault. Pointing to the exact location in the source code where a fault occurred, can be a tough task. However, a debugging session can reveal the point where things started to go wrong.
Initiate a debugging session by selecting the ATTACH mode. When switching to Debug view, select Suspend to pause the code execution.
Program execution should be stuck in an infinite loop under the
HardFault_HandlerC
interrupt handler.
The hardfault handler function provides the whole register set values when the hardfault was triggered. For example, the values of registers R0 to R3, R12, LR, PC, and xPSR are stored in memory position0x07FC5600
.
- Use the Memory Browser tool (1) to view the contents stored in memory. In this tool, enter the base address
where the stack frame is stored (2). Either enter the physical memory address value or the name of the corresponding
macro, that is,
STATUS_BASE
.
If debugging output is enabled, the following information will be displayed on the console:
The most useful information is held in the Program Counter (PC) and Link Register (LR). The PC holds the current instruction address plus four bytes (this is caused by the pipeline nature of the Cortex-M0 processor). The LR is used for storing the return address of a subroutine or function call. At the end of the subroutine or function, the return address stored in LR is loaded into the PC so that the execution of the calling program is resumed.
This information, together with the Disassembly tool, can be used to identify the exact assembly command that caused the error.
- At this stage, we can examine the command pointed to by the PC register value. To do this, select the Dissassembly window (1), enter the value of the PC (2), and press Enter. Next locate the command pointed by the PC register (3) (displayed both in C and Assembly language).
- Similarly, examine the instruction pointed by the Link register. Since the command that caused the fault
is part of the
trigger_hardfault()
function, the LR should point to the instruction that will be executed upon the return of this function call.