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
    • Single-Writer, Multiple-Reader Invariant
      • SWMR Precisely
      • The Data-Value Invariant
      • Invalidate Versus Update
      • What a Violation Looks Like
    • Line States and Transitions
    • Directory Versus Snoop Fundamentals
  • Transactions12
  • Races and Controller Design8
  • Progress and the Fabric16
  • 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

‹Single-Writer, Multiple-Reader Invariant
  1. Topics
  2. Cache Coherency
  3. States and Protocols
  4. Single-Writer, Multiple-Reader Invariant
  5. The Data-Value Invariant

Part 2 · States and Protocols · All levels

The Data-Value Invariant

Track the value inherited from the last write epoch and distinguish an authoritative source from an equal but stale copy.

On this page

    If every cache has a legal permission, can a load still get the wrong answer? Yes. A controller can revoke the old writer correctly and then refill (fetch the line's bytes again) from stale memory. SWMR checks who may access X; the data-value invariant checks what value must survive that transfer. A permission-only checker would miss this failure.

    Prerequisite: SWMR Precisely. The Primer, second edition, Chapter 2 relates each epoch's starting value to the preceding write epoch's ending value. For our examples, initialisation supplies 0 before any write epoch. Within a write epoch, the sole writer's own stores advance the value; a subsequent reader must inherit the final value.

    Use a reference value, not a vote

    Return to the course model: reset has I / - in both caches, H = U, memory = 0. For the following independent candidate snapshots, assume the last completed write epoch ended with X = 2 and there has been no later store. The reference value is therefore 2. These rows classify possible settled snapshots; they are not successive messages or a dirty-read transaction.

    Candidate

    C0 state / value

    C1 state / value

    H directory

    Memory

    Newest data / verdict

    MSI writable holder

    M / 2

    I / -

    exclusive(C0)

    0

    C0: 2, legal, memory is stale

    MSI or MESI clean sharing

    S / 2

    S / 2

    S{C0,C1}

    2

    C0, C1, memory: 2, legal

    MOESI dirty sharing

    O / 2

    S / 2

    O: C0, sharers {C1}

    0

    C0 and C1: 2, C0 dirty-responsible, legal

    Lost final store

    S / 1

    S / 1

    S{C0,C1}

    1

    Required 2 is lost, data-value violation

    Disagreeing readers

    S / 2

    S / 0

    S{C0,C1}

    2

    C0 and memory: 2, C1 is stale, violation

    Every row has a legal count of writable permissions. The fourth row is nevertheless wrong: agreement among both caches and memory cannot erase the obligation to preserve 2. This is why a reference model needs store history, not merely a majority comparison. In the fifth row one correct reader cannot excuse the other reader's stale value.

    Decision tree rejecting illegal permissions first, then comparing usable copies against the reference value two rather than comparing copies only with each other.
    Two independent checks: a legal permission arrangement can still contain the wrong value.

    Memory is a source only when authoritative

    The first row does not require immediate writeback. H must preserve access to C0's newer value and must not answer a competing request with memory's 0. The MOESI row separates read permission from dirty responsibility: both caches can read 2, but C0 remains responsible for the dirty data and neither may write. In our MSI and MESI model, settling a remote dirty read as S/S requires bringing memory up to date; MOESI may instead settle O/S with memory stale. These are different protocol choices, not interchangeable state names.

    H's exclusive(C0) entry is permission metadata, not proof that memory is fresh: a MESI holder can silently change E to M after a local store, leaving that entry unchanged. Data in flight and H's buffers may also carry the newest value during a transfer. A checker must account for those locations before declaring the value lost; these settled classifications deliberately have no message in flight.

    Agreement is not a reference model

    The expected value below comes from the completed write epoch, not from voting on the candidate copies. The second half compares a checker that tests every load against memory (dram_checker) with one that tests against the reference value: each makes the opposite call in the two cases. These assertions classify the stated settled examples only; they do not enumerate races or prescribe how a full scoreboard orders concurrent operations.

    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
    initial_value = 0
    completed_write_epochs = [[1, 2]]
    expected = initial_value
    for stores in completed_write_epochs:
        for value in stores:
            expected = value
    def read_epoch_values_match(readers, reference):
        return all(value == reference for value in readers.values())
    correct = {"C0": 2, "C1": 2}
    agreed_but_stale = {"C0": 1, "C1": 1}
    disagreeing = {"C0": 2, "C1": 0}
    assert expected == 2
    assert read_epoch_values_match(correct, expected)
    assert not read_epoch_values_match(agreed_but_stale, expected)
    assert not read_epoch_values_match(disagreeing, expected)
    
    def dram_checker(load, mem):
        return load == mem
    def reference_checker(load, reference):
        return load == reference
    # Case 1: C0 is M / 2, memory = 0. A load of 2 from C0 is legal.
    assert not dram_checker(2, 0)            # false failure
    assert reference_checker(2, expected)    # correctly accepted
    # Case 2: memory = 1 but the reference value is 2. A load of 1 is stale.
    assert dram_checker(1, 1)                # false pass
    assert not reference_checker(1, expected)  # correctly rejected
    print("PASS: agreement and memory comparison do not establish the required value")

    Common pitfall

    Comparing every load with memory fails in both directions. It falsely flags a legal load of 2 from C0 = M / 2 when memory = 0, and it approves a stale load of 1 merely because memory also holds 1 when the reference value is 2. Compare with the required serialized value and identify the authoritative source.

    Interview

    Offer a counterexample to a state-only proof: two S copies both contain 1 after the preceding write epoch ended at 2. SWMR passes; the value obligation fails. Then explain why memory = 0 beside C0 = M / 2 is legal under write-back.

    Check yourself All caches and memory contain 1. The last write epoch ended at 2. Can a coherent read return 1 because every source agrees?

    No. With no later store, the next epoch must inherit 2. Agreement does not repair a lost update; the store history supplies the expected value. A correct checker rejects the snapshot even though SWMR passes.

    Related topics

    • Course Model

    • SWMR Precisely

    • Next: Invalidate Versus Update

    Previous

    15 / 167

    Next: Invalidate Versus Update

    Up next

    Up nextInvalidate Versus Update

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • What a Violation Looks Like