HomeARM CourseDay 18
DAY 18 · SYSTEM & MEMORY

The NVIC — Interrupts on Cortex-M

By EcrioniX · Updated Jun 6, 2026

Yesterday's classic-ARM interrupts needed hand-written assembly wrappers to save state. Cortex-M throws all of that away. Its built-in NVIC does the vectoring, the register saving, and the prioritising in hardware — so an interrupt handler becomes nothing more than a normal C function. This is the single biggest reason Cortex-M is so beginner-friendly.

1. What the NVIC is

The NVICNested Vectored Interrupt Controller — is a standard block inside every Cortex-M core. "Nested" = interrupts can interrupt each other by priority. "Vectored" = the hardware fetches the right handler address directly. It manages dozens to hundreds of interrupt sources plus core system exceptions.

2. The Cortex-M vector table

Unlike classic ARM (where each vector was a branch instruction), a Cortex-M vector table is a plain array of function-pointer addresses. Slot 0 is even special: it holds the initial stack pointer, loaded automatically at reset before any code runs.

// Cortex-M vector table = array of addresses (not instructions) __vectors: .word _estack // 0x00 initial SP value .word Reset_Handler // 0x04 reset .word NMI_Handler // 0x08 non-maskable interrupt .word HardFault_Handler// 0x0C hard fault // ... system exceptions ... .word SysTick_Handler // system tick timer .word TIM2_IRQHandler // first device interrupt // ... up to ~240 device IRQs ...

3. Automatic stacking — the magic

When an interrupt fires, the core automatically pushes 8 registers onto the stack: the AAPCS caller-saved set r0–r3, r12, LR, PC, and xPSR. On return it pops them back. Because these are exactly the registers a C function is allowed to clobber, the handler doesn't need any special wrapper:

// A complete, correct Cortex-M interrupt handler — just C: void TIM2_IRQHandler(void) { TIM2->SR &= ~TIM_SR_UIF; // clear the interrupt flag (always!) toggle_led(); }

💡 The valet that parks your state

Classic ARM made you save and restore registers in assembly. The NVIC is like a valet: it parks all the volatile state for you on entry and brings it back on exit. You just write the handler logic — in C.

4. Priorities & preemption

Every interrupt gets a priority number, and the golden rule is counter-intuitive: lower number = higher priority. A higher-priority interrupt can preempt a running lower-priority handler, nesting on top of it; equal-or-lower ones wait.

5. Tail-chaining & late arrival

The NVIC has two clever latency optimisations:

FeatureWhat it does
Tail-chainingIf another interrupt is pending when a handler ends, the core skips the pop+push of the stack frame and jumps straight into the next handler.
Late arrivalIf a higher-priority interrupt arrives during the stacking of a lower one, the core re-targets to the higher handler without redoing the stacking.

Both eliminate redundant memory traffic, giving Cortex-M its famously deterministic, low interrupt latency — typically a fixed 12 cycles to enter a handler.

6. Special system handlers

✅ The mental model

The NVIC moves interrupt plumbing into hardware: it vectors directly to your handler, auto-stacks the caller-saved registers (so an ISR is plain C), supports numbered priorities with preemption (lower = higher), and uses tail-chaining for back-to-back interrupts. That's why Cortex-M interrupt handling is both fast and easy.

🎯 Day 18 takeaways

Quick check

  1. Which 8 registers does the NVIC stack automatically, and why those?
  2. Does priority 1 preempt priority 5, or the other way round?
  3. What does tail-chaining save between two back-to-back interrupts?

FAQ

What is the NVIC?

The Nested Vectored Interrupt Controller built into Cortex-M — it handles vectoring, auto-stacking and prioritised, nestable interrupts in hardware.

Why is a Cortex-M ISR just C?

The hardware auto-saves the caller-saved registers (r0–r3, r12, LR, PC, xPSR), so no assembly wrapper is needed.

What is tail-chaining?

Skipping the unstack/restack between back-to-back interrupts, lowering latency.

Previous
← Day 17: Exceptions & interrupts

← Back to the full course roadmap