A hard fault is an unrecoverable error that occurs during the execution of a program on an ARM Cortex processor. It indicates a serious problem such as an illegal instruction, invalid memory access, or a bus error. When a hard fault occurs, the processor immediately halts normal program execution and jumps to the hard fault exception handler routine.
There are several ways to intentionally trigger a hard fault on an ARM Cortex-M processor in order to test exception handling code:
Executing an Illegal/Undefined Instruction
All ARM Cortex-M processors have a number of reserved instructions that are undefined and unimplemented. Executing any of these instructions will lead to a hard fault exception being raised immediately. Some examples include:
- UDF (Undefined instruction)
- SVC (Supervisor Call) – unless configured and handled properly
- LDC/STC (Load/Store Coprocessor) – unless a coprocessor is present
- MRC/MCR (Move to/from Coprocessor) – unless a coprocessor is present
To test this, write a simple program that contains an undefined instruction like UDF and run it on the target processor. The illegal instruction execution will trigger a hard fault exception.
Accessing Invalid Memory
Attempting to access memory locations outside of the valid range will also lead to a hard fault. This includes:
- Reading or writing memory outside of the configured RAM/ROM range
- Accessing unaligned memory in some Cortex-M models like Cortex-M3
- Accessing peripheral memory without properly configuring and enabling its interface
To test this, write a program that tries to read or write an invalid memory address that is known to be inaccessible. The faulty memory access will cause a hard fault exception.
Triggering a Bus Fault
Bus faults occur when there is an error in accessing external memory or peripherals in the system. Some ways to generate bus faults include:
- Reading from a peripheral that is not responding or has improper access settings
- Writing to a read-only peripheral location
- Configuring invalid control signals for external memories
- Accessing external memory that is busy or has timed out
This requires having some peripherals or external memory in your system and manipulating the access methods to cause an error. The resulting bus fault will invoke the hard fault handler.
Enabling Faulty Behavior
Some other ways to trigger hard faults by enabling faulty behavior include:
- Enabling stack limit checking and overflowing the main stack
- Enabling heap corruption checking and corrupting dynamic memory
- Forcing a failed assertion with __ASM(“BKPT #0”);
- Enabling divide by zero checks and then dividing by zero
Many ARM Cortex-M devices allow enabling these kind of runtime checks that will fault on common errors. Refer to the technical reference manual for the specific device to see available options.
Generating a Non-Maskable Interrupt
All ARM Cortex-M processors configure the NMI (Non-Maskable Interrupt) as the highest priority interrupt. By default, the NMI will escalate to a hard fault if left unhandled.
An NMI can be triggered externally by a device or system logic. Or software can trigger the NMI internally via the following methods:
- Setting the NMI bit in the Interrupt Control State Register
- Triggering the Non-Maskable Interrupt vector fetch
With no NMI handler set up, this will result in a hard fault exception when the NMI occurs.
Manual Exception Triggering
The System Control Block on Cortex-M devices contains configurable exception triggering mechanisms that can be used to manually invoke a hard fault.
- Setting the HFSR Hard Fault Status Register to values 0x40000000 or 0x80000000 will trigger the exception
- Setting the SHCSR System Handler Control and State Register BFHFNMIGN bit will also trigger a hard fault on next instruction
This requires direct SCB register access, but allows forcing a hard fault exception at any point in code execution.
Faulty External Event Handling
Hard faults can also occur during exception handling for external events like interrupts, if the handling code itself has bugs and causes exceptions. Some ways this could occur include:
- Having an interrupt occur but its corresponding vector table entry is invalid
- Coding errors, infinite loops, or stack overflows in an interrupt handler leading to faults
- Returning improperly from an exception handler
Introducing bugs into interrupt handlers by having invalid vectors, removing stack pointers, or returning incorrectly can potentially cause a hard fault to occur.
When debug is enabled on Cortex-M devices, debug events will halt execution and enter debug state. There are several debug exceptions that can potentially trigger a hard fault if not handled properly, such as:
- A breakpoint match generates a debug halt event
- Single-stepping with debug triggers a debug exception on each step
- Vector catching can halt execution on specific exceptions
- Debug monitor exceptions for debug access errors
Leaving debug exceptions unhandled when debug is enabled can result in them escalating to a hard fault. This requires debug hardware support to be present.
Faulty Register Usage
Invalid use of some registers can also lead to hard faults, such as:
- Overwriting stack pointers, link registers, or program counters
- Corrupting exception handling registers like IPSR
- Enabling fault exceptions then forcing corresponding faults like division by zero
Some simple examples are overwriting the MSP, PSP, LR or PC registers with invalid values that will lead to a fault when returning or branching.
In summary, the main methods of triggering hard faults intentionally are:
- Executing undefined or illegal instructions
- Accessing invalid memory regions
- Causing bus errors through bad external access
- Enabling other fault checking features and behaviors
- Triggering the NMI exception
- Manual exception triggering via SCB registers
- Introducing bugs into exception/interrupt handling flows
- Generating unhandled debug exceptions
- Overwriting or misusing key registers
This provides a wide range of options for testing and validating the hard fault handler response. The specific methods used will depend on the particular ARM Cortex-M device, tools available, and if the code is executing on physical hardware or on an instruction set simulator. By intentionally introducing these types of errors, developers can thoroughly test and debug the hard fault handling routines.