Skip to content

AMBA CHI · Module 5 · CHI System Components

SN-F (Fully Coherent Slave Node)

SN-F is the subordinate that backs coherent memory — the memory controller behind a fully coherent Home Node. Its name misleads: the F marks the region it serves, coherent memory, not any coherency behavior of its own. Like every subordinate, SN-F does no coherency — no directory, no snoops. It completes the accesses the Home Node forwards, coherency already resolved: reads that fetch on a miss, writebacks of dirty evictions, and writes, all via CHI's data-buffer handshake. This chapter gives SN-F its depth: the downstream transactions it handles and the write handshake it runs. Representative model, not the specification.

Intermediate14 min readAMBA CHISN-FMemory ControllerDBIDWriteback

Module 5 · Chapter 5.5 · CHI System Components

Project thread — 5.3 and 5.4 detailed the homes; this chapter takes the coherent-memory endpoint behind HN-F. 5.6 finishes the taxonomy with the I/O subordinate, SN-I.

1. Learning Outcomes

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

  • Define SN-F as the memory controller behind an HN-F that backs the coherent memory region.
  • Explain why the "F" names the region, not any coherency behavior — SN-F does no coherency.
  • List the downstream transactions HN-F forwards to SN-F — ReadNoSnp, WriteNoSnp, WriteBack.
  • Describe the CHI write handshake: allocate a data buffer, return a DBID, accept tagged data, complete.
  • Trace how a cache writeback reaches memory through the Home Node and SN-F.
  • Implement a representative SN-F write handshake in SystemVerilog, Verilog-2001, and VHDL.

2. Why Should I Learn This?

SN-F is the ultimate backing store for all coherent data — every line that misses in every cache eventually reads from or writes to an SN-F. Its correctness is where "the memory is right" is finally enforced, so understanding how it completes reads, writebacks, and writes is understanding the floor the whole coherent system stands on.

It also clears up a common confusion. "Fully coherent slave node" sounds like it participates in coherency; it does not. Seeing precisely that the F is about the region it serves, not its behavior, sharpens the whole taxonomy — and the write handshake (DBID) it runs is a mechanism you will meet again in every CHI write flow.

3. Key Terms

4. Previous Chapter Connection

Chapter 4.4 established the subordinate as the coherency-agnostic storage endpoint: it completes the reads and writes the Home Node forwards, holds no directory, and always answers to the Home Node, never to a requester. It applied to both SN-F and SN-I.

This chapter gives SN-F its depth. Where 4.4 said "the subordinate completes memory accesses," 5.5 says which transactions HN-F forwards to it, how the write handshake works, and how a writeback reaches memory — the concrete life of the coherent-region memory controller. The role is unchanged from 4.4; the mechanisms are new.

5. Core Concept — coherent-region memory, no coherency

SN-F is a memory controller with one clarifying subtlety and one real mechanism.

  • It backs the coherent region. SN-F is the storage endpoint for the coherent memory address space — DRAM behind an HN-F. Every coherent line ultimately lives here. That is what the F means.
  • It does no coherency. SN-F holds no directory and issues no snoops. By the time HN-F forwards a request, coherency is resolved. The "F" is the region, not the behavior — SN-F is as coherency-agnostic as any subordinate (Chapter 4.4).
  • It completes downstream transactions. HN-F forwards a small set: ReadNoSnp (fetch memory on a miss), WriteNoSnp and WriteBack (write data, including dirty data evicted from caches), and their variants. SN-F reads or writes memory and completes.
  • It runs the write handshake. CHI writes are two-phase: SN-F allocates a data buffer and returns a DBID; the sender then delivers the data tagged with that DBID; SN-F writes it and returns Comp.

The synthesis:

SN-F is the coherent-region memory controller: it completes the reads, writes, and writebacks HN-F forwards, using the DBID write handshake — and it does no coherency, because HN-F already did. The "F" tells you what it stores (coherent memory), not what it does (which is plain, correct storage at the back of every coherent transaction).

6. Engineering Mental Model — the vault behind the registrar

