Exercises — Interrupts — ISR design, NVIC priority, interrupt latency
Level 1 — Recognition
L1.1 — Vocabulary match
Problem. For each phrase, name the term from the parent note: (a) "hardware saves 8 registers automatically before your code runs," (b) "the array of ISR addresses hardware indexes into," (c) "the peripheral that decides which interrupt runs and at what priority."
Recall Solution (L1.1)
(a) Stacking — the CPU auto-pushes –, , (link register, the return address), (program counter, what runs next), and (the Program Status Register defined above) — 8 words total — onto the stack. Why "for free"? Because these are caller-saved, your ISR (Interrupt Service Routine) can be a plain C function. (b) Vector table — an array of ISR start addresses; the NVIC uses the interrupt number as an index. (c) NVIC (Nested Vectored Interrupt Controller).
L1.2 — Which number wins?
Problem. Two interrupts are pending: IRQ-X has priority number , IRQ-Y has priority number . Which is more urgent on a Cortex-M?
Recall Solution (L1.2)
IRQ-X (number ). Why? On Cortex-M, ==lower numeric value = higher urgency==; priority is the most urgent. This is the reverse of everyday "level 10 beats level 1" intuition — see the L1 mistake below.
Level 2 — Application
L2.1 — Cycles to time
Problem. A Cortex-M4 runs at MHz. The deterministic hardware interrupt entry costs cycles. Convert that to nanoseconds.
Recall Solution (L2.1)
One cycle: . Why multiply? Latency counts how many clock ticks pass, and each tick lasts ; total time is (count) × (time per tick).
L2.2 — Priority-level counting
Problem. A chip implements priority bits. PRIGROUP assigns bits to preemption. How many preemption levels and how many sub-priority levels exist? Verify the total.
Recall Solution (L2.2)
Using the rule derived just above, and , with , :
- Preempt levels . Why? bits encode distinct groups that can preempt one another.
- Sub levels . Why? The leftover bit encodes tiebreaker slots.
- Total . ✔ The split never creates or destroys levels.
L2.3 — Worst-case latency
Problem. CPU MHz. Hardware entry cycles. Your longest critical section masks interrupts for cycles. Compute worst-case latency in nanoseconds.
Recall Solution (L2.3)

Level 3 — Analysis
L3.1 — Does X preempt Y?
Problem. Chip with bits, PRIGROUP → preempt bits, sub bit. IRQ-A: preempt , sub . IRQ-B: preempt , sub . IRQ-C: preempt , sub . Answer: (a) Does A preempt a running B? (b) If B and C are pending simultaneously (neither running yet), which runs first, and does it preempt the other?
Recall Solution (L3.1)

L3.2 — The tail-chaining saving
Problem. Two interrupts B then C are pending back-to-back. Naive handling: unstack after B ( cyc) then re-stack for C ( cyc). Tail-chaining costs only cyc for the B→C transition. (a) How many cycles saved? (b) Why is it safe to skip the unstack+restack?
Recall Solution (L3.2)

Level 4 — Synthesis
L4.1 — Design a UART receive ISR
Problem. Bytes arrive on a UART at baud, bits per byte (start + 8 data + stop). (a) How many bytes per second, and what is the inter-byte time budget in µs? (b) List the minimum work the ISR must do so it stays within budget, and where the heavy parsing goes.
Recall Solution (L4.1)
(a) Byte rate bytes/sec. Time budget per byte s µs. So the ISR + everything else must finish handling one byte before the next arrives.
(b) Minimum ISR work: (1) clear the RX interrupt flag first (so it doesn't re-enter forever), (2) read the received byte from the data register (this also often clears the flag on real hardware), (3) push it into a ring buffer, (4) set a volatile "data ready" flag, (5) return.
Heavy work (protocol parsing, checksums, string handling) goes in the main loop — the deferred / bottom-half. Why split it? A long ISR raises latency for every equal/lower-priority interrupt; keeping it to a handful of instructions leaves plenty of the µs budget for the next byte and other IRQs.
L4.2 — Choosing priorities for three tasks
Problem. You have three interrupt sources: a motor safety-stop line (must react instantly), a kHz control timer (hard deadline), and a UART byte (soft — a ring buffer absorbs jitter). Chip has bits, all used as preemption (PRIGROUP → preempt bits, sub bits). Assign preemption priority numbers and justify.
Recall Solution (L4.2)
With preempt bits there are levels, numbers (top) … .
- Motor safety-stop → priority . Most time-critical; must preempt everything. Remember the tightest deadline gets the highest urgency.
- kHz control timer → priority . Hard periodic deadline; must preempt the UART but yield to the safety stop.
- UART byte → priority (or higher number). A ring buffer tolerates jitter, so it can wait behind both — its latency ballooning briefly is harmless. Why this order? Lower number = shouts loudest. Assign numbers in order of "how badly does a late reaction hurt?" Safety > hard timing > buffered I/O.
Level 5 — Mastery
L5.1 — Full latency + throughput audit
Problem. A Cortex-M4 at MHz. Deterministic hardware entry cycles. The worst same-or-higher blocking is a critical section that masks IRQs for cycles. The ISR body itself runs cycles. (a) Worst-case latency (event → first ISR instruction) in ns. (b) Total time the CPU is unavailable to lower-priority interrupts because of this ISR (entry + body + exit unstack cycles), in ns. (c) An event source fires every µs. Can the system keep up? By what margin?
Recall Solution (L5.1)
s ns.
(a) . Why ? Hardware entry cannot start until the -cycle mask lifts; sequential, so add.
(b) cycles µs. Why sum these three? From the moment hardware entry begins until the ISR has fully returned, the CPU is running this ISR and nothing lower-priority can start — so the blocking window a lower-priority interrupt sees is the whole contiguous span: the -cycle entry (stacking + vector fetch), then the -cycle body, then the -cycle exit (unstacking). They happen back-to-back with no gap, so we add all three; there is no overlap to subtract.
(c) First compute the full firing-to-done span — from the event firing to the ISR having completely finished — because that is the total work one event generates. It is the blocked mask plus the busy window: µs. Why compare this to µs? If one event's total work ( µs) finishes before the next event arrives ( µs later), the system never falls behind — each event is fully serviced with time to spare. Since , it keeps up. Margin µs of idle time per period. Busy fraction , leaving about headroom for the main loop and other IRQs. Why headroom matters: if the busy fraction approached , no cycles would remain for anything else and deadlines would start slipping.
L5.2 — Debug the frozen loop
Problem. This code spins forever even though an ISR sets done = 1. Explain the bug and fix, in terms of what the compiler did.
int done = 0;
// ISR: done = 1;
while (!done) { /* wait */ }Recall Solution (L5.2)
Bug: done is not volatile. The optimizing compiler sees the while loop never changes done, so it reads done from RAM into a register once, then loops on the cached register value forever. It has no knowledge that an asynchronous ISR writes RAM.
Fix: volatile int done = 0;. Why it works: volatile forces the compiler to re-read done from memory on every loop iteration, so it eventually sees the ISR's write. This is the classic ISR↔main shared-variable hazard. For multi-byte shared data you also need atomic access (mask IRQs around the read/write) so you never see a half-updated value.
Recall Quick self-test
Lower priority number means ::: more urgent (0 is top). Sub-priority can preempt a running equal-preemption ISR? ::: No — it only breaks pending ties. Why must an ISR clear its flag early? ::: Otherwise it may re-enter forever. Why must ISR-shared variables be volatile? ::: So the compiler re-reads them from memory instead of caching in a register. Worst-case latency adds blocked time and hardware entry — max or sum? ::: Sum (they are sequential).