Armando Montanez | 5104cd6 | 2019-12-10 14:36:43 -0800 | [diff] [blame] | 1 | .. _chapter-cpu-exception-armv7m: |
| 2 | |
| 3 | .. default-domain:: cpp |
| 4 | |
| 5 | .. highlight:: sh |
| 6 | |
| 7 | ----------------------- |
| 8 | pw_cpu_exception_armv7m |
| 9 | ----------------------- |
| 10 | This backend provides an ARMv7-M implementation for the CPU exception module |
| 11 | frontend. See the CPU exception frontend module description for more |
| 12 | information. |
| 13 | |
| 14 | Setup |
| 15 | ===== |
| 16 | There are a few ways to set up the ARMv7-M exception handler so the |
| 17 | application's exception handler is properly called during an exception. |
| 18 | |
| 19 | **1. Use existing CMSIS functions** |
| 20 | Inside of CMSIS fault handler functions, branch to ``pw_CpuExceptionEntry``. |
| 21 | |
| 22 | .. code-block:: cpp |
| 23 | |
| 24 | __attribute__((naked)) void HardFault_Handler(void) { |
| 25 | asm volatile( |
| 26 | " ldr r0, =pw_CpuExceptionEntry \n" |
| 27 | " bx r0 \n"); |
| 28 | } |
| 29 | |
| 30 | **2. Modify a startup file** |
| 31 | Assembly startup files for some microcontrollers initialize the interrupt |
| 32 | vector table. The functions to call for fault handlers can be changed here. |
| 33 | For ARMv7-M, the fault handlers are indexes 3 to 6 of the interrupt vector |
| 34 | table. It's also may be helpful to redirect the NMI handler to the entry |
| 35 | function (if it's otherwise unused in your project). |
| 36 | |
| 37 | Default: |
| 38 | |
| 39 | .. code-block:: cpp |
| 40 | |
| 41 | __isr_vector_table: |
| 42 | .word __stack_start |
| 43 | .word Reset_Handler |
| 44 | .word NMI_Handler |
| 45 | .word HardFault_Handler |
| 46 | .word MemManage_Handler |
| 47 | .word BusFault_Handler |
| 48 | .word UsageFault_Handler |
| 49 | |
| 50 | Using CPU exception module: |
| 51 | |
| 52 | .. code-block:: cpp |
| 53 | |
| 54 | __isr_vector_table: |
| 55 | .word __stack_start |
| 56 | .word Reset_Handler |
| 57 | .word pw_CpuExceptionEntry |
| 58 | .word pw_CpuExceptionEntry |
| 59 | .word pw_CpuExceptionEntry |
| 60 | .word pw_CpuExceptionEntry |
| 61 | .word pw_CpuExceptionEntry |
| 62 | |
| 63 | Note: ``__isr_vector_table`` and ``__stack_start`` are example names, and may |
| 64 | vary by platform. See your platform's assembly startup script. |
| 65 | |
| 66 | **3. Modify interrupt vector table at runtime** |
| 67 | Some applications may choose to modify their interrupt vector tables at |
| 68 | runtime. The ARMv7-M exception handler works with this use case (see the |
| 69 | exception_entry_test integration test), but keep in mind that your |
| 70 | application's exception handler will not be entered if an exception occurs |
| 71 | before the vector table entries are updated to point to |
| 72 | ``pw_CpuExceptionEntry``. |
| 73 | |
| 74 | Module Usage |
| 75 | ============ |
| 76 | For lightweight exception handlers that don't need to access |
| 77 | architecture-specific registers, using the generic exception handler functions |
| 78 | is preferred. |
| 79 | |
| 80 | However, some projects may need to explicitly access architecture-specific |
| 81 | registers to attempt to recover from a CPU exception. ``CpuState`` provides |
| 82 | access to the captured CPU state at the time of the fault. When the |
| 83 | application-provided ``HandleCpuException()`` function returns, the CPU state is |
| 84 | restored. This allows the exception handler to modify the captured state so that |
| 85 | execution can safely continue. |
| 86 | |
| 87 | Expected Behavior |
| 88 | ----------------- |
| 89 | In most cases, the CPU state captured by the exception handler will contain the |
| 90 | ARMv7-M basic register frame in addition to an extended set of registers (see |
| 91 | ``cpu_state.h``). The exception to this is when the program stack pointer is in |
| 92 | an MPU-protected or otherwise invalid memory region when the CPU attempts to |
| 93 | push the exception register frame to it. In this situation, the PC, LR, and PSR |
| 94 | registers will NOT be captured and will be marked with 0xFFFFFFFF to indicate |
| 95 | they are invalid. This backend will still be able to capture all the other |
| 96 | registers though. |
| 97 | |
| 98 | In the situation where the main stack pointer is in a memory protected or |
| 99 | otherwise invalid region and fails to push CPU context, behavior is undefined. |
| 100 | |
| 101 | Nested Exceptions |
| 102 | ----------------- |
| 103 | To enable nested fault handling: |
| 104 | 1. Enable separate detection of usage/bus/memory faults via the SHCSR. |
| 105 | 2. Decrease the priority of the memory, bus, and usage fault handlers. This |
| 106 | gives headroom for escalation. |
| 107 | |
| 108 | While this allows some faults to nest, it doesn't guarantee all will properly |
| 109 | nest. |