Skip to content

AMBA CHI · Module 9 · Snoop Flows

Why Snoops Exist

Module 8's request flows all leaned on snoops; Module 9 turns to the snoops themselves, starting with why they exist. The problem: many caches can hold copies of the same line, and the moment one core writes its copy, every other copy goes stale. Something must reach into those caches and update or clear them. That reaching-in is the snoop — the mechanism by which the home acts on a cache it does not own. It exists for two reasons: to invalidate other copies when a requester needs exclusivity, and to fetch the latest data from a cache holding the line dirty. Without snoops, writes never propagate and coherence is impossible. Representative model, not the specification.

Foundation14 min readAMBA CHISnoopsCoherencyInvalidationDirectory

Module 9 · Chapter 9.1 · Snoop Flows

Project thread — Module 8's requests all relied on snoops to reach other caches. This chapter asks why snoops exist at all; 9.2 catalogues the snoop types.

1. Learning Outcomes

By the end of this chapter you should be able to:

  • State the coherence problem: multiple caches hold copies of a line; a write makes the others stale.
  • Define a snoop as the mechanism by which the home reaches into a cache it does not own.
  • Name the two reasons snoops exist — invalidate for exclusivity, fetch dirty data.
  • Explain why coherence is impossible without snoops.
  • Diagnose what breaks when a write skips the snoop.
  • Implement a representative snoop-needed decision in SystemVerilog, Verilog-2001, and VHDL.

2. Why Should I Learn This?

Every mechanism in Module 8 — the invalidating ReadUnique, the downgrading ReadShared, the dirty-fetching snoop nested in a read — is built on one primitive: the snoop. Before cataloguing the snoop types (9.2) or tracing snoop flows (the rest of Module 9), it is worth being able to answer the plainest question: why does this primitive exist? If you can state that clearly, every snoop type afterward is just a variation on a purpose you already understand.

The answer is the heart of cache coherence. Caches exist to keep local copies of shared data; the instant more than one cache holds a line and one of them writes, the copies disagree. Coherence is the guarantee that they do not — that a read always returns the latest write — and the snoop is the only way to enforce it across caches that cannot see each other. Understanding why snoops exist is understanding what coherence is.

3. Key Terms

4. Previous Chapter Connection

Module 8 used snoops constantly: ReadShared issued SnpShared to downgrade a holder; ReadUnique issued SnpUnique to invalidate every copy; WriteUnique used SnpCleanInvalid; even the nested snoop of Chapter 7.4 fetched dirty data. In each, the snoop was a means to an end — a step inside a request flow.

This chapter inverts the view. Instead of asking what a given request does with a snoop, it asks why the snoop must exist for any of them to work. The requests you traced are all, underneath, ways of reaching into other caches — and that reaching-in has exactly two purposes. Module 9 is that inversion made systematic; 9.1 is its foundation.

5. Core Concept — the coherence problem, and the snoop that solves it

The problem is inherent to caching shared data. Many caches can hold the same line. The moment one writes its copy, the others hold stale data — and nothing in a private cache would ever tell them so. The snoop is the mechanism that bridges caches that cannot see each other.

  • Copies diverge on a write. RN0 and RN1 both hold line L in SC. RN0 writes L. RN0's copy is new; RN1's is now old — but RN1 has no idea.
  • A private cache cannot fix this alone. RN1 will happily serve its stale L to its core. Nothing local corrects it.
  • The snoop reaches in. The home, coordinating the write, sends a snoop to RN1 — reaching into a cache it does not own — to invalidate (or update) RN1's copy, so RN1 can no longer serve stale data.
  • Two purposes, always. A snoop either invalidates/downgrades other copies (so a writer can be the single writer) or fetches the latest data from a cache that holds the line dirty (so a reader gets the current value). Every snoop serves one or both.

The synthesis:

