5.5.7 · D4Embedded Systems & Real-Time Software

Exercises — Interrupts — ISR design, NVIC priority, interrupt latency

3,060 words14 min readBack to topic

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)

Figure — Interrupts — ISR design, NVIC priority, interrupt latency
Read the timeline above: the event fires inside the masked window, so the clock on "when can the ISR start?" cannot even begin ticking through hardware entry until the mask lifts. The two red/amber bars are end-to-end, not overlapping. . Why add the ? Worst case is the event firing just after you masked interrupts — it must wait out the entire critical section before hardware stacking can even begin. See the critical section idea.


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)

Figure — Interrupts — ISR design, NVIC priority, interrupt latency
How to read the figure: the lower cyan band is IRQ-B executing; when the amber IRQ-A bar appears, B's band pauses (there is a gap) — that gap is the visual proof of preemption. B only resumes after A's amber bar ends. IRQ-C, sharing B's preemption priority, sits off to the right in the queue and never gets its own overlapping band, because it can never cut into a running peer. (a) Yes. Preemption is decided by preemption priority only. A has preempt , B has preempt ; lower number = higher urgency, so A can interrupt B mid-execution. (b) B runs first, and it does NOT preempt C. B and C share the same preemption priority (), so neither can interrupt the other. Their sub-priority breaks the pending tie: B sub beats C sub (lower = first). Once B runs to completion, C runs. Why sub-priority never preempts: it only orders things already waiting in the queue; it has no power to stop a running ISR.

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)

Figure — Interrupts — ISR design, NVIC priority, interrupt latency
How to read the figure: the top row is the naive path — after "B body" a wide amber block (unstack + restack ) burns before "C body" starts. The bottom row is tail-chaining — the same "B body" flows through a narrow amber block (chain ) straight into "C body." The width difference between the two amber blocks is the saving, called out in amber underneath. (a) Naive transition cost cyc. Tail-chained cyc. Saved cyc. (b) The 8-register saved context on the stack is still valid the moment B returns — nothing outside the ISRs ran in between. Popping those 8 words only to immediately push the identical 8 words is pure wasted work, so the hardware jumps straight B→C, keeping the stack frame in place.


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).