EmbeddedRelated.com

Nested Interrupts

Category: Rtos

Nested interrupts occur when a higher-priority interrupt preempts an ISR that is already executing, causing the processor to suspend the current ISR, save context, and begin serving the new interrupt before returning to finish the original one. The result is a call-stack-like nesting of interrupt service routines.

In practice

On ARM Cortex-M cores, nesting is handled largely in hardware by the NVIC, though software configuration of priority levels and grouping is still required to define the preemption behavior. Any pending interrupt whose priority number is numerically lower (i.e., higher urgency) than the currently active ISR will preempt it automatically, with the hardware stacking a new exception frame on top of the existing one. The blog post "Cutting Through the Confusion with ARM Cortex-M Interrupt Priorities" is a useful reference for understanding how priority grouping and sub-priorities interact in this scheme.

On many 8-bit architectures, nesting is not automatic; some have no priority scheme at all. On PIC18 devices, for example, there is a hardware high-priority and low-priority interrupt level; a high-priority ISR can interrupt a low-priority one, but interrupts of equal or lower priority do not nest. Deeper nesting on such devices requires manually saving context and explicitly re-enabling the global interrupt flag (GIE) inside an ISR, which is error-prone. "Working with Microchip PIC 8-bit Interrupts" covers this in detail.

Stack sizing is the most common pitfall with nested interrupts. Each level of nesting consumes additional stack space for the saved context frame. On Cortex-M, the hardware pushes at least 8 registers (32 bytes) per nesting level as a baseline, but alignment padding, FP context stacking, and implementation-specific behavior can increase this; any local variables and function call depth within the ISR add further cost. Failing to account for worst-case nesting depth is a frequent cause of stack overflow and hard-to-reproduce corruption bugs.

In an RTOS context, nested interrupts interact with interrupt locking primitives. Many RTOSes expose a "disable all interrupts" API and a separate "disable interrupts up to a ceiling priority" API (e.g., FreeRTOS's configMAX_SYSCALL_INTERRUPT_PRIORITY on Cortex-M). ISRs above the ceiling priority remain active and can still nest, but they are forbidden from calling any RTOS API. Violating this constraint is a common source of data corruption, deadlock, or other undefined behavior in mixed bare-metal/RTOS designs.

Discussed on EmbeddedRelated

Frequently asked

How does nesting priority work on ARM Cortex-M, and what happens when two ISRs have the same priority?
The NVIC compares the priority of the pending interrupt against the currently executing exception's priority. A pending interrupt only preempts if its priority is strictly higher (numerically lower). Two ISRs with identical priority levels will not nest; the second waits until the first completes. 'Cutting Through the Confusion with ARM Cortex-M Interrupt Priorities' explains how priority grouping and preempt/sub-priority fields affect this comparison.
How do I enable nested interrupts on an 8-bit PIC?
PIC18 devices provide a hardware two-level priority scheme (high/low). For deeper nesting, you must manually save the relevant SFRs (STATUS, WREG, BSR, etc.) at the top of the ISR and then set the GIE or PEIE bits to re-enable interrupts before the main ISR body. This is fragile and must be done carefully to avoid re-entrancy issues. 'Working with Microchip PIC 8-bit Interrupts' walks through the mechanics.
How should I size my stack when nested interrupts are in use?
Calculate the worst-case nesting depth (the maximum number of ISRs that can be simultaneously active) and multiply by the per-ISR stack cost. On Cortex-M, the hardware alone pushes 8 registers (32 bytes) per nesting level; if lazy FP stacking is active, up to 26 additional words may be pushed. Add any local variables and function call depth within each ISR on top of that, then add a safety margin.
Can I call RTOS APIs from a nested ISR?
It depends on the RTOS and on the ISR's priority. FreeRTOS on Cortex-M only allows ISR-safe API calls (those ending in 'FromISR') from ISRs whose priority is numerically at or above configMAX_SYSCALL_INTERRUPT_PRIORITY. ISRs with a higher urgency (numerically lower priority) than that ceiling must not call any RTOS API at all, even ISR-safe variants. Violating this rule can corrupt RTOS internal data structures.
Does nesting increase interrupt latency for lower-priority ISRs?
Yes. A lower-priority ISR that is preempted must wait for all higher-priority ISRs above it to complete before it resumes. In systems with many priority levels and long high-priority ISRs, worst-case latency for low-priority interrupts can be difficult to bound. Keeping all ISRs short and deferring heavy work to tasks or deferred interrupt handlers is the standard mitigation.

Differentiators vs similar concepts

Nested interrupts are sometimes conflated with interrupt prioritization. Priority determines which of several pending interrupts is served first when the CPU is not in an ISR. Nesting is what happens when a higher-priority interrupt arrives while an ISR is already executing. A system can have interrupt priorities without supporting nesting (e.g., a baseline PIC device that processes only one interrupt at a time in strict order). Conversely, on Cortex-M, nesting is the direct consequence of priority comparison at runtime and cannot easily be separated from the priority scheme.