|
<< Click to Display Table of Contents >> Navigation: ASA-EMulatR Reference Guide > Introduction > Architecture Overview > Chapter 16 – Device Model & DMA > 16.8 IRQ Integration Architecture |
The device interrupt architecture in EMulatR has been migrated away from the legacy IRQController class. Device IRQ interfaces are now handled by the newer IRQ system located in coreLib/, which provides a more modular, lock-free hot path and cleaner separation between device interrupt assertion, CPU polling, and PAL delivery.
The current IRQ architecture is organized around the following components in coreLib/:
•IRQPendingState — Per-CPU interrupt state using atomic bitmask operations. Provides the lock-free hot path: hasPendingInterrupt() reads an atomic<quint32> pendingIPLMask with no locking. Interrupt assertion sets bits in the mask; acknowledgment clears them.
•IPI_core.h — Inter-processor interrupt definitions: IPICommand enum (TLB shootdown, barrier synchronization, custom messages), IPI delivery through the per-CPU pending state.
•onIPLChanged_inl.h — Inline handler invoked when the current IPL changes (via MTPR SIRR or PAL exit), re-evaluating which pending interrupts are now deliverable.
The device-to-PAL interrupt delivery path using the current coreLib/ IRQ system:
Device completes operation (in device I/O thread)
│
▼
Assert IRQ: set bit in target CPU's IRQPendingState
pendingIPLMask |= (1 << deviceIPL) [atomic, lock-free]
│
▼
CPU run loop: AlphaCPU::runOneInstruction()
if (!inPalMode) poll IRQPendingState [lock-free read]
│
▼
hasPendingInterrupt(currentIPL) → true
(pending IPL > current IPL, not in PAL mode)
│
▼
Deliver to FaultDispatcher → PendingEvent
│
▼
PAL INTERRUPT handler entry
│
▼
PAL identifies source, acknowledges (MMIO write to device)
│
▼
Device clears interrupt condition
│
▼
HW_REI returns to interrupted code
The hot path — polling for pending interrupts during the CPU run loop — is entirely lock-free. The pendingIPLMask is an atomic bitmask where each bit corresponds to an IPL level. Reading this mask costs approximately 5 cycles (single atomic load), making per-instruction interrupt polling practical without performance impact.
Device interrupts are routed to specific CPUs based on routing policy. The supported policies are:
Policy |
Behavior |
|---|---|
FIXED_CPU |
Always delivered to a specific CPU (affinity-pinned). Used for boot devices and primary console. |
ROUND_ROBIN |
Rotates across available CPUs for load distribution. |
LOWEST_IPL |
Delivered to the CPU currently running at the lowest IPL (most likely to accept the interrupt promptly). |
Routing policy is configured per device vector in the IrqTemplate structure within DeviceTemplate. The default for most devices is FIXED_CPU with affinity to CPU 0, matching typical Alpha firmware behavior where the primary CPU handles all device interrupts until the OS reconfigures routing.
The IRQ system supports both trigger modes via the IRQTrigger enumeration. Level-triggered interrupts remain asserted until the device explicitly clears the condition (typically via an MMIO register write from PAL). The inServiceMask in IRQPendingState tracks level-triggered sources to prevent re-delivery while the interrupt is being serviced. Edge-triggered interrupts are one-shot: the pending bit is set on assertion and consumed on delivery, with no in-service tracking needed.
Most Alpha device interrupts are level-triggered. Edge-triggered mode is used for specific IPI types and timer interrupts.
See Also: coreLib/IRQPendingState.h – Per-CPU interrupt state; coreLib/IPI_core.h – IPI definitions; 7.10 Interrupt Handling; 9.6 Inter-Processor Interrupts (IPIs) .