6.3.7Interconnects, Buses & SoC

AXI - AMBA on-chip protocols

3,598 words16 min readdifficulty · medium

What Problem Does AXI Solve?

The scalability crisis: Earlier buses (like APB, simpleAMBA) were:

  • Single-threaded: Only one transaction at a time
  • Blocking: A slow peripheral stalls everyone
  • No pipelining: Address phase must complete before data phase

Why this matters: A modern smartphone SoC has ~100 Gbps of internal data movement. A CPU cache miss shouldn't wait for a slow UART to finish. We need overlapping transactions, out-of-order completion, and quality-of-service.

The AMBA Family Hierarchy

Think of it as a city's transportation:

  • AXI = Highway system (fast, complex, multiple lanes)
  • AHB = Main roads (medium speed, simpler)
  • APB = Residential streets (slow, minimal overhead)

AXI Core Architecture: Five Independent Channels

The five channels:

\text{Write:} \quad &\text{AW (Write Address)} \rightarrow \text{W (Write Data)} \rightarrow \text{B (Write Response)} \\ \text{Read:} \quad &\text{AR (Read Address)} \rightarrow \text{R (Read Data + Response)} \end{aligned}$$ **Derivation from requirements:** 1. **Why separate address and data?** - Master sends address (with burst length, size, type) - While slave prepares, master can send data for *another* transaction - **Result**: Address bandwidth≠ data bandwidth (addresses are small!) 2. **Why separate write response channel?** - Slave needs time to commit write to memory/register - Response comes *after* data buffering completes - Master can issue new requests before receiving old responses 3. **Why combine read data + response?** - Read data *is* the response (success/fail comes with data) - Combining saves a channel (5 instead of 6) > [!example] Channel Independence in Action > **Scenario**: CPU reads from DRAM (slow) and writes to cache (fast) simultaneously. **Timeline (cycles):** ``` Cycle AR Channel AW Channel R Channel W Channel B Channel 0 CPU→DRAM addr CPU→Cache addr - - - 1 - - - CPU→Cache data - 2 - - Cache→CPU (ack) 15 - - DRAM→CPU data - - ``` **Why this step?** - Cycle 0: Both addresses issued in parallel (different channels!) - Cycle 1-2: Cache write completes fast (writes are buffered) - Cycle 15: DRAM read finally returns (slow access) - **Key insight**: Cache write didn't wait for DRAM read because channels are independent ## Handshake Protocol: VALID and READY > [!formula] AXI Handshake Rule > Every channel uses a **two-way handshake**: > $$ > \text{Transfer occurs} \iff (\text{VALID} = 1) \land (\text{READY} = 1) \land (\text{rising edge of ACLK}) > $$ **Derivation of why we need both signals:** Imagine only VALID: ``` Master asserts VALID → Slave must accept immediately Problem: What if slave is busy? We'd drop data! ``` Imagine only READY: ``` Slave asserts READY → Master must send immediately Problem: Master might not have data ready! We'd send garbage! ``` **Solution**: Both assert when ready, transfer on coincidence: ``` Cycle VALID READY Transfer? Why? 0 0 1 No Master not ready 1 1 0 No Slave not ready 2 1 1 YES Both ready! 3 0 1 No Master done ``` **Critical rule**: Once VALID asserts, it *cannot* drop until transfer completes (prevents deadlock). > [!example] Handshake Timing Scenarios **Case 1: Slave slower than master** ``` ___ ___ ___ ___ CLK |__| |___| |___| ________________ VALID |____________ (stays high) _____________ READY |_________ ^ Transfer at cycle 2 ``` **Case 2: Master slower than slave** ``` ___ ___ CLK |___| |___| ____________ VALID |______ ________________ READY |_______________ ^ Transfer at cycle 2 ``` **Why this design?** Neither party blocks the other. No central arbiter needed. Scales to many masters/slaves. ## Burst Transactions: Pipelining Addresses > [!formula] Burst Efficiency Calculation > A burst is ==multiple data transfers from a single address command==. **Without bursts (per-beat addressing):** $$\text{Overhead per transfer} = \frac{T_{\text{addr}} + T_{\text{data}}}{1 \text{ beat}} = T_{\text{addr}} + T_{\text{data}}$$ **With N-beat burst:** $$\text{Overhead per transfer} = \frac{T_{\text{addr}} + N \cdot T_{\text{data}}}{N \text{ beats}} = \frac{T_{\text{addr}}}{N} + T_{\text{data}}$$ As $N \to \infty$, overhead $\to T_{\text{data}}$ (address cost amortized). **Derivation of burst types:** 1. **FIXED**: Address never increments - **Use case**: FIFO register (same address, multiple reads/writes) - Example: Reading from UART RX buffer 2. **INCR**: Address increments by transfer size - **Use case**: Memory arrays (addresses0x1000, 0x1004, 0x1008...) - Most common for cache line fills 3. **WRAP**: Address wraps at boundary - **Use case**: Circular buffers, cache lines - Example: 4-beat wrap at 0x100→ 0x100, 0x104, 0x108, 0x10C, 0x100... > [!example] Burst Efficiency Comparison > **Task**: Transfer 16 bytes from address 0x1000. **Method A: 4 separate single-beat transactions** ``` Cycle Action 0 Send address 0x1000 1 Receive data[0:3] 2 Send address 0x1004 3 Receive data[4:7] 4 Send address 0x1008 5 Receive data[8:11] 6 Send address 0x100C 7 Receive data[12:15] Total: 8 cycles, 4 address cycles ``` **Method B: 1 four-beat burst (INCR)** ``` Cycle Action 0 Send address 0x1000, length=4 1 Receive data[0:3] 2 Receive data[4:7] 3 Receive data[8:11] 4 Receive data[12:15] Total: 5 cycles, 1 address cycle ``` **Why this step?** Slave auto-increments address internally. Bandwidth saved: $(8-5)/8 = 37.5\%$. ## Out-of-Order Completion: Transaction IDs > [!formula] ID-Based Ordering > Each transaction carries an ==ID tag== (ARID for read, AWID for write). **Problem without IDs:** ``` Master issues: Read A (slow DRAM) → Read B (fast cache) Responses: Must wait for A before B can return Result: B blocked by A (head-of-line blocking) ``` **Solution with IDs:** ``` Master issues: Read A (ID=0, DRAM) → Read B (ID=1, cache) Responses: B returns first (ID=1), then A (ID=0) Result: No blocking! ``` **Derivation of ID width:**

