A fault exception in the ARM Cortex-M is an unexpected event that occurs during program execution which transfers control to the fault exception handler. This allows the processor to gracefully handle issues and errors before continuing normal execution. Fault exceptions are caused by events like invalid memory accesses, mathematical errors, or external interrupts. When a fault occurs, the processor immediately stops normal program flow, saves its state, and jumps to a predefined fault handler routine. The handler can then analyze the cause, take appropriate action, and finally return control back to the interrupted program.
Types of Fault Exceptions
There are several common fault exceptions that can occur in the Cortex-M processors:
- HardFault – The catch-all fault exception for severe errors like a bus error or invalid exception entry. This is always enabled.
- MemManage – Triggered by a memory protection unit fault, like invalid memory access. Enabled when MPU is in use.
- BusFault – Generated by a bus error, like a failed bus transaction or invalid address. Always enabled.
- UsageFault – Caused by invalid instructions or illegal unaligned access. Always enabled.
- DebugMonitor – Triggered when debug conditions are met. Enabled when debug is active.
- PendSV – Requests a context switch or interrupt-safe software call. Always enabled.
- SVCall – Requests a supervisor call to an OS or library. Always enabled.
The first four fault types are collectively known as system faults. They indicate an internal error has occurred that requires handler intervention before the program can safely proceed. PendSV and SVCall are software-generated faults used to implement context switching, threading, and software interrupts.
Fault Exception Priority
When multiple faults occur, the Cortex-M hardware prioritizes them in the following order from highest to lowest priority:
- HardFault
- MemManage
- BusFault
- UsageFault
- DebugMonitor (if enabled)
- PendSV
- SVCall
This ensures that the most severe faults are handled first. Lower priority faults may be discarded if they become pending while a higher priority handler is still active.
Fault Status Registers
When a fault exception occurs, the Cortex-M processor automatically saves fault status information to a set of registers known as the fault status registers. These include:
- Fault Address Register (FAR) – Holds the address that caused a memory related fault.
- Fault Status Register (CFSR) – Flags indicating the source of the fault.
- HardFault Status Register (HFSR) – Additional flags providing details for HardFault.
- Debug Fault Status Register (DFSR) – Debug related fault flags.
By checking these registers in the fault handler, the cause of the exception can be identified so the issue can be resolved.
Entering the Fault Handler
When a fault occurs, the processor automatically performs the following steps:
- Complete the current instruction.
- Disable further interrupts/faults.
- Save the stacked program counter and PS registers to the stack.
- Enter the fault handler based on the exception number.
This stacks the context of the interrupted program so it can be resumed afterwards. The processor knows which specific fault handler to jump to based on the numbered exception that occurred.
Fault Handler Actions
Typical tasks performed in the fault exception handler include:
- Stack any additional registers that need saving.
- Inspect the fault status registers to identify the source.
- Clear the fault status flags.
- Attempt corrective actions if possible.
- Set the stacked program counter to a safe restart point.
- Restore the stacked registers and program state.
- Return from the exception with special instructions.
This saves any other important context, gathers debug information, clears the flags so faults can be detected again, applies any workarounds, prepares the program counter to safely resume, restores state, and concludes by returning using special exception-handling instructions.
Returning from the Handler
To conclude fault handling and resume normal execution, the handler must return using one of the special exception return instructions:
- BX LR – Set program counter to the stacked EXC_RETURN value and restore stacked registers.
- POP {…,PC} – Pop stacked registers including program counter to resume execution.
This will set the program counter back to the original interrupted instruction pointer or a safe restart point, restore the stacked registers, and resume execution. Interrupts will also be re-enabled after exiting the handler.
Fault Handler Placement
The fault handlers can be located in several places:
- Assembly startup code – Simple weak default handlers can be defined here.
- Intrinsic functions – Compiler intrinsics provide default handlers.
- FreeRTOS – Tasks can be used to handle specific faults.
- CMSIS functions – The ARM CMSIS libraries include default handlers.
Standard approaches place them in either the assembly startup code or CMSIS libraries. Weak handler definitions allow them to be overridden by application-specific versions.
Debugging Faults
Several techniques can help debug faults:
- Set breakpoints in fault handlers to halt on exception.
- Inspect stacked register contents for context.
- Add debug logging in handlers to output details.
- Check fault status registers to identify root cause.
- Single-step through handler execution.
- Compare behavior against reference devices.
This allows step-by-step analysis of the conditions leading to the fault, examination of program state, tracing handler execution, and comparison against known good examples to identify any differences.
Usage Tips
Some general guidelines for working with Cortex-M fault exceptions:
- Enable fault exceptions to catch serious errors early.
- Use MemManage and BusFaults to detect memory issues.
- Clear flags after handling to re-enable reporting.
- Override weak default handlers with custom debugging.
- Identify root cause by analyzing register contents.
- Set the program counter to a safe restart point.
- Stack all used registers at the start of handlers.
- Return with BX LR or POP {PC} to resume execution.
Properly leveraging fault exceptions and handlers results in more robust programs that can automatically detect and recover from many run-time issues.
Fault Simulation
To test fault handling, issues leading to exceptions must be simulated. Methods include:
- Deliberately invalid address reads/writes.
- Forced bus errors using peripherals.
- Invalid or misaligned data access attempts.
- Divide by zero to generate mathematical faults.
- Intentional SVCall and PendSV triggering.
- Debug breakpoint and watchpoint matching.
Injecting these types of faults during testing will exercise the exception handling logic and handlers to validate they correctly handle all anticipated issues.
Context Switching
The PendSV and SVCall exceptions are commonly used to implement context switching:
- SVCall triggers a supervisor call exception for OS use.
- PendSV initiates a context switch between threads or tasks.
- The handler stores stacked registers to the old thread’s stack.
- The new thread’s registers are then loaded.
- Task switches occur in interrupt-safe handler mode.
- Tail-chaining minimizes overhead between switches.
This allows RTOSes like FreeRTOS to efficiently swap between threads using dedicated fault exceptions for lower latency context switching.
Interrupt Handling
While interrupts are not considered faults, they interact with fault handling:
- The interrupt vector table resides below fault vectors.
- Active faults disable further interrupts.
- Prioritized faults preempt active interrupts.
- SVCall can trigger interrupts for OS use.
- PendSV assertion resumes interrupts after handling.
Thus interrupts relate closely to faults but are considered separately. Dedicated interrupt handling minimizes latency for events like peripheral data availability.
Use Cases
Some example situations where Cortex-M fault exceptions are useful:
- Invalid Memory Access – BusFault or MemManage detect bad address reads/writes.
- Stack Overflow – MemManage can catch stack pointer exceeding limits.
- Math Errors – UsageFault triggered by divide by zero, etc.
- Unaligned Data – UsageFault reports unaligned accesses.
- Context Switching – PendSV for low-latency RTOS thread changes.
- OS Calls – SVCall provides supervisor call capability.
- Error Logging – Handler debugging aids in diagnosis.
Fault exceptions are an essential tool for robust, reliable Cortex-M software in production systems.
Conclusion
In summary, fault exceptions provide Cortex-M processors with a mechanism to detect and handle runtime errors automatically via specialized exception handlers. This allows even low-level code to handle serious issues like invalid memory access, mathematical faults, external interrupts, and system errors in a structured way. Fault handlers can examine the cause, attempt recovery, safely restart execution, and aid debugging. Combined with interrupts and proper design, fault handling results in more resilient software able to operate reliably in mission-critical embedded applications.