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

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Path
  • Cache Coherency Glossary
  • The Shared Course Model
  • Cache Coherency Revision Sheet
  • Why Coherence Exists8
  • States and Protocols12
  • Transactions12
  • Races and Controller Design8
  • Progress and the Fabric16
    • Virtual Networks and Progress
      • A Protocol Deadlock
      • Message Classes and Virtual Networks
      • Deadlock, Livelock, Starvation
      • Stating Progress Assumptions
    • Filtering and Snoop Pruning
    • Latency, Fairness and QoS
    • Hierarchical and Chiplet Coherence
  • Ordering and Synchronization12
  • Performance and Scalability12
  • Real Protocols12
  • SoC Integration12
  • Power, Clocking and Reliability12
  • Verification12
  • Debug and Bring-up12
  • Interview and Design Review17
  • Capstone: A Coherent Model Lab5
Loading lesson

‹Virtual Networks and Progress
  1. Topics
  2. Cache Coherency
  3. Progress and the Fabric
  4. Virtual Networks and Progress
  5. A Protocol Deadlock

Part 5 · Progress and the Fabric · All levels

A Protocol Deadlock

Construct a two-line pending-entry/receive-buffer cycle and compare a reserved-response design.

On this page

    Which resource is held while another is requested?

    Extend the course model with Y in a different line and finite H resources: one pending entry P and one shared ingress slot Q (ingress: the buffer where an arriving message waits before H processes it). An arriving request keeps Q until it can allocate P. A response also needs Q to enter H, although it would service the already allocated P. This is the deliberately bad resource policy.

    C0 also has one egress slot T0 (egress: the outgoing buffer where a message waits for the network to carry it), reserved for its response before it accepts the forward. Once T0 holds Data(1), the cache gives up the line and shows I. T0 is transport storage, not a cache copy and not H's slot Q. Reliable transport keeps the payload while it waits for Q, so the blocked value is Data(1) in flight to H.

    Use MSI. Warm-up from X = 0: C0 acquired M and stored 1, C1 is invalid for X, memory = 0. Y starts at 0, uncached. Use the four-hop route of Three-Hop Versus Four-Hop: C0 returns the forwarded data to H. C1 requests X while C0 independently misses on Y. In this selected arrival order C1's GetM wins P, and C0's GetS(Y) reaches Q before C0's Data(1).

    H holds its only pending entry for C1's X request, but its only receive slot holds C0's Y request, preventing X's needed data response.
    A reachable bad-resource checkpoint: X's response is blocked behind Y's admitted request.

    Step

    C0 state / value

    C1 state / value

    H directory

    Memory

    Newest data

    0 warm-up

    X: M / 1, Y: I / -

    X: I / -, Y: I / -

    X: exclusive(C0), Y: U

    X = 0, Y = 0

    C0: X = 1, memory: Y = 0

    1 GetM(X) at H

    X: M / 1, Y: I / -

    X: IM^D / -, Y: I / -

    X: exclusive(C0), pend GetM C1, Y: U

    X = 0, Y = 0

    C0: X = 1, memory: Y = 0

    2 Fwd-GetM at C0, T0 holds Data(1)

    X: I / -, Y: I / -

    X: IM^D / -, Y: I / -

    X: exclusive(C0), pend GetM C1, Y: U

    X = 0, Y = 0

    Data(1) in flight to H (held at T0), memory: Y = 0

    3 GetS(Y) in Q, no P for Y

    X: I / -, Y: IS^D / -

    X: IM^D / -, Y: I / -

    X: exclusive(C0), pend GetM C1, Y: U

    X = 0, Y = 0

    Data(1) in flight to H (held at T0), memory: Y = 0

    A resource dependency cycle connects the held pending entry and shared receive slot, each waiting for the other.
    The cycle is P waits for Q, Q waits for P. The dirty response remains protected, but nobody can complete.

    Break the dependency with genuinely reserved response capacity

    Compare a corrected design with Qreq and Qresp. When H allocates P for X, it also reserves one Qresp slot and a protected data destination for X's response. Qreq may hold Y, but X's response enters Qresp and is processed using the existing P, never needing another free P. H keeps the accepted data until its grant is delivered, releases P, then services Y. This removes the dependency from Qreq to X's response.

    Note

    General rule: order the classes request < forward < response. Consuming a message may wait only on resources of a later class. Responses wait on nothing, because each pending entry reserves its response slot and data destination when it is allocated, so a response can always be sunk. With one virtual network per class, deadlock-free routing inside each, and endpoints that obey this rule, no cycle of protocol-message dependencies can form.

    Reserved response capacity accepts the current X value, H grants C1, then the freed pending entry services C0's Y read.
    Corrected finite-resource design: the selected X transaction completes before queued Y uses P.

    Step

    C0 state / value

    C1 state / value

    H directory

    Memory

    Newest data

    3 corrected checkpoint

    X: I / -, Y: IS^D / -

    X: IM^D / -, Y: I / -

    X: exclusive(C0), pend GetM C1, Y: U

    X = 0, Y = 0

    Data(1) in flight to H (held at T0), memory: Y = 0

    4 Data(1) at H via Qresp

    X: I / -, Y: IS^D / -

    X: IM^D / -, Y: I / -

    X: exclusive(C0), pend GetM C1, Y: U

    X = 0, Y = 0

    H buffer: X = 1, Data(1) in flight to C1, memory: Y = 0

    5 Data at C1

    X: I / -, Y: IS^D / -

    X: M / 1, Y: I / -

    X: exclusive(C0), pend GetM C1, Y: U

    X = 0, Y = 0

    C1: X = 1, memory: Y = 0

    (assumed) X grant delivered, H admits queued Y

    X: I / -, Y: IS^D / -

    X: M / 1, Y: I / -

    X: exclusive(C1), Y: U, pend GetS C0

    X = 0, Y = 0

    C1: X = 1, memory: Y = 0

    H reads memory, Y = 0

    X: I / -, Y: IS^D / -

    X: M / 1, Y: I / -

    X: exclusive(C1), Y: U, pend GetS C0

    X = 0, Y = 0

    C1: X = 1, memory and H buffer: Y = 0

    6 Data at C0

    X: I / -, Y: S / 0

    X: M / 1, Y: I / -

    X: exclusive(C1), Y: U, pend GetS C0

    X = 0, Y = 0

    C1: X = 1, C0 and memory: Y = 0

    (assumed) Y grant delivered

    X: I / -, Y: S / 0

    X: M / 1, Y: I / -

    X: exclusive(C1), Y: S{C0}

    X = 0, Y = 0

    C1: X = 1, C0 and memory: Y = 0

    The corrected run completes under A1-A6 (assumptions), chiefly A3: the Qresp slot is reserved when P is allocated, and response handling never needs another P. Separate queues without that reservation would merely redraw the problem.

    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
    from itertools import permutations
    MSGS = ("GetM X", "Data X", "GetS Y")
    def run(order, reserved):
        s = {"P": None, "Q": None, "Qresp": None, "net": {"GetM X", "GetS Y"}, "done": set()}
        def slot(m):
            return "Qresp" if reserved and m == "Data X" else "Q"
        def can_deliver(m):
            return m in s["net"] and s[slot(m)] is None
        def internal():
            for b in ("Q", "Qresp"):
                if s[b] == "Data X":          # response uses the existing P
                    s[b], s["P"] = None, None  # grant to C1, delivery known (A6)
                    s["done"].add("X")
                    return True
            if s["Q"] in ("GetM X", "GetS Y") and s["P"] is None:
                req, s["Q"] = s["Q"], None
                if req == "GetM X":
                    s["P"] = "X"
                    s["net"].add("Data X")     # Fwd-GetM, C0 answers into the network
                else:
                    s["done"].add("Y")         # memory-sourced, P used and freed
                return True
            return False
        for m in order:
            while internal():
                pass
            if m not in s["net"]:
                return None                    # order not realisable
            if not can_deliver(m):
                break
            s["net"].remove(m)
            s[slot(m)] = m
        while internal():
            pass
        stuck = not any(can_deliver(m) for m in s["net"])
        return s["done"], stuck
    shared = [r for r in (run(o, False) for o in permutations(MSGS)) if r]
    fixed = [r for r in (run(o, True) for o in permutations(MSGS)) if r]
    assert len(shared) == len(fixed) == 3
    assert any(stuck and "X" not in done for done, stuck in shared)
    assert all(done == {"X", "Y"} for done, _ in fixed)
    print("PASS: shared Q can deadlock, reserved Qresp completes X and Y in every order")

    The explorer delivers the three messages in every realisable order, taking H's internal moves (allocate P, consume a response) whenever they are enabled. With the shared Q, the order GetM X, GetS Y, Data X stops with no enabled move and X incomplete. With Qresp reserved per P, every order completes both X and Y. It checks this small model only, not a production fabric.

    Common pitfall

    Waiting for a timeout does not establish a safe recovery. In the bad checkpoint the only X = 1 is held at T0; resetting or discarding it would add a value loss to the hang.

    Interview

    Name held P, held Q, their requested resources, and the blocked response. Then show precisely why the replacement response path does not require a new pending slot.

    Check yourself Why is a response path that also allocates a fresh P not sufficient?

    P is already held by the transaction awaiting that response. Requiring another free P can preserve the same dependency cycle despite separate receive queues.

    Related topics

    • Shared course model

    • Pending-entry admission

    Previous

    46 / 167

    Next: Message Classes and Virtual Networks

    Up next

    Up nextMessage Classes and Virtual Networks

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Deadlock, Livelock, Starvation
    • Stating Progress Assumptions