Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • The NanoSoC Tour
  • Reading Path
  • SoC Glossary10
  • SoC Metrics Reference
  • SoC Cheatsheet
  • What SoC Integration Is12
  • Architecture and Budgets21
  • Memory Maps and Address Decode26
  • Interconnect and Bus Fabrics26
  • Clock and Reset Architecture24
  • Power Architecture21
  • Interrupts, DMA and Peripherals15
    • Interrupt Controller Integration
      • Levels, Polarity and Synchronisation
      • Priority, Masking and the TOP Register
      • The Wiring Table and the Controller Sketch
    • Interrupt Routing and Latency
    • DMA Integration
    • Peripheral Subsystem
    • Pad Muxing and GPIO
  • Memories, Boot and Security21
  • IP Integration and Handoff21
  • Top-Level Verification and Bring-up21
  • Package, Pads and I/O18
  • Chip Signoff and Tapeout18
  • SoC Interview Prep16
  • Capstone: Integrate NanoSoC14
Loading lesson

←Interrupt Controller Integration
  1. Topics
  2. SoC Design
  3. Interrupts, DMA and Peripherals
  4. Interrupt Controller Integration
  5. The Wiring Table and the Controller Sketch
On this page

    Part 7 of 14, lesson 3 of 15Part 7 of 14/Interrupts, DMA and Peripherals/Lesson 3 of 15All levels

    The Wiring Table and the Controller Sketch

    One table that owns every interrupt wire, the review that signs it, and the controller as compiling RTL.

    Why it matters

    This lesson has one job: show what the integrator signs when the interrupt wiring is declared done. That artefact is a table with one row per controller input: source, wire name, domain, synchroniser, clear mechanism and priority reason. Against it, two real bugs become mechanical to catch: a DMA irq landed on a spare input, and two interrupt clamps missing from the power intent. The lesson ends with the controller itself as a compiling module, so the table and the RTL can be read side by side.

    The core idea

    The wiring table is the single source of truth for every interrupt net. Each row starts at the source register bit that raises the level and ends at the controller input number, naming the wire, the source clock domain and the synchroniser it passes through. Isolation belongs in the same table: irq_dma and irq_sbr cross from the switchable domain into the always-on controller, so each needs a clamp cell driven by the isolation enable, and the power-intent check counts exactly 114 clamp cells across the whole chip including these two. A row is finished only when it also names the software clear: the exact status bit whose clearing drops the level, because a source without a documented clear is a future storm.

    INTC input

    Wire

    From (domain)

    Clear

    Review check

    0

    irq_pwr

    RSTPWR (clk_per)

    W1C PWR_IRQ bits

    Wakes the chip: never masked in sleep.

    1

    irq_dma

    DMA (clk_sys)

    W1C STATUS DONE or ERR

    Sync + clamp + correct input number.

    2

    irq_sbr

    SBR (clk_sys)

    W1C SBR STATUS

    Sync + clamp, same as DMA.

    3

    irq_uart

    UART (clk_per)

    DATA touch

    Same-domain: synchroniser still present.

    4

    irq_spi

    SPI (clk_per)

    DATA read

    Same-domain: synchroniser still present.

    5

    irq_gpio

    GPIO (clk_per)

    Pin condition removed

    Level follows pins: test both edges.

    6-7

    Tied to 0

    n/a

    n/a

    Connectivity test flags any activity.

    nano_intc_agg.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // NanoSoC teaching values: interrupt aggregator sketch (controller core).
    // 8 active-high level inputs in, one active-high level out. PENDING is a
    // wire (RAW & ENABLE); TOP is the lowest pending index, 0x1F when idle.
    // snippet: enables and clear paths are register-backed in the full block.
    module nano_intc_agg (
      input  logic [7:0] raw,
      input  logic [7:0] enable,
      output logic [7:0] pending,
      output logic [4:0] top,
      output logic       irq_cpu
    );
      assign pending = raw & enable;
      assign irq_cpu = |pending;
    
      always_comb begin
        top = 5'h1F;
        for (int i = 7; i >= 0; i--)
          if (pending[i]) top = 5'(i);
      end
    endmodule

    Worked example: the review as a script

    A table in a document is a wish; a table checked by a script is a contract. The checker below encodes the three wiring rules that catch those bugs: every used input has a named non-empty clear, the spares are tied off, and the isolation count includes both cross-domain interrupt wires.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    WIRES = [
        {"irq": 0, "wire": "irq_pwr", "clear": "W1C PWR_IRQ", "tie": False},
        {"irq": 1, "wire": "irq_dma", "clear": "W1C STATUS DONE/ERR", "tie": False},
        {"irq": 2, "wire": "irq_sbr", "clear": "W1C SBR STATUS", "tie": False},
        {"irq": 3, "wire": "irq_uart", "clear": "DATA touch", "tie": False},
        {"irq": 4, "wire": "irq_spi", "clear": "DATA read", "tie": False},
        {"irq": 5, "wire": "irq_gpio", "clear": "pin condition", "tie": False},
        {"irq": 6, "wire": "tie0", "clear": "", "tie": True},
        {"irq": 7, "wire": "tie0", "clear": "", "tie": True},
    ]
    used = [w for w in WIRES if not w["tie"]]
    assert len(used) == 6
    for w in used:
        assert w["clear"], f"input {w['irq']} has no documented clear"
        assert w["wire"].startswith("irq_"), f"input {w['irq']} misnamed"
    spares = [w for w in WIRES if w["tie"]]
    assert [w["irq"] for w in spares] == [6, 7]
    assert all(w["wire"] == "tie0" for w in spares)
    # Isolation budget from the signal lists: every PD_SW -> PD_AON signal is clamped.
    CLAMPED = {"apb_cdc outputs": 12 + 32 + 3, "sram0 port": 14 + 32 + 4 + 2,
               "rom port": 12 + 1, "interrupts": ["irq_dma", "irq_sbr"]}
    def clamp_count(c):
        return sum(v if isinstance(v, int) else len(v) for v in c.values())
    assert clamp_count(CLAMPED) == 114
    missed = dict(CLAMPED, interrupts=[])            # the interrupts forgotten
    assert clamp_count(missed) == 112 != 114         # the audit would flag this
    wrong = [dict(WIRES[1], clear="")] + WIRES[2:]   # a wire with no documented clear
    assert not all(w["clear"] for w in wrong if not w["tie"])
    print("PASS: 6 wired inputs cleared and named; spares tied; 114 clamps, 112 flagged")

    What the review looks for

    • One row per input with source bit, wire name, domain, synchroniser, clear mechanism and priority reason; no row may say to be confirmed.

    • A connectivity test forces each source and checks the matching RAW bit, so a wire landed on the wrong input fails the same day it is miswired.

    • The isolation list counts the two interrupt wires explicitly; the power-intent check fails if either clamp is missing.

    Verify and debug

    The per-IRQ test is the whole method: force the source at its register, read RAW, clear the source, read RAW again. When DMA STATUS reads DONE with the DMA pin high but RAW bit 1 reads 0 while bit 6 reads 1, the wire is on the wrong input, which is the miswired-interrupt bug: reconnect to input 1 and add the forcing test so it can never regress. When RAW flickers for one cycle every few thousand jobs yet every simulation passed, suspect the missing synchroniser: add the two flops and sweep the clock ratio in simulation until the flicker is gone.

    Miswired interrupt: irq_dma connected to spare input 6 instead of input 1, so polling works and the ISR never runs while RAW bit 6 sets. Caught by the per-source forcing test; fixed by connecting to input 1 and by treating any activity on inputs 6 or 7 as a wiring fault.

    If asked what you sign at interrupt review, answer the table: eight rows, each with source, wire, domain, sync, clear and rank reason, plus the forcing test and the clamp count. Anything less is a promise, not a sign-off.

    Check yourself The isolation audit finds 112 clamp cells instead of 114. Which two are missing, and what is the symptom?

    The irq_dma and irq_sbr clamps: the two interrupt wires from the switchable domain were missed. Entering sleep floats them, RAW shows unknown values, and a spurious irq_cpu wakes the chip in a sleep-wake loop.

    Related topics

    • Priority, Masking and the TOP Register

    • Next: Interrupt Routing and Latency

    • UVM: Interrupt Agents

    Previous

    135 / 289

    Next: DVFS Integration

    Up next

    Up nextDVFS Integration

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Levels, Polarity and Synchronisation