Skip to content

AMBA CHI · Module 4 · CHI Architecture Overview

The Request Node (RN)

The Request Node is where every CHI transaction begins. It is the coherency master for its own requests: it allocates a transaction ID, issues a REQ packet to the Home Node, and tracks that transaction until completion — and, if it caches data, it must also answer the snoops the Home Node sends back. Three variants cover the field: RN-F, a fully coherent cached requester; RN-D, the same plus distributed-virtual-memory operations; and RN-I, an I/O-coherent requester with no cache, which issues transactions but is never snooped. This chapter details the RN's role, its variants, the state it holds, and how it both initiates and participates in coherency. The model here is representative, not the specification.

Foundation15 min readAMBA CHIRequest NodeRN-FTxnIDCoherency Master

Module 4 · Chapter 4.2 · CHI Architecture Overview

Project thread — 4.1 introduced the three node roles. This chapter details the first: the Request Node, the initiator. 4.3 takes up the Home Node.

1. Learning Outcomes

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

  • Describe the RN's dual role: initiator of transactions and participant in coherency.
  • Distinguish the RN variants — RN-F (cached, snooped), RN-D (RN-F + DVM), RN-I (I/O, not snooped).
  • List the state an RN holds: cache line states and outstanding-transaction tracking.
  • Trace how an RN issues a REQ, tracks it by TxnID, and completes it (including CompAck).
  • Explain why an RN-F must answer snoops while an RN-I is never snooped.
  • Implement a representative outstanding-transaction (TxnID) tracker in SystemVerilog, Verilog-2001, and VHDL.

2. Why Should I Learn This?

Every CHI transaction starts at a Request Node, so the RN is where you begin reading any flow. Knowing its two jobs — issuing requests and answering snoops — and the state it must hold makes later transaction chapters legible: you will always know who allocated the TxnID, who owns the cache line, and who must respond to a snoop.

The RN variants also encode a real design decision. Whether a device is RN-F, RN-D, or RN-I determines whether it is snooped, whether it needs a cache, and whether it handles DVM — choices that shape the whole subsystem. Getting the variants straight now prevents a class of integration mistakes later.

3. Key Terms

4. Previous Chapter Connection

Chapter 4.1 established the mental model: nodes exchange packets over the REQ/RSP/SNP/DAT channels, and a transaction is a choreography of those packets. It named the RN as the initiator without going inside it.

This chapter opens the RN. Everything 4.1 said about REQ packets, TxnIDs, and snoops now gets an owner: the RN is who allocates a TxnID and sends the REQ, and — for cached variants — who receives a SNP and must respond. The abstractions become a concrete agent with a role, variants, and state.

5. Core Concept — initiator and coherency master

The RN has two jobs, and both matter.

  • Initiator. The RN starts every transaction. It allocates a TxnID, builds a REQ packet (opcode, address, TgtID = the Home Node), and sends it. It then tracks that transaction — by TxnID — through its responses and data until completion, often sending a CompAck to close it. The RN is the coherency master for its own requests: it drives them, not the fabric.
  • Coherency participant. If the RN caches data (RN-F / RN-D), it also receives snoops. When a SNP packet arrives, the RN looks up the line's state, responds on RSP (the snoop response), and supplies data on DAT if it holds a copy the requester needs. An RN-I has no cache and is never snooped — it only initiates.

The state an RN holds follows from those jobs:

  • Cache line states (RN-F / RN-D): each cached line is in one of CHI's states — I, UC (UniqueClean), UD (UniqueDirty), SC (SharedClean), SD (SharedDirty) — the coherency bookkeeping the RN maintains and updates on responses and snoops.
  • Outstanding-transaction tracking: for each in-flight transaction, the RN records its TxnID and state, so it can correlate returning RSP/DAT packets and know when the transaction is complete.

The synthesis:

An RN is a coherency master with memory of what it started and what it holds. It issues requests it tracks by TxnID, and — if cached — answers snoops from its cache state. Read any CHI flow by first asking: which RN initiated this, what TxnID is it, and does that RN hold the line.

6. Engineering Mental Model — a caller with a notebook

