1.. _module-pw_cpu_exception_cortex_m: 2 3========================= 4pw_cpu_exception_cortex_m 5========================= 6This backend provides an implementations for the CPU exception module frontend 7for the following Cortex-M architectures: 8 9* ARMv7-M - Cortex M3 10* ARMv7-EM - Cortex M4, M7 11* ARMv8-M Mainline - Cortex M33, M33P 12 13Setup 14===== 15There are a few ways to set up the Cortex M exception handler so the 16application's exception handler is properly called during an exception. 17 18**1. Use existing CMSIS functions** 19 Inside of CMSIS fault handler functions, branch to ``pw_cpu_exception_Entry``. 20 21 .. code-block:: cpp 22 23 __attribute__((naked)) void HardFault_Handler(void) { 24 asm volatile( 25 " ldr r0, =pw_cpu_exception_Entry \n" 26 " bx r0 \n"); 27 } 28 29**2. Modify a startup file** 30 Assembly startup files for some microcontrollers initialize the interrupt 31 vector table. The functions to call for fault handlers can be changed here. 32 For ARMv7-M and ARMv8-M, the fault handlers are indexes 3 to 6 of the 33 interrupt vector table. It's also may be helpful to redirect the NMI handler 34 to the entry function (if it's otherwise unused in your project). 35 36 Default: 37 38 .. code-block:: cpp 39 40 __isr_vector_table: 41 .word __stack_start 42 .word Reset_Handler 43 .word NMI_Handler 44 .word HardFault_Handler 45 .word MemManage_Handler 46 .word BusFault_Handler 47 .word UsageFault_Handler 48 49 Using CPU exception module: 50 51 .. code-block:: cpp 52 53 __isr_vector_table: 54 .word __stack_start 55 .word Reset_Handler 56 .word pw_cpu_exception_Entry 57 .word pw_cpu_exception_Entry 58 .word pw_cpu_exception_Entry 59 .word pw_cpu_exception_Entry 60 .word pw_cpu_exception_Entry 61 62 Note: ``__isr_vector_table`` and ``__stack_start`` are example names, and may 63 vary by platform. See your platform's assembly startup script. 64 65**3. Modify interrupt vector table at runtime** 66 Some applications may choose to modify their interrupt vector tables at 67 runtime. The exception handler works with this use case (see the 68 exception_entry_test integration test), but keep in mind that your 69 application's exception handler will not be entered if an exception occurs 70 before the vector table entries are updated to point to 71 ``pw_cpu_exception_Entry``. 72 73Module Usage 74============ 75For lightweight exception handlers that don't need to access 76architecture-specific registers, using the generic exception handler functions 77is preferred. 78 79However, some projects may need to explicitly access architecture-specific 80registers to attempt to recover from a CPU exception. ``pw_cpu_exception_State`` 81provides access to the captured CPU state at the time of the fault. When the 82application-provided ``pw_cpu_exception_DefaultHandler()`` function returns, the 83CPU state is restored. This allows the exception handler to modify the captured 84state so that execution can safely continue. 85 86Expected Behavior 87----------------- 88In most cases, the CPU state captured by the exception handler will contain the 89basic register frame in addition to an extended set of registers 90(see ``cpu_state.h``). 91 92The exception to this is when the program stack pointer is in an MPU-protected 93or otherwise invalid memory region when the CPU attempts to push the exception 94register frame to it. In this situation, the PC, LR, and PSR registers will NOT 95be captured and will be marked with ``0xFFFFFFFF`` to indicate they are invalid. 96This backend will still be able to capture all the other registers though. 97 98``0xFFFFFFFF`` is an illegal LR value, which is why it was selected for this 99purpose. PC and PSR values of 0xFFFFFFFF are dubious too, so this constant is 100clear enough at suggesting that the registers weren't properly captured. 101 102In the situation where the main stack pointer is in a memory protected or 103otherwise invalid region and fails to push CPU context, behavior is undefined. 104 105Nested Exceptions 106----------------- 107To enable nested fault handling: 108 1091. Enable separate detection of usage/bus/memory faults via the SHCSR. 1102. Decrease the priority of the memory, bus, and usage fault handlers. This 111 gives headroom for escalation. 112 113While this allows some faults to nest, it doesn't guarantee all will properly 114nest. 115 116Configuration Options 117===================== 118 119- ``PW_CPU_EXCEPTION_CORTEX_M_EXTENDED_CFSR_DUMP``: Enable extended logging in 120 ``pw::cpu_exception::LogCpuState()`` that dumps the active CFSR fields with 121 help strings. This is disabled by default since it increases the binary size 122 by >1.5KB when using plain-text logs, or ~460 Bytes when using tokenized 123 logging. It's useful to enable this for device bringup until your application 124 has an end-to-end crash reporting solution. 125- ``PW_CPU_EXCEPTION_CORTEX_M_LOG_LEVEL``: The log level to use for this module. 126 Logs below this level are omitted. 127 128Exception Analysis 129================== 130This module provides Python tooling to analyze CPU state captured by a Cortex-M 131core during an exception. This can be useful as part of a crash report analyzer. 132 133CFSR decoder 134------------ 135The ARMv7-M and ARMv8-M architectures have a Configurable Fault Status Register 136(CFSR) that explains what illegal behavior caused a fault. This module provides 137a simple command-line tool to decode CFSR contents (e.g. 0x00010000) as 138human-readable information (e.g. "Encountered invalid instruction"). 139 140For example: 141 142 .. code-block:: 143 144 $ python -m pw_cpu_exception_cortex_m.cfsr_decoder 0x00010100 145 20210412 15:11:14 INF Exception caused by a usage fault, bus fault. 146 147 Active Crash Fault Status Register (CFSR) fields: 148 IBUSERR Instruction bus error. 149 The processor attempted to issue an invalid instruction. It 150 detects the instruction bus error on prefecting, but this 151 flag is only set to 1 if it attempts to issue the faulting 152 instruction. When this bit is set, the processor has not 153 written a fault address to the BFAR. 154 UNDEFINSTR Encountered invalid instruction. 155 The processor has attempted to execute an undefined 156 instruction. When this bit is set to 1, the PC value stacked 157 for the exception return points to the undefined instruction. 158 An undefined instruction is an instruction that the processor 159 cannot decode. 160 161 All registers: 162 cfsr 0x00010100 163 164.. note:: 165 The CFSR is not supported on ARMv6-M CPUs (Cortex M0, M0+, M1). 166 167-------------------- 168Snapshot integration 169-------------------- 170This ``pw_cpu_exception`` backend provides helper functions that capture CPU 171exception state to snapshot protos. 172 173SnapshotCpuState() 174================== 175``SnapshotCpuState()`` captures the ``pw_cpu_exception_State`` to a 176``pw.cpu_exception.cortex_m.ArmV7mCpuState`` protobuf encoder. 177 178 179SnapshotMainStackThread() 180========================= 181``SnapshotMainStackThread()`` captures the main stack's execution thread state 182if active either from a given ``pw_cpu_exception_State`` or from the current 183running context. It captures the thread name depending on the processor mode, 184either ``Main Stack (Handler Mode)`` or ``Main Stack (Thread Mode)``. The stack 185limits must be provided along with a stack processing callback. All of this 186information is captured by a ``pw::thread::Thread`` protobuf encoder. 187 188.. note:: 189 We recommend providing the ``pw_cpu_exception_State``, for example through 190 ``pw_cpu_exception_DefaultHandler()`` instead of using the current running 191 context to capture the main stack to minimize how much of the snapshot 192 handling is captured in the stack. 193 194Python processor 195================ 196This module's included Python exception analyzer tooling provides snapshot 197integration via a ``process_snapshot()`` function that produces a multi-line 198dump from a serialized snapshot proto, for example: 199 200.. code-block:: 201 202 Exception caused by a usage fault. 203 204 Active Crash Fault Status Register (CFSR) fields: 205 UNDEFINSTR Undefined Instruction UsageFault. 206 The processor has attempted to execute an undefined 207 instruction. When this bit is set to 1, the PC value stacked 208 for the exception return points to the undefined instruction. 209 An undefined instruction is an instruction that the processor 210 cannot decode. 211 212 All registers: 213 pc 0x0800e1c4 example::Service::Crash(_example_service_CrashRequest const&, _pw_protobuf_Empty&) (src/example_service/service.cc:131) 214 lr 0x0800e141 example::Service::Crash(_example_service_CrashRequest const&, _pw_protobuf_Empty&) (src/example_service/service.cc:128) 215 psr 0x81000000 216 msp 0x20040fd8 217 psp 0x20001488 218 exc_return 0xffffffed 219 cfsr 0x00010000 220 mmfar 0xe000ed34 221 bfar 0xe000ed38 222 icsr 0x00000803 223 hfsr 0x40000000 224 shcsr 0x00000000 225 control 0x00000000 226 r0 0xe03f7847 227 r1 0x714083dc 228 r2 0x0b36dc49 229 r3 0x7fbfbe1a 230 r4 0xc36e8efb 231 r5 0x69a14b13 232 r6 0x0ec35eaa 233 r7 0xa5df5543 234 r8 0xc892b931 235 r9 0xa2372c94 236 r10 0xbd15c968 237 r11 0x759b95ab 238 r12 0x00000000 239 240Module Configuration Options 241============================ 242The following configurations can be adjusted via compile-time configuration of 243this module, see the 244:ref:`module documentation <module-structure-compile-time-configuration>` for 245more details. 246 247.. c:macro:: PW_CPU_EXCEPTION_CORTEX_M_LOG_LEVEL 248 249 The log level to use for this module. Logs below this level are omitted. 250 251 This defaults to ``PW_LOG_LEVEL_DEBUG``. 252 253.. c:macro:: PW_CPU_EXCEPTION_CORTEX_M_EXTENDED_CFSR_DUMP 254 255 Enables extended logging in pw::cpu_exception::LogCpuState() and 256 pw::cpu_exception::cortex_m::LogExceptionAnalysis() that dumps the active 257 CFSR fields with help strings. This is disabled by default since it 258 increases the binary size by >1.5KB when using plain-text logs, or ~460 259 Bytes when using tokenized logging. It's useful to enable this for device 260 bringup until your application has an end-to-end crash reporting solution. 261 262 This is disabled by default. 263