How Do Hardware Interrupt Controllers Prioritize Events?

Hardware interrupt controllers manage asynchronous signals from peripheral devices so that a CPU can pause its current execution, handle urgent tasks, and resume without losing state. They sit between external devices and the processor core, collecting interrupt requests, resolving conflicts when multiple devices signal simultaneously, and enforcing priority rules before notifying the CPU.

Signal Collection and the Interrupt Request Line

Peripheral devices such as keyboards, network cards, and storage controllers operate independently of the CPU clock. When a device needs attention, it asserts an electrical signal on an Interrupt Request (IRQ) line connected to the interrupt controller. In legacy systems like the Intel 8259 Programmable Interrupt Controller (PIC), each device was wired to a dedicated physical pin, whereas modern systems using Advanced Programmable Interrupt Controllers (APIC) or ARM Generic Interrupt Controllers (GIC) often deliver interrupts as memory-mapped messages over a bus, such as Message Signaled Interrupts (MSI).

The controller latches these incoming requests into an Interrupt Request Register (IRR). Because multiple devices can assert their lines at the exact same time, the controller must evaluate all pending bits in the IRR before forwarding a single interrupt to the CPU.

Priority Resolution and Arbitration

When multiple bits are set in the IRR, the interrupt controller uses a priority resolver to select the highest-priority pending event. In classical fixed-priority schemes, each IRQ line is assigned a static rank, often where lower IRQ numbers correspond to higher urgency. For example, the system timer on IRQ 0 typically outranks the keyboard on IRQ 1.

Some controllers also support rotating priority or dynamic priority schemes to prevent lower-priority devices from starving under heavy load. If a controller has \(n\) interrupt lines, the arbitration logic evaluates the active requests in hardware within a single clock cycle or a few cycles, ensuring deterministic selection without software overhead.

Once the highest-priority request is chosen, the controller checks whether an interrupt of equal or higher priority is already being serviced by inspecting its In-Service Register (ISR). If the new request has a strictly higher priority than the currently active ISR bit, and the interrupt is not masked in the Interrupt Mask Register (IMR), the controller asserts the CPU's interrupt pin (such as INTR).

Interrupting the CPU and Context Switching

The CPU checks its interrupt pin at the boundary of each instruction cycle. If hardware interrupts are enabled in the processor's status register (for example, the Interrupt Flag \(IF = 1\) on x86 architectures), the CPU pauses its current instruction stream. It then acknowledges the interrupt by sending an Interrupt Acknowledge (INTA) signal back to the controller.

Upon receiving INTA, the controller places an interrupt vector number onto the data bus. The CPU uses this vector as an index into the Interrupt Vector Table (IVT) or Interrupt Descriptor Table (IDT) to locate the memory address of the corresponding Interrupt Service Routine (ISR). Before jumping to that address, the CPU pushes its current program counter and status flags onto the stack so it can restore the interrupted program later.

Nesting, Masking, and End of Interrupt

While the CPU executes the Interrupt Service Routine, the interrupt controller keeps track of the active priority level in its ISR register. If a higher-priority device asserts an IRQ during this time, the controller can preempt the current handler, causing a nested interrupt. Conversely, lower-priority interrupts remain pending in the IRR until the CPU finishes the higher-priority work.

Software can also influence this behavior by writing to the controller's Interrupt Mask Register to temporarily ignore specific devices, or by disabling interrupts globally on the CPU during critical sections. When the Interrupt Service Routine completes, it sends an End of Interrupt (EOI) command to the controller. The EOI clears the corresponding bit in the ISR register, allowing the controller to dispatch the next highest-priority pending request from the IRR, or letting the CPU pop its saved state from the stack and resume normal execution.