Skip to content

AMBA AXI · Module 15

FIFO-Based Buffering

Use FIFOs to decouple producer and consumer rates on AXI — absorbing bursts, smoothing rate mismatches, and buffering across latency that a two-entry skid can't. The ready/valid FIFO wrapper, depth sizing for burst and rate-mismatch, sync vs async FIFOs, and where FIFOs belong on the five channels.

A skid buffer holds two beats — enough to register a handshake, not enough to decouple two sides that run at different rates or in bursts. When a producer emits a burst faster than the consumer drains it, or the consumer pauses for many cycles, you need real depth: a FIFO. Wrapped in a VALID/READY interface, a FIFO is a drop-in buffer that absorbs bursts, smooths rate mismatches, and bridges latency, all while preserving order and the handshake contract. This chapter builds the ready/valid FIFO wrapper, derives how to size its depth for burst absorption and rate mismatch, distinguishes synchronous from asynchronous (clock-crossing) FIFOs, and places FIFOs correctly on the AXI channels — the last building-block primitive before we assemble reusable RTL templates.

1. FIFO vs. Skid: When Two Slots Aren't Enough

A skid buffer and a FIFO both expose the same VALID/READY interface and both preserve order — the difference is depth and purpose. A skid buffer's two slots exist to register the handshake for timing; it cannot absorb a burst or a long stall. A FIFO has N slots sized for buffering: it lets the producer keep running while the consumer is busy (and vice-versa), decoupling their instantaneous rates as long as the FIFO doesn't fill or empty.

Skid buffer two slots for timing; FIFO N slots for buffering; both VALID/READY and order-preserving.Skid (2 slots)registers handshakePurpose: timingno burst absorbSame interfaceVALID/READY, in-orderFIFO (N slots)decouples ratesPurpose: bufferingabsorb burst/stallDepth = Nsized to need12
Figure 1 — skid buffer vs. FIFO. Both present a VALID/READY interface and preserve order. The skid buffer (two slots) registers the handshake for timing closure and cannot absorb bursts. The FIFO (N slots) decouples producer and consumer rates — the producer fills while the consumer drains — absorbing bursts and rate mismatches up to its depth. Choose by purpose: skid for timing, FIFO for buffering.

2. The Ready/Valid FIFO Wrapper

A FIFO becomes an AXI-friendly buffer by mapping its full/empty/push/pop flags onto the handshake: accept upstream when not full (s_ready = !full, push on s_valid && s_ready), present downstream when not empty (m_valid = !empty, pop on m_valid && m_ready). Order is intrinsic to a FIFO, so the channel's ordering is preserved automatically.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module axi_fifo #(parameter int W = 32, parameter int DEPTH = 16) (
  input  logic         clk, rstn,
  input  logic [W-1:0] s_data,  input  logic s_valid, output logic s_ready,
  output logic [W-1:0] m_data,  output logic m_valid, input  logic m_ready
);
  localparam int AW = $clog2(DEPTH);
  logic [W-1:0] mem [DEPTH];
  logic [AW:0]  wptr, rptr;                 // extra MSB to tell full from empty
  wire full  = (wptr[AW] != rptr[AW]) && (wptr[AW-1:0] == rptr[AW-1:0]);
  wire empty = (wptr == rptr);
 
  assign s_ready = !full;
  assign m_valid = !empty;
  assign m_data  = mem[rptr[AW-1:0]];
 
  always_ff @(posedge clk) begin
    if (!rstn) begin wptr <= '0; rptr <= '0; end
    else begin
      if (s_valid && s_ready) begin mem[wptr[AW-1:0]] <= s_data; wptr <= wptr + 1'b1; end
      if (m_valid && m_ready) rptr <= rptr + 1'b1;
    end
  end
endmodule

