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
    • Topologies and Bridges
    • AXI Integration Details
    • Ordering and Deadlock at the Top
      • What Ordering the Integrator Owes
      • How Deadlock Happens at SoC Level
      • Proving Freedom and Debugging Hangs
    • QoS and Arbitration
    • NoC Basics
    • Coherent Interconnect View
    • Firewalls and Protection
    • Bandwidth and Latency Verification
  • 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
Loading lesson

←Ordering and Deadlock at the Top
  1. Topics
  2. SoC Design
  3. Interconnect and Bus Fabrics
  4. Ordering and Deadlock at the Top
  5. How Deadlock Happens at SoC Level
On this page

    Part 4 of 14, lesson 9 of 26Part 4 of 14/Interconnect and Bus Fabrics/Lesson 9 of 26All levels

    How Deadlock Happens at SoC Level

    Dependency cycles through a serialising bridge: the SoC-level hang, drawn on a real NanoSoC failure.

    Why it matters

    The chip freezes on the first CPU read of a DMA register after software wrote CLK_EN to zero. No error, no timeout, no interrupt; only a reset recovers it. That is the signature of a dependency cycle: each party waits for something another holds, around a ring, and nobody can move first. This lesson draws the ring so the next lesson can break it.

    The core idea

    AXI's own channels cannot deadlock on their own: a master's valid never waits for ready, a slave's response never waits for the master's ready, and every dependency runs from request to response. Rings appear when a component adds an edge the protocol does not have: it serialises independent channels, shares one buffer between request and response, or waits on something outside the fabric. NanoSoC has exactly one such component on a path that matters, the axi2apb bridge, which handles one access at a time and waits for PREADY.

    The real ring is the gated clock. The CPU reads DMA STATUS (a blocking access, one in flight). The bridge starts the APB access and waits for the DMA's PREADY. The DMA drives PREADY only with clk_dma running, and clk_dma is off because CLK_EN[0] is 0. The only thing that could restart it is a store to CLK_EN in RSTPWR, which is the next instruction the CPU would execute, if it were not blocked on the read. Ring: CPU read, bridge access, DMA PREADY, clk_dma, CLK_EN write, CPU. Even if another agent could issue the store, it would queue behind the stuck access in the same bridge. A second real source is power-down with a request in flight: a live master waits for a response from a block whose supply is cut.

    The dependency chain of a CPU read of DMA STATUS, which a stopped clock can turn into a hang.
    The dependency chain of a CPU read of DMA STATUS, which a stopped clock can turn into a hang.
    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
    # NanoSoC teaching values: the gated-clock hang as a channel-dependency graph.
    # An edge a -> b means a cannot complete until b does.
    GRAPH = {
        "cpu_read_dma_status": ["bridge_access_in_flight"],
        "bridge_access_in_flight": ["dma_pready"],
        "dma_pready": ["clk_dma_running"],
        "clk_dma_running": ["clk_en_write"],
        "clk_en_write": ["cpu_issues_store"],
        "cpu_issues_store": ["cpu_read_dma_status"], # the CPU is blocked on the read
    }
    
    def find_cycle(graph):
        done = set()
        def visit(node, stack):
            if node in stack:
                return stack[stack.index(node):] + [node]
            if node in done:
                return None
            for nxt in graph.get(node, []):
                hit = visit(nxt, stack + [node])
                if hit:
                    return hit
            done.add(node)
            return None
        for node in graph:
            hit = visit(node, [])
            if hit:
                return hit
        return None
    
    cycle = find_cycle(GRAPH)
    assert cycle is not None and cycle[0] == cycle[-1]
    assert len(cycle) == 7 # six waiters closing the ring
    # the fix: a 256-cycle watchdog completes the access, so the bridge stops waiting for PREADY
    fixed = dict(GRAPH)
    fixed["bridge_access_in_flight"] = ["watchdog_expiry"]
    fixed["watchdog_expiry"] = []
    assert find_cycle(fixed) is None
    # a different fix: refuse to gate a block that can be addressed, removing the clock edge
    fixed2 = dict(GRAPH)
    fixed2["clk_dma_running"] = []
    assert find_cycle(fixed2) is None
    print("PASS: gated-clock ring found, both fixes (watchdog, no gating) break it")

    A hypothetical variant shows the general shape (it is not NanoSoC): if the SBR could also reach APB registers through the same bridge, an SBR write to a slow register would hold the bridge while a CPU read behind it waited for data that only the SBR's finished job could release. Draw the same way: one edge per wait, ring closed through the serialising component.

    What the review looks for

    • Every bridge states what it serialises and what it waits on, and every wait ends at a slave that always answers (PREADY within the documented wait states) or at a watchdog.

    • Shared slaves behind one bridge list their maximum concurrent users and the quiesce order for reset and power-down.

    • No path is described as always fast enough; every shared path has a full-buffer and a never-answers case.

    Verify and debug

    Drive the ring on purpose: gate clk_dma, then read a DMA register. With the watchdog the read must return SLVERR after 256 clk_sys cycles; without it the access never ends and the dump shows one outstanding access at the bridge. When a chip does hang, dump every outstanding transaction and its dependency: the cycle reads off the dump as a ring of waiters. Confirm the ring by completing one member by hand in the testbench (for example forcing PREADY); if the rest unwind, the diagnosis is proved.

    Clock gated while the bridge waits for PREADY: software stops clk_dma to save power and then touches a DMA register, the single-access bridge waits for ever and the store that would restart the clock can never be issued. Caught by a directed gated-clock test; fixed by refusing to gate an addressable block and by the bridge watchdog.

    If asked how a chip deadlocks, draw boxes in a ring and narrate one dependency per edge: who waits for whom. The gated-clock hang above is that drawing with real NanoSoC block names. Then add the rule: the protocol channels are safe on their own, so look for the component that serialises or waits outside the fabric.

    Check yourself Adding deeper FIFOs everywhere did not fix a bridge hang. What does that tell you?

    Depth only delays a cycle; a true dependency ring fills any finite buffer eventually, or never involves a buffer at all (the gated-clock ring has none). The fix is breaking an edge, for example with a watchdog, not deepening a queue.

    Related topics

    • Protocol First Principles: Deadlock and Liveness

    • Protocol First Principles: Channel Dependencies

    • Next: Proving Freedom and Debugging Hangs

    Previous

    73 / 289

    Next: Proving Freedom and Debugging Hangs

    Up next

    Up nextProving Freedom and Debugging Hangs

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error