Skip to content

AMBA CHI · Module 9 · Snoop Flows

Snoop Forwarding

So far the home has relayed snoop data — holder to home, home to requester, two hops. Snoop forwarding, or Direct Cache Transfer, shortens it. The home issues a forwarding snoop carrying the requester's identity, so the holding cache sends its data straight to the requester in a single hop, bypassing the home — a real latency saving. The catch is the home still needs to know what happened, so the holder also sends it a no-data response. The data path runs peer-to-peer, the response peer-to-home, and the home sends no data — the peer is the sole source. Let the home also send data and the requester gets two sources that duplicate or race. Representative model, not the specification.

Advanced16 min readAMBA CHIDirect Cache TransferForwarded SnoopLatencySnpFwd

Module 9 · Chapter 9.7 · Snoop Flows

Project thread — 9.5 sourced data from a cache; 9.6 moved ownership. This chapter shortens the data path with direct forwarding, and closes Module 9. Module 10 turns to the cache states themselves.

1. Learning Outcomes

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

  • Describe a forwarded snoop: the holder sends data directly to the requester, bypassing the home.
  • Explain the latency win over the home-mediated path — one hop instead of two.
  • State that the data and response paths decouple — data peer→requester, response peer→home.
  • Name the forwarding snoops (SnpSharedFwd, SnpUniqueFwd, …) and the SnpRespFwded response.
  • Diagnose why the home must send no data on a forwarding snoop.
  • Implement a representative forwarding-snoop router in SystemVerilog, Verilog-2001, and VHDL.

2. Why Should I Learn This?

Latency is everything in a coherent fabric, and the snoop data path is a common critical path. In the home-mediated flow, a holder's data travels holder → home → requester — two hops across the mesh, with the home as an intermediary that adds distance and a buffering stage. When the holder and requester are near each other but far from the home, that detour is pure waste.

Direct Cache Transfer removes it: the data goes straight from the holder to the requester. It is one of CHI's most effective latency optimisations, and you will see it wherever performance matters. Its lesson is architectural — that a transaction's data and its control/response need not follow the same path. Splitting them is powerful, but it introduces a new discipline: with two paths, exactly one must carry the data, and the home must know to step back. Understanding forwarded snoops is understanding how to decouple data from control without breaking either.

3. Key Terms

4. Previous Chapter Connection

Chapter 9.5 sourced data from a peer cache but relayed it through the home — holder → home → requester. Chapter 9.6 migrated ownership. This chapter keeps the cache-to-cache idea and removes the middle hop.

A forwarding snoop reuses everything you know — the snoop types, the response fields, the ownership handoff — but redirects the data: the holder sends it straight to the requester, and sends the home only a response. So the data transfer of 9.5 becomes direct, and the home's role shrinks to bookkeeping. The coherence content is identical; only the data's route changes — which is exactly why the home must be careful not to also send data it no longer needs to.

5. Core Concept — the holder sends data direct; the home gets a response

A forwarded snoop splits the exchange into two paths: the data goes peer-to-peer, and the response goes to the home.

  • The forwarding snoop carries the requester's ID. The home issues SnpSharedFwd (or SnpUniqueFwd, etc.), which tells the holder who the requester is — so the holder can address data to it directly.
  • The holder sends data direct. The holder sends CompData straight to the requester, one hop, bypassing the home. This is the Direct Cache Transfer.
  • The holder also responds to the home. The holder sends a SnpRespFwded — a no-data response — to the home, reporting its final state and that it forwarded. The home updates its directory and completes the transaction.
  • The home sends no data. Because the peer is the sole data source, the home must not also send data. Its role is coordination — response handling and completion, not a data relay.

The synthesis:

A forwarded snoop decouples data from response: the holder sends data direct to the requester (one hop, bypassing the home) and sends the home a no-data SnpRespFwded for its bookkeeping. The forwarding snoop carries the requester's ID so the holder knows where to send. The home is now a coordinator, not a relay — it sends no data, because the peer is the sole source. Same coherence, shorter data path.

6. Engineering Mental Model — "send it straight to them, cc me"

