Skip to content

IRQMP — Multi-processor interrupt controller

GRLIB IRQMP (GR712RC §8 / GRLIB IP §31): the cross-bar that turns external interrupt assertions into per-CPU pending bitmaps.

Property Value
Default MMIO base 0x80000200
MMIO window size 0x100
CPUs supported 2 (GR712RC)
IRQ lines 131 (line 0 is reserved as "no interrupt")
Source file src/peripherals/src/irqmp.cpp

Register map

All registers are 32-bit, word-aligned. Byte and half-word accesses return AlignmentError.

Offset Name Access Description
0x000 ILR R/W Interrupt level register (priority bit per line)
0x004 IPR R Interrupt pending register (current asserted lines)
0x008 IFR0 R/W Force register, CPU 0
0x00C ICR W Interrupt clear register (write-1-clear)
0x010 MPSTAT partial R Multiprocessor status
0x014 BROADCAST R/W Broadcast mask (which lines go to all CPUs)
0x040 IMASK0 R/W CPU 0 interrupt mask
0x044 IMASK1 R/W CPU 1 interrupt mask
0x080 IFORCE0 R/W (high half W1C) CPU 0 force register
0x084 IFORCE1 R/W (high half W1C) CPU 1 force register
0x0C0 EID0 R Extended interrupt ID for CPU 0
0x0C4 EID1 R Extended interrupt ID for CPU 1

MPSTAT layout

MPSTAT is read-mostly with a few writable bits (CPU power-down release in particular):

Bits Field Meaning Reset value
31:28 NCPU Number of CPUs minus 1 0 (single-core default)
27 BA Broadcast available 1
19:16 EIRQ Extended IRQ number 12
15:0 STATUS Per-CPU power status (1 = halted) depends on cores

Writing the lower 16 bits releases the corresponding CPU from power-down mode; this is how RTEMS releases secondary cores during SMP boot.

IFORCE / IFR write semantics (Decision 13)

IFORCE0, IFORCE1 and IFR0 use a clear-then-set protocol:

  • The upper 16 bits of the written value are W1C: any bit set in the upper half clears the corresponding force bit.
  • The lower 16 bits are W1S: any bit set in the lower half sets the corresponding force bit.

This matches GRLIB hardware where software can atomically toggle force state in one MMIO write. In code:

uint32_t cleared_then_set =
    (current_force & ~(value >> 16))    // clear bits indicated by hi half
    | (value & 0xFFFF);                 // set bits indicated by lo half

pending_mask(cpu) — what the core sees

The runtime's sample_interrupts calls IrqMP::pending_mask(cpu) once per quantum to find out which lines are pending for that CPU:

pending_mask(cpu) = (IPEND | IFR_for(cpu)) & IMASK[cpu];

…with two refinements (Decision 14):

  • For CPU 0, IFR_for(0) is IFR0.
  • For CPU N > 0, IFR_for(N) is IFORCE[N].

The highest set bit ≥ 1 in the resulting mask is the interrupt level; the runtime then compares it to PSR.PIL and injects a hardware trap with tt = 0x10 + level if needed.

external_assert / external_clear

The C++ API used by other peripherals (and by tests) to drive lines into the IRQMP:

void external_assert(uint32_t bits) noexcept;
void external_clear (uint32_t bits) noexcept;
uint32_t pending_mask(uint32_t cpu) const noexcept;

external_assert(bits) sets the bits in the shared pending vector (IPR). Bits in BROADCAST propagate to all per-CPU force registers. This is the behaviour the runtime's IrqBridge adapter wraps when a peripheral calls IInterruptSource::raise().

Reset state

  • IPR = 0, IFR0 = 0, IFORCE[*] = 0.
  • IMASK[*] = 0 (everything masked).
  • BROADCAST = 0.
  • MPSTAT = (1 << 28) | (1 << 27) | (12 << 16).

Tests

  • tests/unit/test_irqmp.cpp — register-by-register coverage of the IFORCE write semantics, pending_mask, external_assert, broadcast propagation, ICR W1C, and MPSTAT writes that release a CPU.
  • The integration tests under tests/integration/test_rtems_* exercise the IRQMP through the full RTEMS BSP boot.