UVM
Interrupt Verification Strategies
Pulling interrupt modeling, sequences, and checking into a complete strategy — a reusable interrupt verification component configured by the interrupt map, a coverage plan spanning every source, interaction, and negative case, integration with the main functional verification so interrupts are verified during real traffic rather than in isolation, and sign-off only when every source, corner, and negative case has been exercised and checked.
Interrupt Verification · Module 25 · Page 25.4
The Engineering Problem
You can model an interrupt (25.1), provoke it deliberately including the corners (25.2), and check it two-sided against the spec (25.3) — three skills. But three skills aren't a strategy. A strategy answers: how do you organize these into a reusable component (so you don't rebuild the interrupt agent, sequences, checks, and coverage for every DUT)? How do you plan coverage so you know you've verified everything (every source, every interaction, every negative case)? How do you integrate interrupt verification with the main functional verification (interrupts fire during normal operation, not in a quiet testbench)? And how do you sign off — declare the interrupt mechanism verified — with confidence? The trap is treating interrupt verification as three separate activities done in isolation: a standalone interrupt test that provokes and checks every interrupt in a quiet testbench looks thorough, signs off, and misses the interrupt-during-traffic bugs that only appear when interrupt servicing and concurrent functional activity collide. The problem this chapter solves is interrupt verification strategy: turning model, provoke, and check into one coherent plan — a reusable interrupt verification component, a coverage plan spanning sources × interactions × negatives, integration with the main verification, and sign-off that requires every case exercised and checked.
Interrupt verification strategy is organizing modeling, sequences, and checking into one coherent, reusable, signed-off plan. The reusable interrupt verification component (IVC): bundle the interrupt agent (the monitor, 25.1), the interrupt sequences (25.2), the interrupt checks (scoreboard + assertions, 25.3), and the interrupt coverage model into a configurable component parameterized by the interrupt map (sources, line, status register, clear mechanism, priority) — so it plugs into any DUT with interrupts. The coverage plan: three-dimensional — every source (each interrupt fires and is checked), every interaction (simultaneous, nested, storm, clear-race — the corners), and every negative case (no-spurious). The integration: the IVC runs concurrently with the main functional verification — interrupts fire during real traffic, and the interaction between servicing and concurrent functional activity is where bugs hide. The sign-off: the interrupt mechanism is verified only when every source, corner, and negative case has been exercised AND checked — coverage closed, checks passing, no spurious. The cardinal discipline: interrupt verification must be integrated, not isolated — a standalone interrupt test in a quiet testbench misses the interrupt-during-traffic bugs that only the integrated run exposes. This chapter is the strategy: the reusable component, the coverage plan, the integration, and the sign-off.
How do you turn interrupt modeling, sequences, and checking into a complete strategy — a reusable interrupt verification component configured by the interrupt map, a coverage plan spanning every source, interaction, and negative case, integration with the main functional verification so interrupts are verified during real traffic, and sign-off only when every case has been exercised and checked?
Motivation — why strategy, not three separate skills
Model, provoke, and check are necessary but not a strategy; the strategy is what makes interrupt verification complete, reusable, and trustworthy. The reasons:
- Three skills in isolation leave gaps. Model without provoke → corners never hit. Provoke without check → bugs not caught. Check without model → nothing to check against. Only the three together, planned, verify the mechanism.
- Rebuilding per DUT wastes effort and invites bugs. Every DUT with interrupts needs an agent, sequences, checks, and coverage — rebuilding them each time is slow and error-prone. A reusable component configured by the interrupt map is built once, reused.
- Without a coverage plan, "verified" is a guess. "We tested interrupts" says nothing about which sources, interactions, and negatives were covered. A plan (sources × interactions × negatives) makes coverage measurable and closure definable.
- Isolation hides interaction bugs. Interrupts fire during normal operation — the servicing (status reads, clear writes) interacts with concurrent functional traffic. A standalone test in a quiet testbench never exercises that interaction, so interrupt-during-traffic bugs escape.
- Sign-off needs criteria, not vibes. "Interrupts are verified" must mean every source, corner, and negative case exercised AND checked, coverage closed, no spurious — explicit criteria, not a feeling of thoroughness.
The motivation, in one line: model, provoke, and check are three skills that only become interrupt verification when organized into a strategy — a reusable component (built once, configured per DUT), a coverage plan (sources × interactions × negatives), integration with the main verification (so interaction bugs are caught), and explicit sign-off criteria — because three skills used in isolation leave gaps, waste effort, and sign off in a quiet testbench that misses the bugs the real system has.
Mental Model
Hold interrupt verification strategy as a complete fire-safety program — a standard reusable system, a plan covering every zone and scenario and false-alarm, run while the building operates, certified only when all is drilled and inspected:
A building isn't fire-safe just because someone installed alarms, or ran a drill, or did an inspection. It's fire-safe because of a complete safety program. First, a standard, reusable system — the same alarm-and-suppression design, configured for each building's particular zones and layout, not reinvented from scratch every time. Second, a plan that covers everything: every zone has detection, every multi-zone and cascading scenario is drilled, and every false-alarm condition is tested so the system doesn't cry wolf. Third, the program runs while the building operates normally — drills happen with people working, equipment running, elevators moving, because that's when a real fire happens and that's where the interactions are: an alarm during an evacuation, a suppression system competing with normal water pressure. A drill in an empty building proves far less. Fourth, certification: the building is signed off as fire-safe only when every zone, every scenario, and every false-alarm case has been both drilled and inspected — not when someone feels it's probably fine. Skip any part and the certificate is hollow: a standard system never drilled, drills never run during operation, a plan with zones uncovered, or a sign-off with no criteria. The whole program, planned and certified together, is what makes the building actually safe. A building isn't fire-safe just because someone installed alarms, ran a drill, or did an inspection. It's safe because of a complete safety program. First, a standard, reusable system — the same alarm-and-suppression design, configured for each building's zones and layout, not reinvented each time. Second, a plan covering everything: every zone has detection, every multi-zone/cascading scenario is drilled, every false-alarm condition is tested (so it doesn't cry wolf). Third, the program runs while the building operates — drills happen with people working, because that's when a real fire happens and where the interactions are (an alarm during an evacuation, suppression competing with normal water pressure); a drill in an empty building proves far less. Fourth, certification: signed off only when every zone, scenario, and false-alarm case has been both drilled and inspected — not when someone feels it's probably fine. Skip any part and the certificate is hollow. The whole program, planned and certified together, is what makes the building safe.
So interrupt verification strategy is a complete fire-safety program: a standard reusable system (the IVC — agent, sequences, checks, coverage, configured by the interrupt map per DUT), a plan covering every zone/scenario/false-alarm (sources × interactions × negatives), run while the building operates (integrated with the main functional verification, not in a quiet testbench), and certified only when all is drilled and inspected (sign-off = every case exercised AND checked). Skip any part — never drill the reusable system, drill only in an empty building, leave zones uncovered, sign off with no criteria — and the certificate is hollow. Build the interrupt verification as one program: reusable, fully planned, integrated with real traffic, and signed off only when every source, corner, and negative is exercised and checked.
Visual Explanation — the strategy: model + provoke + check, unified
The defining picture is the three legs unified: model (25.1), provoke (25.2), and check (25.3) feeding one reusable, planned, signed-off strategy.
The figure shows the strategy unifying the three legs. Three legs (the brand-colored top): model (25.1 — observe the interrupt), provoke (25.2 — drive conditions and corners), check (25.3 — verify two-sided) — none alone is a strategy. Reusable IVC (default-colored): agent, sequences, checks, coverage bundled and configured by the interrupt map — built once, plugs into any DUT. Coverage plan (success-colored): sources × interactions × negatives — every source fires and is checked, every interaction (simultaneous/nested/storm/clear-race), every negative (no spurious). Integrated + signed off (the warning-colored — the easy-to-skip part): runs concurrently with real traffic; signed off only when every case is exercised AND checked. The crucial reading is vertical: the three legs (top) are the skills, but the strategy is everything below — bundling them into a reusable component, planning coverage across all three dimensions, and integrating + signing off. The warning color on the integration/sign-off layer flags it as the most-skipped: it's tempting to build the component, run the standalone test, see all interrupts provoked and checked, and sign off — but without integration (running concurrently with the main functional verification), the interrupt-during-traffic bugs escape, and without explicit sign-off criteria (every case exercised AND checked), "verified" is a guess. The reusability matters because every DUT with interrupts needs the same machinery (agent, sequences, checks, coverage) — configuring it by the interrupt map (sources, line, status, clear, priority) means building once, reusing everywhere. The coverage plan's three dimensions map directly to the prior chapters: sources and negatives (25.3's two-sided), interactions (25.2's corners). The diagram is the strategy: three legs → reusable component → coverage plan → integrated + signed off — the skills become verification only when organized this way. Unify model, provoke, and check into one reusable, fully-planned, integrated, signed-off strategy — no leg alone is enough.
RTL / Simulation Perspective — the reusable interrupt verification component
In code, the strategy is a configurable IVC: the agent, sequences, checks, and coverage parameterized by the interrupt map, plugged into the env alongside the main verification. The example shows the structure.
// The interrupt map: the per-DUT configuration that makes the IVC reusable
class irq_map extends uvm_object;
rand bit [31:0] status_addr, clear_addr; // status / clear register addresses
string source_names[]; // the interrupt sources
int priority_order[]; // priority assignment (for nesting/simultaneous)
bit is_level; // level vs edge
endclass
// The IVC: agent (monitor) + sequences + checks + coverage, configured by the map
class irq_agent extends uvm_agent;
irq_map map; // configures the monitor, checks, coverage
irq_monitor mon; // 25.1: observes the line + status (concurrent, forked)
irq_sequencer seqr; // 25.2: drives conditions + corners
irq_scoreboard sb; // 25.3: two-sided check (predictor + no-spurious)
irq_coverage cov; // coverage model: sources × interactions × negatives
function void build_phase(uvm_phase phase);
map = irq_map::type_id::create("map");
if (!uvm_config_db#(irq_map)::get(this, "", "irq_map", map)) // configured per DUT
`uvm_fatal("IRQ", "no interrupt map provided");
// mon, seqr, sb, cov all built from `map` — reusable across DUTs
endfunction
endclass
// === INTEGRATION: the IVC runs CONCURRENTLY with the main functional verification ===
class soc_env extends uvm_env;
func_agent func_ag; // the main functional verification
irq_agent irq_ag; // the interrupt verification component — runs ALONGSIDE
// both active at once: interrupts fire DURING functional traffic (not in isolation)
endclass
// In a test: run interrupt sequences CONCURRENTLY with functional stimulus
task run_phase(uvm_phase phase);
fork
main_functional_seq.start(func_ag.seqr); // real traffic
interrupt_seq.start(irq_ag.seqr); // interrupts DURING that traffic — integration!
join // ✓ catches interrupt-during-traffic bugs
endtask
// ✗ MISTAKE: run interrupt_seq ALONE in a quiet testbench → standalone, misses interaction (DebugLab)The code shows the reusable IVC and its integration. The interrupt map (irq_map): the per-DUT configuration — status_addr, clear_addr, source_names, priority_order, is_level — what differs between DUTs. The IVC (irq_agent): the monitor (mon, 25.1), sequencer (seqr, 25.2), scoreboard (sb, 25.3), and coverage (cov) — all configured by the map (built from it in build_phase, fetched via config_db), so the same component plugs into any DUT by supplying a different map. The integration (soc_env): the irq_agent runs alongside the func_agent (the main functional verification) — both active at once. And the test run_phase forks the main functional sequence and the interrupt sequence concurrently (fork ... join) — so interrupts fire during real traffic, catching the interrupt-during-traffic bugs. The mistake (commented) is running the interrupt sequence alone in a quiet testbench — standalone, missing the interaction (the DebugLab). The shape to carry: the strategy in code is a configurable IVC (irq_map makes it reusable — built once, configured per DUT) integrated into the env alongside the main verification, with the interrupt sequences run concurrently with the functional stimulus. The reusability comes from parameterizing by the map; the integration comes from running both agents concurrently. Build the interrupt verification as a reusable component configured by the interrupt map, and run it concurrently with the main functional verification — not alone in a quiet testbench.
Verification Perspective — the three-dimensional coverage plan
The defining artifact of the strategy is the coverage plan — three-dimensional: sources × interactions × negatives. Seeing all three dimensions clarifies what closure means.
The figure shows the three-dimensional coverage plan. Sources (the brand-colored dimension): every interrupt source must fire and be checked. Interactions (default-colored): every interaction corner — simultaneous, nested, storm, clear-race — must be exercised and checked. Negatives (the warning-colored): every negative case — no-spurious, an interrupt that should not fire is confirmed not to — must be covered. The verification insight is that closure requires all three dimensions, not sources alone. A plan that covers only sources (each interrupt fires and is checked) looks substantial — but it leaves the interactions (where the corner bugs are — 25.2) and the negatives (where the spurious bugs are — 25.3) unverified. The three dimensions map to the module's prior chapters: sources (the basic model/provoke/check, 25.1-25.3 for each source), interactions (the corners from 25.2), and negatives (the two-sided check's negative direction from 25.3). All three feed the success-colored closure — and closure is only reached when all three are covered. The coverage model (the irq_coverage from the code) implements this plan: coverpoints on each source firing, cross-coverage on interaction corners, and coverpoints on negative cases (a window where no interrupt should fire, confirmed clean). The reason this matters for strategy is that closure is the sign-off criterion — you sign off the interrupt mechanism when the coverage plan is closed, and a plan that only tracks sources lets you sign off with the interactions and negatives unverified. The diagram is the coverage plan: sources + interactions + negatives → closure — all three, or closure is false. Plan interrupt coverage across all three dimensions — sources, interactions, and negatives — because closure on sources alone leaves the corners and spurious cases unverified.
Runtime / Execution Flow — from skills to sign-off
At the strategy level, the flow is building the component → planning coverage → integrating → signing off. The flow shows how the skills become a signed-off mechanism.
The flow shows skills becoming a signed-off mechanism. Bundle (step 1): agent, sequences, checks, coverage configured by the interrupt map — built once, reused per DUT. Plan (step 2): sources × interactions × negatives — what closure means, measurable. Integrate (step 3): run the IVC concurrently with functional stimulus — interrupts during real traffic. Sign off (step 4): only when the plan is closed AND every case is checked — not a guess. The runtime insight is that each step depends on the previous. A component with no coverage plan (skip step 2) can't measure closure — you'd run it but not know what's covered. An unintegrated component (skip step 3) misses interaction bugs — the interrupt-during-traffic failures escape. And sign-off without closure (skip the criteria in step 4) is a guess — "interrupts are verified" with no evidence. The brand-colored bundle/plan are the construction, the default-colored integrate is the crucial (often-skipped) connection to the real testbench, and the success-colored sign-off is the gated conclusion. The crucial point is that the flow is ordered and gated: you can't meaningfully sign off (step 4) without closure (from the plan, step 2) and integration (step 3) — so the strategy is not "build and run an interrupt test" but "build the reusable component, plan the coverage, integrate it with the main verification, and gate sign-off on closure and checks." This is the difference between having interrupt tests and having a verified interrupt mechanism: the former is step 1 alone; the latter is the whole flow. The flow is the strategy's spine: bundle → plan → integrate → sign off, each gating the next. Turn the skills into a verified mechanism by bundling them into a reusable component, planning coverage, integrating with the main verification, and gating sign-off on closure and checks.
Waveform Perspective — the interaction bug isolation misses
The integration point is visible: an interrupt's clear write collides with concurrent functional traffic on the shared bus — a bug a standalone (quiet-testbench) run never exposes. The waveform shows it.
An interrupt clear write collides with concurrent functional traffic — a bug only the integrated run exposes
12 cyclesThe waveform shows the interaction bug isolation misses. In the integrated run, the main functional verification drives traffic (func_txn high throughout) while an interrupt fires (irq). The handler issues the clear write (clear_wr at cycle 5), but it collides with a concurrent functional transaction on the shared bus (bus_busy high) — the clear is dropped, so irq stays asserted (never deasserts — stuck). The crucial reading is the contrast with the standalone run: a standalone interrupt test runs in a quiet testbench with no functional traffic, so bus_busy is always low, the clear never collides, irq clears cleanly, and the bug is invisible. The only way to expose the collision is to run the interrupt verification concurrently with real traffic — integration. The picture to carry is that the bug lives in the interaction, not in the interrupt or the functional path alone: the interrupt servicing (the clear write) is correct in isolation, the functional traffic is correct in isolation, but together — when the clear competes with a functional transaction on the shared bus — the clear is dropped. A standalone test exercises each alone and passes both; only the integrated run exercises them together and catches the collision. Reading the waveform this way — did the clear land, or did concurrent traffic drop it? — is why integration matters: the stuck irq from the dropped clear is the signature of an interaction bug that only appears when interrupt servicing and functional traffic coexist. An interrupt-servicing-versus-traffic collision is invisible in a quiet testbench and only appears when the interrupt verification runs concurrently with real traffic.
DebugLab — the interrupt mechanism verified in isolation that broke in the system
A signed-off interrupt mechanism that broke in the system because it was never verified during real traffic
A team built a thorough standalone interrupt verification: a reusable interrupt component with an agent, sequences, and a two-sided scoreboard. The standalone interrupt test provoked every source, forced every corner (simultaneous, nested, storm, clear-race), and checked everything two-sided — no spurious, right source, clean clears. The interrupt coverage on sources and interactions closed. The team signed off the interrupt mechanism. Months later, in SoC-level testing with the full system running, an intermittent hang appeared: under heavy bus traffic, an interrupt would occasionally fail to clear — the line stayed asserted, the handler re-entered in a loop, and the system hung. The standalone interrupt regression still passed cleanly. Tracing the SoC hang, the team found the interrupt handler's clear write was occasionally colliding with a concurrent functional transaction on the shared register bus — under contention, the clear write was being dropped, so the interrupt never cleared. The standalone interrupt test had never exposed this, because it ran in a quiet testbench: the interrupt verification ran in isolation, with no concurrent functional traffic, so the clear write never competed for the bus and always landed.
The interrupt verification was run in isolation — a standalone interrupt test in a quiet testbench — and never integrated with the main functional verification, so the interaction between interrupt servicing and concurrent functional traffic was never exercised, and the interrupt-during-traffic collision bug escaped:
✗ ISOLATED — interrupt verification run alone in a quiet testbench:
task run_phase(uvm_phase phase);
interrupt_seq.start(irq_ag.seqr); // ONLY interrupts — no functional traffic
endtask
// → every source provoked, every corner forced, all checked two-sided, coverage closes
// BUT the clear write NEVER competes for the shared bus (no concurrent functional txns)
// → the interrupt-during-traffic collision is NEVER exercised → bug ships → SoC hang
✓ INTEGRATED — interrupt verification run CONCURRENTLY with the main functional verification:
task run_phase(uvm_phase phase);
fork
main_functional_seq.start(func_ag.seqr); // real traffic on the shared bus
interrupt_seq.start(irq_ag.seqr); // interrupts DURING that traffic
join
endtask
// now the clear write COMPETES with functional transactions → the collision is exercised
// → the dropped-clear / stuck-irq bug is caught in regression, before silicon
// + coverage cross: interrupt-servicing × concurrent-bus-traffic must be covered (not just sources)This is the isolation bug — the cardinal interrupt-verification-strategy failure. The interrupt verification was thorough on every axis except integration: the reusable component was well-built, every source provoked, every corner forced, everything checked two-sided, coverage closed — which looked like complete sign-off. But it ran in isolation — a standalone interrupt test in a quiet testbench, with no concurrent functional traffic. So the interaction between interrupt servicing (the handler's clear write on the shared register bus) and concurrent functional activity (other transactions on that bus) was never exercised. The clear write always landed (it never competed for the bus), so the interrupt always cleared cleanly — in the test. In the system, under heavy traffic, the clear write collided with a functional transaction, was dropped, and the interrupt never cleared → stuck line → handler re-entry loop → hang. The deep reason is that interrupts fire during normal operation, and the bugs live in the interaction — the interrupt path is correct in isolation and the functional path is correct in isolation, but together, competing for shared resources, they break — and a standalone test that exercises each alone passes both and misses the interaction. The fix is integration: run the interrupt verification component concurrently with the main functional verification (fork main_functional_seq ... interrupt_seq join), so the clear write competes with functional transactions and the collision is exercised — plus a coverage cross (interrupt-servicing × concurrent-bus-traffic) so closure requires the interaction to be covered, not just sources. The general lesson, and the chapter's thesis: interrupt verification must be integrated with the main functional verification, not run only in isolation — interrupts fire during real traffic, and bugs hide in the interaction between interrupt servicing and concurrent functional activity; a standalone interrupt test that signs off in a quiet testbench misses the interrupt-during-traffic bugs, so run the interrupt verification component concurrently with the main functional tests and cover the interaction, because an interrupt mechanism verified only in isolation is not verified for the system where interrupts and functional traffic coexist. A standalone interrupt test passes the interrupt path and the functional path separately and ships the bug that lives only in their interaction — integrate, and verify interrupts during real traffic.
The tell is an interrupt bug at SoC or system level that the standalone interrupt regression passes. Diagnose isolation:
- Check whether interrupts run concurrently with functional traffic. An interrupt test that runs interrupt sequences alone, with no main functional stimulus, is isolated.
- Look at the shared resources. Interrupt servicing that touches a shared bus or register block contends with functional traffic only when both run together.
- Map the system bug to the test environment. A collision or contention bug that the standalone test can't reproduce confirms the missing integration.
- Audit the coverage for interaction crosses. Coverage that tracks sources and interrupt-corners but not interrupt-servicing × concurrent-traffic has the integration gap.
Integrate, don't isolate:
- Run the interrupt verification component concurrently with the main functional verification. Fork interrupt sequences alongside functional stimulus so interrupts fire during real traffic.
- Cover the interaction. Cross interrupt servicing with concurrent functional activity; closure requires the interaction covered, not just sources.
- Build the IVC to plug into the full env. A reusable component configured by the interrupt map drops into the SoC env alongside the functional agents.
- Gate sign-off on integrated runs. Sign off the interrupt mechanism only after it has been verified during real traffic, not only in a standalone test.
The one-sentence lesson: interrupt verification must be integrated with the main functional verification, not run only in isolation, because interrupts fire during real traffic and bugs hide in the interaction between interrupt servicing and concurrent functional activity, so run the interrupt verification component concurrently with the functional tests and cover the interaction — an interrupt mechanism verified only in a quiet testbench is not verified for the system where interrupts and traffic coexist.
Common Mistakes
- Treating model, provoke, and check as separate activities. They're three legs of one strategy; planned and signed off together, not in isolation.
- Rebuilding the interrupt machinery per DUT. Build a reusable component configured by the interrupt map; don't reinvent the agent, sequences, checks, and coverage each time.
- Running interrupts only in a quiet testbench. Integrate with the main functional verification so interrupts fire during real traffic and interaction bugs are caught.
- Closing coverage on sources alone. Plan coverage across sources, interactions, and negatives; closure on sources leaves the corners and spurious cases unverified.
- Signing off on a feeling. Sign-off needs explicit criteria — every source, corner, and negative exercised AND checked, coverage closed, no spurious.
- Provoking corners without checking them. A corner exercised but unchecked verifies nothing; coverage must pair with checks.
Senior Design Review Notes
Interview Insights
Interrupt verification becomes a strategy when modeling, sequences, and checking are organized into one coherent, reusable, signed-off plan, rather than performed as three isolated activities. The three skills are the legs. Modeling is how you observe the interrupt — the agent and monitor that watch the line and status, with the handler forked concurrently. Sequences are how you provoke it — driving the conditions that raise each source and forcing the corners. Checking is how you verify it — the two-sided scoreboard and assertions confirming it fires when it should and not when it shouldn't, with the right source, clean clears, and honored priority. But three skills aren't a strategy, for several reasons. First, in isolation they leave gaps: model without provoke means corners are never hit, provoke without check means bugs aren't caught, check without model means there's nothing to check against. Only all three together, planned, verify the mechanism. Second, rebuilding the machinery for every DUT is wasteful and error-prone, so the strategy bundles the agent, sequences, checks, and coverage into a reusable component configured by an interrupt map — the per-DUT specifics like the status and clear addresses, the sources, the priority order — so it's built once and reused. Third, without a coverage plan, verified is a guess; the strategy defines coverage across sources, interactions, and negatives so closure is measurable. Fourth, and most important, interrupts fire during normal operation, so the strategy integrates the interrupt verification with the main functional verification — running them concurrently — because the bugs hide in the interaction between interrupt servicing and concurrent functional traffic, which a standalone test in a quiet testbench never exercises. Fifth, sign-off needs explicit criteria — every source, corner, and negative exercised and checked, coverage closed, no spurious — not a feeling. So the strategy is the reusable component, the three-dimensional coverage plan, the integration with real traffic, and the gated sign-off, which together turn three skills into a verified interrupt mechanism. The analogy is a fire-safety program: not just installing alarms, running a drill, or inspecting, but a standard reusable system, a plan covering every zone and scenario and false-alarm, run while the building operates, and certified only when everything is drilled and inspected.
Because interrupts fire during normal operation, and the bugs hide in the interaction between interrupt servicing and concurrent functional activity, which a standalone interrupt test in a quiet testbench never exercises. In the real system, an interrupt doesn't fire in isolation — it fires while the design is doing its normal work, with traffic on the buses, transactions in flight, the register block being accessed. And the interrupt servicing — the handler reading the status register and writing the clear register — happens on shared resources that the functional traffic also uses. So there's an interaction: the clear write competes with functional transactions for the shared bus, the handler runs concurrently with the main stimulus, the status read overlaps other accesses. The bugs live in that interaction. A classic example: under heavy bus traffic, the handler's clear write collides with a concurrent functional transaction on the shared register bus, the clear is dropped under contention, and the interrupt never clears — the line stays asserted, the handler re-enters in a loop, and the system hangs. Now, a standalone interrupt test exercises the interrupt path alone, in a quiet testbench with no functional traffic. The clear write never competes for the bus, so it always lands, the interrupt always clears cleanly, and the collision bug is invisible. The standalone test can be thorough on every other axis — every source provoked, every corner forced, everything checked two-sided, coverage closed on sources and interactions — and still pass while shipping the interaction bug, because it verifies the interrupt path and the functional path separately and never together. The deep point is that each path is correct in isolation; the bug only exists when they coexist and compete for shared resources. So you must run the interrupt verification concurrently with the main functional verification — fork the interrupt sequences alongside the functional stimulus — so the servicing competes with real traffic and the interaction is exercised. And you cover the interaction explicitly, crossing interrupt servicing with concurrent functional activity, so closure requires it. An interrupt mechanism verified only in isolation is not verified for the system where interrupts and functional traffic coexist, which is the system that ships.
You build a reusable interrupt verification component by bundling the agent, sequences, checks, and coverage and configuring them by an interrupt map — the per-DUT specifics — so the same component plugs into any DUT with interrupts. The component contains everything for interrupt verification: the interrupt monitor that observes the line and status, the interrupt sequencer that runs the sequences provoking conditions and corners, the scoreboard implementing the two-sided check, and the coverage model spanning sources, interactions, and negatives. What differs between DUTs is captured in an interrupt map — a configuration object holding the status register address, the clear register address, the list of interrupt sources, the priority order for nesting and simultaneous handling, whether the interrupts are level or edge. The component is parameterized by this map: in the build phase, it fetches the map from the config database and builds the monitor, sequences, scoreboard, and coverage from it. So to reuse the component on a new DUT, you supply a different map — the machinery stays the same, only the configuration changes. This is the verification IP idea applied to interrupts: build the interrupt verification once, as a configurable component, rather than reinventing the agent, sequences, checks, and coverage for every project. The benefits are it's faster, because you're not rebuilding, and it's more reliable, because the well-tested component carries its correctness forward, and you're not reintroducing bugs each time. The component then drops into the environment alongside the functional agents — and crucially, for integration, it runs concurrently with the main functional verification, with the interrupt sequences forked alongside the functional stimulus, so interrupts fire during real traffic. The map also makes the coverage reusable: the coverage model covers the sources named in the map, crosses the interaction corners, and checks the negatives, all driven by the configuration. So the reusable component is the agent plus sequences plus checks plus coverage, configured by the interrupt map, built once and reused per DUT by changing only the map — which is the strategy's answer to not rebuilding interrupt verification from scratch every project.
The interrupt coverage plan needs to cover three dimensions for sign-off: sources, interactions, and negatives — closure on sources alone is not enough. The first dimension is sources: every interrupt source must fire and be checked. Each source has its triggering condition provoked, the interrupt observed, and the two-sided check confirming it fired correctly with the right source. This is the baseline — every interrupt the design can raise is exercised and verified. The second dimension is interactions: every interaction corner must be exercised and checked — simultaneous interrupts firing in the same window, nested interrupts where a higher-priority one preempts a lower one's service, back-to-back and storm scenarios, and clear-race conditions where a new event arrives as the clear is written. These are where interrupt bugs concentrate, because the controller's hardest logic — priority, preemption, arbitration — only activates under these corners, and random stimulus rarely aligns them, so they're forced with directed sequences and must be covered. The third dimension is negatives: every negative case must be covered — the no-spurious cases, where you confirm that in a window with no triggering condition, no interrupt fires. This is the negative direction of the two-sided check, and it's the most-forgotten, because it's a check for the absence of behavior, but spurious interrupts are real bugs, so the plan must cover them. Why all three matter for sign-off: closure is the sign-off criterion, and a plan that covers only sources lets you sign off having checked that each interrupt fires while leaving the interactions and negatives unverified — exactly where the bugs are. And for integration, you also cross interrupt servicing with concurrent functional traffic, so the plan covers interrupts during real traffic, not just in isolation. So the coverage plan is three-dimensional — sources, interactions, negatives — plus the integration cross, and sign-off requires all of it closed, with every covered case also checked, not just exercised. That's what makes closure mean the interrupt mechanism is actually verified, rather than just that each interrupt was seen to fire once.
The sign-off criteria for an interrupt mechanism are that every source, every interaction corner, and every negative case has been both exercised and checked, the coverage plan is closed including the integration cross, and the checks pass with no spurious interrupts — explicit criteria, not a feeling of thoroughness. Breaking that down. Every source exercised and checked: each interrupt source has had its triggering condition provoked, the interrupt observed, and verified to fire correctly with the right source and a clean clear. Every interaction corner exercised and checked: simultaneous, nested, storm, back-to-back, and clear-race scenarios have been forced with directed sequences and the priority, preemption, and arbitration behavior checked against the spec — not just provoked, but verified, because a corner exercised without checking verifies nothing. Every negative case exercised and checked: the no-spurious cases are covered, confirming no interrupt fires without a cause. Coverage closed: the three-dimensional plan — sources, interactions, negatives — reads as fully covered, plus the integration cross of interrupt servicing with concurrent functional traffic, so you have evidence, not a guess. Checks passing: the two-sided scoreboard and the assertions pass across all those scenarios, with no spurious interrupts flagged. And critically, integrated: the interrupt verification has run concurrently with the main functional verification, so the mechanism is signed off as verified during real traffic, not only in a standalone quiet-testbench test. The reason these need to be explicit is that interrupts are verified means something specific and checkable, not a vibe. A sign-off that rests on we ran a lot of interrupt tests, or that closes coverage on sources alone, or that's based only on a standalone run, is hollow — it can pass while leaving interactions unverified, negatives unchecked, or the interrupt-during-traffic bugs unexercised. So you gate sign-off on the explicit criteria: all three coverage dimensions plus integration closed, every covered case also checked, no spurious, verified during real traffic. That gated sign-off is the difference between having interrupt tests and having a verified interrupt mechanism — which is the whole point of treating interrupt verification as a strategy rather than three separate skills.
Exercises
- Configure the IVC. Given an interrupt map (sources, status/clear addresses, priority), describe how the reusable component builds its monitor, sequences, checks, and coverage from it.
- Plan the coverage. Write the three-dimensional coverage plan for a DUT with four interrupt sources — sources, interactions, negatives.
- Integrate. Show how to fork the interrupt sequences concurrently with the main functional stimulus, and what interaction the integration exposes.
- Define sign-off. List the explicit sign-off criteria for the interrupt mechanism and explain why closure on sources alone is insufficient.
Summary
- Interrupt verification strategy organizes modeling (25.1), sequences (25.2), and checking (25.3) into one coherent, reusable, signed-off plan — three legs of one strategy, not separate activities.
- The reusable interrupt verification component (IVC) bundles the agent, sequences, checks, and coverage, configured by the interrupt map (sources, line, status, clear, priority) — built once, plugs into any DUT.
- The coverage plan is three-dimensional — sources (each fires and is checked), interactions (simultaneous/nested/storm/clear-race), negatives (no-spurious) — and closure requires all three, not sources alone.
- The cardinal discipline: interrupt verification must be integrated, not isolated — the IVC runs concurrently with the main functional verification, because interrupt-during-traffic bugs (a clear write colliding with functional traffic) live in the interaction and are invisible in a quiet testbench.
- The durable rule of thumb: build interrupt verification as one strategy — a reusable component configured by the interrupt map, a coverage plan spanning sources, interactions, and negatives, integrated with the main functional verification so interrupts run during real traffic, and signed off only when every source, corner, and negative has been exercised AND checked; three skills used in isolation leave gaps and sign off in a quiet testbench that misses the interaction bugs the real system has.
Next — Enterprise Testbench Design: Module 26 turns to reusable UVM architecture. With interrupts complete, the next module steps up from specific verification techniques to the architecture of large, reusable, enterprise-grade testbenches — starting with the mindset shift. What makes a testbench enterprise-grade rather than merely working: reusability, scalability, maintainability, configurability, and consistency, and why an ad-hoc testbench that passes the block becomes a liability the moment it must be reused, scaled, or maintained.