\text{Max concurrent transactions} = 2^{W_{\text{ID}}}

Typical: 4-bit ID → 16 outstanding transactions per master. **Reordering constraint**: Transactions with the *same* ID must complete in order (data dependency). > [!example] Out-of-Order Read Scenario > **Setup**: Master issues three reads: > - ID=5, address0x8000 (DRAM, 100 cycles) > - ID=3, address 0x2000 (SRAM, 10 cycles) > - ID=5, address 0x8100 (DRAM, 100 cycles) **Completion order:** ``` Cycle Event Why? 10 R channel: ID=3, data from0x2000 (Fast SRAM) 100 R channel: ID=5, data from 0x8000 (DRAM #1) 110 R channel: ID=5, data from 0x8100 (DRAM #2, waits for #1!) ``` **Why this step?** - Cycle 10: ID=3 completes first (different ID, no dependency) - Cycle 110: Second ID=5 waits for first ID=5 (same ID = ordered) **Critical insight**: Same-ID ordering preserves memory consistency (e.g., write pointer before write data). ## Write Response Channel: Why It Exists > [!intuition] The Write Completion Problem > **Naive thought**: "Just send data, assume it's written." **Why that breaks:** - Write buffering: Slave accepts data but hasn't committed to memory - Error handling: Write to bad address needs to report failure - Synchronization: Software needs to know when write is *globally visible* **AXI solution**: Separate B channel with: - BID: Which write transaction completed - BRESP: Success (OKAY), bus error (SLVERR), decode error (DECERR) **Derivation of response timing:**

