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.