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
  • 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
On this page

    Part 11 of 14, lesson 10 of 12Part 11 of 14/Verification/Lesson 10 of 12All 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.

    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.

    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.

    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.

    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 lessonPrevious lessonReport an error

    See also

    • Closure Evidence