blob: f8718d44d2bea374205c4bf31169e8f8408bfef9 [file] [log] [blame]
.. _module-pw_cpu_exception_cortex_m:
-------------------------
pw_cpu_exception_cortex_m
-------------------------
This backend provides an ARMv7-M implementation for the CPU exception module
frontend. See the CPU exception frontend module description for more
information.
Setup
=====
There are a few ways to set up the ARMv7-M exception handler so the
application's exception handler is properly called during an exception.
**1. Use existing CMSIS functions**
Inside of CMSIS fault handler functions, branch to ``pw_cpu_exception_Entry``.
.. code-block:: cpp
__attribute__((naked)) void HardFault_Handler(void) {
asm volatile(
" ldr r0, =pw_cpu_exception_Entry \n"
" bx r0 \n");
}
**2. Modify a startup file**
Assembly startup files for some microcontrollers initialize the interrupt
vector table. The functions to call for fault handlers can be changed here.
For ARMv7-M, the fault handlers are indexes 3 to 6 of the interrupt vector
table. It's also may be helpful to redirect the NMI handler to the entry
function (if it's otherwise unused in your project).
Default:
.. code-block:: cpp
__isr_vector_table:
.word __stack_start
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
Using CPU exception module:
.. code-block:: cpp
__isr_vector_table:
.word __stack_start
.word Reset_Handler
.word pw_cpu_exception_Entry
.word pw_cpu_exception_Entry
.word pw_cpu_exception_Entry
.word pw_cpu_exception_Entry
.word pw_cpu_exception_Entry
Note: ``__isr_vector_table`` and ``__stack_start`` are example names, and may
vary by platform. See your platform's assembly startup script.
**3. Modify interrupt vector table at runtime**
Some applications may choose to modify their interrupt vector tables at
runtime. The ARMv7-M exception handler works with this use case (see the
exception_entry_test integration test), but keep in mind that your
application's exception handler will not be entered if an exception occurs
before the vector table entries are updated to point to
``pw_cpu_exception_Entry``.
Module Usage
============
For lightweight exception handlers that don't need to access
architecture-specific registers, using the generic exception handler functions
is preferred.
However, some projects may need to explicitly access architecture-specific
registers to attempt to recover from a CPU exception. ``pw_cpu_exception_State``
provides access to the captured CPU state at the time of the fault. When the
application-provided ``pw_cpu_exception_DefaultHandler()`` function returns, the
CPU state is restored. This allows the exception handler to modify the captured
state so that execution can safely continue.
Expected Behavior
-----------------
In most cases, the CPU state captured by the exception handler will contain the
ARMv7-M basic register frame in addition to an extended set of registers (see
``cpu_state.h``). The exception to this is when the program stack pointer is in
an MPU-protected or otherwise invalid memory region when the CPU attempts to
push the exception register frame to it. In this situation, the PC, LR, and PSR
registers will NOT be captured and will be marked with 0xFFFFFFFF to indicate
they are invalid. This backend will still be able to capture all the other
registers though.
In the situation where the main stack pointer is in a memory protected or
otherwise invalid region and fails to push CPU context, behavior is undefined.
Nested Exceptions
-----------------
To enable nested fault handling:
1. Enable separate detection of usage/bus/memory faults via the SHCSR.
2. Decrease the priority of the memory, bus, and usage fault handlers. This
gives headroom for escalation.
While this allows some faults to nest, it doesn't guarantee all will properly
nest.
Configuration Options
=====================
- ``PW_CPU_EXCEPTION_EXTENDED_CFSR_DUMP``: Enable extended logging in
``pw::cpu_exception::LogCpuState()`` that dumps the active CFSR fields with
help strings. This is disabled by default since it increases the binary size
by >1.5KB when using plain-text logs, or ~460 Bytes when using tokenized
logging. It's useful to enable this for device bringup until your application
has an end-to-end crash reporting solution.
Exception Analysis
==================
This module provides Python tooling to analyze CPU state captured by a Cortex-M
core during an exception. This can be useful as part of a crash report analyzer.
CFSR decoder
------------
The ARMv7-M and ARMv8-M architectures have a Configurable Fault Status Register
(CFSR) that explains what illegal behavior caused a fault. This module provides
a simple command-line tool to decode CFSR contents (e.g. 0x00010000) as
human-readable information (e.g. "Encountered invalid instruction").
For example:
.. code-block::
$ python -m pw_cpu_exception_cortex_m.cfsr_decoder 0x00010100
20210412 15:11:14 INF Exception caused by a usage fault, bus fault.
Active Crash Fault Status Register (CFSR) fields:
IBUSERR Instruction bus error.
The processor attempted to issue an invalid instruction. It
detects the instruction bus error on prefecting, but this
flag is only set to 1 if it attempts to issue the faulting
instruction. When this bit is set, the processor has not
written a fault address to the BFAR.
UNDEFINSTR Encountered invalid instruction.
The processor has attempted to execute an undefined
instruction. When this bit is set to 1, the PC value stacked
for the exception return points to the undefined instruction.
An undefined instruction is an instruction that the processor
cannot decode.
All registers:
cfsr 0x00010100
.. note::
The CFSR is not supported on ARMv6-M CPUs (Cortex M0, M0+, M1).