You (home) are coordinating a document handoff between two colleagues.

  • The slow way: the holder mails the document to you, and you mail it on to the requester. Two trips, and you sit in the middle handling paper you do not need.
  • The direct way: you tell the holder "send it straight to the requester — here's their address — and just cc me a note that you did." Now the document takes one trip, peer to peer, and you get a lightweight confirmation for your records.
  • Crucially, once you have said "send it direct," you must not also mail your own copy — the requester would get two, and if yours is an old draft, they might use the wrong one.

The optimisation is obvious (one trip, not two), and so is the discipline: having delegated the send, you step back from the data and keep only the note. Send it direct, cc me — but I send nothing.

7. Engineering Diagram — a forwarded snoop (Direct Cache Transfer)

A forwarded snoop. RN0 sends a REQ ReadShared to the Home Node. The Home Node issues a SNP SnpSharedFwd to RN1, carrying RN0's identity. RN1 sends the data directly to RN0 as a DAT CompData, bypassing the home. RN1 separately sends the Home Node a RSP SnpRespFwded with no data. The data path from RN1 to RN0 and the response path from RN1 to the Home Node are decoupled, and the home sends no data.Forwarded snoop — data direct to requester, response to homeRN0 · requesterHN · home (coordinator)RN1 · holderREQ: ReadSharedSNP: SnpSharedFwd(carries RN0 id)DAT: CompData(DIRECT, bypasseshome)RSP: SnpRespFwded(no data)
Figure 1 — a forwarded snoop. RN0's ReadShared makes the Home Node issue SnpSharedFwd to RN1, carrying RN0's identity. RN1 sends the data directly to RN0 as CompData — one hop, bypassing the home — and separately sends the Home Node a no-data SnpRespFwded for its directory and completion. The data path (RN1 to RN0) and the response path (RN1 to HN) are decoupled; the home sends no data.

Read the third message: the data goes RN1 → RN0, not RN1 → HN → RN0. The home receives only the fourth message, a no-data response. Two destinations, two purposes: the requester gets the line, the home gets the record.

8. Two Paths — Home-Mediated versus Forwarded

The same snoop, two data routes.

AspectHome-mediated (9.5)Forwarded / DCT (9.7)
Data routeholder → home → requesterholder → requester (direct)
Data hopstwoone
Home relays data?yesno
Holder's response to homeSnpRespData (with data)SnpRespFwded (no data)
Snoop opcodeSnpShared, SnpUnique, …SnpSharedFwd, SnpUniqueFwd, …

The rule to carry: a forwarding snoop trades a home relay for a direct hop. The data reaches the requester one hop sooner, and the home's response becomes a lightweight SnpRespFwded carrying no data. The coherence outcome — states, ownership, PassDirty — is unchanged; only the data's path is shorter, and the home's job shrinks from relaying to recording.

9. Decoupled Data and Response — the two channels

The defining structure of a forwarded snoop is that data and response take different paths, and each must be complete.

  • Data path: peer → requester. The holder sends the line directly to the requester. This is the only copy of the data in flight — the peer is the sole source.
  • Response path: peer → home. The holder sends SnpRespFwded to the home — no data, just the final state and confirmation it forwarded. The home needs this to update its directory and complete the transaction.
  • Both are required. Miss the data path and the requester gets nothing; miss the response path and the home never learns the outcome (stale directory, or the transaction stalls).
  • The home adds nothing to the data. Having delegated the data to the peer, the home must send no data of its own — one source, not two.

The point to carry:

A forwarded snoop is a fork: one exchange, two destinations. The data and the response are independent messages with independent jobs — deliver the line, and inform the home — and correctness requires both to complete while neither duplicates the other. The subtlety over a home-mediated snoop is precisely this decoupling: the home is no longer on the data path, so it must actively refrain from sending data, and rely on the separate response to stay informed. Splitting the paths buys latency; keeping each path singular and complete is the price.

10. Walkthrough — a SnpSharedFwd

