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. SWMR Precisely

Part 2 · States and Protocols · All levels

SWMR Precisely

State the permission invariant without requiring a writer to exist or confusing stored bits with usable data.

On this page

    Can one cache write while another keeps a read-only copy? For the invalidation protocols in this course, no: writable permission excludes every other cache's usable copy of that line. A writer is allowed to read its own copy. When nobody has writable permission, any number of caches, including zero, may have read-only permission. A writable holder need not be executing a store right now.

    Prerequisite: read the course model. SWMR is a rule about permitted accesses at a logical instant, not a count of requests or occupied data arrays. It divides the life of a line into a write epoch (an interval in which exactly one cache may read and write) and a read epoch (an interval in which any number of caches, including none, may only read). A pending GetM is not permission to write. This permission-based definition follows the Primer, second edition, Chapter 2.

    Classify permissions before values

    Independent snapshot

    C0 state / value

    C1 state / value

    Permission verdict

    Idle read epoch, H = U, memory = 0

    I / -

    I / -

    Legal: zero readers, no active writer

    MSI shared, H = S{C0,C1}, memory = 0

    S / 0

    S / 0

    Legal: two readers, no writer

    MSI exclusive, H = exclusive(C0), memory = 0

    M / 1

    I / -

    Legal: C0 alone may read and write

    MESI clean exclusive, H = exclusive(C0), memory = 0

    E / 0

    I / -

    Legal: C0 already has write permission

    MOESI dirty sharing, H = O: C0, sharers {C1}, memory = 0

    O / 1

    S / 1

    Legal: both read-only, C0 has dirty responsibility

    Candidate with equal bits

    M / 0

    S / 0

    Illegal: a writer coexists with another reader

    These are independent classifications, not a transaction history. I has no usable value: '-' does not mean zero. M and E (MESI, MOESI) are writable; S and O are read-only. O names dirty responsibility, never write permission. H's directory entry exclusive(C0) records exclusive permission without necessarily distinguishing E from M after a silent local store. It is neither the location X nor a cache state. The MESI permissions agree with gem5's stable-state definitions.

    A cold write acquires permission first

    Use MSI for this trace. Reset is C0 = I, C1 = I, H = U, memory = 0. H serializes requests for X: it handles them one at a time in a single order. Because no cache holds X, memory is authoritative (it holds the value the next reader must get), so H reads it inside the home node; that read is a note, not a cache request.

    Sequence showing C0 sending GetM to H, H reading zero from memory, H returning Data with M permission, and C0 storing one while C1 stays invalid.
    Cold MSI write: GetM requests permission, H reads memory, and Data grants M before local event L stores 1.

    Step

    C0 state / value

    C1 state / value

    H directory

    Memory

    Newest data

    Initial

    I / -

    I / -

    U

    0

    memory: 0

    1. GetM received

    IM^D / -

    I / -

    U, pend GetM C0

    0

    memory: 0

    H reads memory

    IM^D / -

    I / -

    U, pend GetM C0

    0

    memory: 0, Data(0) in flight to C0

    2. Data received

    M / 0

    I / -

    U, pend GetM C0

    0

    C0 and memory: 0

    (assumed) grant delivered

    M / 0

    I / -

    exclusive(C0)

    0

    C0 and memory: 0

    L. Local store

    M / 1

    I / -

    exclusive(C0)

    0

    C0: 1, memory stale

    IM^D is the transient state 'was I, wants M, waiting for Data'; C0 may neither load nor store in it. At step 2 the M grant assigns writable permission and dirty responsibility even though the bits still equal memory. H keeps its pending entry in that row; the next row, labelled (assumed), is the course model's assumption that H learns the grant has been delivered and records exclusive(C0). At L a write-back cache changes its local value without updating memory. The newest-data column also names Data in flight, so no value disappears between rows.

    Check permissions without comparing values

    This small program classifies stable permission vectors only. A state outside the chosen protocol, such as O in MESI, fails the membership assertion: the program refuses to mix protocol variants rather than returning a verdict. Beyond SWMR it enforces one protocol rule, at most one dirty-responsible holder (M or O). It checks neither transaction completion nor data preservation; the next lesson adds the value obligation.

    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
    states_by_protocol = {
        "MSI": {"I", "S", "M"},
        "MESI": {"I", "S", "E", "M"},
        "MOESI": {"I", "S", "E", "M", "O"},
    }
    def legal(protocol, states):
        assert all(s in states_by_protocol[protocol] for s in states)
        writers = [i for i, s in enumerate(states) if s in {"M", "E"}]
        swmr = len(writers) <= 1 and (
            not writers or all(i == writers[0] or s == "I"
                               for i, s in enumerate(states)))
        # Two O holders are read-only, so SWMR alone would pass them, but each
        # would believe it alone must write the dirty line back: H records one
        # dirty-responsible holder, so the protocol allows at most one M or O.
        one_responsible = sum(s in {"M", "O"} for s in states) <= 1
        return swmr and one_responsible
    assert legal("MSI", ["I", "I"])
    assert legal("MSI", ["S", "S"])
    assert legal("MSI", ["M", "I"])
    assert legal("MESI", ["E", "I"])
    assert legal("MOESI", ["O", "S"])
    assert not legal("MSI", ["M", "S"])
    assert not legal("MESI", ["M", "M"])
    assert not legal("MOESI", ["E", "S"])
    assert not legal("MOESI", ["O", "O"])
    try:
        legal("MESI", ["O", "S"])
        raise SystemExit("O must be rejected in MESI")
    except AssertionError:
        pass
    print("PASS: stable permission vectors classified, O rejected in MESI")

    Common pitfall

    Counting only simultaneous stores misses the bug. If C0 can write and C1 can independently satisfy a read hit, permission safety is already broken, even if neither core is currently accessing X or both copies contain zero.

    Interview

    State the two allowed permission arrangements, then classify E and O. E is writable without a store being active; O is read-only despite dirty responsibility. That is why 'owner' must always be qualified: exclusive holder (E or M, may write) or dirty-responsible holder (M or O, memory may be stale).

    Check yourself Does C0 = O / 7, C1 = S / 7 require C0 to be the single writer?

    No. In MOESI this is a read epoch: neither cache may store. C0 is the dirty-responsible holder for value 7 while memory may be stale. A later write must first acquire exclusive permission.

    Related topics

    • Course Model

    • Next: The Data-Value Invariant

    Previous

    14 / 167

    Next: The Data-Value Invariant

    Up next

    Up nextThe Data-Value Invariant

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Invalidate Versus Update
    • What a Violation Looks Like