AMBA CHI · Module 4 · CHI Architecture Overview
The CHI Mental Model
CHI stops describing an interconnect as wires and handshakes and starts describing it as a network. Three abstractions carry the whole protocol: nodes — the Request, Home, and Subordinate Nodes; channels — the four independent message classes REQ, RSP, SNP, and DAT, each with its own credited flow control; and packets — messages tagged with source and target Node IDs and a transaction ID that ride those channels. Every CHI transaction is a choreography of packets across channels between nodes. This chapter builds that mental model, follows one read through it, and shows the credit flow control that keeps channels from overrunning. Get nodes, channels, and packets right and the rest of CHI is detail. Representative model, not the specification.
Foundation16 min readAMBA CHINodesChannelsPacketsFlow Control
Module 4 · Chapter 4.1 · CHI Architecture Overview — module opener
Project thread — Module 3 explained why CHI exists. Module 4 builds it, starting here with the three abstractions. 4.2–4.4 then drill into each node type.
1. Learning Outcomes
By the end of this chapter you should be able to:
- Name CHI's three abstractions — nodes, channels, packets — and what each represents.
- Identify the node roles (RN, HN, SN) and that the interconnect routes by Node ID.
- List the four channel classes — REQ, RSP, SNP, DAT — and their directions.
- Describe a packet's key fields — opcode, address, source/target Node ID, transaction ID, data.
- Explain why CHI is message-based, and how a transaction is a choreography of packets.
- Implement a representative credited flow-control counter in SystemVerilog, Verilog-2001, and VHDL.
2. Why Should I Learn This?
This is the mental model the entire rest of CHI assumes. Every later chapter — request nodes, home nodes, transaction flows, snoops, retries — is described in terms of nodes exchanging packets over channels. Miss this and CHI reads as an alphabet soup of opcodes; get it and each transaction becomes a readable sequence of messages.
It is also the single biggest shift from AXI and AHB. Those are wire protocols — you reason about signals and handshakes on a bus. CHI is a message protocol — you reason about packets routed between named nodes. Internalizing that shift now is what makes everything downstream click.
3. Key Terms
4. Previous Chapter Connection
Chapter 3.8 closed Module 3 by stating CHI's goals and naming its vocabulary — RN/HN/SN, layers, packets, directory — as the bridge from why to what. It deliberately left the mechanics for here.
This chapter picks up exactly those words and turns them into a working model. Where 3.8 said "CHI uses a node model and packets," this chapter says which nodes, which channels, what a packet contains, and how they combine into a transaction. It is the first chapter that describes CHI as it actually operates, not as a set of aims.
5. Core Concept — nodes, channels, packets
Everything in CHI is built from three abstractions.
- Nodes — who talks. Every endpoint is a node with a Node ID. Three roles: a Request Node (RN) initiates transactions (a CPU cluster); a Home Node (HN) is the point of coherence for an address range and holds its directory; a Subordinate Node (SN) provides backing storage (a memory controller). The interconnect routes purely by Node ID.
- Channels — the lanes they talk on. Each link carries four independent message classes, each with its own credited flow control: REQ (requests, RN→HN), RSP (responses without data — acknowledgements, completions, snoop responses), SNP (snoops, HN→RN), and DAT (data — read data, write data, snoop data). Independent channels mean a data transfer never blocks a request, and a snoop never blocks a response.
- Packets — what they say. A message is a self-contained packet: an opcode (the transaction or response type), an address, a source and target Node ID (SrcID/TgtID) for routing, a transaction ID (TxnID) tying the transaction together, and, on DAT, the data itself. Packets route independently across the fabric.
The synthesis that makes CHI legible:
A CHI transaction is a choreography of packets across channels between nodes. A read is not a bus handshake — it is a REQ packet from an RN to an HN, perhaps a SNP packet from the HN to another RN, RSP and DAT packets back, and a final completion. Learn to read a transaction as "who sent what packet, on which channel, to whom," and CHI's every flow becomes a short script.
6. Engineering Mental Model — a postal network, not a party line
AXI and AHB are a party line: everyone shares wires, and you coordinate by signalling and handshakes on those shared wires.
CHI is a postal network:
- Nodes are addresses — houses with Node IDs. The interconnect is the postal service; it delivers by address, and never needs to know what is inside.
- Channels are separate mail streams — regular letters (REQ), receipts (RSP), inquiries the post office initiates (SNP), and parcels (DAT) — sorted so a slow parcel never holds up a letter.
- Packets are the envelopes: each carries a from-address, a to-address, a tracking number (TxnID), and its contents. Every envelope stands alone.
- Credits are prepaid postage slots at the destination: you may only mail as many items as the recipient has confirmed room for, so no mailbox ever overflows.
Reason about CHI the way you reason about mail — who sends which envelope to whom — not about voltages on a shared wire.
7. Engineering Diagram — nodes and the four channels
Note the asymmetry: the RN↔HN link carries all four channels including SNP (the HN snoops requesters), while the HN↔SN link has no SNP — memory is never snooped. That difference is a direct reading of the node roles.
8. Nodes on the Fabric — routing by Node ID
The interconnect is a router of Node IDs, nothing more. A packet from RN ID 0 to HN ID 4 is delivered by ID; the fabric never opens the envelope.
9. The Four Channels
Each channel is an independent message class with its own credits. Together they carry every CHI transaction.
| Channel | Carries | Typical direction | Example |
|---|---|---|---|
| REQ | requests | RN → HN | ReadShared, ReadUnique, WriteBack |
| RSP | responses without data | both ways | completion, snoop response, retry |
| SNP | snoops | HN → RN | SnpShared, SnpUnique |
| DAT | data | both ways | read data, write data, snoop data |
Two facts to carry: REQ and SNP flow in opposite directions (requesters ask the home; the home snoops requesters), and data has its own channel (DAT) so a large transfer never blocks control messages. Separating these classes is what lets CHI keep many transactions in flight without head-of-line blocking.
10. Anatomy of a Packet
A packet is self-contained — it carries everything the fabric and the destination need. Representative key fields:
| Field | Meaning |
|---|---|
| Opcode | the transaction or response type (e.g. ReadShared, CompData) |
| SrcID | source Node ID — who sent it |
| TgtID | target Node ID — who the interconnect routes it to |
| TxnID | transaction ID — ties this transaction's packets together at the requester |
| Addr | the address (on request packets) |
| Data | the payload (on DAT packets only) |
The TxnID is CHI's version of AXI's ID: it lets a requester correlate responses and data to the request that produced them, so many transactions can be outstanding and complete out of order. The SrcID/TgtID pair is what the interconnect actually routes on — the coherence meaning lives in the opcode, the routing in the IDs.
11. Transaction Walkthrough — a read as packets
Follow a ReadShared as a sequence of packets (simplified; Module 5 makes it exact). CPU0 is RN ID 0; the address's home is HN ID 4; another requester, RN ID 1, holds the line.
- REQ. RN 0 sends a REQ packet — opcode ReadShared, TgtID 4, SrcID 0, TxnID 7 — to the Home Node.
- SNP. HN 4 checks its directory, sees RN 1 holds the line, and sends a SNP packet (SnpShared, TgtID 1) to RN 1.
- RSP / DAT. RN 1 responds on RSP (snoop response) and, if it must supply data, on DAT (snoop data) back to the Home Node.
- DAT. HN 4 sends a DAT packet (CompData) carrying the line to RN 0, tagged with TxnID 7 so RN 0 matches it to its request.
- Completion. The transaction completes; the directory now records RN 0 as a sharer.
Every step is one packet on one channel between two Node IDs. Read that way, the whole transaction is five messages — no bus, no shared handshake.
12. RTL / Hardware View — credited flow control
Channels do not overrun because CHI's link layer is credit-based: a sender may launch a flit only while it holds a credit, and the receiver returns a credit when it frees a buffer slot. Here is that counter. Representative and simplified — one channel's L-credit accounting.
// Representative CHI channel credit counter (educational, link-layer credits).
// A flit may launch only when a credit is held; sending consumes one, and the
// receiver returns one as it frees a buffer slot. Credits must never exceed the
// receiver's buffer depth MAXCR — that is what prevents overflow.
module chi_credit #(
parameter int MAXCR = 8 // = receiver buffer depth
)(
input logic clk,
input logic rst_n,
input logic want_send, // sender has a flit ready
input logic credit_return, // receiver freed a slot this cycle
output logic can_send, // a credit is available
output logic [3:0] credits
);
logic [3:0] cr;
wire send = want_send && (cr != 4'd0); // a flit actually launches
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) cr <= MAXCR[3:0]; // start with full credits
else cr <= cr - (send ? 4'd1 : 4'd0)
+ (credit_return ? 4'd1 : 4'd0);
end
assign credits = cr;
assign can_send = (cr != 4'd0);
endmoduleThe same behavior in Verilog-2001:
// Representative CHI channel credit counter (Verilog-2001).
module chi_credit #(
parameter MAXCR = 8
)(
input clk, rst_n,
input want_send, credit_return,
output can_send,
output [3:0] credits
);
reg [3:0] cr;
wire send = want_send & (cr != 4'd0);
always @(posedge clk or negedge rst_n)
if (!rst_n) cr <= MAXCR[3:0];
else cr <= cr - (send ? 4'd1 : 4'd0) + (credit_return ? 4'd1 : 4'd0);
assign credits = cr;
assign can_send = (cr != 4'd0);
endmoduleAnd in VHDL:
-- Representative CHI channel credit counter (VHDL).
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity chi_credit is
generic ( MAXCR : integer := 8 );
port (
clk, rst_n : in std_logic;
want_send, credit_return : in std_logic;
can_send : out std_logic;
credits : out std_logic_vector(3 downto 0)
);
end entity;
architecture rtl of chi_credit is
signal cr : unsigned(3 downto 0) := to_unsigned(MAXCR, 4);
begin
process(clk, rst_n)
variable snd : std_logic;
begin
if rst_n = '0' then
cr <= to_unsigned(MAXCR, 4);
elsif rising_edge(clk) then
if want_send = '1' and cr /= 0 then snd := '1'; else snd := '0'; end if;
if snd = '1' and credit_return = '0' then
cr <= cr - 1;
elsif snd = '0' and credit_return = '1' then
cr <= cr + 1;
end if; -- both or neither: hold
end if;
end process;
credits <= std_logic_vector(cr);
can_send <= '0' when cr = 0 else '1';
end architecture;All three enforce the rule: no credit, no flit. Because the sender starts with exactly the receiver's buffer depth and only sends against a held credit, the receiver's buffer can never overflow — as long as the credit count never exceeds that depth.
13. Verification View — credits bound the sender to the buffer
Two properties: a flit never launches without a credit, and the credit count never exceeds the receiver's buffer depth.
// Bind to chi_credit (MAXCR = 8).
// 1. A flit is launched only when a credit is held.
property p_no_send_without_credit;
@(posedge clk) disable iff (!rst_n)
(want_send && (credits == 0)) |-> !can_send;
endproperty
assert property (p_no_send_without_credit);
// 2. Credits never exceed the receiver's buffer depth — the overflow guard.
property p_credits_bounded;
@(posedge clk) disable iff (!rst_n) credits <= MAXCR;
endproperty
assert property (p_credits_bounded);The system point, beyond the two checks:
Credited flow control is what lets CHI keep many packets in flight on independent channels without any shared stall signal. Each channel is throttled only by its own credits, so a full DAT buffer cannot block a REQ, and a busy HN cannot wedge the whole fabric — it simply stops returning credits on its channel. The one invariant the entire scheme rests on is credits ≤ buffer depth: break it and a sender ships a flit into a full buffer. The next section shows exactly that.
- What it proves: the sender never sends without a credit; the count is bounded.
- What it does not prove: end-to-end deadlock freedom across channels (a protocol-level property).
- Bug signature: credits exceeding buffer depth → a flit delivered to a full receiver → dropped or corrupted.
14. Testbench — spend and refill credits
Drains credits by sending, then refills by returning, checking the counter and can_send.
module tb_chi_credit;
logic clk = 0, rst_n, want_send, credit_return;
logic can_send;
logic [3:0] credits;
int errors = 0;
chi_credit #(.MAXCR(4)) dut (.*); // small depth for a short test
always #5 clk = ~clk;
task automatic step(input logic ws, input logic cret,
input logic [3:0] exp, input string tag);
want_send = ws; credit_return = cret;
@(posedge clk); #1;
if (credits !== exp) begin
errors++; $display("FAIL [%s] credits=%0d exp=%0d", tag, credits, exp);
end else
$display("PASS [%s] credits=%0d can_send=%b", tag, credits, can_send);
endtask
initial begin
rst_n = 0; step(0, 0, 4'd4, "reset -> full"); rst_n = 1;
step(1, 0, 4'd3, "send -> 3");
step(1, 0, 4'd2, "send -> 2");
step(1, 0, 4'd1, "send -> 1");
step(1, 0, 4'd0, "send -> 0 (last credit)");
step(1, 0, 4'd0, "send blocked -> stays 0"); // no credit: no send
step(0, 1, 4'd1, "return -> 1");
step(1, 1, 4'd1, "send+return -> 1 (net 0)");
if (errors == 0) $display("ALL TESTS PASSED");
else $display("%0d FAILURE(S)", errors);
$finish;
end
endmoduleExpected output:
PASS [reset -> full] credits=4 can_send=1
PASS [send -> 3] credits=3 can_send=1
PASS [send -> 2] credits=2 can_send=1
PASS [send -> 1] credits=1 can_send=1
PASS [send -> 0 (last credit)] credits=0 can_send=0
PASS [send blocked -> stays 0] credits=0 can_send=0
PASS [return -> 1] credits=1 can_send=1
PASS [send+return -> 1 (net 0)] credits=1 can_send=115. DebugLab — one more credit than the buffer can hold
One more credit than the buffer can hold
CREDITS > BUFFER DEPTH -> FLIT OVERFLOWRare packet corruption or loss on a channel under sustained traffic — a response goes missing or arrives garbled — only when the channel is driven near its full rate. Light traffic never triggers it.
The credit count sitting one above the receiver's buffer depth:
buffer_depth credits note
8 9 one more credit than slots
-> sender can launch a 9th flit into an 8-slot bufferThe sender believes it has room for nine outstanding flits; the receiver has room for eight. The ninth overflows.
Initialization: the credit counter was loaded with buffer_depth + 1 (an off-by-one in the reset value or an extra credit granted at bring-up). From that point the invariant credits ≤ buffer_depth is already violated, before any flit is even sent.
A held credit is a promise of a free buffer slot. If credits exceed buffer slots, that promise is false: eventually the sender spends the extra credit and ships a flit the receiver cannot store. The whole safety of credited flow control rests on credits = buffer depth; one too many breaks it silently, surfacing only under load.
Initialize and cap the credit count at exactly the receiver's buffer depth — never more. Every credit must correspond to a real, reserved slot; the receiver returns a credit only after it frees one, and never grants a spurious extra. With credits ≤ buffer_depth held as an invariant, a held credit always guarantees room, and overflow is impossible by construction. This exact accounting is why CHI's link layer can run channels at full rate without a shared stall signal.
16. Common Mistakes
- Reasoning about CHI as wires. Assumption: it is a bus with signals. Bug: looking for handshakes instead of packets. Prevention: CHI is messages — nodes, channels, packets — not shared wires.
- Merging the channels. Assumption: one path carries everything. Bug: expecting data to block requests. Prevention: REQ/RSP/SNP/DAT are independent, separately credited channels.
- Confusing Node ID with TxnID. Assumption: one identifier. Bug: mis-routing or mis-correlating. Prevention: Node ID (SrcID/TgtID) routes; TxnID correlates a transaction's packets at the requester.
- Thinking the interconnect understands coherence. Assumption: the fabric interprets packets. Bug: expecting routing logic to enforce coherence. Prevention: the fabric routes by Node ID; coherence lives in the nodes (the HN).
- Ignoring credits. Assumption: send whenever ready. Bug: buffer overflow (the DebugLab). Prevention: no credit, no flit; credits equal buffer depth.
- Expecting full node detail here. Assumption: this defines every node type. Bug: missing the depth in 4.2–4.4. Prevention: this is the overview; each node gets its own chapter.
17. Engineering Checklist
- Describe any CHI activity as nodes exchanging packets over channels — not wires and handshakes.
- Identify each endpoint's role (RN / HN / SN) and its Node ID.
- Place each message on its channel — REQ, RSP, SNP, or DAT.
- Read a packet's opcode (meaning), SrcID/TgtID (routing), and TxnID (correlation).
- Treat every channel as independently credited — credits equal to buffer depth.
- Read a transaction as a sequence of packets, one per step.
18. Key Takeaways
- CHI rests on three abstractions: nodes (RN/HN/SN, addressed by Node ID), channels (REQ/RSP/SNP/DAT, independently credited), and packets (self-contained messages).
- CHI is message-based, not wire-based — the fundamental shift from AXI/AHB.
- The four channels are independent, so data never blocks control and one class cannot wedge another.
- A packet carries opcode (meaning), SrcID/TgtID (routing), TxnID (correlation), and, on DAT, data.
- A transaction is a choreography of packets across channels between nodes — a read is just five messages.
- Credited flow control (credits = buffer depth) keeps channels from overrunning without any shared stall — the invariant that makes full-rate, many-in-flight channels safe.
19. Quick Revision
The CHI mental model. Three abstractions. Nodes — endpoints with a Node ID: RN (requester), HN (home / point of coherence, holds the directory), SN (subordinate / memory). Channels — four independent, separately credited message classes: REQ (requests, RN→HN), RSP (responses without data), SNP (snoops, HN→RN), DAT (data). Packets — self-contained messages carrying opcode (meaning), SrcID/TgtID (routing), TxnID (correlation), and data on DAT. CHI is message-based, not wire-based; the interconnect routes by Node ID and never inspects contents. A transaction is a choreography of packets — a read is REQ → SNP → RSP/DAT → DAT → complete. Credited flow control (credits = buffer depth) prevents overflow with no shared stall. Representative model — 4.2–4.4 detail each node.
Coming Next
Chapter 4.2 — The Request Node (RN). With the mental model in place, Module 4 zooms into each node in turn, starting with the initiator. The next chapter details the Request Node: its role as the coherency master that starts every transaction, the RN-F / RN-D / RN-I variants, what state it holds, and how it issues REQ packets and handles the snoops that come back — the first of the three nodes brought into focus.