Skip to content

AMBA CHI · Module 5 · CHI System Components

RN-F (Fully Coherent Request Node)

RN-F is the fully coherent Request Node — a CPU cluster with a coherent cache that participates completely, and the archetype every other node is defined against. Fully coherent means three things at once: it holds cache lines in the full set of CHI states and keeps them accurate; it issues the whole repertoire of coherent requests, choosing the right one for each need; and it answers every snoop the Home Node sends, transitioning its line and supplying data when it holds a dirty copy. This chapter details that full participation — states, requests, and snoop responses — the behavior that makes RN-F the reference point for the taxonomy. Representative model, not the specification.

Intermediate16 min readAMBA CHIRN-FCache StatesSnoopsCoherency Master

Module 5 · Chapter 5.1 · CHI System Components — module opener

Project thread — Module 4 built the architecture; Module 5 details each node kind. This opener takes RN-F, the fully coherent requester and archetype. 5.2 takes the IO-coherent requester.

1. Learning Outcomes

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

  • Define RN-F as the fully coherent Request Node and what "fully coherent" entails.
  • List the CHI cache states an RN-F line can hold — I, UC, UD, SC, SD (and the empty/partial variants).
  • Match a need (share, own, upgrade, evict) to the coherent request an RN-F issues.
  • Describe how RN-F answers each snoop type — SnpShared downgrades, SnpUnique invalidates.
  • Explain why RN-F must supply data on a snoop when it holds a dirty line.
  • Implement a representative RN-F snoop-response machine in SystemVerilog, Verilog-2001, and VHDL.

2. Why Should I Learn This?

RN-F is the reference node. Every other CHI node — RN-D, HN-F, SN-F, and the rest — is understood by how its participation differs from RN-F's complete one. Learn RN-F thoroughly and the taxonomy becomes a set of subtractions from a known whole, rather than six unrelated definitions.

It is also the node most engineers design against: a CPU cluster with a coherent cache is the RN-F, and its correctness — right state, right request, right snoop response — is where multi-core bugs live. Chapter 4.2 introduced the RN's role; this chapter gives RN-F the depth its central position deserves.

3. Key Terms

4. Previous Chapter Connection

Chapter 4.2 introduced the Request Node's dual role — initiate transactions, answer snoops — and named the RN-F/RN-D/RN-I variants. It stayed at the level of role and outstanding-transaction tracking.

This chapter opens RN-F itself. Where 4.2 said "an RN-F caches data and is snooped," 5.1 says which states its lines hold, which requests it issues for which need, and exactly how it responds to each snoop type from its cache state. It is the depth behind the role — and, as the archetype, the baseline Module 5 measures every other node against.

5. Core Concept — full participation, three ways

"Fully coherent" is not a single feature; it is complete participation on three fronts.

  • Full state. An RN-F line lives in one of the CHI cache states — I, UC, UD, SC, SD (with UCE UniqueCleanEmpty and UDP UniqueDirtyPartial as refinements). The RN-F maintains these accurately: they record whether its copy is the only one (Unique) or shared, and whether it is modified (Dirty) or clean.
  • Full request repertoire. RN-F issues the whole set of coherent transactions and picks the right one for the need: ReadShared (read to share), ReadUnique (read to write), MakeUnique / CleanUnique (upgrade a shared line to writable), WriteBack / WriteClean (return dirty data), Evict (drop a clean line). The request encodes intent.
  • Full snoop response. RN-F answers every snoop the Home Node sends. SnpShared asks it to keep only a shared copy (downgrade); SnpUnique asks it to give up the line entirely (invalidate). On either, if RN-F holds the line dirty, it must supply the data so the current value is not lost.

The synthesis:

RN-F is a coherency master with a complete cache-state machine: it holds lines in the full state set, issues the right request for each need, and responds correctly to every snoop — supplying dirty data whenever a snoop removes or downgrades it. That completeness is what "fully coherent" means, and it makes RN-F the node every other kind is defined by omission from.

6. Engineering Mental Model — a full member of the co-op

