Skip to content

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

AMBA generations stacked newest at top: CHI is packet-based, layered, and directory-coherent, scaling to many-core; ACE adds broadcast coherency to AXI but caps at a few cores; AXI is point-to-point with five channels but non-coherent; AHB is a pipelined shared bus with one master at a time; APB is a simple low-power peripheral bus.AMBA generations — what each addedCHI (AMBA 5)packet-based · layered · directory-coherent — scales to many-core NoCspacket-based · layered · directory-coherent — scales to many-core NoCsACE (AMBA 4)AXI + broadcast coherency — coherent, but snoop traffic caps scaleAXI + broadcast coherency — coherent, but snoop traffic caps scaleAXI (AMBA 3/4)5 point-to-point channels, outstanding / out-of-order — non-coherent5 point-to-point channels, outstanding / out-of-order — non-coherentAHB (AMBA 2)pipelined shared bus, bursts — one master at a time (arbitration)pipelined shared bus, bursts — one master at a time (arbitration)APB (AMBA 2)simple low-power peripheral bus — non-pipelinedsimple low-power peripheral bus — non-pipelined
Figure 1 — the AMBA generations as a capability stack (representative), newest at the top. Each band lists what the generation added and the wall that motivated the next. CHI sits at the top as the directory-coherent, packet-based answer to the limitations below it.

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.

GenerationEraWhat it addedThe wall it hit
APBAMBA 2simple low-power peripheral busnon-pipelined — far too slow for a CPU path
AHBAMBA 2pipelined shared bus, burstsone master transfers at a time — contention, not bandwidth
AXIAMBA 3/45 point-to-point channels, outstanding/OoOnon-coherent — caches invisible to each other
ACEAMBA 4snoop channels, coherency on AXIbroadcast snoops — scaling wall past a few cores
CHIAMBA 5packets, 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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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;
endmodule

The same behavior in Verilog-2001:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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;
endmodule

And in VHDL:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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
endmodule

Expected output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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=0

13. DebugLab — the master that never gets the bus

1

The master that never gets the bus

FIXED-PRIORITY ARBITRATION -> LOW-PRIORITY MASTER STARVES
Symptom

One 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.

Evidence

The grant history with masters 0 and 1 streaming and master 3 requesting throughout:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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 -> starvation

grant is never 1000: master 3's request bit is set every cycle, yet fixed priority keeps re-selecting a lower index.

First Divergence

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.

Root Cause

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.

Fix

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.