Armando Montanez | a9ca999 | 2021-01-26 17:06:10 -0800 | [diff] [blame] | 1 | .. _module-pw_cpu_exception_cortex_m: |
Armando Montanez | 5104cd6 | 2019-12-10 14:36:43 -0800 | [diff] [blame] | 2 | |
Armando Montanez | a9ca999 | 2021-01-26 17:06:10 -0800 | [diff] [blame] | 3 | ------------------------- |
| 4 | pw_cpu_exception_cortex_m |
| 5 | ------------------------- |
Armando Montanez | 5104cd6 | 2019-12-10 14:36:43 -0800 | [diff] [blame] | 6 | This backend provides an ARMv7-M implementation for the CPU exception module |
| 7 | frontend. See the CPU exception frontend module description for more |
| 8 | information. |
| 9 | |
| 10 | Setup |
| 11 | ===== |
| 12 | There are a few ways to set up the ARMv7-M exception handler so the |
| 13 | application's exception handler is properly called during an exception. |
| 14 | |
| 15 | **1. Use existing CMSIS functions** |
Armando Montanez | a9ca999 | 2021-01-26 17:06:10 -0800 | [diff] [blame] | 16 | Inside of CMSIS fault handler functions, branch to ``pw_cpu_exception_Entry``. |
Armando Montanez | 5104cd6 | 2019-12-10 14:36:43 -0800 | [diff] [blame] | 17 | |
| 18 | .. code-block:: cpp |
| 19 | |
| 20 | __attribute__((naked)) void HardFault_Handler(void) { |
| 21 | asm volatile( |
Armando Montanez | a9ca999 | 2021-01-26 17:06:10 -0800 | [diff] [blame] | 22 | " ldr r0, =pw_cpu_exception_Entry \n" |
Armando Montanez | 5104cd6 | 2019-12-10 14:36:43 -0800 | [diff] [blame] | 23 | " bx r0 \n"); |
| 24 | } |
| 25 | |
| 26 | **2. Modify a startup file** |
| 27 | Assembly startup files for some microcontrollers initialize the interrupt |
| 28 | vector table. The functions to call for fault handlers can be changed here. |
| 29 | For ARMv7-M, the fault handlers are indexes 3 to 6 of the interrupt vector |
| 30 | table. It's also may be helpful to redirect the NMI handler to the entry |
| 31 | function (if it's otherwise unused in your project). |
| 32 | |
| 33 | Default: |
| 34 | |
| 35 | .. code-block:: cpp |
| 36 | |
| 37 | __isr_vector_table: |
| 38 | .word __stack_start |
| 39 | .word Reset_Handler |
| 40 | .word NMI_Handler |
| 41 | .word HardFault_Handler |
| 42 | .word MemManage_Handler |
| 43 | .word BusFault_Handler |
| 44 | .word UsageFault_Handler |
| 45 | |
| 46 | Using CPU exception module: |
| 47 | |
| 48 | .. code-block:: cpp |
| 49 | |
| 50 | __isr_vector_table: |
| 51 | .word __stack_start |
| 52 | .word Reset_Handler |
Armando Montanez | a9ca999 | 2021-01-26 17:06:10 -0800 | [diff] [blame] | 53 | .word pw_cpu_exception_Entry |
| 54 | .word pw_cpu_exception_Entry |
| 55 | .word pw_cpu_exception_Entry |
| 56 | .word pw_cpu_exception_Entry |
| 57 | .word pw_cpu_exception_Entry |
Armando Montanez | 5104cd6 | 2019-12-10 14:36:43 -0800 | [diff] [blame] | 58 | |
| 59 | Note: ``__isr_vector_table`` and ``__stack_start`` are example names, and may |
| 60 | vary by platform. See your platform's assembly startup script. |
| 61 | |
| 62 | **3. Modify interrupt vector table at runtime** |
| 63 | Some applications may choose to modify their interrupt vector tables at |
| 64 | runtime. The ARMv7-M exception handler works with this use case (see the |
| 65 | exception_entry_test integration test), but keep in mind that your |
| 66 | application's exception handler will not be entered if an exception occurs |
| 67 | before the vector table entries are updated to point to |
Armando Montanez | a9ca999 | 2021-01-26 17:06:10 -0800 | [diff] [blame] | 68 | ``pw_cpu_exception_Entry``. |
Armando Montanez | 5104cd6 | 2019-12-10 14:36:43 -0800 | [diff] [blame] | 69 | |
| 70 | Module Usage |
| 71 | ============ |
| 72 | For lightweight exception handlers that don't need to access |
| 73 | architecture-specific registers, using the generic exception handler functions |
| 74 | is preferred. |
| 75 | |
| 76 | However, some projects may need to explicitly access architecture-specific |
Armando Montanez | a9ca999 | 2021-01-26 17:06:10 -0800 | [diff] [blame] | 77 | registers to attempt to recover from a CPU exception. ``pw_cpu_exception_State`` |
Armando Montanez | 356bf97 | 2020-06-04 10:35:55 -0700 | [diff] [blame] | 78 | provides access to the captured CPU state at the time of the fault. When the |
Armando Montanez | a9ca999 | 2021-01-26 17:06:10 -0800 | [diff] [blame] | 79 | application-provided ``pw_cpu_exception_DefaultHandler()`` function returns, the |
Armando Montanez | 356bf97 | 2020-06-04 10:35:55 -0700 | [diff] [blame] | 80 | CPU state is restored. This allows the exception handler to modify the captured |
| 81 | state so that execution can safely continue. |
Armando Montanez | 5104cd6 | 2019-12-10 14:36:43 -0800 | [diff] [blame] | 82 | |
| 83 | Expected Behavior |
| 84 | ----------------- |
| 85 | In most cases, the CPU state captured by the exception handler will contain the |
| 86 | ARMv7-M basic register frame in addition to an extended set of registers (see |
| 87 | ``cpu_state.h``). The exception to this is when the program stack pointer is in |
| 88 | an MPU-protected or otherwise invalid memory region when the CPU attempts to |
| 89 | push the exception register frame to it. In this situation, the PC, LR, and PSR |
| 90 | registers will NOT be captured and will be marked with 0xFFFFFFFF to indicate |
| 91 | they are invalid. This backend will still be able to capture all the other |
| 92 | registers though. |
| 93 | |
| 94 | In the situation where the main stack pointer is in a memory protected or |
| 95 | otherwise invalid region and fails to push CPU context, behavior is undefined. |
| 96 | |
| 97 | Nested Exceptions |
| 98 | ----------------- |
| 99 | To enable nested fault handling: |
| 100 | 1. Enable separate detection of usage/bus/memory faults via the SHCSR. |
| 101 | 2. Decrease the priority of the memory, bus, and usage fault handlers. This |
| 102 | gives headroom for escalation. |
| 103 | |
| 104 | While this allows some faults to nest, it doesn't guarantee all will properly |
| 105 | nest. |
Armando Montanez | cddab2d | 2021-01-11 19:11:05 -0800 | [diff] [blame] | 106 | |
| 107 | Configuration Options |
| 108 | ===================== |
| 109 | |
| 110 | - ``PW_CPU_EXCEPTION_EXTENDED_CFSR_DUMP``: Enable extended logging in |
| 111 | ``pw::cpu_exception::LogCpuState()`` that dumps the active CFSR fields with |
| 112 | help strings. This is disabled by default since it increases the binary size |
| 113 | by >1.5KB when using plain-text logs, or ~460 Bytes when using tokenized |
| 114 | logging. It's useful to enable this for device bringup until your application |
| 115 | has an end-to-end crash reporting solution. |
Armando Montanez | 84acca8 | 2021-04-12 15:37:01 -0700 | [diff] [blame] | 116 | |
| 117 | Exception Analysis |
| 118 | ================== |
| 119 | This module provides Python tooling to analyze CPU state captured by a Cortex-M |
Armando Montanez | 11ea213 | 2021-04-13 13:16:54 -0700 | [diff] [blame] | 120 | core during an exception. This can be useful as part of a crash report analyzer. |
Armando Montanez | 84acca8 | 2021-04-12 15:37:01 -0700 | [diff] [blame] | 121 | |
| 122 | CFSR decoder |
| 123 | ------------ |
| 124 | The ARMv7-M and ARMv8-M architectures have a Configurable Fault Status Register |
Armando Montanez | 11ea213 | 2021-04-13 13:16:54 -0700 | [diff] [blame] | 125 | (CFSR) that explains what illegal behavior caused a fault. This module provides |
| 126 | a simple command-line tool to decode CFSR contents (e.g. 0x00010000) as |
| 127 | human-readable information (e.g. "Encountered invalid instruction"). |
| 128 | |
| 129 | For example: |
Armando Montanez | 84acca8 | 2021-04-12 15:37:01 -0700 | [diff] [blame] | 130 | |
| 131 | .. code-block:: |
| 132 | |
| 133 | $ python -m pw_cpu_exception_cortex_m.cfsr_decoder 0x00010100 |
| 134 | 20210412 15:11:14 INF Exception caused by a usage fault, bus fault. |
| 135 | |
| 136 | Active Crash Fault Status Register (CFSR) fields: |
| 137 | IBUSERR Bus fault on instruction fetch. |
| 138 | UNDEFINSTR Encountered invalid instruction. |
| 139 | |
| 140 | All registers: |
| 141 | cfsr 0x00010100 |
| 142 | |
| 143 | .. note:: |
| 144 | The CFSR is not supported on ARMv6-M CPUs (Cortex M0, M0+, M1). |