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
  • 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
    • Capstone Brief
    • Stage 1: Memory Map and Interconnect
    • Stage 2: Clocks, Resets, Power
    • Stage 3: Interrupts and Boot
      • Interrupt Map and Latency Budget
      • DMA Programming Sequence and Contract Traps
      • Boot Flow and Secure Partition
    • Stage 4: Verification and Signoff
Loading lesson

←Stage 3: Interrupts and Boot
  1. Topics
  2. SoC Design
  3. Capstone: Integrate NanoSoC
  4. Stage 3: Interrupts and Boot
  5. Interrupt Map and Latency Budget
On this page

    Part 14 of 14, lesson 9 of 14Part 14 of 14/Capstone: Integrate NanoSoC/Lesson 9 of 14All levels

    Interrupt Map and Latency Budget

    File the 8-line map, recompute the latency bounds, and catch planted bug 3.

    Why it matters

    DMA STATUS reads DONE = 1 and the irq pin at the DMA is high, yet the ISR for IRQ 1 never runs while INTC RAW bit 6 sits at 1. Polling works, interrupts do not. The wire went to spare input 6 instead of input 1. Reviews that read the table pass; only a test that forces each source and checks its own RAW bit catches the wiring.

    Goal, inputs and steps

    • Goal: the wired map plus a latency bound per urgent line.

    • Inputs: the 8 sources with clear methods and priorities, the sync depths.

    • Steps: file the map; recompute best and worst latency; swap irq_dma to input 6 in the model and confirm the connectivity test names it and RAW bit 6 lights; take one flop out of a synchroniser and confirm the latency bound fires; restore.

    IRQ

    Source

    Cleared by

    Priority

    0 irq_pwr

    PWR_IRQ OR

    W1C PWR_IRQ

    0, highest

    1 irq_dma

    irq_en AND (done OR err)

    W1C DONE/ERR or clear IRQ_EN

    1

    2 irq_sbr

    IRQ_EN AND (DONE OR ERR)

    W1C SBR STATUS

    2

    3 irq_uart

    TXIE/TXE or RXIE/RXNE

    Read/write DATA

    3

    4 irq_spi

    IE AND RXNE

    Read DATA

    4

    5 irq_gpio

    OR of DATA_IN AND INT_EN

    Remove the condition

    5

    6, 7

    Reserved, tied to 0

    n/a

    Lowest

    The checker

    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    MAP = [
        ("BOOTROM", 0x00000000, 0x4000),
        ("SRAM0", 0x20000000, 0x10000),
        ("SRAM1", 0x20010000, 0x10000),
        ("DMA", 0x40000000, 0x1000),
        ("SBR", 0x40001000, 0x1000),
        ("UART", 0x40002000, 0x1000),
        ("SPI", 0x40003000, 0x1000),
        ("GPIO", 0x40004000, 0x1000),
        ("INTC", 0x40005000, 0x1000),
        ("RSTPWR", 0x40006000, 0x1000),
    ]
    SECURE_ONLY = {"SRAM1", "RSTPWR"}
    SLOTS = ["DMA", "SBR", "UART", "SPI", "GPIO", "INTC", "RSTPWR"]
    T_CPU, T_SYS, T_PER = 1e9 / 192e6, 1e9 / 96e6, 1e9 / 24e6
    def end_of(base, size):
        return base + size - 1
    def overlaps(entries=MAP):
        bad = []
        xs = sorted(entries, key=lambda e: e[1])
        for a, b in zip(xs, xs[1:]):
            if b[1] < a[1] + a[2]:
                bad.append((a[0], b[0]))
        return bad
    def misaligned(entries=MAP):
        return [n for n, b, s in entries if b % s != 0]
    def decode(addr, slots=SLOTS):
        if addr >> 14 == 0:
            return "BOOTROM"
        if addr >> 16 == 0x2000:
            return "SRAM0"
        if addr >> 16 == 0x2001:
            return "SRAM1"
        if addr >> 16 == 0x4000:
            slot = (addr >> 12) & 0xF
            return slots[slot] if slot < 7 else "H3"
        if addr < 0x20000000:
            return "H1"
        if addr < 0x40000000:
            return "H2"
        return "H4"
    def response(addr, secure, slots=SLOTS):
        t = decode(addr, slots)
        if t in ("H1", "H2", "H4"):
            return "DECERR"
        if t == "H3":
            return "SLVERR"
        if t in SECURE_ONLY and not secure:
            return "SLVERR"
        return "OK"
    def sustained(peak, beats):
        return peak * beats / (beats + 1)
    
    # ---- this lesson starts here ----
    
    def latency(sync_per, ctrl, sync_cpu):
        best = sync_per * T_PER + ctrl * T_PER + sync_cpu * T_CPU      # two syncs, the INTC register, two syncs
        return best, best + T_PER + T_CPU                              # worst adds one clk_per and one clk_cpu of alignment
    best, worst = latency(2, 1, 2)
    assert abs(best - 135.4) < 0.1 and abs(worst - 182.3) < 0.1
    assert abs(latency(1, 1, 2)[0] - 135.4) > 0.1                      # a missing flop moves the bound
    ORACLE = {"irq_pwr": 0, "irq_dma": 1, "irq_sbr": 2, "irq_uart": 3, "irq_spi": 4, "irq_gpio": 5}
    def intc_raw(wiring, active):
        return sum(1 << wiring[s] for s in active)                     # RAW bit i is set when the line wired to input i is high
    def conn_test(w):
        return [s for s in ORACLE if intc_raw(w, [s]) != 1 << ORACLE[s]]    # force one source at a time
    wiring = dict(ORACLE)
    assert conn_test(wiring) == []
    assert intc_raw(wiring, ["irq_dma"]) == 0x02 and intc_raw(wiring, list(ORACLE)) == 0x3F      # spares 6 and 7 stay 0
    bad = dict(wiring, irq_dma=6)                                      # bug 3
    assert conn_test(bad) == ["irq_dma"] and intc_raw(bad, ["irq_dma"]) == 0x40                 # RAW bit 6, not bit 1
    swap = dict(wiring, irq_dma=2, irq_sbr=1)
    assert conn_test(swap) == ["irq_dma", "irq_sbr"]
    print("PASS: map wired 0-5, spares tied; IRQ latency 135.4 best, 182.3 worst ns")

    Expected output

    One PASS line with the wiring verdict and both latency bounds. The bad wiring is the bug-3 demonstration: forcing irq_dma lights RAW bit 6 instead of bit 1, and the test names the source. The swapped pair shows the test also catches two lines crossed, and the one-flop latency shows the bound reacting to a synchroniser change.

    What each failure means

    A latency failure means a stage was dropped: two peripheral syncs, one controller register, two CPU syncs, then one peripheral plus one CPU cycle of worst-case alignment. A connectivity failure on clean data means the oracle map drifted. An empty report on the bad dict means the test compared against the wiring under test instead of the oracle.

    Hint ladder

    Hint 1: the symptom pairs a live source pin with a dead expected bit and a live spare bit. Hint 2: polling works because the DMA status is fine; only the path to the CPU is broken. Hint 3: unused inputs tie to 0, so any activity on 6 or 7 is wiring, not noise. Answer: connect irq_dma to input 1, tie 6 and 7 to 0, and force each source in turn while checking its matching RAW bit.

    A level source cleared by a posted bridge write: the handler returns before the clear lands, the line is still high, and the interrupt fires again. Read back the status register after the clear to flush the write.

    If asked why the DMA outranks the UART, answer with cost: a stalled stream loses throughput every cycle it waits, while the one-byte UART FIFO only risks overrun at line rate.

    Check yourself Priority is fixed, lower number wins. What does TOP return with nothing pending?

    0x1F: the no-pending code, distinct from every valid index.

    Related topics

    • Next: DMA Programming Sequence and Contract Traps

    • Protocol First Principles: Interrupts and Notification

    • Protocol First Principles: Interrupt and Data Ordering

    Previous

    274 / 289

    Next: DMA Programming Sequence and Contract Traps

    Up next

    Up nextDMA Programming Sequence and Contract Traps

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Boot Flow and Secure Partition