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