AMBA CHI · Module 3 · Why CHI Exists
Evolution of AMBA
Module 2 taught coherence protocols in the abstract; Module 3 asks why AMBA needed a new one. The answer is a chain of walls. APB and AHB gave SoCs a simple then a faster shared bus, but a shared bus serializes every master through one arbiter. AXI replaced it with point-to-point channels for throughput, yet left caches non-coherent. ACE bolted coherency onto AXI with broadcast snoops, which cannot scale past a handful of cores. CHI answers all three with a packet-based, layered, directory-coherent interconnect. This chapter traces that evolution, grounds the AHB bottleneck in a real arbiter, and sets up exactly the problems CHI was built to solve. The history here is representative, not a specification.
Foundation13 min readAMBA CHIAMBAAHBAXIACEInterconnect
Module 3 · Chapter 3.1 · Why CHI Exists
Project thread — Module 2 built the coherence protocols. Module 3 explains why AMBA needed CHI to carry them at scale. This opener traces the generations; 3.2 and 3.3 recap AHB and AXI as CHI context.
1. Learning Outcomes
By the end of this chapter you should be able to:
- Trace the AMBA generations — APB, AHB, AXI, ACE, CHI — in order.
- Identify the specific wall each generation hit that motivated the next.
- Distinguish the two pressures driving the evolution: performance and coherency-at-scale.
- Explain why a shared bus serializes masters, and why broadcast coherency does not scale.
- Implement a representative AHB-style shared-bus arbiter in SystemVerilog, Verilog-2001, and VHDL.
- Verify that a shared bus grants exactly one master at a time — the bottleneck CHI removes.
2. Why Should I Learn This?
CHI reads like a wall of new terminology until you know the problems it was built to solve. Each of its defining choices — point-to-point links, packets, a layered stack, distributed directories — is a direct answer to a limitation of an earlier AMBA generation. Learn the evolution and CHI stops being arbitrary; every feature has a "because AXI/ACE couldn't."
It is also how architects actually reason: not "CHI is best," but "this workload outgrew a shared bus / broadcast coherency, so we moved to CHI." The history is the decision framework.
3. Key Terms
4. Previous Chapter Connection
Module 2 built MSI, MESI, MOESI, MESIF and the machinery — invalidations, snoops (broadcast vs directory), ownership, dirty data. Those are protocols: rules for keeping copies consistent. They say nothing about the wires and transactions that carry them.
This module is about the carrier. A coherence protocol needs an interconnect that can move its requests, snoops, and data efficiently and at scale — and the AMBA generations are the story of that interconnect growing to meet the protocols Module 2 described. CHI is where the two finally fit.
5. Core Concept — five generations, two pressures
AMBA evolved under two pressures at once: raw performance and coherency at scale. Each generation relieved one wall and exposed the next.
- APB — a simple, low-power peripheral bus. Non-pipelined; fine for registers and slow devices, far too slow for a CPU-memory path.
- AHB — a pipelined, high-performance shared bus with bursts. Fast, but shared: every master arbitrates for one set of wires and only one transfers at a time. Adding masters adds contention, not bandwidth.
- AXI — five independent, point-to-point channels (read/write address, read/write data, write response), with multiple outstanding and out-of-order transactions. Throughput soared — but AXI is non-coherent: each master sees only its own view, and cached copies are invisible to each other.
- ACE — AXI Coherency Extensions: snoop channels that let masters see each other's caches, enabling coherent multi-core (Arm big.LITTLE). But ACE coherency is broadcast — every coherent request snoops every cache — which, per 2.8, is a scaling wall past a handful of cores.
- CHI — the Coherent Hub Interface: packet-based messages, a layered protocol/link/network stack, and directory-based coherency across distributed Home Nodes. It scales to many-core, NoC-style systems.
The through-line:
Each generation removed one wall and revealed the next. AHB fixed APB's speed but serialized masters; AXI fixed the shared bus but dropped coherency; ACE added coherency but by broadcast; CHI fixes coherency's scale with a directory. CHI is not a fresh start — it is the accumulated answer to four specific limitations.
6. Engineering Mental Model — one road, then a highway interchange
Picture on-chip traffic as vehicles.
- AHB is one single-lane road with a traffic officer (the arbiter): only one car crosses the intersection at a time, and more cars just mean a longer queue.
- AXI is a set of dedicated ramps — many cars move at once, point to point — but no one coordinates what each driver knows; two drivers can hold conflicting ideas of the same destination (no coherency).
- ACE adds a radio call to every driver on each trip ("does anyone have this?") — coordination at last, but the radio saturates as the city grows (broadcast).
- CHI adds a dispatch center with a live map (the directory) that calls only the drivers who actually matter, over a packet-switched road network. It coordinates and scales.
Each step keeps what worked and fixes what broke — the same way AMBA accreted rather than restarted.
7. Engineering Diagram — the AMBA capability stack
Read it bottom to top as time: each layer keeps the capability below and adds one more, until CHI closes the gap between fast interconnect and scalable coherency.
8. Worked Example — the wall each generation hit
The evolution as cause and effect: what each generation added, and the specific limitation that forced the next.
| Generation | Era | What it added | The wall it hit |
|---|---|---|---|
| APB | AMBA 2 | simple low-power peripheral bus | non-pipelined — far too slow for a CPU path |
| AHB | AMBA 2 | pipelined shared bus, bursts | one master transfers at a time — contention, not bandwidth |
| AXI | AMBA 3/4 | 5 point-to-point channels, outstanding/OoO | non-coherent — caches invisible to each other |
| ACE | AMBA 4 | snoop channels, coherency on AXI | broadcast snoops — scaling wall past a few cores |
| CHI | AMBA 5 | packets, layered stack, directory coherency | (the answer — scales to many-core) |
Each row's "wall" is the next row's reason to exist. The shape repeats: solve one problem, expose the next. APB's speed wall gave AHB; AHB's sharing wall gave AXI; AXI's coherency gap gave ACE; ACE's scaling wall gave CHI.
9. Transaction Walkthrough — one shared read across the generations
Take a single operation — CPU1 reads a line CPU0 has written — and follow it through the generations. Representative behavioral flow.
- AHB. No hardware coherency. CPU1 reads memory and gets the stale value unless software explicitly flushed CPU0's cache first. And every access serializes through the bus arbiter.
- AXI. Still non-coherent — same stale-read hazard; software or a flush must fix it. AXI's gain is throughput (concurrent channels), not correctness of shared data.
- ACE. Coherent at last: CPU1's read broadcasts a snoop to every master; CPU0 responds with the current data. Correct — but the snoop touched every cache, whether or not it held the line.
- CHI. The directory in the Home Node knows CPU0 holds the line, so it snoops only CPU0 (2.8). Correct and targeted — the same read that ACE broadcast, CHI routes to one holder.
The same operation is impossible-in-hardware (AHB/AXI), correct-but-broadcast (ACE), then correct-and-scalable (CHI). That progression is the whole reason CHI exists.
10. RTL / Hardware View — the AHB shared-bus arbiter
To feel the AHB wall concretely, here is its defining hardware: an arbiter that grants the bus to one master and holds it for the transfer. This single-owner constraint is exactly what AXI's point-to-point channels and CHI's packet links remove. Representative and simplified: fixed priority (lowest index wins), one line, no full AHB signalling.
// Representative AHB-style shared-bus arbiter (educational, not full AHB).
// Grants the bus to ONE master and holds it until the transfer completes.
// Fixed priority: the lowest-index requester wins. This single-owner rule is
// the shared-bus bottleneck that AXI (point-to-point) and CHI (packets) remove.
module ahb_arbiter #(
parameter int N = 4 // number of masters
)(
input logic clk,
input logic rst_n,
input logic [N-1:0] req, // masters requesting the bus
input logic busy, // current owner's transfer still in progress
output logic [N-1:0] grant, // one-hot: the master that owns the bus
output logic gnt_valid // a master owns the bus
);
logic [N-1:0] owner; // registered one-hot owner
logic [N-1:0] pick;
// Isolate the lowest set request bit — fixed priority, master 0 highest.
assign pick = req & (~req + 1'b1);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) owner <= '0;
else if (!busy) owner <= pick; // re-arbitrate only when the bus is free
// while busy: hold the current owner — others wait, even if requesting
end
assign grant = owner;
assign gnt_valid = |owner;
endmoduleThe same behavior in Verilog-2001:
// Representative AHB-style shared-bus arbiter (Verilog-2001).
module ahb_arbiter #(
parameter N = 4
)(
input clk,
input rst_n,
input [N-1:0] req,
input busy,
output [N-1:0] grant,
output gnt_valid
);
reg [N-1:0] owner;
wire [N-1:0] pick = req & (~req + 1'b1); // lowest set bit — fixed priority
always @(posedge clk or negedge rst_n)
if (!rst_n) owner <= {N{1'b0}};
else if (!busy) owner <= pick;
assign grant = owner;
assign gnt_valid = |owner;
endmoduleAnd in VHDL:
-- Representative AHB-style shared-bus arbiter (VHDL).
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity ahb_arbiter is
generic ( N : integer := 4 );
port (
clk, rst_n : in std_logic;
req : in std_logic_vector(N-1 downto 0);
busy : in std_logic;
grant : out std_logic_vector(N-1 downto 0);
gnt_valid : out std_logic
);
end entity;
architecture rtl of ahb_arbiter is
signal owner : std_logic_vector(N-1 downto 0) := (others => '0');
signal pick : std_logic_vector(N-1 downto 0);
begin
-- lowest set bit: req and (two's complement of req)
pick <= req and std_logic_vector(unsigned(not req) + 1);
process(clk, rst_n)
begin
if rst_n = '0' then
owner <= (others => '0');
elsif rising_edge(clk) then
if busy = '0' then owner <= pick; end if; -- re-arbitrate only when free
end if;
end process;
grant <= owner;
gnt_valid <= '1' when owner /= (owner'range => '0') else '0';
end architecture;All three encode the shared-bus rule: one owner at a time, held for the transfer — the serialization that motivated point-to-point interconnects.
11. Verification View — one master at a time
Two properties capture the shared-bus bottleneck as invariants.
// Bind to ahb_arbiter.
// 1. At most one master ever owns the bus — the shared-bus serialization.
property p_one_owner;
@(posedge clk) disable iff (!rst_n) $onehot0(grant);
endproperty
assert property (p_one_owner);
// 2. While a transfer is in progress, the owner does not change (no preemption).
property p_hold_while_busy;
@(posedge clk) disable iff (!rst_n) (busy && gnt_valid) |=> $stable(grant);
endproperty
assert property (p_hold_while_busy);The system observation, not merely a check:
On a shared bus, the aggregate transfer bandwidth is fixed no matter how many masters request — only one owns the bus at a time. Adding masters lengthens the queue; it does not add throughput. That invariant is the wall.
- What it proves: the arbiter serializes correctly — exactly one owner, held for the transfer.
- What it does not prove: fairness (fixed priority can starve — the DebugLab), and it says nothing about coherency (AHB has none; that gap is AXI→ACE→CHI).
- Bug signature when it fails: two grant bits set (bus contention — two masters driving at once), or the owner changing mid-transfer (a corrupted burst).
12. Testbench — grant one, hold, then re-arbitrate
Deterministic stimulus; the registered grant is checked after each edge.
module tb_ahb_arbiter;
localparam int N = 4;
logic clk = 0, rst_n;
logic [N-1:0] req;
logic busy;
logic [N-1:0] grant;
logic gnt_valid;
int errors = 0;
ahb_arbiter #(.N(N)) dut (.*);
always #5 clk = ~clk;
task automatic step(input logic [N-1:0] r, input logic b,
input logic [N-1:0] exp_grant, input string tag);
req = r; busy = b;
@(posedge clk); #1;
if (grant !== exp_grant) begin
errors++; $display("FAIL [%s] grant=%b exp=%b", tag, grant, exp_grant);
end else
$display("PASS [%s] grant=%b gnt_valid=%b", tag, grant, gnt_valid);
endtask
initial begin
rst_n = 0; step(4'b0000, 0, 4'b0000, "reset"); rst_n = 1;
// Masters 1 and 3 request; fixed priority -> master 1 (lowest index) wins.
step(4'b1010, 0, 4'b0010, "req 1,3 -> grant 1");
// Bus busy: owner held even though master 3 still requests.
step(4'b1010, 1, 4'b0010, "busy: hold grant 1");
// Transfer done (not busy): re-arbitrate -> master 3 now wins (1 dropped).
step(4'b1000, 0, 4'b1000, "free: grant 3");
// No requests -> no owner.
step(4'b0000, 0, 4'b0000, "idle: no grant");
if (errors == 0) $display("ALL TESTS PASSED");
else $display("%0d FAILURE(S)", errors);
$finish;
end
endmoduleExpected output:
PASS [reset] grant=0000 gnt_valid=0
PASS [req 1,3 -> grant 1] grant=0010 gnt_valid=1
PASS [busy: hold grant 1] grant=0010 gnt_valid=1
PASS [free: grant 3] grant=1000 gnt_valid=1
PASS [idle: no grant] grant=0000 gnt_valid=013. DebugLab — the master that never gets the bus
The master that never gets the bus
FIXED-PRIORITY ARBITRATION -> LOW-PRIORITY MASTER STARVESOne master's transactions never complete under load — a device times out or a queue backs up — while higher-priority masters run fine. It only happens when the high-priority masters are busy continuously.
The grant history with masters 0 and 1 streaming and master 3 requesting throughout:
cyc req busy grant note
2 1011 0 0001 master 0 (highest) wins
3 1011 1 0001 held
4 1011 0 0001 master 0 re-selected (still requesting)
... master 3 (req=1) never granted -> starvationgrant is never 1000: master 3's request bit is set every cycle, yet fixed priority keeps re-selecting a lower index.
There is no single "wrong" cycle — the defect is the policy. As soon as higher-priority masters request continuously, the arbiter's fixed-priority pick = req & (~req + 1) structurally excludes master 3 forever.
Fixed-priority arbitration always grants the lowest-index requester. With no memory of who was served last, a steady stream from high-priority masters starves every lower-priority one. The arbiter is correct (one owner at a time) but unfair — and on a shared bus, unfairness is a hang.
Use fair arbitration — round-robin: keep a pointer to the last-granted master and start each selection after it, so priority rotates and every requester is eventually served. (This is a policy change to the pick logic, not to the one-owner rule.) The deeper lesson is architectural: shared-bus arbitration is where fairness and throughput fight, which is precisely why AXI moved to point-to-point and CHI to packet links — no shared owner to starve for.
14. Common Mistakes
- Thinking CHI replaced AXI. Assumption: newer means it supersedes. Bug: dropping AXI for non-coherent throughput traffic. Prevention: CHI is for the coherent fabric; AXI stays for high-throughput non-coherent paths — real SoCs use both.
- Assuming a shared bus scales with masters. Assumption: more masters, more bandwidth. Bug: contention and starvation instead. Prevention: a shared bus grants one at a time; scale needs point-to-point or a network.
- Treating AXI as coherent. Assumption: high-performance implies cache-coherent. Bug: stale shared reads (Chapter 1.8). Prevention: AXI is non-coherent; coherency is ACE/CHI.
- Believing broadcast coherency scales. Assumption: ACE's snoops are fine at any size. Bug: snoop bandwidth wall past a few cores (2.8). Prevention: many-core needs directory coherency (CHI).
- Learning CHI features without their motivation. Assumption: memorize the mechanisms. Bug: no intuition for why — packets, layers, directories look arbitrary. Prevention: tie each CHI choice to the AMBA wall it answers.
- Treating this arbiter as a full AHB. Assumption: one arbiter is the protocol. Bug: missing AHB's address/data phases, HREADY, split transfers. Prevention: this illustrates the bottleneck, not the whole bus.
15. Engineering Checklist
- Place any AMBA interface in its generation — APB, AHB, AXI, ACE, or CHI.
- For each generation, name what it added and the wall that motivated the next.
- Recognize a shared bus by its one-master-at-a-time arbitration.
- Recognize non-coherence (AXI) versus broadcast (ACE) versus directory (CHI) coherency.
- Justify a move to CHI by a scaling or coherency limit of the prior generation, not by novelty.
- Keep AXI in mind for non-coherent throughput even in a CHI-based SoC.
16. Key Takeaways
- AMBA evolved under two pressures: performance (bus → point-to-point → network) and coherency at scale (none → broadcast → directory).
- APB → AHB → AXI → ACE → CHI, each removing one wall and exposing the next.
- AHB's shared bus serializes masters (one owner at a time); AXI fixed throughput but is non-coherent.
- ACE added coherency by broadcast, which does not scale; CHI uses a directory and scales.
- CHI is additive, not a restart — its packets, layers, and directories each answer a specific AMBA limitation; AXI still serves non-coherent traffic.
- This history is representative — the motivation behind CHI, detailed protocol-by-protocol in the chapters ahead.
17. Quick Revision
Evolution of AMBA. Two pressures: performance and coherency-at-scale. APB (simple peripheral bus) → AHB (pipelined shared bus, one master at a time — arbitration bottleneck) → AXI (5 point-to-point channels, outstanding/OoO, high throughput — non-coherent) → ACE (AXI + broadcast coherency — scaling wall past a few cores) → CHI (packet-based, layered, directory coherency — scales to many-core). Each generation removed one wall and exposed the next. A shared bus grants one master at a time (no throughput scaling); AXI is non-coherent; ACE is broadcast; CHI is directory. CHI is additive — AXI still carries non-coherent traffic. Representative history, not a specification.
Coming Next
Chapter 3.2 — AHB Recap (for CHI Context). With the whole evolution in view, the next chapter zooms in on AHB — its address and data phases, HREADY handshake, and single-master arbitration — to make precise where it fits and exactly why a pipelined shared bus cannot carry coherent multi-master traffic, the gap AXI and then CHI were built to close.
Standards & specifications
- Governing standard
- Arm AMBA CHI Architecture Specification(opens Arm in a new tab)
Defines CHI node types, the coherence protocol and the transaction flows between them. Mesh topology, home-node placement and performance tuning are implementation decisions.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the AMBA CHI curriculum.
