.. _chapter-cpu-exception-armv7m: .. default-domain:: cpp .. highlight:: sh ----------------------- pw_cpu_exception_armv7m ----------------------- This backend provides an ARMv7-M implementation for the CPU exception module frontend. See the CPU exception frontend module description for more information. Setup ===== There are a few ways to set up the ARMv7-M exception handler so the application's exception handler is properly called during an exception. **1. Use existing CMSIS functions** Inside of CMSIS fault handler functions, branch to ``pw_CpuExceptionEntry``. .. code-block:: cpp __attribute__((naked)) void HardFault_Handler(void) { asm volatile( " ldr r0, =pw_CpuExceptionEntry \n" " bx r0 \n"); } **2. Modify a startup file** Assembly startup files for some microcontrollers initialize the interrupt vector table. The functions to call for fault handlers can be changed here. For ARMv7-M, the fault handlers are indexes 3 to 6 of the interrupt vector table. It's also may be helpful to redirect the NMI handler to the entry function (if it's otherwise unused in your project). Default: .. code-block:: cpp __isr_vector_table: .word __stack_start .word Reset_Handler .word NMI_Handler .word HardFault_Handler .word MemManage_Handler .word BusFault_Handler .word UsageFault_Handler Using CPU exception module: .. code-block:: cpp __isr_vector_table: .word __stack_start .word Reset_Handler .word pw_CpuExceptionEntry .word pw_CpuExceptionEntry .word pw_CpuExceptionEntry .word pw_CpuExceptionEntry .word pw_CpuExceptionEntry Note: ``__isr_vector_table`` and ``__stack_start`` are example names, and may vary by platform. See your platform's assembly startup script. **3. Modify interrupt vector table at runtime** Some applications may choose to modify their interrupt vector tables at runtime. The ARMv7-M exception handler works with this use case (see the exception_entry_test integration test), but keep in mind that your application's exception handler will not be entered if an exception occurs before the vector table entries are updated to point to ``pw_CpuExceptionEntry``. Module Usage ============ For lightweight exception handlers that don't need to access architecture-specific registers, using the generic exception handler functions is preferred. However, some projects may need to explicitly access architecture-specific registers to attempt to recover from a CPU exception. ``CpuState`` provides access to the captured CPU state at the time of the fault. When the application-provided ``HandleCpuException()`` function returns, the CPU state is restored. This allows the exception handler to modify the captured state so that execution can safely continue. Expected Behavior ----------------- In most cases, the CPU state captured by the exception handler will contain the ARMv7-M basic register frame in addition to an extended set of registers (see ``cpu_state.h``). The exception to this is when the program stack pointer is in an MPU-protected or otherwise invalid memory region when the CPU attempts to push the exception register frame to it. In this situation, the PC, LR, and PSR registers will NOT be captured and will be marked with 0xFFFFFFFF to indicate they are invalid. This backend will still be able to capture all the other registers though. In the situation where the main stack pointer is in a memory protected or otherwise invalid region and fails to push CPU context, behavior is undefined. Nested Exceptions ----------------- To enable nested fault handling: 1. Enable separate detection of usage/bus/memory faults via the SHCSR. 2. Decrease the priority of the memory, bus, and usage fault handlers. This gives headroom for escalation. While this allows some faults to nest, it doesn't guarantee all will properly nest.