Caching shared data creates the coherence problem: copies go stale on a write, and a private cache cannot know. The snoop is the mechanism by which the home reaches into another cache to fix this — invalidating copies so there is a single writer, and fetching dirty data so a reader sees the latest. Without it, writes stay local and coherence collapses. Every snoop exists for one of these two reasons.

6. Engineering Mental Model — the shared whiteboard

Imagine a team where everyone keeps a personal copy of a shared figure from a whiteboard.

  • Everyone's copy starts identical to the board. Fine so far — all copies agree.
  • Then one person erases and rewrites their copy with a new number. Their copy is current; everyone else's is now wrong, and none of them know it. If they act on their copies, they act on stale data.
  • The only fix is for someone to go to each person and tell them — "your copy is out of date, cross it out" (invalidate) or "here is the new value" (update). That messenger, reaching each person who holds a copy, is the snoop.
  • And if the person who rewrote it has the only current version, someone must go get it from them before others can use it — the second reason snoops exist: fetching the latest.

No messenger, no coherence: each person keeps acting on a private, outdated figure. The snoop is the messenger that makes the copies agree.

7. Engineering Diagram — the home reaching into caches

A Home Node with a directory at the top, and three Request Node caches below it, each holding a copy of line L. The Home Node sends a snoop edge to each of the three caches. The snoop is the mechanism by which the home reaches into a cache it does not own to invalidate, downgrade, or fetch the copy, keeping the copies coherent.HN + Directoryknows who holds LRN0copy of LRN1copy of LRN2copy of Lsnoopsnoopsnoop12
Figure 1 — why snoops exist. Three caches (RN0, RN1, RN2) each hold a copy of line L. The Home Node keeps a directory of who holds it, and when coherence requires it — a write that must invalidate, or a read that must fetch dirty data — the home sends a snoop reaching into each holder's cache. The snoop is the only path by which one cache's operation affects another's copy; without it, the copies drift apart.

The home's directory knows which caches hold L, so its snoops are targeted — it reaches only the actual holders. But the essential point is the reach itself: the snoop is the only channel by which one cache's operation can affect another's copy. Remove it and the caches are islands.

8. The Two Reasons Snoops Exist

Every snoop serves one or both of these purposes.

ReasonTriggerWhat the snoop does
Enforce single writera requester needs exclusivity and others hold copiesinvalidate / downgrade the other copies
Locate latest dataanother cache holds the line dirtyfetch the dirty data from that cache

The rule to carry: a snoop is needed when either another cache's copy would become stale (so it must be invalidated or downgraded before the write) or another cache holds the only current data (so it must be fetched before the read). Both come down to the same principle — a private cache cannot be left holding a copy that conflicts with the operation, and only a snoop can reach it.

9. Why Coherence Is Impossible Without Them

State the counterfactual plainly: remove snoops and coherence cannot hold.

  • Writes would not propagate. A write updates only the writer's cache and memory; every other cache keeps its old copy and serves it. Readers see stale data — a coherence violation by definition.
  • Dirty data would be unreachable. If a cache holds the only current value (dirty), a reader that goes to memory gets a stale value — the latest lives in a cache no one consulted.
  • The single-writer invariant would fail. Two caches could believe they hold the line writable, or one writes while another reads an old copy — exactly the disagreement coherence forbids.
  • No local fix exists. A private cache has no way to learn that a line it holds was written elsewhere. The knowledge lives at the home, and the only way to act on it in another cache is a snoop.

The point to carry:

Coherence is a global property — every read returns the latest write — enforced across caches that are local and cannot see each other. That gap between global requirement and local visibility is exactly what the snoop bridges. It is not an optimization or a convenience; it is the enabling mechanism. Take it away and each cache becomes an island holding whatever it last saw, and "the latest write" becomes unknowable from anywhere but the one cache that made it.

10. Walkthrough — a write without a snoop breaks coherence

