AMBA CHI · Module 18 · CHI Debugging
Missing Snoop
A missing snoop is a fabric-level coherence failure. When a core writes a shared line, the home must snoop every sharer to invalidate their copies; a missing snoop skips one, which keeps its stale copy and later reads it. The symptom is a cache using a value a write elsewhere should have invalidated. The signature is a snoop channel showing nothing to a cache the directory lists as a sharer. The diagnosis is a set difference: snoops actually sent versus the directory's sharer set — the sharer in the set but not snooped was skipped. The root cause is a wrong sharer set (a directory race or eviction) or snoop-generation that dropped a target. Representative model, not the specification.
Advanced16 min readAMBA CHIDebuggingMissing SnoopSharer SetDirectory
Module 18 · Chapter 18.3 · CHI Debugging
Project thread — 18.2 debugged incorrect transitions. 18.3 debugs missing snoops; 18.4 debugs data corruption.
1. Learning Outcomes
By the end of this chapter you should be able to:
- Define a missing snoop — a sharer not snooped on a coherence action.
- Recognize the symptom — a cache using a value a write should have invalidated.
- Read the waveform signature — an empty snoop to a known sharer.
- Diagnose by diffing the snoops sent against the directory's sharer set.
- Classify the root cause — a wrong sharer set (16.1/16.3/filter) or dropped snoop target.
- Implement a representative missing-snoop detector in SystemVerilog, Verilog-2001, and VHDL.
2. Why Should I Learn This?
Coherence is enforced by snoops: when a line is written or taken unique, every cache that shares it must be snooped so its copy is invalidated (or updated). If one sharer is missed, that cache keeps a stale copy and later uses it — two cores now disagree about the line's value. A missing snoop is one of the most direct causes of a coherence violation, and its symptom (a stale read) is far from its cause (a snoop that never fired).
The diagnosis is unusually clean because the correct behavior is known: the home records which caches share a line (the directory's sharer set), and it must snoop exactly that set. So the debug is a set difference — take the snoops actually sent and the sharer set, and the sharer in the set but not in the sent snoops is the skipped one. The waveform makes it visible: an empty snoop channel to a cache the directory lists as a sharer. Learning to diff snooped vs sharers turns a baffling coherence failure into a one-comparison localization, and points the root cause at either the sharer tracking or the snoop generation. This chapter is that method.
3. Key Terms
4. Previous Chapter Connection
This chapter debugs the snoop generation of Chapter 15.6 (targeting the sharer vector) and the directory tracking of Chapters 16.1/16.3. A missing snoop is what happens when either goes wrong: the sharer set is incomplete (a dropped or orphaned sharer), or the generation logic skips a target it should snoop.
It connects tightly to the coherency verification of Chapter 17.2. There, the SWMR invariant flags the end state (a writer coexisting with a stale reader); here, the missing snoop is the mechanism that produced that state, and this chapter finds it. The two are complementary: 17.2 says "coherence is broken," 18.3 says "because this sharer was not snooped." It also sets up Chapter 18.5, which debugs the directory corruption that is the most common source of a wrong sharer set — 18.3 finds the missing snoop; 18.5 finds why the sharer set was wrong.
5. Core Concept — diff snooped against the sharer set
A missing snoop is a sharer in the directory's sharer set that is not snooped on a coherence action; the diagnosis is the set difference of the sharer set minus the snooped set.
- A coherence action must snoop all sharers. A write or ReadUnique to a shared line must snoop every cache in the sharer set so each stale copy is invalidated.
- Missing one leaves a stale copy. If a sharer is not snooped, it keeps its now-stale copy — and later uses it, so two cores disagree (a coherence violation).
- The correct set is known. The directory records the sharer set, and the action must snoop exactly it — so the correct behavior is precisely specified.
- Diff to localize. Compare the snooped set (actually sent) against the sharer set (should be sent). The sharer in the set but not snooped is the skipped one — the missing snoop.
The synthesis:
A missing snoop is a cache in the directory's sharer set that is not snooped on a coherence action — so it keeps a stale copy and later uses it (a coherence violation). Because the directory records which caches must be snooped, the diagnosis is a set difference: sharer set minus snooped set = the skipped sharer. The signature is an empty snoop channel to a cache the directory lists as a sharer.
6. Engineering Mental Model — a recall notice that misses a household
Think of a product recall where every household that bought the item (the sharers) must be notified to stop using it.
- The manufacturer has a registry of buyers (the sharer set) and must send a recall notice (a snoop) to each one so they stop using the recalled item (invalidate their copy).
- Correct recall: every registered buyer gets a notice, stops using the item, and confirms. No one keeps using the recalled product.
- Missing snoop: one registered household is skipped — no notice is sent to them. They keep using the recalled item, unaware it was recalled — the stale copy in use.
- Finding the miss: you do not re-derive who should have been notified; you diff the registry against the notices actually sent. The household in the registry with no notice is the one missed — an immediate, exact answer.
The registry is the sharer set; the notices sent are the snoops; the diff finds the skipped household. You never guess — the registry says who must be notified, so the missing notice is a simple set difference.
7. Engineering Diagram — the missing-snoop scenario
The home snooped RN0 but not RN1, though the directory listed both. RN1 keeps its stale copy. The bug is the skipped snoop to RN1 — found by diffing the sent snoops (RN0) against the sharer set (RN0, RN1). The debugger sees the empty snoop to a known sharer.
8. Waveform Signature
Missing snoop: empty snoop to a known sharer
6 cyclesThe signature is SNP.rn1 staying empty while sharers lists RN0,1 and snooped shows only RN0 — the missing signal marks the gap. A cache the directory lists as a sharer with an empty snoop channel on a coherence action is the missing snoop. The diff sharers − snooped = RN1 names it.
9. Diagnosis Path
The methodical trace via the set difference.
| Step | Action | What it finds |
|---|---|---|
| 1. Symptom | a cache uses a stale line | confirm it should have been invalidated |
| 2. The action | find the write/ReadUnique that should have snooped it | the coherence action |
| 3. Sharer set | read the directory's sharer set for the line | who should be snooped |
| 4. Snooped set | collect the snoops actually sent | who was snooped |
| 5. Difference | sharers − snooped | the skipped sharer(s) |
| 6. Root cause | was the sharer set wrong, or a target dropped? | 16.1/16.3/filter or generation |
The rule to carry: the correct snoop set is recorded, so the missing snoop is a diff, not a deduction. Unlike bugs where the correct behavior must be inferred, the directory states exactly which caches to snoop — so the diagnosis is a mechanical set difference, not reasoning. Step 6 then splits the root cause: if the sharer set was wrong (a sharer missing from it), the bug is in directory tracking (Chapters 16.1/16.3, and Chapter 18.5); if the sharer set was right but a target was dropped, the bug is in snoop generation (Chapter 15.6).
10. Tracing a Missing Snoop — a worked trace
Core C2's cache uses a stale value for line X after C3 wrote X.
- Symptom: stale use. C2 reads X and gets the old value; C3 wrote a new value and it was confirmed. C2 should have been invalidated.
- Find the action. C3's write was a ReadUnique to X, which the home processed — the coherence action that should have snooped X's sharers.
- Read the sharer set. The directory listed X's sharers as (C2, RN1) at the time of the action. C2 was a recorded sharer.
- Collect the snoops sent. The transaction's snoops went to (RN1) only — C2 was not snooped.
- Difference. (C2, RN1) − (RN1) = (C2) — the skipped sharer. C2's missing snoop left it with the stale copy it later used.
- Root cause. The sharer set correctly listed C2, but the snoop-generation logic dropped C2 from the targets (a Chapter 15.6-style bug) — or, if C2 was missing from the set, a directory-tracking bug. Fix the one that applies.
The stale use localized to a single skipped snoop via the set difference. The DebugLab formalizes this.
11. Detector View — a missing-snoop detector
Compare the snooped set against the sharer set; flag any sharer not snooped. Representative.
// Representative missing-snoop detector (educational).
// On a coherence action, the snooped set MUST cover the directory's sharer set. A sharer
// present in sharer_mask but absent from snooped_mask was SKIPPED -> missing snoop. The
// skipped sharers are exactly (sharer_mask & ~snooped_mask).
module chi_missing_snoop_detect #(parameter NCACHE = 8) (
input logic action_done, // the coherence action completed its snoops
input logic [NCACHE-1:0] sharer_mask, // directory's sharers of the line (must snoop)
input logic [NCACHE-1:0] snooped_mask, // caches actually snooped this action
output logic [NCACHE-1:0] skipped, // sharers that were NOT snooped
output logic missing_snoop // any sharer skipped
);
// Sharers that should have been snooped but were not.
assign skipped = sharer_mask & ~snooped_mask;
assign missing_snoop = action_done && (skipped != '0);
endmoduleThe same behavior in Verilog-2001:
// Representative missing-snoop detector (Verilog-2001).
module chi_missing_snoop_detect #(parameter NCACHE = 8) (
input action_done,
input [NCACHE-1:0] sharer_mask, snooped_mask,
output [NCACHE-1:0] skipped,
output missing_snoop
);
assign skipped = sharer_mask & ~snooped_mask;
assign missing_snoop = action_done & (skipped != {NCACHE{1'b0}});
endmoduleAnd in VHDL:
-- Representative missing-snoop detector (VHDL).
library ieee;
use ieee.std_logic_1164.all;
entity chi_missing_snoop_detect is
generic ( NCACHE : integer := 8 );
port (
action_done : in std_logic;
sharer_mask : in std_logic_vector(NCACHE-1 downto 0);
snooped_mask : in std_logic_vector(NCACHE-1 downto 0);
skipped : out std_logic_vector(NCACHE-1 downto 0);
missing_snoop : out std_logic
);
end entity;
architecture rtl of chi_missing_snoop_detect is
signal skp : std_logic_vector(NCACHE-1 downto 0);
begin
skp <= sharer_mask and (not snooped_mask); -- sharers not snooped
skipped <= skp;
missing_snoop <= '1' when (action_done = '1' and skp /= (skp'range => '0')) else '0';
end architecture;All three compute skipped = sharer_mask & ~snooped_mask and flag missing_snoop when a sharer was not snooped — the set difference, directly. Bind it to each coherence action and it names the skipped cache at the action, not the far-away stale read.
12. Assertion View — every sharer is snooped
The properties formalize the rule: the snooped set covers the sharer set.
// Bind to chi_missing_snoop_detect.
// 1. On a coherence action, every sharer is snooped (snooped covers sharers).
property p_all_sharers_snooped;
@(posedge clk) disable iff (!rst_n)
action_done |-> ((sharer_mask & ~snooped_mask) == '0);
endproperty
// 2. No non-sharer is snooped needlessly (efficiency; not a correctness bug).
property p_no_extra_snoops;
@(posedge clk) disable iff (!rst_n)
action_done |-> ((snooped_mask & ~sharer_mask) == '0);
endproperty
// 3. The skipped set is exactly the sharers not snooped.
property p_skipped_is_diff;
@(posedge clk) disable iff (!rst_n)
skipped == (sharer_mask & ~snooped_mask);
endpropertyThe system point, beyond the checks:
The missing-snoop detector is the cleanest in this module because the correct answer is not inferred but recorded — the directory is the specification of who must be snooped, so the check is a pure coverage relation (snooped ⊇ sharers) with no modeling required. This is worth internalizing as a debugging heuristic: whenever the design records what it intends to do (a target set, a request list, an expected-response mask), the corresponding bug is a set difference between the intent and the action, and the detector writes itself. The subtlety, which Chapter 18.5 pursues, is that this check assumes the recorded intent (the sharer set) is itself correct — if the directory's sharer set is wrong, the snoops can perfectly cover a wrong set and still miss a real sharer. So the missing-snoop check has two failure modes it distinguishes: a right sharer set with dropped snoops (a generation bug, caught by this diff), and a wrong sharer set (a tracking bug, where this diff passes but coherence still breaks). Recognizing which one you have — is the sharer set right? — is the branch point of the diagnosis, and it separates a Chapter 15.6 fix from a Chapter 16.1/16.3/18.5 fix.
- What it proves: the snooped set covers the (recorded) sharer set.
- What it does not prove: the sharer set itself is correct — that is directory corruption (Chapter 18.5).
- Bug signature:
missing_snoopasserted —skippednames the un-snooped sharer.
13. Testbench — a skipped sharer must be flagged
Snoops a subset of the sharers and checks the detector names the skipped one.
module tb_chi_missing_snoop_detect;
localparam NCACHE = 8;
logic action_done;
logic [NCACHE-1:0] sharer_mask, snooped_mask, skipped;
logic missing_snoop;
int errors = 0;
chi_missing_snoop_detect #(.NCACHE(NCACHE)) dut (.*);
initial begin
// Sharers are caches 0 and 3.
sharer_mask = 8'b0000_1001;
// CORRECT: both sharers snooped.
snooped_mask = 8'b0000_1001; action_done = 1; #1;
if (missing_snoop) begin errors++; $display("FAIL false missing-snoop when all covered"); end
else $display("PASS all sharers snooped -> no missing snoop");
// BUG: only cache 0 snooped; cache 3 SKIPPED.
snooped_mask = 8'b0000_0001; #1;
if (!missing_snoop) begin errors++; $display("FAIL skipped sharer NOT detected"); end
else if (skipped !== 8'b0000_1000) begin errors++; $display("FAIL wrong skipped set: %b", skipped); end
else $display("PASS missing snoop detected: skipped = %b (cache 3)", skipped);
// Action not done yet -> do not flag (snoops may still be in flight).
action_done = 0; #1;
if (missing_snoop) begin errors++; $display("FAIL flagged before action_done"); end
else $display("PASS not flagged before action completes");
if (errors == 0) $display("ALL TESTS PASSED");
else $display("%0d FAILURE(S)", errors);
$finish;
end
endmoduleExpected output:
PASS all sharers snooped -> no missing snoop
PASS missing snoop detected: skipped = 00001000 (cache 3)
PASS not flagged before action completes
ALL TESTS PASSED14. DebugLab — a sharer skipped on a write
A sharer skipped on a write
A SHARER IN THE DIRECTORY IS NOT SNOOPED ON A WRITE -> KEEPS A STALE COPY -> STALE READ (MISSING SNOOP)A cache uses a stale value after another core wrote the line — two cores disagree about the value. There is no error; the reading core simply never saw the invalidation. It correlates with write-shared lines that had multiple sharers, not private or single-sharer lines.
Diffing snoops-sent against the sharer set names the skipped cache:
symptom: C2 reads X = old value; C3 wrote X = new value (confirmed)
step 2 - action: C3's write = ReadUnique(X); home must snoop X's sharers
step 3 - sharer set: directory listed X sharers = {C2, RN1} (C2 IS a recorded sharer)
step 4 - snoops sent: {RN1} only (C2 NOT snooped)
step 5 - diff: {C2, RN1} - {RN1} = {C2} -> C2 was SKIPPED
-> C2 never invalidated -> keeps stale X -> later read returns stale value
signature: SNP channel to C2 EMPTY while directory lists C2 as a sharer
correct: snoop every recorded sharer -> C2 snooped -> invalidated -> no stale readThe recorded sharer set and the sent snoops differed by exactly the skipped cache.
On the coherence action, the snoop-generation covered (RN1) but the directory's sharer set was (C2, RN1) — C2 was in the set but received no snoop. The divergence is the empty snoop to a recorded sharer.
A coherence action must snoop every cache in the directory's sharer set, so a sharer that is in the set but not snooped keeps a stale copy; the diagnosis is the set difference of sharers minus snooped. Because the directory records who must be snooped, the missing snoop is a mechanical diff, not a deduction. The branch point is whether the sharer set was right: if right but a target was dropped, the bug is in snoop generation (Chapter 15.6); if the sharer set was wrong (a sharer missing or a stale entry), the bug is in directory tracking (Chapters 16.1/16.3, debugged in Chapter 18.5). Here the set correctly listed C2 but generation dropped it. The detector flags the skipped cache at the action, localizing the bug in time far ahead of the stale read.
Ensure the coherence action snoops every cache in the sharer set — the snooped set must cover the sharer set — as Chapter 15.6 requires. If the sharer set itself was wrong, fix the directory tracking (Chapters 16.1/16.3). Confirm with the detector that skipped is always empty on a completed action. Snoop all recorded sharers, always.
15. Common Mistakes
- Debugging the stale read. Assumption: the bug is at the read. Bug: it is at the missed snoop. Prevention: diff snooped vs sharers at the action.
- Not reading the sharer set. Assumption: guess who should be snooped. Bug: the directory records it. Prevention: use the recorded set.
- Assuming the sharer set is right. Assumption: a passing diff means correct. Bug: a wrong set can be fully snooped. Prevention: also check the set (Chapter 18.5).
- Ignoring the empty snoop channel. Assumption: some snoops means all. Bug: one sharer skipped. Prevention: check every sharer's channel.
- Conflating generation and tracking. Assumption: one root cause. Bug: two families. Prevention: branch on whether the set was right.
- Missing multi-sharer cases. Assumption: one sharer. Bug: the skipped one among many. Prevention: check the full mask.
16. Engineering Checklist
- On a stale use, find the coherence action that should have snooped the cache.
- Read the directory's sharer set for the line at that action.
- Collect the snoops actually sent (the snooped set).
- Compute sharers − snooped — the skipped sharer(s).
- Branch: was the sharer set right (generation bug) or wrong (tracking bug)?
- Bind a detector that flags a sharer not snooped on a completed action.
17. Key Takeaways
- A missing snoop leaves a sharer un-invalidated, keeping a stale copy.
- The symptom is a cache using a value a write should have invalidated.
- The signature is an empty snoop to a known sharer.
- The diagnosis is a set difference — sharers − snooped.
- The root cause is a wrong sharer set (tracking) or a dropped target (generation).
- Snoop every recorded sharer; the model here is representative.
18. Quick Revision
Missing snoop. A coherence action (a write or ReadUnique) to a shared line must snoop every cache in the directory's sharer set so each stale copy is invalidated; a missing snoop is when one sharer is not snooped — it keeps its stale copy and later uses it, so two cores disagree (a coherence violation). The symptom is a cache using a value a write elsewhere should have invalidated. The waveform signature is an empty snoop channel to a cache the directory lists as a sharer. Because the directory records who must be snooped, the diagnosis is a set difference — the sharer set minus the snooped set = the skipped sharer — a mechanical diff, not a deduction. The branch point for the root cause is whether the sharer set was right: if right but a target was dropped, the bug is in snoop generation (Chapter 15.6); if the sharer set was wrong (a sharer missing or stale), the bug is in directory tracking (Chapters 16.1/16.3, debugged in Chapter 18.5) — where this same diff can pass while coherence still breaks, because the snoops cover a wrong set. The detector computes
skipped = sharer_mask & ~snooped_maskand flags it at the action, localizing the bug far ahead of the stale read. Snoop every recorded sharer, always. Representative model; 18.4 debugs data corruption.
Coming Next
Chapter 18.4 — Data Corruption. A missing snoop delivers stale-but-intact data; data corruption delivers mangled data. Chapter 18.4 covers data corruption — wrong bytes delivered, the waveform signature of a data beat whose value diverges from its source, and the diagnosis of following the data from source to sink to find the hop or beat where the value changed.