Picture a shared-tool co-op where members borrow and lend a tool (a cache line).

  • A full member (RN-F) can hold a tool in any status — the only copy, a shared copy, a marked-up (dirty) copy — and keeps its status card accurate.
  • It requests tools with the right intent: "lend me a copy to use" (ReadShared), "give me the only copy so I can modify it" (ReadUnique), "upgrade my copy to exclusive" (MakeUnique).
  • It answers the desk whenever asked: "someone else wants a copy — downgrade yours to shared" (SnpShared), or "someone needs exclusive — return yours" (SnpUnique). If its copy is marked-up, it hands over the marked-up version so the changes are not lost.

A full member does all three — hold, request, answer — completely. Later chapters describe members who do less: consume but never lend a cache (an IO requester), or manage the desk rather than borrow (a Home Node). RN-F is the complete member they are compared to.

7. Engineering Diagram — inside an RN-F

Inside an RN-F. A CPU cluster with cores and a coherent cache connects to an RN-F coherency engine. The engine maintains cache-line states, issues coherent REQ packets, tracks transactions by TxnID, and answers SNP packets from the cache state. It connects to the interconnect, and through it the Home Node, on the REQ, RSP, SNP, and DAT channels.CPU clustercores + coherent cacheRN-F coherency enginecache states · issues REQ ·answers SNP · TxnIDInterconnect → HNREQ / RSP / SNP / DAT12
Figure 1 — an RN-F. A CPU cluster with a coherent cache drives an RN-F coherency engine that maintains the cache-line states, issues coherent requests to the Home Node, tracks them by TxnID, and answers incoming snoops from the cache state. The engine is the node's interface to the interconnect on all four channels.

The coherency engine is the whole of RN-F's "fully coherent" behavior: state maintenance, request issue, and snoop response, all in one block between the cache and the fabric.

8. The RN-F Cache States

Each cached line is in one CHI state, along two axes: Unique vs Shared (is this the only copy?) and Clean vs Dirty (is memory up to date?).

StateUnique/SharedClean/DirtyMeaning
IInvalid — no valid copy
UCUniqueCleanonly copy; matches memory; may write (→ UD) without a bus request
UDUniqueDirtyonly copy; modified; owes a writeback
SCSharedCleanothers may hold it; must upgrade before writing
SDSharedDirtyshared, but this cache owns the dirty data (writeback responsibility)

Two facts to carry: Unique means writable without asking further (UC can silently become UD on a store), and Dirty (UD/SD) means this cache holds the only current value — so a snoop that removes or downgrades a dirty line must move the data. CHI also defines UCE (UniqueCleanEmpty) and UDP (UniqueDirtyPartial) for partial-line cases; the five above are the core.

9. The Request Repertoire

RN-F chooses a request by what it needs. A sample of the core set:

NeedRequestResult
read to shareReadSharedline in SC (or UC if no other holder)
read to modifyReadUniqueline in UD; others invalidated
upgrade a shared line to writeMakeUnique / CleanUniqueSC → UC/UD; others invalidated
return modified data on evictionWriteBackUD/SD data to memory; line → I
drop a clean lineEvictUC/SC → I (notifies the Home Node)

The intent is encoded in the opcode (Chapter 4.7), and the Home Node acts on it — snooping holders, updating the directory, returning data. Two facts to carry: read intent matters (ReadShared shares; ReadUnique takes ownership and invalidates others), and writing a shared line always requires gaining Unique first — a shared cache cannot silently write.

10. Transaction Walkthrough — read for ownership, then get invalidated

CPU0's RN-F wants to write a line it does not hold; later, another core takes it.

  1. Read for ownership. CPU0 needs to write, so RN-F issues ReadUnique for line A.
  2. Home resolves. The Home Node snoops any holders (invalidating them), fetches data, and returns it; RN-F installs line A in UD and writes — it now holds the only, modified copy.
  3. Another core wants it exclusive. Later, CPU1 issues ReadUnique for line A. The Home Node sends CPU0's RN-F a SnpUnique.
  4. RN-F responds. RN-F holds A in UD (dirty). On SnpUnique it must invalidate (line → I) and supply the dirty data so the current value reaches CPU1.
  5. State settles. CPU0's line is now I; CPU1 receives the data and holds A in UD. Single-writer is preserved throughout — only one cache held it writable at any moment.

