$npx -y skills add mohitmishra786/low-level-dev-skills --skill interrupts-and-exceptions-baremetalBare-metal interrupt and exception skill for Cortex-M NVIC. Use when writing ISRs, configuring priorities, handling HardFault, or measuring interrupt latency. Activates on queries about NVIC, ISR, vector table, HardFault, tail-chaining, or interrupt priority.
| 1 | # Interrupts and Exceptions (Bare-Metal) |
| 2 | |
| 3 | ## Purpose |
| 4 | |
| 5 | Guide agents through bare-metal interrupt handling on ARM Cortex-M: NVIC configuration, ISR writing rules, exception handlers (HardFault, BusFault), priority grouping, nesting, tail-chaining, and latency considerations. |
| 6 | |
| 7 | ## When to Use |
| 8 | |
| 9 | - Configuring peripheral IRQ priorities |
| 10 | - Writing ISRs that must not block |
| 11 | - Debugging HardFault after enabling interrupts |
| 12 | - Sharing data between ISR and main loop |
| 13 | - Optimizing interrupt latency |
| 14 | |
| 15 | ## Workflow |
| 16 | |
| 17 | ### 1. NVIC overview (Cortex-M) |
| 18 | |
| 19 | ``` |
| 20 | Exception / IRQ flow |
| 21 | ├── NVIC receives IRQ (priority compare with BASEPRI/PRIMask) |
| 22 | ├── Stacking: automatic save r0-r3, r12, lr, pc, psr |
| 23 | ├── Branch to handler from vector table |
| 24 | ├── Handler runs (should be short) |
| 25 | └── Unstack and return — tail-chain if another IRQ pending |
| 26 | ``` |
| 27 | |
| 28 | ### 2. Enable and prioritize an IRQ |
| 29 | |
| 30 | ```c |
| 31 | #include "stm32f4xx.h" /* CMSIS device header */ |
| 32 | |
| 33 | void uart_irq_init(void) { |
| 34 | NVIC_SetPriority(USART2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 2, 0)); |
| 35 | NVIC_EnableIRQ(USART2_IRQn); |
| 36 | } |
| 37 | ``` |
| 38 | |
| 39 | Priority: lower numeric value = higher urgency (on most Cortex-M implementations). Check vendor docs for grouping bits. |
| 40 | |
| 41 | ### 3. ISR template |
| 42 | |
| 43 | ```c |
| 44 | void USART2_IRQHandler(void) { |
| 45 | if (USART2->SR & USART_SR_RXNE) { |
| 46 | uint8_t b = (uint8_t)USART2->DR; /* read clears RXNE */ |
| 47 | ringbuf_push(b); |
| 48 | } |
| 49 | if (USART2->SR & USART_SR_ORE) { |
| 50 | (void)USART2->DR; /* clear overrun */ |
| 51 | } |
| 52 | } |
| 53 | ``` |
| 54 | |
| 55 | ISR rules: |
| 56 | - No blocking calls (`printf`, `malloc`, long loops) |
| 57 | - Minimize work — defer to main via flag/ring buffer |
| 58 | - Clear interrupt flags per datasheet (read-to-clear vs write-1-clear) |
| 59 | |
| 60 | ### 4. Critical sections |
| 61 | |
| 62 | ```c |
| 63 | uint32_t primask = __get_PRIMASK(); |
| 64 | __disable_irq(); |
| 65 | /* atomic section */ |
| 66 | __set_PRIMASK(primask); |
| 67 | ``` |
| 68 | |
| 69 | Or raise `BASEPRI` to mask lower-priority IRQs only. |
| 70 | |
| 71 | ### 5. HardFault handler |
| 72 | |
| 73 | ```c |
| 74 | void HardFault_Handler(void) { |
| 75 | __asm volatile( |
| 76 | "tst lr, #4\n" |
| 77 | "ite eq\n" |
| 78 | "mrseq r0, msp\n" |
| 79 | "mrsne r0, psp\n" |
| 80 | "b hard_fault_c\n" |
| 81 | ); |
| 82 | } |
| 83 | |
| 84 | void hard_fault_c(uint32_t *stack) { |
| 85 | uint32_t r0 = stack[0]; |
| 86 | uint32_t pc = stack[6]; |
| 87 | uint32_t psr = stack[7]; |
| 88 | /* log pc — GDB: info registers, bt */ |
| 89 | while (1); |
| 90 | } |
| 91 | ``` |
| 92 | |
| 93 | Decode CFSR/HFSR registers for fault cause: |
| 94 | |
| 95 | ```c |
| 96 | volatile uint32_t cfsr = SCB->CFSR; |
| 97 | volatile uint32_t hfsr = SCB->HFSR; |
| 98 | volatile uint32_t bfar = SCB->BFAR; |
| 99 | ``` |
| 100 | |
| 101 | ### 6. Latency and tail-chaining |
| 102 | |
| 103 | | Factor | Impact | |
| 104 | |--------|--------| |
| 105 | | Higher priority IRQ | Preempts lower | |
| 106 | | Tail-chaining | Back-to-back IRQs skip unstack/restack | |
| 107 | | FPU context | Lazy stacking adds latency on M4F/M7 | |
| 108 | | Long ISRs | Starves other IRQs and main | |
| 109 | |
| 110 | Measure with GPIO toggle + scope, or DWT cycle counter (`DWT->CYCCNT`). |
| 111 | |
| 112 | ### 7. Agent usage examples |
| 113 | |
| 114 | ``` |
| 115 | /interrupts-and-exceptions-baremetal Configure NVIC priority for UART vs SysTick |
| 116 | /interrupts-and-exceptions-baremetal Decode HardFault stacked PC with GDB |
| 117 | ``` |
| 118 | |
| 119 | ## Common Problems |
| 120 | |
| 121 | | Symptom | Cause | Fix | |
| 122 | |---------|-------|-----| |
| 123 | | IRQ never fires | NVIC not enabled or IRQ masked | `NVIC_EnableIRQ`; check `PRIMASK` | |
| 124 | | Spurious re-entry | Flag not cleared | Clear per RM (ORE needs DR read) | |
| 125 | | HardFault in ISR | Stack overflow | Increase `_estack`; check ISR stack | |
| 126 | | Lost bytes | ISR too slow | Ring buffer + higher IRQ priority | |
| 127 | | Priority inversion | Long critical section | Shorten `__disable_irq` window | |
| 128 | |
| 129 | ## Related Skills |
| 130 | |
| 131 | - `skills/baremetal/baremetal-startup` — vector table entries |
| 132 | - `skills/baremetal/uart-serial-baremetal` — UART IRQ handlers |
| 133 | - `skills/embedded/openocd-jtag` — GDB breakpoint in ISR |
| 134 | - `skills/debuggers/gdb` — examine fault stack frame |
| 135 | - `skills/low-level-programming/assembly-arm` — fault handler asm |