IRQ / NVIC Verifier
NVIC vectors and EXTI lines are shared, finite resources CubeMX never checks
for you: two GPIO pins wired to interrupts that happen to share an EXTI line,
a peripheral interrupt enabled with no vector modeled, or a DMA-vs-IRQ
priority inversion that quietly starves a completion handler. The M3 verifier
(Conflict::IrqConflict) catches all three at nucleus check time.
The model (nucleus-db/src/irq.rs)
Pure data, hand-maintained (the pack XML carries no NVIC/IRQ information), cross-checked by a reference-manual seed test:
- EXTI → NVIC grouping: lines 0–4 each get a dedicated vector
(
EXTI0..EXTI4); lines 5–9 shareEXTI9_5; lines 10–15 shareEXTI15_10. Identical on F446RE and F411RE (RM0390 / RM0383 Table 38). - Peripheral → vector map: one row per modeled peripheral kind
(USART/UART, SPI, I2C, TIM), restricted to the instances each family
actually has. I2Cx is the one irregular case — two vectors per
instance,
I2Cx_EV(event) andI2Cx_ER(error); everything else has exactly one.
The solver (nucleus-compiler/src/irq.rs)
Three independent checks, run in fixed order, each producing its own
IrqConflict entries:
- Unhandled IRQ — a peripheral table sets
irq = truefor a peripheral the family'sIrqMaphas no vector for. (Error.) - EXTI collision — two or more
[[exti]]entries resolve to the same EXTI line (shared across all eight GPIO ports —PA0,PB0, ...PH0all land on line 0). One conflict per contested line, naming every distinct port claiming it — not one conflict per colliding pair. (Error.) - Priority inversion — a peripheral with both
dma_priorityandirq_priorityset, where the DMA priority is numerically less urgent (a larger number) than the IRQ priority: the peripheral's own ISR can then preempt the DMA-completion interrupt it's meant to hand off to. (Warning —nucleus checkdoes not fail on this alone.)
IrqConflict is the first Conflict variant carrying its own explicit
Severity rather than always being an error — see Pinout &
Peripheral Verification for the severity rule
nucleus check's exit code follows.
stm32.toml schema
[peripherals.usart2]
tx = "PA2"
rx = "PA3"
irq = true # opt in to NVIC-vector verification (never inferred)
irq_priority = 5
dma = true
dma_priority = 2 # numerically smaller = more urgent
[[exti]]
pin = "PA0"
priority = 3
Example: an EXTI line collision
# tests/fixtures/exti_collision.toml — PA0 and PB0 both resolve to EXTI0
[device]
family = "STM32F446RE"
[[exti]]
pin = "PA0"
[[exti]]
pin = "PB0"
$ nucleus check tests/fixtures/exti_collision.toml
tests/fixtures/exti_collision.toml: 1 conflict found:
error: IRQ conflict [PA0]: EXTI0 is shared by PA0 and PB0 but only one can trigger it