RN0 issues ReadShared; RN1 holds the line and is near RN0 but far from HN.

  1. REQ. RN0 asks for the line. The home decides to forward — RN1 is close to RN0, so a direct hop saves latency.
  2. Forwarding snoop. HN sends SnpSharedFwd to RN1, carrying RN0's identity so RN1 knows where to send.
  3. Direct data. RN1 sends CompData straight to RN0 — one hop, bypassing HN. RN0 installs the line. RN1 downgrades to SC as the snoop requires.
  4. Response to home. RN1 sends SnpRespFwded (no data) to HN, reporting it forwarded and its final state. HN updates its directory.
  5. Home completes, sends no data. HN completes the transaction from the response alone — it never sent data, because RN1 was the sole source.

RN0 got the line one hop sooner than a home relay would allow; HN stayed informed via the no-data response. Had HN also sent data in step 5, RN0 would receive two copies — the DebugLab.

11. RTL / Hardware View — a forwarding-snoop router

The home's routing decision is combinational: on a forwarding snoop with a holder, the peer sends data direct and the home sends none; on a normal snoop, the home relays. Either way the peer responds to the home. Representative.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative forwarding-snoop routing decision (educational).
// On a FORWARDING snoop, the holder (peer) sends data DIRECT to the requester and
// the home sends NO data (peer is the sole source). On a normal snoop, the home
// relays the data. In both cases the peer sends a response to the home so the
// directory and completion stay correct.
module chi_fwd_snoop_route (
  input  logic is_fwd,            // the snoop is a forwarding (DCT) variant
  input  logic holder_present,    // a cache holds the line to supply data
  output logic peer_to_req_data,  // peer sends data directly to the requester
  output logic home_to_req_data,  // home relays data to the requester
  output logic peer_to_home_resp, // peer sends a response to the home (bookkeeping)
  output logic one_data_source    // exactly one data source when a holder exists
);
  // Forwarding snoop with a holder: peer -> requester, direct.
  assign peer_to_req_data  = is_fwd && holder_present;
  // Normal snoop with a holder: home relays the data.
  assign home_to_req_data  = holder_present && !is_fwd;
  // The peer ALWAYS responds to the home, forwarded or not.
  assign peer_to_home_resp = holder_present;
  // When a holder exists, exactly one of the two data paths carries the line.
  assign one_data_source   = !holder_present || (peer_to_req_data ^ home_to_req_data);
endmodule

The same behavior in Verilog-2001:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative forwarding-snoop routing decision (Verilog-2001).
module chi_fwd_snoop_route (
  input  is_fwd,
  input  holder_present,
  output peer_to_req_data,
  output home_to_req_data,
  output peer_to_home_resp,
  output one_data_source
);
  assign peer_to_req_data  = is_fwd && holder_present;
  assign home_to_req_data  = holder_present && !is_fwd;
  assign peer_to_home_resp = holder_present;
  assign one_data_source   = !holder_present || (peer_to_req_data ^ home_to_req_data);
endmodule

And in VHDL:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- Representative forwarding-snoop routing decision (VHDL).
library ieee;
use ieee.std_logic_1164.all;
 
entity chi_fwd_snoop_route is
  port (
    is_fwd           : in  std_logic;
    holder_present   : in  std_logic;
    peer_to_req_data : out std_logic;
    home_to_req_data : out std_logic;
    peer_to_home_resp: out std_logic;
    one_data_source  : out std_logic
  );
end entity;
 
architecture rtl of chi_fwd_snoop_route is
  signal p2r, h2r : std_logic;
begin
  p2r <= is_fwd and holder_present;
  h2r <= holder_present and (not is_fwd);
 
  peer_to_req_data  <= p2r;
  home_to_req_data  <= h2r;
  peer_to_home_resp <= holder_present;
  one_data_source   <= '1' when (holder_present = '0') or ((p2r xor h2r) = '1') else '0';
end architecture;

All three route data peer-to-requester on a forwarding snoop (with the home sending none), relay through the home on a normal snoop, and always have the peer respond to the home. The one_data_source check enforces exactly one data path. The DebugLab shows the double-source hazard when the home fails to step back.

12. Verification View — one data source, home always informed