Return to the registrar (HN-F) and the vault (the subordinate) from Chapter 4.4. SN-F is the main vault — the one holding the co-op's shared records (coherent memory).

  • The registrar sends the vault clear instructions: "retrieve record A" (ReadNoSnp) or "store this updated record" (WriteBack of dirty data). The vault does exactly that — no more.
  • For storing, the vault first hands back a claim ticket for a shelf slot (DBID); the courier brings the box and shows the ticket; the vault files it on that slot and confirms it is stored (Comp). The ticket makes sure each box lands on the shelf reserved for it.
  • The vault never decides who may access a record or in what order relative to caches — that is the registrar's job (coherency). It just stores and retrieves, correctly and on request.

Being the main vault (coherent region) versus a side vault (I/O, SN-I) changes what it holds, not what it does. Both are just vaults.

7. Engineering Diagram — SN-F between HN-F and memory

SN-F in the coherent path. A fully coherent Home Node HN-F forwards a resolved memory access to SN-F. SN-F contains a memory controller and write-data buffers keyed by DBID, and no coherency logic. It reads from or writes to DRAM, the coherent-region storage, and returns data or a completion to HN-F.HN-Fforwards resolved accessSN-F completermemory ctrl · write buffers(DBID) · no coherencyDRAMcoherent-region storage12
Figure 1 — SN-F in the coherent path. The Home Node HN-F, having resolved coherency, forwards a memory access to SN-F; SN-F's memory controller and write-data buffers complete it against DRAM and respond to HN-F. SN-F holds no directory and issues no snoops — it is coherency-agnostic storage that happens to back the coherent region.

SN-F sits between the Home Node and DRAM. It talks only to the Home Node (Chapter 4.4), never to a requester, and its internals are a memory controller plus write buffers — not a scrap of coherency logic.

8. The Downstream Transaction Set

HN-F forwards a narrow set of transactions to SN-F — the memory-side operations, coherency already resolved.

TransactionWhen HN-F sends itSN-F does
ReadNoSnpa coherent read missed all cachesread memory, return data
WriteNoSnpHN-F writes memory (e.g. non-cached write)accept data, write memory
WriteBacka cache evicted a dirty lineaccept dirty data, write memory
WriteCleanwrite dirty data but keep it cachedaccept data, write memory

Two facts to carry: every one is a NoSnp / write operation — there is nothing coherent for SN-F to interpret, because HN-F resolved it — and writebacks (dirty evictions) are how modified data finally reaches memory, always routed through the Home Node to SN-F. SN-F never sees a snoop or a directory; it sees reads and writes.

9. The Write Handshake — DBID

CHI writes are two-phase so the data has a reserved place to land. The steps for a write to SN-F:

  1. Request. The write request (WriteBack / WriteNoSnp) arrives at SN-F.
  2. Allocate + DBID. SN-F allocates a write-data buffer and returns its DBID (Data Buffer ID) to the sender — "send the data, tagged with this ID."
  3. Data. The sender delivers the write data on the DAT channel, carrying that DBID.
  4. Write + complete. SN-F matches the data's DBID to the allocated buffer, writes it to memory, frees the buffer, and returns Comp (write committed).

The point to carry:

The DBID ties a write's data to the buffer reserved for it. SN-F must accept data only for a currently-allocated DBID, and must not free or reuse a buffer until its data has been written. Get the DBID accounting wrong — accept data for an unallocated or reused buffer — and one write's data lands in another's slot, corrupting memory. It is the write-side analogue of the TxnID discipline from Chapter 4.2.

10. Transaction Walkthrough — a writeback reaches memory

CPU0 evicts a dirty line; the data must reach memory through HN-F and SN-F.

  1. Evict. CPU0's RN-F evicts line A, held UD (dirty), issuing a WriteBack to the Home Node.
  2. Forward. HN-F, the point of coherence for line A, forwards the writeback to the SN-F that backs A's address, and updates its directory (A no longer cached).
  3. DBID. SN-F allocates a write buffer and returns a DBID to HN-F.
  4. Data. The dirty data flows (from CPU0 via HN-F) to SN-F on DAT, tagged with the DBID.
  5. Commit. SN-F writes the data to DRAM, frees the buffer, and returns Comp. The dirty line is now safely in memory; nothing caches it.

SN-F did pure storage: allocate, accept, write, complete. Coherency — deciding the line was dirty, routing it, updating the directory — all happened at HN-F before SN-F ever saw the write.

11. RTL / Hardware View — the SN-F write handshake

