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 Paths
  • Concept Map
  • How to Read the Diagrams
  • Part 0: What Is a Protocol?10
  • Part 1: Timing and Clocking11
  • Part 2: Encoding and Framing12
  • Part 3: Handshakes13
  • Part 4: Flow Control17
  • Part 5: Transactions19
  • Part 6: Concurrency and Performance17
  • Part 7: Ordering13
  • Part 8: Sharing and Arbitration12
  • Part 9: Deadlock and Liveness13
  • Part 10: Reliability and Recovery20
  • Part 11: Bring-Up and Link Management10
  • Part 12: Power8
  • Part 13: Shared State and Coherence9
    • Why Coherence
    • Invalidate or Update
    • MSI, MESI and MOESI
    • Snooping or Directory
    • Ownership and Forwarding
    • Coherence and Consistency
    • I/O Coherence and Cache Maintenance
    • Protocols Compared: Shared State and Coherence
    • Drills: Shared State and Coherence
  • Part 14: Security and Isolation7
  • Part 15: Specifying and Verifying11
  • Part 16: Protocols Dissected17
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 13: Shared State and Coherence
  1. Topics
  2. First Principles of Hardware Protocols
  3. Part 13: Shared State and Coherence
  4. MSI, MESI and MOESI

Part 13 · Shared State and Coherence · Working (W)

MSI, MESI and MOESI

Each cache line carries a state that says what its cache may do with it. MSI is the minimum; MESI adds Exclusive to make private data cheap; MOESI adds Owned to share dirty data without writing memory. A model counts what each state saves.

