Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
Fault Generation & Analysis
Understand UsageFault, BusFault, MemManageFault, and HardFault on Cortex-M4. Learn to enable individual fault handlers, analyze fault status registers, and dump the exception stack frame for post-mortem debugging.
1. Fault Hierarchy on Cortex-M4
Cortex-M4 defines a layered fault system. Three configurable fault handlers can be individually enabled. When a fault occurs but its dedicated handler is disabled (or when a fault happens inside a fault handler), it escalates to HardFault.
Fault Exception Numbers
| Exception Number | Name | Priority | Configurable | Vector Offset |
|---|---|---|---|---|
| 3 | HardFault | -1 (fixed) | No | 0x0C |
| 4 | MemManageFault | Programmable | Yes (SHCSR bit16) | 0x10 |
| 5 | BusFault | Programmable | Yes (SHCSR bit17) | 0x14 |
| 6 | UsageFault | Programmable | Yes (SHCSR bit18) | 0x18 |
2. SHCSR — System Handler Control and State Register (0xE000ED24)
SHCSR enables the three configurable fault handlers and shows their active/pending state. Address: 0xE000ED24, part of the SCB (System Control Block).
| Bit | Name | Function |
|---|---|---|
| [18] | USGFAULTENA | 1 = UsageFault handler enabled. 0 = UsageFaults escalate to HardFault. |
| [17] | BUSFAULTENA | 1 = BusFault handler enabled. 0 = BusFaults escalate to HardFault. |
| [16] | MEMFAULTENA | 1 = MemManageFault handler enabled. 0 = MemFaults escalate to HardFault. |
| [15] | SVCALLPENDED | SVCall is pending |
| [14] | BUSFAULTPENDED | BusFault is pending (pre-empted by higher priority) |
| [13] | MEMFAULTPENDED | MemFault is pending |
| [12] | USGFAULTPENDED | UsageFault is pending |
| [11] | SYSTICKACT | SysTick is active |
| [10] | PENDSVACT | PendSV is active |
| [8] | MONITORACT | Debug monitor is active |
| [7] | SVCALLACT | SVCall is active |
| [3] | USGFAULTACT | UsageFault is active |
| [1] | BUSFAULTACT | BusFault is active |
| [0] | MEMFAULTACT | MemManageFault is active |
/* Enable all three configurable fault handlers */
void enable_all_fault_handlers(void)
{
uint32_t *pSHCSR = (uint32_t*)0xE000ED24;
*pSHCSR |= (1 << 16); /* Enable MemManageFault */
*pSHCSR |= (1 << 17); /* Enable BusFault */
*pSHCSR |= (1 << 18); /* Enable UsageFault */
/* Without these, all three fault types route to HardFault_Handler */
}
============================================================
3. Fault Status Registers — Full Bit Reference
CFSR — Configurable Fault Status Register (0xE000ED28)
CFSR is a 32-bit register containing all three configurable fault status sub-registers concatenated:
UFSR — UsageFault Status Register (bits [31:16] of CFSR, addr 0xE000ED2A)
| Bit (UFSR) | Bit (CFSR) | Name | Cause |
|---|---|---|---|
| [9] | [25] | DIVBYZERO | Integer divide by zero when CCR.DIV_0_TRP=1 |
| [8] | [24] | UNALIGNED | Unaligned word/halfword access when CCR.UNALIGN_TRP=1 |
| [3] | [19] | NOCP | Coprocessor instruction when CP not present/disabled |
| [2] | [18] | INVPC | Invalid PC loaded on exception return (bad EXC_RETURN) |
| [1] | [17] | INVSTATE | Invalid state — T-bit=0 or illegal EPSR combo |
| [0] | [16] | UNDEFINSTR | Undefined instruction executed |
BFSR — BusFault Status Register (bits [15:8] of CFSR, addr 0xE000ED29)
| Bit (BFSR) | Name | Cause |
|---|---|---|
| [7] | BFARVALID | BFAR register contains valid fault address |
| [5] | LSPERR | Fault during lazy FP state preserve |
| [4] | STKERR | Bus fault during exception stacking |
| [3] | UNSTKERR | Bus fault during exception unstacking |
| [2] | IMPRECISERR | Imprecise bus error (instruction already retired) |
| [1] | PRECISERR | Precise bus error, BFAR valid |
| [0] | IBUSERR | Instruction bus error (prefetch fault) |
MMFSR — MemManage Fault Status Register (bits [7:0] of CFSR, addr 0xE000ED28)
| Bit | Name | Cause |
|---|---|---|
| [7] | MMARVALID | MMFAR contains valid fault address |
| [5] | MLSPERR | MemFault during lazy FP state preserve |
| [4] | MSTKERR | MemFault during exception stacking |
| [3] | MUNSTKERR | MemFault during exception unstacking |
| [1] | DACCVIOL | Data access violation (MPU or XN region) |
| [0] | IACCVIOL | Instruction access violation (fetch from no-execute region) |
HFSR — HardFault Status Register (0xE000ED2C)
| Bit | Name | Meaning |
|---|---|---|
| [31] | DEBUGEVT | HardFault caused by a debug event (breakpoint with no debugger) |
| [30] | FORCED | HardFault caused by escalation from a configurable fault. Check CFSR for the original fault. |
| [1] | VECTTBL | HardFault during vector table read on exception entry |
4. CCR — Configuration and Control Register (0xE000ED14)
| Bit | Name | Description |
|---|---|---|
| [9] | STKALIGN | 1 (default) = 8-byte stack alignment on exception. 0 = 4-byte. |
| [8] | BFHFNMIGN | 1 = BusFaults ignored in NMI/HardFault handlers. Used for fault probing. |
| [4] | DIV_0_TRP | 1 = Generate UsageFault on integer divide-by-zero. Default=0 (silent result=0). |
| [3] | UNALIGN_TRP | 1 = Trap all unaligned accesses (not just atomic ones). Default=0. |
| [1] | USERSETMPEND | 1 = Unprivileged code can write to STIR (software interrupt trigger). |
| [0] | NONBASETHRDENA | 1 = Allow Thread mode when exception number is non-zero (rare use). |
/* Enable divide-by-zero trap */
void enable_div0_trap(void)
{
uint32_t *pCCR = (uint32_t*)0xE000ED14;
*pCCR |= (1 << 4); /* DIV_0_TRP = 1 */
/* Now integer divide by zero → UsageFault with UFSR.DIVBYZERO=1 */
}
int risky_divide(int x, int y)
{
return x / y; /* if y==0 and DIV_0_TRP=1 → UsageFault fires */
}
============================================================
5. Scenario A — INVSTATE UsageFault (Undefined Instruction)
This example demonstrates what happens when you jump to an address containing 0xFFFFFFFF (an undefined instruction). The function pointer has LSB=1 (correct Thumb mode), but the instruction at the address is invalid.
/* ================================================================
* FAULT SCENARIO A: Undefined Instruction → UsageFault INVSTATE
* ================================================================
*
* Memory layout:
* 0x20010000: [0xFFFFFFFF] ← invalid/undefined instruction word
* Function pointer: 0x20010001 ← LSB=1 ensures Thumb mode entry
*
* What happens:
* 1. CPU enters at 0x20010000 in Thumb state
* 2. Fetches 0xFFFF — undefined Thumb opcode
* 3. UFSR.INVSTATE = 1 (or UFSR.UNDEFINSTR = 1)
* 4. UsageFault_Handler fires (if enabled via SHCSR)
* 5. If not enabled → HardFault fires instead
* ================================================================ */
#include <stdint.h>
#include <stdio.h>
void enable_fault_handlers(void)
{
uint32_t *pSHCSR = (uint32_t*)0xE000ED24;
*pSHCSR |= (1 << 16); /* MemManageFault */
*pSHCSR |= (1 << 17); /* BusFault */
*pSHCSR |= (1 << 18); /* UsageFault */
}
int main(void)
{
enable_fault_handlers();
/* Write 0xFFFFFFFF (two undefined 16-bit Thumb opcodes) to SRAM */
uint32_t *pSRAM = (uint32_t*)0x20010000;
*pSRAM = 0xFFFFFFFF;
/* Create function pointer: address with LSB=1 (Thumb mode indicator)
Actual execution starts at 0x20010000 (LSB stripped by processor) */
void (*fn)(void) = (void*)0x20010001;
fn(); /* → INVSTATE or UNDEFINSTR UsageFault */
for(;;);
}
/* ── Naked entry: capture MSP before any push/pop ── */
__attribute__((naked)) void UsageFault_Handler(void)
{
/* MSP points to the exception stack frame pushed by hardware */
__asm("MRS R0, MSP");
__asm("B UsageFault_Handler_c");
}
void UsageFault_Handler_c(uint32_t *frame)
{
uint32_t *pUFSR = (uint32_t*)0xE000ED2A; /* UFSR at CFSR byte [31:16] */
uint32_t ufsr = (*pUFSR) & 0xFFFF;
printf("=== UsageFault Handler ===\n");
printf("UFSR = 0x%04lX\n", ufsr);
/* Decode UFSR bits */
if (ufsr & (1 << 1)) printf(" INVSTATE : Invalid state / bad T-bit\n");
if (ufsr & (1 << 0)) printf(" UNDEFINSTR : Undefined instruction\n");
if (ufsr & (1 << 9)) printf(" DIVBYZERO : Integer divide by zero\n");
if (ufsr & (1 << 8)) printf(" UNALIGNED : Unaligned memory access\n");
if (ufsr & (1 << 3)) printf(" NOCP : No coprocessor\n");
if (ufsr & (1 << 2)) printf(" INVPC : Invalid PC on exception return\n");
printf("\n=== Exception Stack Frame ===\n");
printf("R0 = 0x%08lX\n", frame[0]);
printf("R1 = 0x%08lX\n", frame[1]);
printf("R2 = 0x%08lX\n", frame[2]);
printf("R3 = 0x%08lX\n", frame[3]);
printf("R12 = 0x%08lX\n", frame[4]);
printf("LR = 0x%08lX\n", frame[5]);
printf("PC = 0x%08lX\n", frame[6]); /* address of faulting instruction */
printf("xPSR = 0x%08lX\n", frame[7]);
/* PC should be 0x20010000 — the address that caused the fault */
while(1);
}
============================================================
6. Scenario B — Divide by Zero Trap
/* ================================================================
* FAULT SCENARIO B: Divide by Zero → UsageFault DIVBYZERO
* CCR.DIV_0_TRP must be set first (default = 0 = silent)
* ================================================================ */
int safe_divide(int x, int y)
{
return x / y;
}
int main(void)
{
uint32_t *pSHCSR = (uint32_t*)0xE000ED24;
*pSHCSR |= (1 << 16) | (1 << 17) | (1 << 18);
/* Enable divide-by-zero trap in CCR */
uint32_t *pCCR = (uint32_t*)0xE000ED14;
*pCCR |= (1 << 4); /* DIV_0_TRP = 1 */
/* This will trigger UsageFault with UFSR.DIVBYZERO = 1 */
volatile int result = safe_divide(10, 0);
(void)result;
for(;;);
}
__attribute__((naked)) void UsageFault_Handler(void)
{
__asm("MRS R0, MSP");
__asm("B UsageFault_Handler_c");
}
void UsageFault_Handler_c(uint32_t *frame)
{
uint32_t ufsr = *((uint32_t*)0xE000ED2A) & 0xFFFF;
printf("UsageFault! UFSR=0x%04lX\n", ufsr);
/* UFSR bit9=DIVBYZERO will be set */
/* frame[6]=PC will point into safe_divide() at the SDIV instruction */
printf("Fault at PC = 0x%08lX\n", frame[6]);
while(1);
}
============================================================
7. Fault Status Register Reading Utilities
/* ── Complete fault analysis utility ───────────────────────────── */
#define CFSR_ADDR 0xE000ED28
#define HFSR_ADDR 0xE000ED2C
#define BFAR_ADDR 0xE000ED38
#define MMFAR_ADDR 0xE000ED34
void analyse_fault_registers(void)
{
uint32_t cfsr = *(volatile uint32_t*)CFSR_ADDR;
uint32_t hfsr = *(volatile uint32_t*)HFSR_ADDR;
uint32_t mmfsr = cfsr & 0xFF;
uint32_t bfsr = (cfsr >> 8) & 0xFF;
uint32_t ufsr = (cfsr >> 16) & 0xFFFF;
printf("── Fault Status ──\n");
printf("CFSR = 0x%08lX\n", cfsr);
printf("HFSR = 0x%08lX\n", hfsr);
printf("MMFSR = 0x%02lX\n", mmfsr);
printf("BFSR = 0x%02lX\n", bfsr);
printf("UFSR = 0x%04lX\n", ufsr);
if (hfsr & (1u << 30))
printf(" HardFault: FORCED (escalation from configurable fault)\n");
if (ufsr & (1 << 9)) printf(" UsageFault: DIVBYZERO\n");
if (ufsr & (1 << 1)) printf(" UsageFault: INVSTATE\n");
if (ufsr & (1 << 0)) printf(" UsageFault: UNDEFINSTR\n");
if (ufsr & (1 << 3)) printf(" UsageFault: NOCP (no coprocessor)\n");
if (bfsr & (1 << 1)) {
printf(" BusFault: PRECISERR at BFAR = 0x%08lX\n",
*(volatile uint32_t*)BFAR_ADDR);
}
if (bfsr & (1 << 2)) printf(" BusFault: IMPRECISERR\n");
if (mmfsr & (1 << 1)) {
printf(" MemFault: DACCVIOL at MMFAR = 0x%08lX\n",
*(volatile uint32_t*)MMFAR_ADDR);
}
if (mmfsr & (1 << 0)) printf(" MemFault: IACCVIOL\n");
}
============================================================
8. HardFault Handler — Universal Safety Net
/* Universal HardFault handler for any Cortex-M4 project */
__attribute__((naked)) void HardFault_Handler(void)
{
/* Determine which stack was active: PSP or MSP
If bit 2 of LR (EXC_RETURN) is set → PSP was active in Thread
else MSP was active */
__asm volatile(
"TST LR, #4 \n" /* test bit 2 of EXC_RETURN */
"ITE EQ \n" /* if EQ (bit2=0) */
"MRSEQ R0, MSP \n" /* then R0 = MSP */
"MRSNE R0, PSP \n" /* else R0 = PSP */
"B HardFault_Handler_c \n"
);
}
void HardFault_Handler_c(uint32_t *frame)
{
printf("=== HARD FAULT ===\n");
/* Check if it was an escalation */
uint32_t hfsr = *(volatile uint32_t*)0xE000ED2C;
if (hfsr & (1u << 30)) {
printf("FORCED (configurable fault escalated)\n");
analyse_fault_registers(); /* print CFSR details */
}
if (hfsr & (1u << 1))
printf("VECTTBL (vector table read error)\n");
printf("Faulting PC = 0x%08lX\n", frame[6]);
printf("LR at fault = 0x%08lX\n", frame[5]);
printf("R0 = 0x%08lX\n", frame[0]);
printf("xPSR = 0x%08lX\n", frame[7]);
while(1); /* halt */
}
============================================================
9. Interview Questions & Answers
Q1. What is the difference between UsageFault, BusFault, MemManageFault, and HardFault?
BusFault: AHB bus errors during memory access — accessing non-existent memory regions, or bus slave returning an error response.
MemManageFault: MPU access violations — code accessing a region it doesn’t have permission to, or executing from a no-execute region.
HardFault: Catch-all — fires when any configurable fault is disabled, or when a fault occurs inside a fault handler (double fault).
Q2. How do you enable UsageFault, BusFault, and MemManageFault handlers?
Q3. Why must UsageFault_Handler be declared as __attribute__((naked))?
Q4. What does UFSR.INVSTATE mean and how is it triggered?
Q5. How do you find the address of the instruction that caused a fault?
Q6. What is HFSR.FORCED and how do you find the root cause?
Q7. By default, does integer divide by zero cause a fault on Cortex-M4?
Q8. What is the purpose of reading both MSP and PSP in HardFault_Handler?
Q9. What is the difference between PRECISERR and IMPRECISERR in BusFault?
IMPRECISERR: The bus error is detected late — the write buffer has completed and the faulting instruction has already retired. The PC in the stack frame is a few instructions past the actual fault, and BFAR is not valid. Imprecise errors are harder to debug.
Q10. What happens when a fault occurs inside a fault handler?
Q11. What is processor LockUp state?
Q12. How do you implement a production-quality fault handler for shipping firmware?
2. Use naked fault handlers to capture the exact frame pointer.
3. Read and decode CFSR, HFSR, BFAR, MMFAR.
4. Save fault info to non-volatile memory (e.g., backup SRAM, RTC backup registers, or flash).
5. Print or log all stacked registers (R0-R3, R12, LR, PC, xPSR).
6. Trigger a system reset via AIRCR.SYSRESETREQ (0xE000ED0C, write key 0x05FA0004).
7. On next boot, check the saved fault data to diagnose the issue.
STM32F407 Deep Dive Series — Example 12: Fault Generation & Analysis
ARM Cortex-M4 TRM · ARMv7-M Architecture Reference Manual
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments