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
    • Transient States and Races
    • Controller Resources
      • MSHRs and Pending Entries
      • Ack Counting and Late Data
      • Retries, NACKs and Credits
      • A Controller Block Diagram
  • 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

‹Controller Resources
  1. Topics
  2. Cache Coherency
  3. Races and Controller Design
  4. Controller Resources
  5. MSHRs and Pending Entries

Part 4 · Races and Controller Design · All levels

MSHRs and Pending Entries

Track local misses and home obligations separately, and reserve storage before accepting work.

On this page

    Which controller is waiting for what?

    A core-side MSHR, or miss-status holding register, records an outstanding miss and the local operation that awaits completion. H's pending entry records system work for a line: requester, current holders, required revocations, data source and grant progress. They can refer to the same X without being the same record.

    Use the two-core model with private write-back caches and 64-byte X initially 0. The rows below are separate checkpoints, not a message sequence. Assume H has one active slot for X and can keep one waiting request. C0's read A takes the active slot; C1's write B waits without obtaining permission.

    Step

    C0 state / value

    C1 state / value

    H directory

    Memory

    Newest data

    C0 miss accepted

    IS^D / -, MSHR A

    I / -

    U, pend GetS C0

    0

    memory: 0

    C1 waits behind A

    IS^D / -, MSHR A

    IM^D / -, MSHR B

    U, pend GetS C0, queued GetM C1

    0

    memory: 0

    H has read memory for A

    IS^D / -, MSHR A

    IM^D / -, MSHR B

    U, pend GetS C0, queued GetM C1

    0

    H buffer and memory: 0, Data(0) in flight to C0

    A's Data at C0

    S / 0, MSHR A freed

    IM^D / -, MSHR B

    U, pend GetS C0, queued GetM C1

    0

    C0 and memory: 0

    (assumed) grant delivered

    S / 0

    IM^D / -, MSHR B

    S{C0}, queued GetM C1

    0

    C0 and memory: 0

    Flowchart connects a private-cache MSHR to the home's pending entry and back to a delivered grant.
    Two kinds of pending state: local operation tracking and home coherence obligations.

    What each record holds

    MSHR field (core side)

    Why it is needed

    Line address

    Matches incoming Data, Inv and Fwd to this miss.

    Request type

    GetS, GetM or upgrade: decides which grant completes it.

    Transient state

    IS^D, IM^D, SM^D, IS^D_I: decides how each incoming event is handled.

    Waiting loads and stores, with store data

    The core operations to perform when the grant arrives.

    Transaction id or generation

    Tells this miss's responses from a delayed response to an older one on the same line.

    Merged secondary misses

    Later loads or stores to the same line that join this miss instead of sending a second request.

    H pending-entry field

    Why it is needed

    Requester

    Where the grant goes.

    Request

    GetS, GetM or Put, and the identity that responses must match.

    Acks needed and received

    When every required Inv-Ack has arrived.

    Data valid and data buffer

    Whether authoritative data has arrived, and where it is protected until sent.

    Queued next request

    The waiting request for the same line, served when this one completes.

    Simulation: two MSHRs allocate and free

    The waveform below is simulation output for a private controller with two MSHRs. It misses on line X (address 0x40), then on line Y (address 0x80) while X is still pending. Each allocation sends one request to H, and each returning Data frees the entry it matches. One slot is one clock cycle.

    How to read it: the miss to X appears in slot 1. In slot 2 entry 0 becomes valid, req pulses with req_line = 40, and mshr_count is 1. The miss to Y in slot 4 finds entry 0 busy, so in slot 5 entry 1 becomes valid, req pulses again with req_line = 80, and mshr_count is 2: two misses are outstanding at once. Data for line 40 arrives in slot 7 and entry 0 is free in slot 8 (count 1) while entry 1 still waits. Data for line 80 arrives in slot 10 and entry 1 is free in slot 11 (count 0). req_line simply keeps the last address after each one-cycle request.

    Acceptance consumes a promise

    Before accepting A, H needs a place to keep its identity and obligations. Before accepting dirty data, H needs protected storage for it. If those resources are unavailable, the controller must apply backpressure or refuse the request by a stated rule; it cannot accept and overwrite the active record. With a wait queue, a conflicting request for the same line can be accepted without touching the active entry.

    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
    ACTIVE_SLOTS = 1
    QUEUE_SLOTS = 1
    active = {}
    queue = {}
    def offer(line, request):
        """Return 'active', 'queued' or 'refused'."""
        if line not in active and len(active) < ACTIVE_SLOTS:
            active[line] = {"request": request, "data": None}
            return "active"
        if line in active and len(queue.get(line, [])) < QUEUE_SLOTS:
            queue.setdefault(line, []).append(request)
            return "queued"
        return "refused"
    def complete(line):
        done = active.pop(line)
        if queue.get(line):
            active[line] = {"request": queue[line].pop(0), "data": None}
        return done["request"]
    assert offer("X", "A") == "active"
    a_record = dict(active["X"])
    assert offer("X", "B") == "queued"      # B waits, it does not take the active slot
    assert active["X"] == a_record and queue["X"] == ["B"]
    assert offer("X", "C") == "refused"     # one-slot queue is full
    assert active["X"] == a_record and queue["X"] == ["B"]
    assert offer("Y", "D") == "refused"     # no active slot left for another line
    assert complete("X") == "A"
    assert active["X"]["request"] == "B" and queue["X"] == []
    print("PASS: B queues behind A, a third request is refused, A's record is unchanged")

    The check covers three outcomes: A takes the active slot, B enters the wait queue without changing A's record, and a third request for X is refused because the queue is full. A request for another line Y is also refused, because the only active slot is in use. When A completes, B moves into the active slot. A merge path may attach compatible readers to a miss, but a queued writer has different permission requirements and may require invalidating A's eventual copy.

    Freeing C0's MSHR after its read does not imply that every home queue or accepted writeback buffer is free. Conversely, H may have completed permission work while the requester still awaits delivered Data. Define a release condition for each resource instead of one global done bit.

    Common pitfall

    Using the line address as the only response identity after an entry is reused. A delayed response for an old transaction must not complete a new one at the same address.

    Interview

    For each entry field, name the future event that needs it. If a field can be discarded before that event, identify the resource that now owns the obligation.

    Check yourself Why can C1 have a local MSHR for B while H is still working on A?

    The local entry tracks C1's outstanding operation, not a permission grant or ownership of H's active slot. B can wait in a defined queue while A remains the only active transaction for X.

    Related topics

    • Shared course model

    • Numbered example: read before queued writer

    Previous

    42 / 167

    Next: Ack Counting and Late Data

    Up next

    Up nextAck Counting and Late Data

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Retries, NACKs and Credits
    • A Controller Block Diagram
    clkmissmiss_line0400800reqreq_line04080data_indata_line0400800mshr0_validmshr1_validmshr_count01210
    Simulation output: two misses to different lines allocate two MSHRs, and each Data frees its own entry