• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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