The Cortex-M architecture defines Fault Handlers that are entered when the core attempts to execute an invalid operation such as an invalid opcode or accessing non-mapped memory.
On parts with a Cortex-M3 or Cortex-M4 core, the following handlers are defined:
Memory Management Fault
The first three must be specifically activated and will automatically escalate to a Hard Fault if not enabled.
When a fault occurs the SCB->CFSR register contains information about what caused the fault. In addition, the hardware automatically pushes several CPU registers on the stack before entering the Hard Fault handler. These can be inspected to further debug the cause of the hard fault.
In addition to CFSR, the SCB->MMFAR and SCB->BFARgives the address that caused a fault in the case of a Memory Management Fault or Bus Fault, respectively.
The code below shows a way to implement a Hard Fault handler for debug. The handler will fetch and dump the fault registers and the stacked CPU registers over Serial Wire Output (SWO).
Note that this requires that the SWO pin is connected to the debugger. The code makes use of stdio retargeting which means the file retargetio.c (found in the EFM32 SDK directory underkits/common/drivers/) must be included in the project. The setupSWOForPrint() function is found in the energyAware Commander under the SWO tab.
The Hard Fault Handler needs a bit of assembly. The code checks which stack is in use and copies the stack pointer to R0. It then calls debugHardfault() with the stack pointer as the argument. This function fetches the register values and prints them to SWO. The Hard Fault Handler is defined with__attribute__((naked)) to avoid the compiler generating a function prologue which modifies the stack pointer.
The SWO output can be viewed in energyAware Commander under the 'SWO' tab. In the example shown here, a Hard Fault is generated by trying to branch to address zero.
Note: If SWO output is not desired or possible, the printf statements can be removed and the values inspected directly with the debugger by placing a breakpoint at the while(1) loop in the handler.
The SWO output shows that bit 17 in SCB->CFSR is set. Referring to , this bit indicates INVSTATE:
One of the possible causes for INVSTATEis "Loading a branch target address to PC with LSB = 0". On Cortex-M all branches must have the last address bit set to '1' to indicate "Thumb State".
The LR value on the stack points to the instruction following the instruction that generated the fault. By inserting a breakpoint at the instruction immediately before this address (in this case 0x58e) it is possible to halt the CPU right before the fault is generated.
In the screenshot above, the next instruction is a branch to the address in register R3 which has the value '0x00'.
On devices with a Cortex-M0+ core (e.g. Zero Gecko), none of the fault status registers are available, and there are no SWO support. Debugging a hard fault on a Cortex-M0+ is therefore a bit more difficult and involves inspecting only the stacked registers with a debugger. The following code can be used on a Cortex-M0+ device.