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