Picture the RN as a caller running several conversations at once.

  • Every call it starts gets a ticket number (TxnID) written in a notebook, so when a reply comes back — possibly out of order — it knows which conversation it belongs to. It keeps the ticket open until the conversation is fully done (including a final "got it" — CompAck).
  • The caller also answers its own phone: the Home Node may call to ask "do you have this line, and in what state?" (a snoop). A cached caller (RN-F) must look at what it holds and answer honestly, handing over data if asked. An I/O caller (RN-I) has no such records, so it is never called about them.
  • The notebook (outstanding transactions) and the filing cabinet (cache line states) are the RN's whole memory.

Read a transaction by opening the initiating RN's notebook: which ticket, which line, what state.

7. Engineering Diagram — the RN variants

CHI Request Node variants. RN-F is a fully coherent cached requester that is snooped; RN-D is an RN-F that also handles DVM operations; RN-I is an I/O-coherent requester with no cache that is never snooped. All three connect to a single Home Node that serves every requester. The cached variants both initiate and answer snoops; RN-I only initiates.RN-Fcached · snooped · fullcoherencyRN-DRN-F + DVM (TLBmaintenance)RN-II/O coherent · no cache ·not snoopedHome Nodepoint of coherence · servesall RNs12
Figure 1 — the three Request Node variants and their relationship to a Home Node. RN-F is a fully coherent cached requester and is snooped; RN-D adds DVM (TLB-maintenance) operations; RN-I is an I/O-coherent requester with no cache and is never snooped. All three initiate transactions to the Home Node; only the cached variants also answer snoops.

The one line to remember: cached RNs are snooped; RN-I is not. Whether a requester holds coherent state decides whether the Home Node ever needs to ask it about a line.

8. RN Variants

The three variants differ along a few axes — cache, snoopability, and extra duties.

VariantCache?Snooped?Extra roleTypical use
RN-FyesyesCPU cluster (fully coherent)
RN-DyesyesDVM (TLB / MMU maintenance)CPU cluster with virtual-memory coherence
RN-InonoI/O bridge, accelerator (I/O-coherent)

Two facts to carry: only cached RNs (RN-F, RN-D) are snooped — an RN-I has no coherent state for the Home Node to query — and RN-D is an RN-F plus DVM, so DVM support is the only thing separating them. Choosing the variant is choosing whether the device caches, is snooped, and participates in virtual-memory coherence.

9. What the RN Holds

An RN's state is exactly what its two jobs require.

  • Cache line states (cached RNs). Each line the RN caches is in one CHI state — I / UC / UD / SC / SD — updated when responses arrive and when snoops downgrade or invalidate it. This is the RN's coherency memory, and it is what a snoop reads.
  • Outstanding transactions. Each in-flight transaction has a TxnID and a tracked state (issued, awaiting data, awaiting completion). The RN needs this to correlate out-of-order RSP/DAT packets and to know when to send CompAck and retire the TxnID.

The coupling matters: a snoop can arrive for a line while a transaction on that line is still outstanding. The RN must reconcile its outstanding-transaction state with the incoming snoop — which is exactly the kind of race the protocol's ordering rules (and CompAck) exist to resolve. For now, hold the two pieces of state as the RN's essential memory.

10. Transaction Walkthrough — issue a read, get snooped mid-flight

CPU0's RN-F reads a shared line, and a snoop for a different line arrives while it waits.

  1. Allocate. RN-F picks a free TxnID (say 5) and records the transaction as outstanding.
  2. Issue REQ. It sends a REQ packet — ReadShared, TxnID 5, TgtID = Home Node — and waits.
  3. Snoop arrives. Before the data returns, a SNP packet arrives for a line the RN holds UD (unique dirty). This is the RN's coherency-participant hat: it looks up the state, responds on RSP, and supplies the dirty data on DAT to the Home Node, downgrading its own copy.
  4. Data returns. The Home Node's DAT (CompData) for TxnID 5 arrives; the RN matches it by TxnID, installs the line, and updates its cache state.
  5. CompAck. The RN sends CompAck to confirm completion, then retires TxnID 5 — freeing it for reuse.

Notice the two roles interleaving: the RN was initiating one transaction and answering a snoop for another at the same time. Tracking outstanding transactions by TxnID is what keeps them from getting confused.

11. RTL / Hardware View — an outstanding-transaction tracker