On this page

    Note

    Level: Working (W) · Answers: What if there are several copies of one value?

    Before this: Invalidate or Update

    A core reads a line nobody else has, then writes it. With the simplest protocol, that costs two bus transactions: one to read the line, and one to announce the write so other copies can be invalidated, even though there are no other copies. Almost every private variable in every program follows this read-then-write pattern. One extra state removes the second transaction.

    What states does a cache line need, and what does each extra state save?

    The obvious answer, and why it breaks

    The minimum is three states: Modified (this cache has the only copy and it is dirty), Shared (a clean copy, others may exist) and Invalid. That is MSI, and it is correct. But a cache holding a line in S cannot know whether it is the only holder, so every first write must ask the bus, even for private data. And a Modified line read by another cache must be written back to memory before it becomes Shared, even if it is about to be written again.

    The idea: each state is a piece of knowledge

    State

    Meaning

    May read

    May write

    Dirty

    M Modified

    Only copy; memory is stale

    Yes

    Yes, silently

    Yes

    O Owned (MOESI)

    Dirty, but others may hold Shared copies; this cache supplies data and writes back later

    Yes

    No (must invalidate others first)

    Yes

    E Exclusive (MESI)

    Only copy; clean

    Yes

    Yes, silently (becomes M)

    No

    S Shared

    Clean copy; others may exist

    Yes

    No (must invalidate others first)

    No

    I Invalid

    No copy

    No

    No

    —

    E is the state that knows it is alone: writing it needs no bus transaction. Invalidations from other cores' writes are left out to keep the picture readable.

    Build it: what E and O save

    The model runs MSI, MESI and MOESI over four caches on a snooping bus, with three workloads of 100 lines, counting bus transactions and memory accesses (reads plus writebacks). In this model, memory supplies clean data; only an M or O cache supplies data itself.

    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
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    """MSI, MESI and MOESI on a snooping bus: bus transactions and memory traffic.
    
    Four caches share a bus and a memory. Each cache line is in one state per cache.
    Bus requests: BusRd (read miss), BusRdX (write miss), BusUpgr (write to a shared copy).
    Data comes from a cache holding the line Modified or Owned, otherwise from memory.
      MSI:   a read miss always installs Shared; writing it later needs a BusUpgr.
      MESI:  a read miss with no other copy installs Exclusive; writing E is silent (E -> M).
      MOESI: as MESI, but a Modified line that is read by another cache becomes Owned and
             supplies the data without writing memory; in MSI and MESI it writes memory back
             and becomes Shared.
    Three workloads, 100 lines each:
      private:           each core reads then writes its own lines
      producer-consumer: core 0 writes a line, cores 1-3 read it, repeated 100 times on one line
      read-shared:       every core reads every line
    Counts: bus transactions, and memory accesses (reads from memory plus writebacks).
    """
    CORES, LINES = 4, 100
    
    
    def run(protocol, ops):
        st = {}                     # (core, line) -> 'M', 'O', 'E', 'S' (absent = I)
        bus = mem = 0
    
        def holders(line, exclude):
            return [c for c in range(CORES) if c != exclude and (c, line) in st]
    
        def supply(line, exclude):
            nonlocal mem
            if not any(st.get((c, line)) in ("M", "O") for c in holders(line, exclude)):
                mem += 1            # no dirty owner: memory supplies the data
    
        for core, op, line in ops:
            s = st.get((core, line))
            if op == "R":
                if s:
                    continue        # hit
                bus += 1            # BusRd
                others = holders(line, core)
                supply(line, core)
                for c in others:
                    if st[(c, line)] == "M":
                        if protocol == "MOESI":
                            st[(c, line)] = "O"
                        else:
                            st[(c, line)] = "S"; mem += 1      # write back on downgrade
                    elif st[(c, line)] == "E":
                        st[(c, line)] = "S"
                st[(core, line)] = "S" if (others or protocol == "MSI") else "E"
            else:
                if s in ("M",):
                    continue
                if s == "E":
                    st[(core, line)] = "M"; continue           # silent upgrade
                bus += 1                                       # BusUpgr (from S/O) or BusRdX (from I)
                if s is None:
                    supply(line, core)
                for c in holders(line, core):
                    del st[(c, line)]                          # invalidate every other copy
                st[(core, line)] = "M"
        return bus, mem
    
    
    workloads = {
        "private": [op for c in range(CORES) for l in range(LINES) for op in ((c, "R", c * LINES + l), (c, "W", c * LINES + l))],
        "producer-consumer": [op for _ in range(LINES) for op in [(0, "W", 0)] + [(c, "R", 0) for c in range(1, CORES)]],
        "read-shared": [(c, "R", l) for l in range(LINES) for c in range(CORES)],
    }
    res = {}
    print(f"{'workload':18s} {'protocol':8s} {'bus':>5s} {'memory':>7s}")
    for w, ops in workloads.items():
        for p in ("MSI", "MESI", "MOESI"):
            res[(w, p)] = run(p, ops)
            print(f"{w:18s} {p:8s} {res[(w, p)][0]:5d} {res[(w, p)][1]:7d}")
    
    assert res[("private", "MESI")][0] * 2 == res[("private", "MSI")][0]
    assert res[("producer-consumer", "MOESI")][1] < res[("producer-consumer", "MESI")][1]
    assert res[("read-shared", "MSI")] == res[("read-shared", "MESI")] == res[("read-shared", "MOESI")]
    print("PASS: E halves bus traffic for private data; O removes the writebacks of shared dirty data; read-only sharing gains nothing from either")

    It prints:

    Diagram
    workload           protocol   bus  memory
    private            MSI        800     400
    private            MESI       400     400
    private            MOESI      400     400
    producer-consumer  MSI        400     301
    producer-consumer  MESI       400     301
    producer-consumer  MOESI      400       1
    read-shared        MSI        400     400
    read-shared        MESI       400     400
    read-shared        MOESI      400     400
    PASS: E halves bus traffic for private data; O removes the writebacks of shared dirty data; read-only sharing gains nothing from either
    • Private data: MESI halves the bus traffic (800 to 400). Each line is read into E and written silently; MSI needs a second, useless transaction per line.

    • Producer-consumer on one line: MSI and MESI write the line back to memory every time a consumer first reads it, then serve the other consumers from memory: 301 memory accesses. MOESI keeps it Owned in the producer's cache and supplies it from there: one memory access in total.

    • Read-only sharing: nothing to gain; all three are identical.

    How real protocols chose

    Protocol

    States

    MESI

    Widely used in processors; the common textbook protocol

    MOESI

    Adds Owned for dirty sharing (used by AMD) (fact T23)

    MESIF

    Adds Forward: one designated clean sharer answers requests, so many sharers do not all reply (Intel, introduced with QuickPath) (fact T23)

    AMBA CHI

    Its own state names (Unique Dirty, Shared Clean and others) with the same ideas (fact A23)

    What breaks

    A protocol implementation (illustrative) let a cache in E respond to another core's read by moving to S without telling the requester that another copy existed. The requester also installed E, and both believed they could write silently. Two cores wrote the line without either invalidating the other, a direct SWMR violation. The fix: a read that finds any other copy must install S, never E, which the requester learns from a "shared" response.

    Why this matters

    Senior lens. Read every state as knowledge the cache holds: E means "I know I am alone", O means "I am responsible for the dirty data", F means "I am the one who answers". Each extra state saves traffic only if that knowledge is always true, so every transition that could make it false must be covered. That is where state-machine verification of coherence protocols spends its effort.

    Interview

    Asked: "What does the E state add to MSI?"

    Weak answer: Another state for exclusive access.

    Strong answer: It records that this cache has the only copy and it's clean. A read miss with no other sharers installs E; a later write then upgrades E to M silently, with no bus transaction, because there are no other copies to invalidate. Private data is almost always read then written, so MESI saves a bus transaction per line on it; in a model of private data it halves bus traffic. MOESI goes further with Owned, which lets dirty data be shared without writing memory back.

    Follow-ups: (1) What does O add? (2) What must happen if another core reads an E line?

    Check yourself A line is in E and another core reads it. What happens to this cache's copy?

    It moves to S, and the requester installs S too, since there are now two copies.

    Check yourself Why does MOESI avoid the writebacks in the producer-consumer workload?

    When a consumer reads the producer's M line, the producer moves to O and supplies the data, keeping responsibility for the dirty line instead of writing it to memory.

    Key takeaways

    • MSI is the minimum: Modified, Shared, Invalid.

    • E (only copy, clean) makes private data's first write silent; O (dirty but shared) avoids writebacks when sharing dirty data.

    • Each state is knowledge that must always be true; every transition that could break it must be covered.

    Keep going

    Next: Lesson 13.4, Snooping or Directory. Broadcast every request, or keep a list of who has what.

    Related topics

    • Part 13 overview: Shared State and Coherence

    • Architecture course: MESI Transactions

    Previous

    182 / 234

    Next: Snooping or Directory

    Up next

    Up nextSnooping or Directory

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Why Coherence
    • Ownership and Forwarding
    • Coherence and Consistency