The core of SN-F's write path is the DBID handshake: allocate a buffer, return its ID, accept data tagged with it, complete. Representative and simplified — buffer allocation and the DBID match, no actual memory array.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative SN-F write handshake with DBID (educational).
// On a write request, allocate a data buffer and return its DBID. Accept write
// data only when it is tagged with a currently-allocated DBID; then commit and
// free the buffer. SN-F does NO coherency — the HN already resolved it.
module snf_write_handshake #(
  parameter int NBUF = 4                          // write-data buffers
)(
  input  logic clk,
  input  logic rst_n,
  input  logic       wr_req,                       // a write request arrived
  output logic       dbid_valid,                   // returning a DBID this cycle
  output logic [1:0] dbid,                          // allocated buffer id
  input  logic       data_valid,                    // write data arrived
  input  logic [1:0] data_dbid,                     // the DBID the data carries
  output logic       wr_complete                    // write committed (Comp)
);
  logic [NBUF-1:0] busy;                            // allocated buffers
  logic [1:0] free_id;
  logic       has_free;
 
  always_comb begin
    free_id = 2'd0; has_free = 1'b0;
    for (int i = NBUF-1; i >= 0; i--)
      if (!busy[i]) begin free_id = i[1:0]; has_free = 1'b1; end
  end
 
  wire do_alloc = wr_req && has_free;
  wire do_write = data_valid && busy[data_dbid];    // ONLY for an allocated DBID
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) busy <= '0;
    else begin
      if (do_alloc) busy[free_id]   <= 1'b1;        // reserve on request
      if (do_write) busy[data_dbid] <= 1'b0;        // free on commit
    end
  end
 
  assign dbid_valid  = do_alloc;
  assign dbid        = free_id;
  assign wr_complete = do_write;
endmodule

The same behavior in Verilog-2001:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative SN-F write handshake with DBID (Verilog-2001).
module snf_write_handshake #(
  parameter NBUF = 4
)(
  input        clk, rst_n,
  input        wr_req,
  output       dbid_valid,
  output [1:0] dbid,
  input        data_valid,
  input  [1:0] data_dbid,
  output       wr_complete
);
  reg  [NBUF-1:0] busy;
  reg  [1:0] free_id;
  reg        has_free;
  integer i;
 
  always @* begin
    free_id = 2'd0; has_free = 1'b0;
    for (i = NBUF-1; i >= 0; i = i - 1)
      if (!busy[i]) begin free_id = i[1:0]; has_free = 1'b1; end
  end
 
  wire do_alloc = wr_req & has_free;
  wire do_write = data_valid & busy[data_dbid];
 
  always @(posedge clk or negedge rst_n)
    if (!rst_n) busy <= {NBUF{1'b0}};
    else begin
      if (do_alloc) busy[free_id]   <= 1'b1;
      if (do_write) busy[data_dbid] <= 1'b0;
    end
 
  assign dbid_valid  = do_alloc;
  assign dbid        = free_id;
  assign wr_complete = do_write;
endmodule

And in VHDL:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- Representative SN-F write handshake with DBID (VHDL).
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
entity snf_write_handshake is
  generic ( NBUF : integer := 4 );
  port (
    clk, rst_n  : in  std_logic;
    wr_req      : in  std_logic;
    dbid_valid  : out std_logic;
    dbid        : out std_logic_vector(1 downto 0);
    data_valid  : in  std_logic;
    data_dbid   : in  std_logic_vector(1 downto 0);
    wr_complete : out std_logic
  );
end entity;
 
architecture rtl of snf_write_handshake is
  signal busy     : std_logic_vector(NBUF-1 downto 0) := (others => '0');
  signal free_id  : integer range 0 to NBUF-1;
  signal has_free : std_logic;
  signal do_alloc, do_write : std_logic;
begin
  process(busy)
  begin
    free_id <= 0; has_free <= '0';
    for i in NBUF-1 downto 0 loop
      if busy(i) = '0' then free_id <= i; has_free <= '1'; end if;
    end loop;
  end process;
 
  do_alloc <= wr_req and has_free;
  do_write <= data_valid and busy(to_integer(unsigned(data_dbid)));  -- only allocated DBID
 
  process(clk, rst_n)
  begin
    if rst_n = '0' then
      busy <= (others => '0');
    elsif rising_edge(clk) then
      if do_alloc = '1' then busy(free_id) <= '1'; end if;
      if do_write = '1' then busy(to_integer(unsigned(data_dbid))) <= '0'; end if;
    end if;
  end process;
 
  dbid_valid  <= do_alloc;
  dbid        <= std_logic_vector(to_unsigned(free_id, 2));
  wr_complete <= do_write;
end architecture;

All three gate the write on busy[data_dbid] — data is committed only for a currently-allocated buffer. That single guard is the write handshake's safety; the DebugLab is its omission.

12. Verification View — data lands only in an allocated buffer

Two properties: a write commits only for an allocated DBID, and a DBID is returned only when a buffer is free.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bind to snf_write_handshake.
// 1. A write commits only for a currently-allocated buffer.
property p_write_only_if_allocated;
  @(posedge clk) disable iff (!rst_n) wr_complete |-> busy[data_dbid];
endproperty
assert property (p_write_only_if_allocated);
 
// 2. A DBID is returned only when a buffer is actually free to allocate.
property p_dbid_only_if_free;
  @(posedge clk) disable iff (!rst_n) dbid_valid |-> !busy[dbid];
endproperty
assert property (p_dbid_only_if_free);

The system point, beyond the two checks:

SN-F's correctness is storage correctness: the right data reaches the right memory location, once. The DBID is what pins a write's data to its reserved buffer, so the guard write only for an allocated DBID is what prevents one write's data from overwriting another's slot. SN-F does no coherency, but it must be impeccable about which data goes where — because it is the final resting place of every coherent line, and a crossed write here is silent memory corruption no cache can undo.

  • What it proves: writes land only in allocated buffers; DBIDs point at free buffers.
  • What it does not prove: the actual memory array's read-after-write behavior (a memory-model concern).
  • Bug signature: write data accepted for an unallocated / reused DBID → crossed writes (the DebugLab).

13. Testbench — allocate, write, and reject an unallocated DBID

Allocates a buffer, writes to it, and confirms data for an unallocated DBID does not commit.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module tb_snf_write_handshake;
  logic clk = 0, rst_n, wr_req, dbid_valid, data_valid, wr_complete;
  logic [1:0] dbid, data_dbid;
  int errors = 0;
 
  snf_write_handshake #(.NBUF(4)) dut (.*);
  always #5 clk = ~clk;
 
  initial begin
    rst_n = 0; wr_req = 0; data_valid = 0; data_dbid = 0; @(posedge clk); rst_n = 1;
 
    // Allocate a buffer.
    wr_req = 1; @(posedge clk); #1;
    if (!dbid_valid) begin errors++; $display("FAIL no DBID"); end
    else $display("PASS allocated DBID=%0d", dbid);
    wr_req = 0;
 
    // Send data for that DBID -> commits.
    data_valid = 1; data_dbid = 2'd0; @(posedge clk); #1;
    if (!wr_complete) begin errors++; $display("FAIL write to allocated DBID did not commit"); end
    else $display("PASS write to DBID 0 committed");
    data_valid = 0;
 
    // Send data for an unallocated DBID -> must NOT commit.
    data_valid = 1; data_dbid = 2'd3; @(posedge clk); #1;
    if (wr_complete) begin errors++; $display("FAIL committed unallocated DBID 3"); end
    else $display("PASS unallocated DBID 3 rejected");
    data_valid = 0;
 
    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 allocated DBID=0
PASS write to DBID 0 committed
PASS unallocated DBID 3 rejected
ALL TESTS PASSED

14. DebugLab — accepting write data without checking the DBID

1

Accepting write data without checking the DBID

WRITE DATA WITHOUT DBID CHECK -> CROSSED WRITES -> MEMORY CORRUPTION
Symptom

Rare, silent memory corruption: a coherent line occasionally reads back a value that belongs to a different write. It only shows under heavy write traffic with many buffers in flight, and no cache or directory error is logged — the data was wrong when it landed in DRAM.

Evidence

Write data committed for a DBID that was not allocated to it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
event                         DBID  busy[DBID]  note
data beat arrives             3        0        buffer 3 not allocated for THIS data
committed anyway (no check)   3        0        -> writes into whatever buffer 3 is now

busy[3] is 0, meaning buffer 3 is not reserved for this data — yet the write committed, landing in the wrong place.

First Divergence

The commit condition: it used data_valid alone instead of data_valid && busy[data_dbid]. From that omission, any data beat commits regardless of whether its DBID names a live buffer, so a stale or misrouted beat corrupts an unrelated write's slot.

Root Cause

The DBID exists precisely to bind write data to its reserved buffer. Committing without checking busy[data_dbid] breaks that binding: a data beat for a DBID that is not currently allocated to it writes into whatever the buffer now holds — often a different, newer write. The two writes cross, and memory — the final store of coherent data — is silently corrupted.

Fix

Accept and commit write data only when busy[data_dbid] is set — the buffer is currently allocated. Then data lands only in the buffer reserved for it, and a stale or misrouted beat is dropped rather than corrupting an unrelated write. Verify it as an invariant (commit implies allocated) so the guard can never be dropped. SN-F does no coherency, but it must be exact about which data lands where.

15. Common Mistakes

  • Thinking SN-F does coherency. Assumption: "fully coherent" means it snoops. Bug: expecting a directory. Prevention: the F is the region; SN-F does no coherency.
  • Committing writes without a DBID check. Assumption: any data beat is valid. Bug: crossed writes, corruption (the DebugLab). Prevention: commit only for an allocated DBID.
  • Freeing a buffer before the write lands. Assumption: DBID returned means done. Bug: reuse aliasing. Prevention: free only after commit.
  • Expecting SN-F to talk to requesters. Assumption: memory answers RNs. Bug: bypassing the HN. Prevention: SN-F answers only the Home Node (Chapter 4.4).
  • Confusing SN-F with SN-I by capability. Assumption: one is coherent. Bug: mis-modeling. Prevention: neither does coherency; they differ by region.
  • Ignoring writebacks. Assumption: memory only serves reads. Bug: dirty data lost. Prevention: writebacks route dirty evictions to SN-F through the HN.

16. Engineering Checklist

  • Use SN-F for the coherent memory region (DRAM behind HN-F).
  • Expect no coherency in SN-F — no directory, no snoops.
  • Handle the downstream set: ReadNoSnp, WriteNoSnp, WriteBack, WriteClean.
  • Run the write handshake: allocate buffer, return DBID, accept tagged data, Comp.
  • Commit write data only for an allocated DBID; free the buffer only after commit.
  • Answer only the Home Node; complete every request.

17. Key Takeaways

  • SN-F is the coherent-region memory controller behind an HN-F — the ultimate store for all coherent data.
  • The "F" names the region (coherent memory), not any coherency behavior — SN-F does no coherency (no directory, no snoops).
  • It completes the downstream set HN-F forwards: ReadNoSnp, WriteNoSnp, WriteBack, WriteClean.
  • CHI writes use the DBID handshake: allocate a buffer, return the DBID, accept data tagged with it, then Comp.
  • The write safety rule: commit data only for an allocated DBID — else writes cross and memory is silently corrupted.
  • It answers only the Home Node and does pure storage; the model here is representative.

18. Quick Revision

SN-F (coherent-region slave node). The memory controller behind an HN-F, backing the coherent memory region. The "F" is the region, not the behavior — SN-F does no coherency (no directory, no snoops), like every subordinate. It completes the downstream transactions HN-F forwards after coherency is resolved: ReadNoSnp (fetch on a miss), WriteNoSnp / WriteBack (write, including dirty evictions). CHI writes use the DBID handshake — SN-F allocates a data buffer, returns its DBID, accepts data tagged with it, writes memory, and returns Comp. Safety rule: commit write data only for an allocated DBID, and free a buffer only after commit — else writes cross and coherent memory is silently corrupted. It answers only the Home Node. Representative model; 5.6 covers SN-I.

Coming Next

Chapter 5.6 — SN-I (IO Slave Node). The last node in the taxonomy. Chapter 5.6 details SN-I, the subordinate for I/O and peripheral space — the endpoint behind an HN-I. Like SN-F it is a coherency-agnostic completer, but it backs non-coherent device memory and registers rather than coherent DRAM. We will see the final corner of the matrix — request and home nodes each split coherent/I/O, and now the subordinates too — completing the RN / HN / SN taxonomy that Module 5 set out to detail.