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