The properties that keep forwarding correct: on a forwarding snoop the peer is the sole data source, and the home is always informed.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bind to chi_fwd_snoop_route.
// 1. On a forwarding snoop with a holder, the peer sends data and the home does NOT.
property p_fwd_peer_sole_source;
  @(*) (is_fwd && holder_present) |-> (peer_to_req_data && !home_to_req_data);
endproperty
 
// 2. Exactly one data source reaches the requester when a holder exists.
property p_one_source;
  @(*) holder_present |-> (peer_to_req_data ^ home_to_req_data);
endproperty
 
// 3. The peer always responds to the home so the directory/completion stay correct.
property p_home_always_informed;
  @(*) holder_present |-> peer_to_home_resp;
endproperty

The system point, beyond the checks:

Forwarding is a lesson in separating a transaction's planes: the data plane (the bytes) and the control plane (the response that keeps the home's model correct). Home-mediated snoops fuse them — the data is the response, both flowing through the home. Forwarding splits them so the data can take the fast direct path while control still reaches the home. The correctness conditions are then about each plane independently: the data plane must have exactly one source (the peer), and the control plane must always reach the home. A design that keeps the home on the data plane out of habit — sending data it no longer owns — violates the first; one that forgets the peer's response violates the second. Split cleanly, and you get the latency without losing the bookkeeping.

  • What it proves: peer is the sole data source on a forwarding snoop; exactly one source; home always informed.
  • What it does not prove: the direct data actually arrived — that is the fabric's delivery.
  • Bug signature: both peer_to_req_data and home_to_req_data asserted — two data sources to the requester.

13. Testbench — forwarded and normal routing