T_{\text{response}} \geq T_{\text{last data}} + T_{\text{commit}}

Slave can buffer data quickly but response waits for actual memory write. > [!example] Write Response Timing > **Scenario**: CPU writes to DRAM controller with buffering. ``` Cycle AW Channel W Channel B Channel Memory State 0 CPU→addr - - - 1 - CPU→data[0] - Buffered 2 - CPU→data[1] - Buffered 3 - CPU→data[2], LAST - Buffered 4 - - - Writing to DRAM... 10 - - - Writing to DRAM... 15 - - Ctrl→CPU (OKAY) Committed! ``` **Why this step?** - Cycles 1-3: Data accepted into controller's buffer (fast) - Cycles 4-14: Controller arbitrates for DRAM, performs write (slow) - Cycle 15: Response sent only after DRAM acknowledges write **Without B channel**, CPU couldn't know if write succeeded or when it's safe to read back. ## Quality of Service (QoS): Priority Encoding > [!formula] QoS Signal Encoding > Each address channel carries ==AxQOS[3:0]==, a 4-bit priority: > $$ > \text{Priority}: 0 \text{ (lowest)} \to 15 \text{ (highest)} > $$ **Why QoS is needed:** Imagine SoC with: - CPU (needs low latency) - GPU (needs high bandwidth) - DMA (background tasks) **Problem**: Simple round-robin gives GPU equal priority → CPU starves. **Solution**: Assign QoS levels: ``` CPU → QoS = 12 (time-critical) GPU → QoS = 6 (bandwidth, not latency) DMA → QoS = 2 (best-effort) ``` Interconnect arbitrates: CPU gets first shot, GPU gets bandwidth when CPU idle, DMA fills gaps. **Derivation of arbiter logic:**

\text{Grant master } i \text{ if: } \text{QoS}i = \max(\text{QoS}{\text{ready}}) \land \text{VALID}_i = 1

> [!example] QoS Arbitration Timeline > **Contention**: 3 masters request same slave simultaneously. ``` Cycle Master A (QoS=15) Master B (QoS=8) Master C (QoS=4) Grant 0 VALID=1 VALID=1 A (highest QoS) 1 Transfer done Waiting Waiting B (next highest) 2 - Transfer done Waiting C (only one left) 3 VALID=1 (new req) - Transfer done A (QoS=15 wins) ``` **Why this step?** QoS doesn't guarantee *throughput*, it guarantees *latency* for high-priority masters. A sees max2-cycle delay (B and C might be mid-transfer). ## Common Transaction Types > [!definition] AXI Transaction Categories **1. Read Transaction** ``` Master → Slave: AR channel (address, burst QoS) Slave → Master: R channel (data + response, per beat) ``` **2. Write Transaction** ``` Master → Slave: AW channel (address, burst, QoS) Master → Slave: W channel (data, per beat) Slave → Master: B channel (single response) ``` **3. Exclusive Access (for atomics)** ``` Read-exclusive: AR with AxLOCK=1 → locks address Write-exclusive: AW with AxLOCK=1 → succeds only if lock held ``` Used for implementing `compare-and-swap`, `test-and-set`. ## Strobe Signals: Partial Writes > [!formula] Write Strobe Encoding > Each W channel beat carries ==WSTRB[n-1:0]==, one bit per byte: > $$ > \text{WSTRB}[i] = > \begin{cases} > 1 & \text{Write byte } i \\ > 0 & \text{Don't modify byte } i > \end{cases} > $$ **Why needed?** Often write only *part* of a word. **Example**: 32-bit bus, write 16-bit value to address 0x1002: ``` Address: 0x1002 (aligned to 0x1000) Data: 0xXXXABCDXXX (only ABCD matters) WSTRB: 0b110 (write bytes 1 and 2 only) Memory before: [FF][FF][FF][FF] at 0x1000 Memory after: [FF][AB][CD][FF] at 0x1000 ``` **Derivation**: For N-byte bus:

W_{\text{STRB width}} = \frac{W_{\text{data}}}{8}

64-bit bus → 8 strobe bits. > [!mistake] Common AXI Protocol Violations **Mistake 1: Dropping VALID before READY** ```verilog always @(posedge clk) if (some_condition) axi_valid <= 0; // WRONG! Violates spec ``` **Why it feels right**: "I changed my mind, cancel the request." **Why it's wrong**: Slave might have already started processing. VALID must stay high until transfer. **Fix**: Once asserted, VALID stays high until READY seen: ```verilog always @(posedge clk) if (axi_valid && axi_ready) axi_valid <= 0; // OK now ``` **Mistake 2: Ignoring burst alignment** ``` AWADDR = 0x1003 (unaligned) AWSIZE = 3 (8 bytes per beat) AWBURST = INCR ``` **Why it feels right**: "Just start at0x1003, increment by 8." **Why it's wrong**: Creates misaligned accesses (0x1003, 0x100B, 0x1013..). Many slaves can't handle this. **Fix**: Align start address to transfer size:

\text{AWADDR}_{\text{aligned}} = \text{AWADDR} \land \neg(2^{\text{AWSIZE}} - 1)

For AWSIZE=3: $0x1003 \land \neg 7 = 0x1000$ (aligned). **Mistake 3: Assuming fixed latency** ```c write_axi(addr, data); // Assume done in 1 cycle read_axi(addr); // Might read stale data! ``` **Why it feels right**: "Hardware is deterministic, right?" **Why it's wrong**: AXI is explicitly *variable latency*. Buffering, arbitration, memory timing all vary. **Fix**: Wait for write response before dependent read: ```c write_axi(addr, data); wait_for_bresp(); // Block until B channel responds read_axi(addr); // Now safe ``` ## AXI vs. Older AMBA Protocols > [!formula] Performance Comparison | Feature | APB | AHB | AXI | |---------|-----|-----|-----| | Max Clock | Low (~50 MHz) | Medium (~200 MHz) | High (500+ MHz) | | Pipeline stages | 0 (combinational) | 1 (address/data separated) | Fully pipelined | | Outstanding transactions | 1 | 1 | $2^{\text{ID width}}$ | | Typical use | GPIO, UARTs | Mid-speed periph | Cache, memory, DMA | **Why AXI is faster:**

\text{Bandwidth}{\text{AXI}} = \text{Clock} \times \text{Width} \times \underbrace{\text{Pipeline depth}}{\text{APB/AHB = 1}}