Notice both halves of RN-F's role: it initiated (ReadUnique) and later responded (SnpUnique → I, supply data). Getting step 4 right — invalidate and supply — is the crux, and the DebugLab.

11. RTL / Hardware View — an RN-F snoop-response machine

The heart of RN-F's coherency participation is how it answers a snoop from its cache state. Here is that logic for the two core snoop types. Representative and combinational — CHI core states, SnpShared and SnpUnique.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative RN-F snoop-response FSM (educational, CHI core states).
// SnpShared: keep a shared copy (downgrade Unique -> Shared), supply data if dirty.
// SnpUnique: give up the line entirely (-> Invalid), supply data if dirty.
// A dirty line (UD or SD) MUST supply data on any snoop that removes/downgrades it.
module rnf_snoop_fsm (
  input  logic [2:0] state,        // current line state
  input  logic       snp_unique,   // 1 = SnpUnique (invalidate), 0 = SnpShared (share)
  output logic       data_xfer,    // supply data on DAT (dirty line)
  output logic [2:0] next_state
);
  localparam logic [2:0] I=3'd0, SC=3'd1, SD=3'd2, UC=3'd3, UD=3'd4;
  always_comb begin
    data_xfer = 1'b0; next_state = state;
    if (snp_unique) begin
      // SnpUnique: requester wants exclusive -> invalidate; supply if dirty
      data_xfer  = (state == UD) || (state == SD);
      next_state = I;
    end else begin
      // SnpShared: requester wants a shared copy -> downgrade to Shared
      unique case (state)
        UC:      next_state = SC;                              // clean: just downgrade
        UD:      begin data_xfer = 1'b1; next_state = SD; end  // dirty: supply, become SD
        SD:      begin data_xfer = 1'b1; next_state = SD; end  // owner: supply, stay SD
        SC:      next_state = SC;                              // already shared
        default: next_state = I;                              // I: nothing to do
      endcase
    end
  end
endmodule

The same behavior in Verilog-2001:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative RN-F snoop-response FSM (Verilog-2001).
module rnf_snoop_fsm (
  input      [2:0] state,
  input            snp_unique,
  output reg       data_xfer,
  output reg [2:0] next_state
);
  localparam I=3'd0, SC=3'd1, SD=3'd2, UC=3'd3, UD=3'd4;
  always @* begin
    data_xfer = 1'b0; next_state = state;
    if (snp_unique) begin
      data_xfer  = (state == UD) || (state == SD);
      next_state = I;
    end else begin
      case (state)
        UC:      next_state = SC;
        UD:      begin data_xfer = 1'b1; next_state = SD; end
        SD:      begin data_xfer = 1'b1; next_state = SD; end
        SC:      next_state = SC;
        default: next_state = I;
      endcase
    end
  end
endmodule

And in VHDL:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- Representative RN-F snoop-response FSM (VHDL).
library ieee;
use ieee.std_logic_1164.all;
 
entity rnf_snoop_fsm is
  port (
    state      : in  std_logic_vector(2 downto 0);
    snp_unique : in  std_logic;
    data_xfer  : out std_logic;
    next_state : out std_logic_vector(2 downto 0)
  );
end entity;
 
architecture rtl of rnf_snoop_fsm is
  constant I  : std_logic_vector(2 downto 0) := "000";
  constant SC : std_logic_vector(2 downto 0) := "001";
  constant SD : std_logic_vector(2 downto 0) := "010";
  constant UC : std_logic_vector(2 downto 0) := "011";
  constant UD : std_logic_vector(2 downto 0) := "100";
begin
  process(state, snp_unique)
  begin
    data_xfer <= '0'; next_state <= state;
    if snp_unique = '1' then
      if state = UD or state = SD then data_xfer <= '1'; end if;
      next_state <= I;
    else
      case state is
        when UC => next_state <= SC;
        when UD => data_xfer <= '1'; next_state <= SD;
        when SD => data_xfer <= '1'; next_state <= SD;
        when SC => next_state <= SC;
        when others => next_state <= I;
      end case;
    end if;
  end process;
end architecture;

All three encode RN-F's full snoop participation: SnpUnique always invalidates, SnpShared downgrades to Shared, and a dirty line always supplies data. The single most important line is next_state = I under SnpUnique — the DebugLab is what happens when it is wrong.

12. Verification View — SnpUnique invalidates, dirty always supplies

Two properties: a SnpUnique always ends in Invalid, and any dirty line supplies data on a snoop.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bind to rnf_snoop_fsm.
localparam logic [2:0] I=3'd0, SC=3'd1, SD=3'd2, UC=3'd3, UD=3'd4;
 
// 1. SnpUnique always invalidates — the requester needs the ONLY copy.
always_comb if (snp_unique) assert (next_state == I);
 
// 2. A dirty line (UD or SD) must supply data on any snoop, or the current
//    value would be lost.
always_comb if (state == UD || state == SD) assert (data_xfer);

The system point, beyond the two checks:

RN-F's completeness is a set of small invariants that together preserve coherency. SnpUnique → Invalid enforces single-writer: after another cache gains the line Unique, no other cache may keep a copy. Dirty → supply data enforces no-lost-update: the only current value must survive every state change. Miss either and coherency breaks — a stale reader after an invalidation, or lost data after a downgrade. Because RN-F is the archetype, these are exactly the invariants every coherent participant must uphold.

  • What it proves: SnpUnique invalidates; dirty lines supply data.
  • What it does not prove: the request-side state machine (issue paths) — a separate concern.
  • Bug signature: a cache that stays valid after SnpUnique (the DebugLab) or a dropped dirty line on a downgrade.

13. Testbench — snoop each state, both types

Drives every state through SnpShared and SnpUnique and checks the response and next state.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module tb_rnf_snoop_fsm;
  logic [2:0] state, next_state;
  logic snp_unique, data_xfer;
  int errors = 0;
  localparam logic [2:0] I=3'd0, SC=3'd1, SD=3'd2, UC=3'd3, UD=3'd4;
 
  rnf_snoop_fsm dut (.*);
 
  task automatic check(input logic [2:0] s, input logic u, input logic exp_d,
                       input logic [2:0] exp_ns, input string tag);
    state = s; snp_unique = u; #1;
    if (data_xfer !== exp_d || next_state !== exp_ns) begin
      errors++; $display("FAIL [%s] d=%b next=%0d (exp d=%b ns=%0d)", tag, data_xfer, next_state, exp_d, exp_ns);
    end else $display("PASS [%s] d=%b next=%0d", tag, data_xfer, next_state);
  endtask
 
  initial begin
    // SnpShared (u=0): downgrade
    check(UC, 1'b0, 1'b0, SC, "SnpShared UC->SC");
    check(UD, 1'b0, 1'b1, SD, "SnpShared UD->SD (supply)");
    check(SC, 1'b0, 1'b0, SC, "SnpShared SC->SC");
    // SnpUnique (u=1): invalidate
    check(UC, 1'b1, 1'b0, I, "SnpUnique UC->I");
    check(UD, 1'b1, 1'b1, I, "SnpUnique UD->I (supply)");
    check(SD, 1'b1, 1'b1, I, "SnpUnique SD->I (supply)");
 
    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 [SnpShared UC->SC] d=0 next=1
PASS [SnpShared UD->SD (supply)] d=1 next=2
PASS [SnpShared SC->SC] d=0 next=1
PASS [SnpUnique UC->I] d=0 next=0
PASS [SnpUnique UD->I (supply)] d=1 next=0
PASS [SnpUnique SD->I (supply)] d=1 next=0

14. DebugLab — SnpUnique that downgrades instead of invalidating

1

SnpUnique that downgrades instead of invalidating

SnpUnique DOWNGRADES (not INVALIDATE) -> STALE SHARED COPY -> SWMR VIOLATION
Symptom

A core reads stale data for a line another core recently wrote — but only for lines that passed through a specific RN-F while it held them. Clean, un-snooped lines are fine; the corruption follows a particular cache's snoop handling.

Evidence

The RN-F's response to SnpUnique leaving the line valid:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
state  snp_unique  data_xfer  next_state  note
 UC        1           0          SC        WRONG: downgraded, should be I

After a SnpUnique, the line is left SC (still a valid shared copy) instead of I. The requester now holds it Unique, yet this cache also holds a copy.

First Divergence

The SnpUnique handling: it reused the SnpShared downgrade path (Unique → Shared) instead of forcing Invalid. From that response, two caches hold the line while one believes it is the sole (Unique) owner.

Root Cause

SnpUnique exists precisely to give the requester the only copy so it may write. Downgrading rather than invalidating leaves a second copy behind; when the new Unique owner writes, this stale copy violates Single-Writer/Multiple-Reader. Treating SnpUnique like SnpShared confuses "let others share" with "clear the way for a writer" — opposite intents.

Fix

SnpUnique must always transition the line to Invalid (supplying data first if dirty). The rule is intent-driven: a Unique snoop clears every other copy; a Shared snoop merely downgrades. Encode the snoop type's intent directly — Unique invalidates, Shared downgrades — and verify SnpUnique-ends-in-Invalid as an invariant so the two paths can never be conflated.

15. Common Mistakes

  • Treating all snoops alike. Assumption: a snoop is a snoop. Bug: SnpUnique left valid (the DebugLab). Prevention: SnpUnique invalidates; SnpShared downgrades — opposite intents.
  • Dropping dirty data on a snoop. Assumption: state change is enough. Bug: lost update. Prevention: a dirty line (UD/SD) must supply data on any snoop that removes or downgrades it.
  • Writing a shared line silently. Assumption: any cached line is writable. Bug: two writers. Prevention: gain Unique (MakeUnique/CleanUnique) before writing a shared line.
  • Confusing Unique with Dirty. Assumption: Unique means modified. Bug: mishandling UC vs UD. Prevention: Unique = only copy; Dirty = modified — independent axes.
  • Picking the wrong request. Assumption: one read fits all. Bug: sharing when you meant to own. Prevention: ReadShared to share, ReadUnique to write.
  • Assuming other nodes match RN-F. Assumption: every node participates fully. Bug: expecting snoops of an IO requester. Prevention: RN-F is the complete archetype; others do less.

16. Engineering Checklist

  • Track each RN-F line in a CHI state — I / UC / UD / SC / SD — and keep it accurate.
  • Choose the request by intent: ReadShared to share, ReadUnique to write.
  • Gain Unique before writing a shared line (MakeUnique / CleanUnique).
  • On SnpUnique invalidate to I; on SnpShared downgrade to Shared.
  • Always supply data on a snoop of a dirty (UD/SD) line.
  • WriteBack dirty data on eviction; Evict-notify clean lines.

17. Key Takeaways

  • RN-F is the fully coherent Request Node — a CPU cluster with a coherent cache — and the archetype every other node is defined against.
  • Full participation is threefold: full cache states (I/UC/UD/SC/SD), full request repertoire, and full snoop response.
  • Unique = the only copy (writable without asking); Dirty = modified (owes the current value) — independent axes.
  • SnpUnique invalidates (clears the way for a writer); SnpShared downgrades (lets others share) — opposite intents.
  • A dirty line always supplies data on a snoop that removes or downgrades it — no-lost-update.
  • These invariants are what every coherent participant must uphold; the model here is representative.

18. Quick Revision

RN-F (fully coherent Request Node). A CPU cluster with a coherent cache that participates completely — the archetype. Three fronts: full cache states (I, UC UniqueClean, UD UniqueDirty, SC SharedClean, SD SharedDirty, plus UCE/UDP); full request repertoire (ReadShared to share, ReadUnique to write, MakeUnique/CleanUnique to upgrade, WriteBack/Evict to give up); full snoop responseSnpUnique invalidates to I, SnpShared downgrades Unique→Shared, and a dirty line (UD/SD) always supplies data. Axes: Unique = only copy (writable without asking); Dirty = holds the current value. Writing a shared line always needs Unique first. Getting SnpUnique to invalidate (not merely downgrade) is the crux of single-writer. Every other CHI node is defined by how it participates less than RN-F. Representative model.

Coming Next

Chapter 5.2 — RN-D (IO-Coherent Request Node). RN-F participates completely; the next node participates partly. Chapter 5.2 details the IO-coherent request node: a device that consumes and produces coherent data — issuing coherent reads and writes so it sees the caches' current values — but without hosting a coherent cache of its own, so it is never snooped. We will see exactly which of RN-F's behaviors it keeps and which it drops, the first subtraction from the archetype this chapter established.