RN0 and RN1 both hold line L in SC, value 5. RN0 writes L = 6.

  1. RN0 writes locally. RN0's copy of L becomes 6. It updates memory to 6 as well.
  2. No snoop is sent. Suppose the system skips snooping RN1 (the bug this whole chapter argues against).
  3. RN1 still holds 5. RN1's copy of L is untouched — still 5 — and RN1 has no way to know it is stale.
  4. RN1 reads L → 5. RN1's core reads L and gets 5, the old value, even though the latest write was 6. Coherence is violated: a read did not return the most recent write.

Now add the snoop back: at step 2, the home snoops RN1, invalidating its copy. At step 4, RN1 misses, fetches the current L, and reads 6. The snoop is the single step that turns a broken system into a coherent one.

11. RTL / Hardware View — a snoop-needed decision

The foundational decision is combinational: given a request and what other caches hold, is a snoop required — to invalidate for exclusivity, to fetch dirty data, or both. Representative.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative snoop-needed decision (educational).
// A snoop is required for two reasons: (1) the requester needs EXCLUSIVITY and
// other caches hold copies -> invalidate them; (2) another cache holds the line
// DIRTY -> fetch the latest data. Either reason means a snoop must be sent.
module chi_snoop_needed (
  input  logic needs_exclusive,     // requester will write / needs sole ownership
  input  logic others_share,        // another cache holds a copy of the line
  input  logic other_has_dirty,     // another cache holds the line dirty
  output logic snoop_to_invalidate, // invalidate other copies for exclusivity
  output logic snoop_for_data,      // fetch the latest data from a dirty holder
  output logic snoop_needed         // a snoop must be issued
);
  // Reason 1: to be the single writer, clear other copies.
  assign snoop_to_invalidate = needs_exclusive && others_share;
  // Reason 2: the current value lives in a cache holding it dirty.
  assign snoop_for_data      = other_has_dirty;
  // A snoop is needed if either reason applies.
  assign snoop_needed        = snoop_to_invalidate || snoop_for_data;
endmodule

The same behavior in Verilog-2001:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative snoop-needed decision (Verilog-2001).
module chi_snoop_needed (
  input  needs_exclusive,
  input  others_share,
  input  other_has_dirty,
  output snoop_to_invalidate,
  output snoop_for_data,
  output snoop_needed
);
  assign snoop_to_invalidate = needs_exclusive && others_share;
  assign snoop_for_data      = other_has_dirty;
  assign snoop_needed        = snoop_to_invalidate || snoop_for_data;
endmodule

And in VHDL:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- Representative snoop-needed decision (VHDL).
library ieee;
use ieee.std_logic_1164.all;
 
entity chi_snoop_needed is
  port (
    needs_exclusive     : in  std_logic;
    others_share        : in  std_logic;
    other_has_dirty     : in  std_logic;
    snoop_to_invalidate : out std_logic;
    snoop_for_data      : out std_logic;
    snoop_needed        : out std_logic
  );
end entity;
 
architecture rtl of chi_snoop_needed is
  signal inv, dat : std_logic;
begin
  inv <= needs_exclusive and others_share;
  dat <= other_has_dirty;
  snoop_to_invalidate <= inv;
  snoop_for_data      <= dat;
  snoop_needed        <= inv or dat;
end architecture;

All three flag a snoop when the requester needs exclusivity over shared copies, or when another cache holds the line dirty. Those are the two reasons; the DebugLab shows the coherence break when the first is ignored.

12. Verification View — a snoop is needed exactly when it must be

The properties that capture the two reasons: exclusivity over shared copies needs an invalidating snoop, and a dirty holder needs a data snoop.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bind to chi_snoop_needed.
// 1. A requester needing exclusivity over shared copies must snoop to invalidate.
property p_exclusive_needs_invalidate;
  @(*) (needs_exclusive && others_share) |-> snoop_to_invalidate;
endproperty
 
// 2. A line held dirty elsewhere must be snooped for its data.
property p_dirty_needs_fetch;
  @(*) other_has_dirty |-> snoop_for_data;