AXI pipeline depth ≈ 8-16 → 8-16× more transactions in flight. **When to use each:** - **APB**: < 1 MHz data rate, minimize gates (e.g., config registers) - **AHB**: 100 Mbps-1 Gbps, single master (e.g., microcontroller bus) - **AXI**: > 10 Gbps, multi-master (e.g., SoC interconnect) > [!mnemonic] AXI Five Channel Mnemonic > **"A War Between Real Rivals"** > - **A**W: Address Write > - **W**: Write data > - **B**: (write) Back response > - **R**: Read data** (again): aR (Address Read) Or think: **Two ways in (AW+W), one back (B). One way out (AR), one back (R).** > [!recall]- Explain to a 12-year-old > Imagine you're playing a video game where you control a factory. The factory has: > - **Workers** (CPU, GPU, DMA) who need to send packages (data) > - **Warehouses** (memory, peripherals) that store stuff > - **Conveyor belts** (buses) to move packages Old system (APB): One narrow conveyor belt. Only one package at a time. If someone sends a big package, everyone waits. Slow! Better system (AXI): Five separate conveyor belts! 1. "I want to send a package" belt (Address Write) 2. "Here's the package" belt (Write Data) 3. "Got it, thanks!" belt (Write Response) 4. "I need a package" belt (Address Read) 5. "Here's what you asked for" belt (Read Data) **Magic trick**: While Worker A is still sending their package on belt 2, Worker B can already ask for something on belt 4! They don't wait for each other. That's why it's so much faster. Also, each worker has a **special ID badge**. Even if packages arrive out of order, the warehouses can match them back to who asked. Like when you order pizza online and track it with an order number! ## Connections - [[6.3.01-bus-architectures-and-topologies]] - AXI is a crossbar/mesh interconnect - [[6.3.04-memory-coherence-protocols]] - AXI ACE extends AXI for cache coherence - [[6.2.05-dma-controllers]] - DMA engines are AXI master devices - [[7.1.03-arm-architectureoverview]] - AXI is ARM's standard (but used by others too) - [[6.3.08-pcie-architecture]] - PCIe is the off-chip equivalent for host-device links #flashcards/hardware What are the five AXI channels and their purpose? :: Write: AW (write address), W (write data), B (write response). Read: AR (read address), R (read data+response). Separation enables channel-level parallelism. Why does AXI use both VALID and READY handshake signals? ::: Prevents deadlock and data loss. VALID indicates source has data, READY indicates destination can accept. Transfer occurs only when both are high, so neither party is forced to proceed before ready. What is the purpose of transaction IDs (ARID/AWID) in AXI? ::: Enable out-of-order completion. Multiple transactions can be in-flight simultaneously, and responses can return in any order. IDs match responses back to requests. Same-ID transactions must complete in order. How does AXI burst addressing work for INCR bursts? ::: Single address command specifies start address and burst length. Slave auto-increments address by transfer size (AWSIZE/ARSIZE) for each beat. Amortizes address overhead across multiple data transfers. What does the AXI write response (B) channel indicate? ::: Signals write completion and status. Returns BRESP (OKAY, SLVERR, DECERR) to indicate success or error, and BID to match which write transaction completed. Response sent only after data committed to final destination. What is the purpose of AxQOS signals in AXI? ::: 4-bit quality-of-service priority (0-15). Interconnect uses QoS for arbitration to ensure high-priority masters (e.g., CPU) get lower latency than best-effort masters (e.g., background DMA). Doesn't guarantee bandwidth, guarantees latency bounds. Why can't VALID drop before a transfer completes? ::: AXI spec requirement to prevent deadlock. If master drops VALID while slave preparing (READY not asserted yet), slave might have already started processing. VALID must remain high until transfer occurs (both VALID and READY seen together). What do WSTRB bits control in AXI write transactions? ::: Write strobes - one bit per byte lane. WSTRB[i]=1 means write byte i, WSTRB[i]=0 means leave that byte unchanged. Enables sub-word writes (e.g., writing a 16-bit value to a 64-bit bus). ## 🖼️ Concept Map ```mermaid flowchart TD AMBA[AMBA on-chip standards] -->|includes| AXI[AXI high-performance] AMBA -->|includes| AHB[AHB simple pipelined] AMBA -->|includes| APB[APB low-power peripherals] SCALE[Scalability crisis] -->|motivates| AXI SCALE -->|caused by| BLOCK[Blocking single-threaded buses] AXI -->|uses| CHAN[Five independent channels] CHAN -->|enables| PAR[Channel-level parallelism] PAR -->|allows| OOO[Out-of-order overlapping transactions] CHAN -->|write path| WR[AW then W then B] CHAN -->|read path| RD[AR then R combined] RD -->|combines data plus response| SAVE[Saves one channel] ``` ## 🔊 Hinglish (regional understanding) > [!intuition]- Hinglish mein samjho > ![[audio/6.3.07-AXI---AMBA-on-chip-protocols.mp3]] ## 🔊 Hinglish (regional understanding) > [!intuition]- Hinglish mein samjho > Dekhiye, AXI protocol ka core idea ye hai ki ek modern SoC mein bahut sare components hain - CPU, GPU, memory, peripherals - aur sabko ek sath efficiently communicate karna hai. Purane simple bus systems mein ek hi "phone line" thi, jismein agar koi slow device busy ho to sare devices wait karte the. AXI ne is problem ko solve kiya **five independent channels** banakar - do address channels (read aur write ke liye alag), do data channels, aur ek response channel. Is separation ki wajah se ek transaction ke address phase mein hi dosre transaction ka data flow ho sakta hai, bilkul jaise highway pe multiple lanes hotiain. Ye **channel-level parallelism** hi actual magic hai - jab CPU slow DRAM se read karaha ho, tab bhi wo fast cache mein write kar sakta hai simultaneously, kyunki dono operations alag channels use kar rahe hain. > > Is protocol ki zaroorat kyun hai? Kyunki apke smartphone ke andar lagbhag 100 Gbps ki internal data movement hoti hai har second. Agar ek slow UART peripheral pori bus ko block kar de, to aapka CPU cache miss pe atka rahega aur performance drastically gir jayegi. AXI ka **VALID-READY handshake** mechanism ensure karta hai ki koi bhi device data lose nahi kare chahe wo temporarily busy ho - sender VALID assert karta hai jab data ready ho, receiver READY assert karta hai jab wo accept kar sake, aur transfer tab hota hai jab dono signals high hon. Ye **flow control** mechanism realistic hardware constraints ko handle karta hai bina deadlock ke, isliye modern ARM-based chips (Apple M-series se lekar Qualcomm Snapdragon tak) sabhi AXI use karte hain apne internal communication ke liye. ## 🔊 Hinglish (regional understanding) > [!intuition]- Hinglish mein samjho > Dekho beta, imagine ek modern chip ke andar dozens components hain — CPU, GPU, memory controller, chhote-chhote peripherals — aur sabko aapas mein baat karni hai. Agar sab ek hi simple shared bus use karein, toh woh ekdum ek phone line pe 50 log baat karne jaisa ho jaayega — chaos! Isliye ARM ne AMBA family banaya, jisme AXI high-performance protocol hai. Core intuition yeh hai ki AXI ek smart traffic system ki tarah kaam karta hai jo multiple conversations ek saath handle karta hai, urgent requests ko priority deta hai, aur deadlocks ko rokta hai. Simple AHB aur APB bhi hain, lekin AXI highway system jaisa hai — fast, multi-lane, complex. > > Ab AXI ka sabse clever design idea hai paanch independent channels. Write ke liye teen channels (address, data, response) aur read ke liye do (address, aur data+response combined). Iska matlab yeh ki jab ek channel pe address travel kar raha hota hai, tab dusre channel pe data flow ho sakta hai — yani channel-level parallelism. Iska practical fayda samjho: agar CPU DRAM se read kar raha hai (slow) aur cache mein write kar raha hai (fast) same time pe, toh cache write ko DRAM read ke complete hone ka wait nahi karna padta, kyunki channels alag-alag independent hain. Yeh overlapping transactions hi modern SoC ki ~100 Gbps internal data movement ko possible banate hain. > > Aur ek important cheez hai handshake protocol — har channel pe VALID aur READY do signals hote hain. Transfer tabhi hota hai jab dono ek high ho aur clock ka rising edge aaye. Yeh dono kyun zaruri hain? Agar sirf VALID hota, toh slave busy hone par bhi data accept karna padta, aur data drop ho jaata. Agar sirf READY hota, toh master ko data ready na hone par bhi bhejna padta, aur garbage chala jaata. Isliye dono side ka confirmation zaruri hai — yeh ensure karta hai ki data tabhi move ho jab dono taraf ready ho. Yeh concept isliye matter karta hai kyunki har modern smartphone, laptop, ya embedded system ke andar exactly yahi protocols reliably data move karwa rahe hain.

Go deeper — visual, from zero

Test yourself — Interconnects, Buses & SoC

Connections