The RN's initiator role needs a way to allocate and retire TxnIDs. Here is that tracker: a pool that hands out a free TxnID per request and frees it on completion. Representative and simplified — allocation and freeing only, no per-transaction state machine.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative RN outstanding-transaction (TxnID) tracker (educational).
// Allocates a free TxnID per new request; frees it when the transaction
// completes. Tracks up to NUM_TXN outstanding transactions at once.
module rn_txn_tracker #(
  parameter int NUM_TXN = 8                    // outstanding capacity (power of two)
)(
  input  logic       clk,
  input  logic       rst_n,
  input  logic       alloc_req,                // RN wants to start a transaction
  output logic       alloc_valid,              // a TxnID was allocated this cycle
  output logic [2:0] alloc_id,                 // the allocated TxnID
  input  logic       free_req,                 // a transaction completed
  input  logic [2:0] free_id,                  // the completing transaction's TxnID
  output logic       full                       // no free TxnIDs
);
  logic [NUM_TXN-1:0] busy;                     // 1 = TxnID currently outstanding
  logic [2:0] free_slot;
  logic       has_free;
 
  // Lowest free TxnID (priority encode ~busy).
  always_comb begin
    free_slot = 3'd0; has_free = 1'b0;
    for (int i = NUM_TXN-1; i >= 0; i--)
      if (!busy[i]) begin free_slot = i[2:0]; has_free = 1'b1; end
  end
 
  wire do_alloc = alloc_req && has_free;
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) busy <= '0;
    else begin
      if (do_alloc) busy[free_slot] <= 1'b1;    // mark allocated
      if (free_req) busy[free_id]   <= 1'b0;     // free on completion
    end
  end
 
  assign alloc_valid = do_alloc;
  assign alloc_id    = free_slot;
  assign full        = &busy;
endmodule