This wrapper drops into any AXI channel exactly where a skid buffer would — same ports — but with depth DEPTH instead of 2. (For full throughput at speed you may still wrap a skid buffer around the FIFO's output, since a simple FIFO read can have a combinational empty → m_valid path; production FIFOs register the output.)

Upstream push when not full; write pointer; FIFO memory; read pointer; downstream pop when not empty; full/empty from pointer comparison.Pushs_valid && !fullWrite pointeradvances on pushFIFO memoryDEPTH entriesPopm_ready && !emptyRead pointeradvances on popfull / emptyptr compare12
Figure 2 — the ready/valid FIFO wrapper. The upstream handshake pushes on s_valid && !full; the downstream handshake pops on m_ready && !empty. Write and read pointers (with an extra MSB bit) track occupancy and distinguish full from empty. The FIFO presents the standard VALID/READY interface, so it drops into a channel wherever buffering is needed.

3. Sizing the Depth

Depth is the design decision a FIFO forces. Too shallow and it fills (back-pressuring the producer, defeating the point); too deep and it wastes area and adds latency. Three sizing drivers:

  • Burst absorption. To swallow a burst of B beats while the consumer is stalled, you need DEPTH ≥ B (plus margin). E.g. buffering one AXI burst of up to 256 beats needs depth ≥ 256 if the consumer may be fully stalled across it.
  • Rate mismatch over a window. If the producer averages r_p beats/cycle and the consumer r_c < r_p over a bursty window of length T, the FIFO must hold the overflow (r_p − r_c)·T. Sustained r_p > r_c cannot be fixed by any finite FIFO — it only smooths transient mismatches.
  • Latency / round-trip coverage. To keep a pipeline full across a consumer that responds with latency L (e.g. a credit return or a memory round-trip), the FIFO must hold ≥ L beats so the producer never starves waiting — the same idea as covering outstanding depth (Chapter 8.x).
Depth from burst absorption, rate-mismatch transient overflow, and latency coverage; take the maximum plus margin.Burst absorbDEPTH ≥ burst beatsRate mismatch≥ gap × windowLatency cover≥ round-trip beatsDEPTH = max(...)+ marginSustained deficitno FIFO fixes it12
Figure 3 — the three depth-sizing drivers. Burst absorption sets DEPTH ≥ burst length the consumer might stall across. Rate-mismatch buffering sets DEPTH ≥ the transient overflow (rate gap × window) — a FIFO cannot fix a sustained rate deficit. Latency coverage sets DEPTH ≥ the round-trip beats needed to avoid starving. Take the max of the applicable drivers, plus margin.

4. Synchronous vs. Asynchronous FIFOs, and Where They Go on AXI

A synchronous FIFO has one clock for both ports — the common case for buffering within a clock domain. An asynchronous FIFO has separate write and read clocks and is the canonical safe clock-domain crossing for a data stream (Chapter 14.2): Gray-coded pointers cross the boundary through synchronizers so the full/empty comparison is metastability-safe. On AXI, FIFOs appear per channel as needed:

  • Data FIFOs on W and R to buffer burst payloads (decouple the data mover from memory rate).
  • Address/command FIFOs on AW/AR to queue outstanding requests (more in flight without stalling).
  • Response FIFOs on B/R so responses don't back-pressure the data path.
  • Async FIFOs on any channel that crosses a clock domain — the standard AXI CDC bridge buffers each channel through its own async FIFO.
Need buffering: same clock uses sync FIFO; crossing clocks uses async FIFO with Gray pointers; both present VALID/READY.noyesNeed a buffer ona channelCross clockdomain?Sync FIFO (1 clock)Async FIFO (Grayptrs + sync)VALID/READYinterface
Figure 4 — sync vs. async FIFOs and their AXI placement. A synchronous FIFO buffers within one clock domain (data/command/response queues on the five channels). An asynchronous FIFO (separate read/write clocks, Gray-coded pointers through synchronizers) is the safe clock-domain crossing — the standard AXI CDC bridge puts one async FIFO per channel. Same VALID/READY interface either way.

5. Common Misconceptions

6. Debugging Insight

7. Verification Insight

8. Interview Questions

9. Summary

A FIFO is the buffering primitive that a two-slot skid buffer can't be: wrapped in a VALID/READY interface (s_ready = !full, m_valid = !empty, push/pop on the handshakes), it decouples producer and consumer rates, absorbing bursts and smoothing transient mismatches while preserving order. Its defining design choice is depth, sized to the max of three drivers — burst absorption (≥ burst beats the consumer may stall across), transient rate mismatch (≥ rate-gap × window), and latency coverage (≥ round-trip beats) — with the hard caveat that no FIFO fixes a sustained rate deficit. Distinguishing full from empty needs the extra-MSB pointer trick (or a counter), and getting it wrong causes overflow/underflow.

Synchronous FIFOs buffer within a clock domain; asynchronous FIFOs (Gray-coded pointers through synchronizers) are the safe clock-domain crossing — the standard AXI CDC bridge places one async FIFO per channel. On AXI, FIFOs sit wherever decoupling is needed: data FIFOs on W/R, command FIFOs on AW/AR (deepening outstanding), response FIFOs on B/R (avoiding response backpressure). The system-level hazard is undersizing into deadlock when a FIFO sits in a dependency loop — so response-FIFO depth ties to maximum outstanding count. Verification asserts no overflow/underflow, conservation/ordering, and full/empty correctness, with CDC-aware checks for async FIFOs. With skid buffers, pipelines, and FIFOs in hand, we can now assemble them into reusable, parameterised AXI RTL templates.

10. What Comes Next

You now have the full set of flow-control building blocks; next we package them for reuse:

  • 15.8 — Reusable AXI RTL Templates (coming next) — drop-in, parameterised AXI RTL building blocks (slaves, register banks, skid buffers, FIFOs, width/protocol adapters) assembled into a reusable library.

Previous: 15.6 — Ready/Valid Pipelining. Related: 15.5 — The Skid Buffer for the two-slot timing primitive, 14.2 — Asynchronous Bridges for async-FIFO clock crossing, and 8.5 — Outstanding in the Interconnect for the outstanding-depth sizing that drives command/response FIFO depth.