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