The same behavior in Verilog-2001:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative RN outstanding-transaction (TxnID) tracker (Verilog-2001).
module rn_txn_tracker #(
  parameter NUM_TXN = 8
)(
  input        clk, rst_n,
  input        alloc_req,
  output       alloc_valid,
  output [2:0] alloc_id,
  input        free_req,
  input  [2:0] free_id,
  output       full
);
  reg  [NUM_TXN-1:0] busy;
  reg  [2:0] free_slot;
  reg        has_free;
  integer i;
 
  always @* begin
    free_slot = 3'd0; has_free = 1'b0;
    for (i = NUM_TXN-1; i >= 0; i = i - 1)
      if (!busy[i]) begin free_slot = i[2:0]; has_free = 1'b1; end
  end
 
  wire do_alloc = alloc_req & has_free;
  always @(posedge clk or negedge rst_n)
    if (!rst_n) busy <= {NUM_TXN{1'b0}};
    else begin
      if (do_alloc) busy[free_slot] <= 1'b1;
      if (free_req) busy[free_id]   <= 1'b0;
    end
 
  assign alloc_valid = do_alloc;
  assign alloc_id    = free_slot;
  assign full        = &busy;
endmodule

And in VHDL:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- Representative RN outstanding-transaction (TxnID) tracker (VHDL).
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
entity rn_txn_tracker is
  generic ( NUM_TXN : integer := 8 );
  port (
    clk, rst_n  : in  std_logic;
    alloc_req   : in  std_logic;
    alloc_valid : out std_logic;
    alloc_id    : out std_logic_vector(2 downto 0);
    free_req    : in  std_logic;
    free_id     : in  std_logic_vector(2 downto 0);
    full        : out std_logic
  );
end entity;
 
architecture rtl of rn_txn_tracker is
  signal busy      : std_logic_vector(NUM_TXN-1 downto 0) := (others => '0');
  signal free_slot : integer range 0 to NUM_TXN-1;
  signal has_free  : std_logic;
begin
  process(busy)
  begin
    free_slot <= 0; has_free <= '0';
    for i in NUM_TXN-1 downto 0 loop
      if busy(i) = '0' then free_slot <= i; has_free <= '1'; end if;
    end loop;
  end process;
 
  process(clk, rst_n)
  begin
    if rst_n = '0' then
      busy <= (others => '0');
    elsif rising_edge(clk) then
      if alloc_req = '1' and has_free = '1' then busy(free_slot) <= '1'; end if;
      if free_req = '1' then busy(to_integer(unsigned(free_id))) <= '0'; end if;
    end if;
  end process;
 
  alloc_valid <= alloc_req and has_free;
  alloc_id    <= std_logic_vector(to_unsigned(free_slot, 3));
  full        <= '1' when busy = (busy'range => '1') else '0';
end architecture;

All three enforce the initiator's core rule: one TxnID per outstanding transaction, allocated from the free pool and returned only on completion. Get that accounting wrong and responses land on the wrong transaction — the DebugLab.

12. Verification View — every outstanding transaction has a unique TxnID

Two properties: the RN never allocates when full, and an allocated TxnID is genuinely free at allocation (no reuse of an outstanding ID).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bind to rn_txn_tracker.
// 1. Never signal a valid allocation when the pool is full.
property p_no_alloc_when_full;
  @(posedge clk) disable iff (!rst_n) full |-> !alloc_valid;
endproperty
assert property (p_no_alloc_when_full);
 
// 2. An allocated TxnID must be currently free — never hand out an ID that is
//    still outstanding (that would let two transactions share a TxnID).
property p_alloc_id_is_free;
  @(posedge clk) disable iff (!rst_n) alloc_valid |-> (busy[alloc_id] == 1'b0);
endproperty
assert property (p_alloc_id_is_free);

The system point, beyond the two checks:

The TxnID is how an RN tells its own transactions apart. Its correctness rests on one invariant: no two outstanding transactions share a TxnID. If they do, a returning DAT or RSP packet is ambiguous — the RN cannot know which transaction it completes — and coherent data can be delivered to the wrong request. The tracker guarantees uniqueness only if a TxnID is freed exactly when its transaction is truly complete: not before (it may still receive packets) and not never (the pool would leak).

  • What it proves: allocations are unique and bounded — no two live transactions share an ID.
  • What it does not prove: that the RN frees a TxnID at the right time — that is a protocol-completion property (the DebugLab).
  • Bug signature: a response with a TxnID that matches two transactions, or that matches none — a mis-correlated or dropped completion.

13. Testbench — allocate, fill, free, reuse

Allocates until full, frees one, and checks the freed ID becomes reusable.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module tb_rn_txn_tracker;
  localparam int NUM_TXN = 4;
  logic clk = 0, rst_n;
  logic alloc_req, alloc_valid, free_req, full;
  logic [2:0] alloc_id, free_id;
  int errors = 0;
 
  rn_txn_tracker #(.NUM_TXN(NUM_TXN)) dut (.*);
  always #5 clk = ~clk;
 
  task automatic do_alloc(input string tag);
    alloc_req = 1; free_req = 0;
    @(posedge clk); #1;
    $display("[%s] alloc_valid=%b id=%0d full=%b", tag, alloc_valid, alloc_id, full);
    alloc_req = 0;
  endtask
 
  task automatic do_free(input logic [2:0] id, input string tag);
    free_req = 1; free_id = id; alloc_req = 0;
    @(posedge clk); #1; $display("[%s] freed id=%0d full=%b", tag, id, full);
    free_req = 0;
  endtask
 
  initial begin
    rst_n = 0; @(posedge clk); rst_n = 1; alloc_req = 0; free_req = 0;
    do_alloc("a0"); do_alloc("a1"); do_alloc("a2"); do_alloc("a3"); // fill 4
    if (!full) begin errors++; $display("FAIL expected full"); end
    else $display("PASS pool full after 4 allocs");
    do_free(3'd1, "free 1");
    if (full) begin errors++; $display("FAIL should not be full after free"); end
    else $display("PASS not full after free");
    do_alloc("reuse");   // should reallocate the freed id (1)
 
    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
[a0] alloc_valid=1 id=0 full=0
[a1] alloc_valid=1 id=1 full=0
[a2] alloc_valid=1 id=2 full=0
[a3] alloc_valid=1 id=3 full=1
PASS pool full after 4 allocs
[free 1] freed id=1 full=0
PASS not full after free
[reuse] alloc_valid=1 id=1 full=0
ALL TESTS PASSED

14. DebugLab — a TxnID reused while still outstanding

1

A TxnID reused while still outstanding

EARLY TxnID FREE -> REUSE -> RESPONSE MIS-CORRELATION
Symptom

Rare data corruption under high transaction rates: a request occasionally completes with another transaction's data. It only happens when the outstanding pool is nearly full and TxnIDs are being reused quickly.

Evidence

A TxnID freed and reallocated with an old packet still in flight for it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
cyc  event                         TxnID  note
 10  data received for read A        5     RN frees TxnID 5 here (too early)
 11  new read B allocated            5     reuses TxnID 5
 13  late RSP for read A arrives     5     matched to read B -> WRONG

The late response for transaction A carries TxnID 5, but TxnID 5 now belongs to transaction B.

First Divergence

Cycle 10: the RN retires TxnID 5 on receiving data, before the transaction is truly finished (CompAck not yet sent, a trailing packet still possible). From that early free, the TxnID is eligible for reuse while transaction A is not fully closed.

Root Cause

A TxnID uniquely identifies a transaction only while every packet that can carry it is still expected or done. Freeing it at data-receipt — rather than at true completion — creates a window where two transactions can be associated with the same ID: the old one still trailing, the new one just started. Any late packet then aliases.

Fix

Retire a TxnID only when the transaction is genuinely complete — all expected RSP/DAT received and CompAck sent — never at first data. CHI's completion handshake (CompAck) exists precisely to mark that point; freeing on it guarantees no trailing packet can alias a reused ID. The rule is simple and absolute: a TxnID is free only when nothing in flight can still carry it.

15. Common Mistakes

  • Thinking the RN only initiates. Assumption: RNs just send requests. Bug: forgetting cached RNs must answer snoops. Prevention: an RN-F both initiates and participates in coherency.
  • Snooping an RN-I. Assumption: all RNs are snooped. Bug: expecting a coherent response from a cacheless node. Prevention: RN-I has no cache and is never snooped.
  • Confusing RN-D with RN-F. Assumption: they are interchangeable. Bug: missing DVM handling. Prevention: RN-D is RN-F plus DVM (TLB/MMU maintenance).
  • Freeing a TxnID too early. Assumption: data received means done. Bug: TxnID reuse and response aliasing (the DebugLab). Prevention: free only at true completion (CompAck sent).
  • Ignoring the snoop-during-outstanding race. Assumption: requests and snoops never overlap. Bug: mishandling a snoop for a line with an in-flight transaction. Prevention: reconcile outstanding-transaction state with incoming snoops per the ordering rules.
  • Skipping CompAck. Assumption: the transaction ends at data. Bug: broken ordering the HN relies on. Prevention: send CompAck where the transaction requires it.

16. Engineering Checklist

  • Identify the initiating RN and its variant (RN-F / RN-D / RN-I) for any transaction.
  • Confirm whether that RN is cached and snooped, or an I/O RN-I that is not.
  • Track each transaction by its TxnID from allocation to completion.
  • Maintain cache line states (I/UC/UD/SC/SD) for cached RNs; update on responses and snoops.
  • Send CompAck where required, and free the TxnID only at true completion.
  • Handle snoops that arrive mid-transaction per the ordering rules.

17. Key Takeaways

  • The Request Node is the coherency master: it initiates transactions and, if cached, participates in coherency by answering snoops.
  • Variants: RN-F (cached, snooped), RN-D (RN-F + DVM), RN-I (I/O-coherent, no cache, not snooped).
  • An RN holds two kinds of state: cache line states (I/UC/UD/SC/SD) and outstanding-transaction tracking by TxnID.
  • A transaction is: allocate TxnID → issue REQ → track responses/data → send CompAck → retire the TxnID.
  • The core invariant is one TxnID per outstanding transaction — free it only at true completion, or responses alias.
  • The model here is representative; 4.3 takes up the Home Node, the RN's counterpart.

18. Quick Revision

The Request Node (RN). The RN is the coherency master: it initiates transactions (allocate TxnID → issue a REQ packet to the Home Node → track RSP/DAT → send CompAck → retire the TxnID) and, if cached, participates in coherency by answering SNP packets from its cache state. Variants: RN-F (cached, snooped, full coherency), RN-D (RN-F + DVM / TLB maintenance), RN-I (I/O-coherent, no cache, never snooped). State held: cache line states (I / UC / UD / SC / SD) and outstanding transactions by TxnID. Core invariant: one TxnID per outstanding transaction, freed only at true completion — free early and a late packet aliases a reused ID. Representative model; 4.3 covers the Home Node.

Coming Next

Chapter 4.3 — The Home Node (HN). The Request Node initiates; the Home Node answers. The next chapter details the RN's counterpart: the node that owns an address range, serializes every access to it, holds the directory, decides which requesters to snoop, and returns the response and data. Where the RN is the coherency master of a request, the Home Node is its point of coherence — the second of the three nodes, and the one where CHI's coherency actually lives.