endproperty
 
// 3. A snoop is needed exactly when at least one reason applies.
property p_needed_iff_reason;
  @(*) snoop_needed == (snoop_to_invalidate || snoop_for_data);
endproperty

The system point, beyond the checks:

The two reasons are not arbitrary — they are the two ways a private copy can conflict with an operation. A copy can be wrong-to-keep: it will go stale when a writer proceeds, so it must be invalidated (reason one). Or a copy can be the-only-right-one: it holds data more current than memory, so it must be fetched (reason two). Every coherence action reduces to preventing a stale copy from being read or ensuring the latest copy is found — and both require reaching into a cache the requester does not own. That reach is the snoop, and it is why the mechanism is irreducible: coherence is a statement about other caches' copies, and only a snoop can touch them.

  • What it proves: a snoop is flagged exactly when exclusivity conflicts with sharers or a dirty holder exists.
  • What it does not prove: the specific snoop type to send — that is Chapter 9.2.
  • Bug signature: a write proceeding with others_share but no invalidating snoop — a surviving stale copy.

13. Testbench — the reasons a snoop is needed

Drives the combinations of exclusivity, sharing, and dirtiness, and checks the snoop decision.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module tb_chi_snoop_needed;
  logic needs_exclusive, others_share, other_has_dirty;
  logic snoop_to_invalidate, snoop_for_data, snoop_needed;
  int errors = 0;
 
  chi_snoop_needed dut (.*);
 
  task automatic check(input logic ex, sh, dy,
                       input logic exp_inv, exp_dat, exp_need, input string name);
    needs_exclusive = ex; others_share = sh; other_has_dirty = dy; #1;
    if (snoop_to_invalidate !== exp_inv || snoop_for_data !== exp_dat ||
        snoop_needed !== exp_need) begin
      errors++; $display("FAIL %s: inv=%0b dat=%0b need=%0b",
                         name, snoop_to_invalidate, snoop_for_data, snoop_needed);
    end else $display("PASS %s: inv=%0b dat=%0b need=%0b",
                      name, snoop_to_invalidate, snoop_for_data, snoop_needed);
  endtask
 
  initial begin
    check(1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, "read, no others, clean -> none");
    check(1'b1, 1'b1, 1'b0, 1'b1, 1'b0, 1'b1, "write over sharers    -> invalidate");
    check(1'b0, 1'b1, 1'b1, 1'b0, 1'b1, 1'b1, "read, dirty elsewhere -> fetch");
    check(1'b1, 1'b1, 1'b1, 1'b1, 1'b1, 1'b1, "write over dirty      -> both");
    check(1'b1, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, "write, no others      -> none");
 
    if (errors == 0) $display("ALL TESTS PASSED");
    else             $display("%0d FAILURE(S)", errors);
    $finish;
  end
endmodule

Expected output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
PASS read, no others, clean -> none: inv=0 dat=0 need=0
PASS write over sharers    -> invalidate: inv=1 dat=0 need=1
PASS read, dirty elsewhere -> fetch: inv=0 dat=1 need=1
PASS write over dirty      -> both: inv=1 dat=1 need=1
PASS write, no others      -> none: inv=0 dat=0 need=0
ALL TESTS PASSED

14. DebugLab — a write that skips the snoop

1

A write that skips the snoop

WRITE WITHOUT SNOOP -> STALE SHARERS READ OLD DATA
Symptom

A core reads a stale value — older than a write another core definitely performed — with no error. It happens whenever a line is shared across caches and one core writes it; single-cache access is always correct.

Evidence

The write never reached the sharers:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
RN0 = SC(L=5),  RN1 = SC(L=5),  memory = 5
RN0 writes L = 6 -> RN0 = 6, memory = 6
  (no snoop sent to RN1)
RN1 still = SC(L=5)  <-- stale, never told
RN1 reads L -> 5   (should be 6) -> COHERENCE VIOLATION

RN1 held a copy the write never touched, and served it.

First Divergence

The write updated only the local cache and memory, issuing no snoop to the other holders. From that point every other cache's copy was stale, with nothing to correct it.

Root Cause

Writes must propagate to every cache holding the line, and the only mechanism for that is the snoop. A write to shared data is not complete when the local cache and memory are updated — the other copies still exist and will be read. Coherence requires reaching into those caches to invalidate or update them, which is exactly what a snoop does. Skip it and the write stays local: the definition of a coherence violation, and the reason the snoop primitive exists at all.

Fix

Snoop every other holder as part of the write — invalidate their copies (or update them) so none can serve stale data. The directory tells the home which caches to snoop; the snoop reaches them. With the snoop in place, a sharer that read the line misses and fetches the current value, and coherence holds.

15. Common Mistakes

  • Writing without snooping sharers. Assumption: local update suffices. Bug: stale readers (the DebugLab). Prevention: snoop every other holder on a write.
  • Reading past a dirty holder. Assumption: memory has the latest. Bug: stale read; the current value is in a cache. Prevention: snoop the dirty holder for its data.
  • Broadcasting to every cache. Assumption: snoop all, always. Bug: wasted bandwidth. Prevention: use the directory to snoop only holders.
  • Treating snoops as optional. Assumption: coherence works without them. Bug: it does not. Prevention: snoops are the enabling mechanism.
  • Forgetting the two reasons. Assumption: snoops only invalidate. Bug: missing dirty-data fetch. Prevention: snoops also fetch the latest.
  • Assuming caches can self-correct. Assumption: a stale cache will notice. Bug: it cannot. Prevention: the home must reach in.

16. Engineering Checklist

  • Recognize the coherence problem: shared copies go stale on a write.
  • Treat the snoop as the mechanism that reaches into another cache.
  • Snoop to invalidate when a requester needs exclusivity over shared copies.
  • Snoop to fetch when another cache holds the line dirty.
  • Use the directory so snoops are targeted, not broadcast.
  • Never let a write to shared data complete without snooping the holders.

17. Key Takeaways

  • Caching shared data creates the coherence problem: a write makes other caches' copies stale.
  • A private cache cannot learn that a line it holds was written elsewhere — the knowledge is at the home.
  • The snoop is the mechanism by which the home reaches into another cache to act on its copy.
  • Snoops exist for two reasons: invalidate copies for exclusivity, and fetch dirty data.
  • Without snoops, writes stay local, dirty data is unreachable, and coherence is impossible.
  • Snoop to invalidate or to fetch, targeted by the directory; the model here is representative.

18. Quick Revision

Why snoops exist. Caching shared data creates the coherence problem: many caches can hold copies of one line, and the moment one core writes, every other copy is stale — and a private cache has no way to know. Coherence — every read returns the latest write — is a global property across caches that are local and blind to each other, and the snoop is the mechanism that bridges that gap: a home-initiated action that reaches into a cache it does not own. Snoops exist for exactly two reasons: to enforce the single-writer invariant by invalidating (or downgrading) other copies when a requester needs exclusivity, and to locate the latest data by fetching it from a cache that holds the line dirty. The home's directory makes snoops targeted — only actual holders. Without snoops a write stays local, sharers read stale data, and dirty data is unreachable: coherence collapses. Snoop to invalidate or to fetch. Representative model; 9.2 catalogues the snoop request types.

Coming Next

Chapter 9.2 — Snoop Request Types. This chapter established why snoops exist; the next names which snoops there are. Chapter 9.2 catalogues the snoop request types — SnpShared, SnpUnique, SnpClean, SnpMakeInvalid, and their relatives — mapping each to the state it forces in the snooped cache and the request flow that issues it. It turns the two abstract reasons for snooping into the concrete vocabulary of snoop opcodes you will trace for the rest of the module.