Drives forwarding and normal snoops with and without a holder, checking the data paths and the response.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module tb_chi_fwd_snoop_route;
  logic is_fwd, holder_present;
  logic peer_to_req_data, home_to_req_data, peer_to_home_resp, one_data_source;
  int errors = 0;
 
  chi_fwd_snoop_route dut (.*);
 
  task automatic check(input logic fw, hp,
                       input logic exp_peer, exp_home, exp_resp, input string name);
    is_fwd = fw; holder_present = hp; #1;
    if (peer_to_req_data !== exp_peer || home_to_req_data !== exp_home ||
        peer_to_home_resp !== exp_resp || !one_data_source) begin
      errors++; $display("FAIL %s: peer=%0b home=%0b resp=%0b one=%0b",
                         name, peer_to_req_data, home_to_req_data, peer_to_home_resp, one_data_source);
    end else $display("PASS %s: peer=%0b home=%0b resp=%0b", name, peer_to_req_data, home_to_req_data, peer_to_home_resp);
  endtask
 
  initial begin
    check(1'b1, 1'b1, 1'b1, 1'b0, 1'b1, "forwarded, holder -> peer direct, home no data");
    check(1'b0, 1'b1, 1'b0, 1'b1, 1'b1, "normal,    holder -> home relays");
    check(1'b1, 1'b0, 1'b0, 1'b0, 1'b0, "forwarded, no holder -> no data path");
 
    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 forwarded, holder -> peer direct, home no data: peer=1 home=0 resp=1
PASS normal,    holder -> home relays: peer=0 home=1 resp=1
PASS forwarded, no holder -> no data path: peer=0 home=0 resp=0
ALL TESTS PASSED

14. DebugLab — the home also sends data on a forwarding snoop

1

The home also sends data on a forwarding snoop

HOME ALSO SENDS DATA ON A FORWARDING SNOOP -> DUPLICATE/RACING DATA
Symptom

A requester occasionally installs a wrong or stale value — or the fabric shows duplicate data for one line — but only for transactions the home chose to forward. Home-mediated transactions are always clean.

Evidence

Two data sources reached the requester:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
RN0 ReadShared; RN1 holds the line (fresh)
HN issues SnpSharedFwd -> RN1 sends CompData DIRECT to RN0 (fresh)
HN also sends CompData to RN0 from memory  <-- did not suppress its relay path
memory may be stale relative to RN1's copy
RN0 receives TWO CompData -> duplicate, or installs the stale one -> corrupt

The peer delivered correctly; the home should have delivered nothing but sent data anyway.

First Divergence

On a forwarding snoop, the home left its data-relay path active and sent data in addition to the peer's direct forward. From that point two data sources targeted the requester for one line.

Root Cause

On a forwarding snoop the peer is the sole data source; the home must send no data. The whole point of forwarding is to move the data off the home's path, so the home's role collapses to coordination — the response and completion — with no data. Failing to suppress its relay creates a second source, which duplicates at best and, when the home's copy is stale (memory behind a dirty peer, Chapter 9.5), delivers the wrong value. This is a data-plane failure specific to the decoupled paths — distinct from choosing the wrong source in a home-mediated flow (Chapter 9.5): here the source is right, but the home added a second one.

Fix

On a forwarding snoop, the home sends no data — only the response handling and completion. Suppress the relay path when is_fwd, so exactly one data source (the peer) reaches the requester, as one_data_source enforces. Keep the home on the control plane and off the data plane once it has delegated the send.

15. Common Mistakes

  • Home sends data on a forwarding snoop. Assumption: the home always relays. Bug: two data sources (the DebugLab). Prevention: the peer is sole source; home sends none.
  • Omitting the peer's response to home. Assumption: direct data is enough. Bug: stale directory / stalled completion. Prevention: send SnpRespFwded to the home.
  • Not carrying the requester's ID. Assumption: the holder knows the target. Bug: the holder cannot address the direct data. Prevention: the forwarding snoop carries the requester ID.
  • Forwarding with no holder. Assumption: forwarding always applies. Bug: no source. Prevention: forward only when a holder can supply the data.
  • Expecting data from the home. Assumption: data comes from the home. Bug: the requester waits forever. Prevention: on a forwarded flow, expect peer data.
  • Assuming forwarding changes coherence. Assumption: DCT alters states. Bug: mis-modeled outcome. Prevention: only the data path changes; states are the same.

16. Engineering Checklist

  • Use a forwarding snoop (…Fwd) carrying the requester's ID when a direct hop saves latency.
  • Have the holder send data directly to the requester (one hop).
  • Have the holder send SnpRespFwded (no data) to the home for its records.
  • The home sends no data on a forwarding snoop — the peer is the sole source.
  • Ensure the requester expects peer data, not home data.
  • Keep the coherence outcome (states, ownership, PassDirty) unchanged — only the data path moves.

17. Key Takeaways

  • Forwarded snoops (Direct Cache Transfer) send the holder's data straight to the requester, bypassing the home.
  • The data path drops from two hops to one, cutting snoop latency.
  • Data and response paths decouple: data peer→requester, response (SnpRespFwded, no data) peer→home.
  • The home sends no data on a forwarding snoop — the peer is the sole source.
  • Letting the home also send data creates two sources — duplicate or racing, possibly stale.
  • Forward for latency; keep one data source and inform the home; the model here is representative.

18. Quick Revision

Snoop forwarding. A forwarded snoop, or Direct Cache Transfer, shortens the data path: instead of holder → home → requester (two hops), the home issues a forwarding snoop (SnpSharedFwd, SnpUniqueFwd, …) carrying the requester's ID, and the holder sends its data directly to the requester (one hop, bypassing the home). The data and response paths decouple — the data goes peer→requester, while the holder also sends the home a no-data SnpRespFwded so the directory and completion stay correct. Because the peer is the sole data source, the home must send no data — its role shrinks from relay to coordinator. The coherence outcome (states, ownership, PassDirty) is unchanged; only the data's route is shorter. The trap: the home leaving its relay path active and also sending data, so the requester gets two sources — a duplicate, or a race where a stale home copy corrupts the line. Forward for latency; one data source; always inform the home. Representative model; Module 10 turns to the CHI cache states.

Coming Next

Chapter 10.1 — The CHI Cache States. Modules 8 and 9 kept naming states — UC, UD, SC, SD, I — and moving lines between them. Module 10 makes those states the subject. Chapter 10.1 lays out the CHI cache states in full: what each of UniqueClean, UniqueDirty, SharedClean, SharedDirty, and Invalid means, the two axes of unique-versus-shared and clean-versus-dirty that organize them, and how they map to the classic MOESI model — the state vocabulary underneath every flow you have traced.