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
  • Ordering and Synchronization12
  • Performance and Scalability12
  • Real Protocols12
  • SoC Integration12
  • Power, Clocking and Reliability12
  • Verification12
    • Coherency Scoreboards and Reference Models
    • Formal Properties for Coherency Safety
    • Coverage Closure and Waiver Governance
      • State and Transition Coverage
      • Race and Cross Coverage
      • Directed Race Stress
      • Closure Evidence
  • Debug and Bring-up12
  • Interview and Design Review17
  • Capstone: A Coherent Model Lab5
Loading lesson

‹Coverage Closure and Waiver Governance
  1. Topics
  2. Cache Coherency
  3. Verification
  4. Coverage Closure and Waiver Governance
  5. Race and Cross Coverage

Part 11 · Verification · All levels

Race and Cross Coverage

Cover the crosses where a conflicting message meets a transient state, in both arrival orders, and see why random traffic rarely reaches them.

On this page

    Where do coherence bugs actually live?

    Stable-state transitions are exercised by almost any test. The bugs found late are in races: a message for one transaction arriving while the cache or H is in the middle of another. A race cross is a coverage item that names three things: the transient state, the conflicting message that arrives in it, and which request caused that message. Where the result depends on which of two messages H sees first, the cross also names the order, and both orders are bins.

    The race crosses of the course controller

    Race cross

    Where it is taught

    What a bug here looks like

    (SM^D, Inv) caused by the other core's GetM

    Simultaneous Upgrades, step 3

    The losing upgrader keeps its S copy, or later receives permission-only Data with no bytes.

    (MI^A, Fwd-GetS) caused by GetS: a PutM crossing a forward

    Eviction Versus Intervention, order B step 2

    The cache answers 'absent' from its tag while the WB entry holds the only current value.

    (MI^A, Fwd-GetM) caused by GetM

    Same mechanism, writer instead of reader: C0 answers from the WB entry and goes to II^A

    The cache answers from its array instead of the WB entry, or frees the WB entry before Put-Ack; or H treats the late PutM as a writeback and erases C1's exclusive record.

    (SI^A, Inv) caused by GetM: a PutS crossing an Inv

    Clean Eviction: Silent or Notified

    The cache ignores the Inv because it is evicting, and H waits for an Inv-Ack for ever.

    At H: PutM from a non-holder after a forward

    Eviction Versus Intervention, order B step 5

    H treats the late PutM as a writeback, overwrites memory or erases C1's sharer record.

    At H: GetS arrives while PutM is pending

    Eviction Versus Intervention, order A step 2

    H serves the reader from stale memory before the writeback is written.

    At H: GetM arrives while GetS is pending

    Racing Requests at the Home, step 2

    H invalidates C0 before its S grant is delivered.

    The two orders of the eviction race are separate bins: PutM reaches H first (order A) or the GetS does (order B). A regression that only ever produces order A has not tested the forward-from-WB path at all, even though both cells of the plain matrix (MI^A, Put-Ack) and (M, Fwd-GetS) look covered.

    Four inputs feed one race cross bin: the requester's request type, the victim's transient state, the arriving message and the arrival order at H, and the bin counts only on a run the value checker passed.
    A race bin combines the cause, the victim's transient state, the arriving message and the order at H.

    Why random traffic hits them rarely

    A race needs a second request for the same line inside a short window: the cycles a cache spends in one transient state. Suppose (illustrative numbers) each core issues a request every 20 cycles on average (rate 0.05), addresses are uniform over A lines, and the window is 20 cycles. The chance that one of the other N - 1 cores requests this line during the window is 1 - (1 - 0.05 / A)^((N - 1) x 20).

    Traffic

    Cores N

    Lines A

    P(conflict in one window)

    Windows for 100 expected conflicts

    Spread, course model

    2 (C0, C1)

    4,096

    0.024%

    about 409,600

    Hot spot, extended model

    8 (C0 to C7)

    4

    82.8%

    about 121

    The second row extends the course model to eight cores to show the effect; the protocol is unchanged. Few lines and many cores raise the rate by a factor of about 3,400. The same factor applies to every race cross, since each one starts with a same-line conflict, and a specific cross then also needs the right states and order. A plain random regression with realistic address spread can run for days without one PutM crossing a forward.

    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
    import random
    def p_conflict(cores, lines, rate, window):
        """P(at least one other core requests the same line during one transient window)."""
        per_cycle = rate / lines                 # one other core hits this line in one cycle
        return 1 - (1 - per_cycle) ** ((cores - 1) * window)
    def simulate(cores, lines, rate, window, trials, seed=1):
        rng, hits = random.Random(seed), 0
        for _ in range(trials):
            line = 0                             # the line in its transient window
            hits += any(rng.random() < rate and rng.randrange(lines) == line
                        for _ in range((cores - 1) * window))
        return hits / trials
    WINDOW, RATE = 20, 0.05                      # illustrative: 20-cycle window, 1 request per 20 cycles
    spread = p_conflict(cores=2, lines=4096, rate=RATE, window=WINDOW)
    hot = p_conflict(cores=8, lines=4, rate=RATE, window=WINDOW)
    assert abs(spread - 2.44e-4) < 1e-5 and abs(hot - 0.828) < 0.001
    assert hot / spread > 3000                   # hot spot: thousands of times more races per window
    for cores, lines in [(2, 64), (8, 4), (8, 16)]:
        model, sim = p_conflict(cores, lines, RATE, WINDOW), simulate(cores, lines, RATE, WINDOW, 4000)
        assert abs(model - sim) < 0.03, (cores, lines, model, sim)
    # failing case: an estimate that forgets the other cores (counts one competitor) is wrong at 8 cores
    naive = p_conflict(cores=2, lines=4, rate=RATE, window=WINDOW)
    assert abs(naive - simulate(8, 4, RATE, WINDOW, 4000)) > 0.3
    # transient windows needed for 100 expected conflicts
    assert round(100 / spread) == 409648 and round(100 / hot) == 121
    print("PASS: few lines and many cores raise the per-window race probability from 0.02% to 83%")

    The formula assumes independent uniform requests; real traffic is burstier, and locks and shared counters produce natural hot spots (see Hot Lines and Locks). The point survives: race coverage has to be engineered, which is the next lesson.

    Common pitfall

    Counting a race bin from a run that later failed the value checker, or from a run where the checker was off. A race that was reached and handled wrongly is a bug, not coverage.

    Common pitfall

    Crossing everything with everything. State x event x request type x order x core x address explodes into bins nobody can close. Cross only the dimensions that change the controller's behaviour: here the transient state, the arriving message, its cause and the order.

    Interview

    Name three race crosses on a directory MSI controller (Inv in SM^D, Fwd-GetM in MI^A, a late PutM at H after a forward), say what each would catch, and explain with the window arithmetic why uniform random traffic rarely reaches them.

    Check yourself Both (MI^A, Put-Ack) and (M, Fwd-GetS) are covered. Does that mean the eviction-versus-forward race is covered?

    No. Each cell can be hit in a run with no race: an eviction that completes alone, and a forward to a line that was never being evicted. The race is the cross (MI^A, Fwd-GetS), plus the H-side bin for a PutM from a non-holder, and the order bin that says the GetS reached H before the PutM. Only those show that the forward was answered from the WB entry.

    Related topics

    • Shared course model

    • The eviction-versus-forward race, both orders

    • The full state x event matrix

    • Crosses and explosion (First Principles of Verification Strategy)

    Previous

    131 / 167

    Next: Directed Race Stress

    Up next

    Up nextDirected Race Stress

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Closure Evidence