# AXI4-Lite mini slave

## What You Will Learn

AXI4-Lite splits reads and writes into address/data channels with valid/ready handshakes. `axi_lite_min` maps AW/W/B and AR/R into four registers—a thin but complete skeleton for studying bus protocol timing.

## Learning Objectives

- Identify write handshake `wr_hs` combining AW and W channels.
- Read read handshake `rd_hs` on AR channel.
- See back-pressure via `awready`/`wready` tied to `!bvalid`.
- Map `awaddr[3:2]` and `araddr[3:2]` to register indices.

## Theory

Writes latch data when both address and data phases are valid and the slave can accept (`!s_axi_bvalid`). `bvalid` holds until `bready`. Reads capture address on `rd_hs`, assert `rvalid` with `rdata`, clear when `rready`. Responses are always OKAY (`2'b00`).

This is not a full AXI compliance IP—it's minimal RTL to visualize five-channel flow without burst, protection, or strobes.

## When to use this pattern

AXI4-Lite fronts high-bandwidth SoC interconnects to control registers—DMA engines, GPIO, timers, and PHY config.

## Files

| File | Role |
|------|------|
| `tb/tb_axil.v` | Testbench — **`tb_axil`** |
| `rtl/axi_lite_min.v` | RTL / DUT — **`axi_lite_min`** |

## Practice Architecture

```mermaid
flowchart TB
  tb[tb_axil] -->|AXI4-Lite master signals| dut[axi_lite_min]
  dut -->|rdata bresp rresp| tb
```

## What to explore

- Waveform: follow one write from AW/W valid to B valid.
- Follow one read from AR valid to R valid.
- Explain why `awready` deasserts while `bvalid` is high.
- Compare register map to `apb_regs`—same four slots, different protocol.
- List channels you would add for full AXI4 (not Lite).

## Summary

AXI-Lite mini slave demystifies valid/ready handshakes on all five channels—stepping stone to vendor interconnect and Zynq PS ports.

# Appendix (Code Review)

This section is an optional deep dive into the example source files. Line numbers refer to the copies in your workspace under `rtl/` and `tb/`.

## rtl/axi_lite_min.v

**Purpose:** Minimal AXI4-Lite slave with 4×32b regs (`axi_lite_min`).

Lines 8–71 — Module `axi_lite_min`

```verilog
`default_nettype none
// Extremely thin AXI4-Lite-like slave: 4×32b regs (AW/W/B + AR/R handshakes).
module axi_lite_min (
  input wire        aclk,
  input wire        aresetn,
  // write address
  input wire [7:0]  s_axi_awaddr,
  input wire        s_axi_awvalid,
  output wire s_axi_awready,
  // write data
  input wire [31:0] s_axi_wdata,
  input wire        s_axi_wvalid,
  output wire s_axi_wready,
  // write response
  output wire [1:0]  s_axi_bresp,
  output reg s_axi_bvalid,
  input wire        s_axi_bready,
  // read address
  input wire [7:0]  s_axi_araddr,
  input wire        s_axi_arvalid,
  output wire s_axi_arready,
  // read data
  output reg [31:0] s_axi_rdata,
  output wire [1:0]  s_axi_rresp,
  output reg s_axi_rvalid,
  input wire        s_axi_rready
);
  reg [31:0] bank [0:3];
  wire [1:0]  aw_idx, ar_idx;
  wire wr_hs, rd_hs;
  integer i;

  assign aw_idx = s_axi_awaddr[3:2];
  assign ar_idx = s_axi_araddr[3:2];
  assign wr_hs  = s_axi_awvalid && s_axi_awready && s_axi_wvalid && s_axi_wready;
  assign rd_hs  = s_axi_arvalid && s_axi_arready;

  assign s_axi_awready = !s_axi_bvalid;
  assign s_axi_wready  = !s_axi_bvalid;
  assign s_axi_arready = !s_axi_rvalid;
  assign s_axi_bresp   = 2'b00;
  assign s_axi_rresp   = 2'b00;

  always @(posedge aclk or negedge aresetn) begin
    if (!aresetn) begin
      for (i = 0; i < 4; i = i + 1) bank[i] <= 32'h0;
      s_axi_bvalid <= 1'b0;
      s_axi_rvalid <= 1'b0;
      s_axi_rdata  <= 32'h0;
    end else begin
      if (wr_hs) begin
        bank[aw_idx] <= s_axi_wdata;
        s_axi_bvalid <= 1'b1;
      end else if (s_axi_bvalid && s_axi_bready) begin
        s_axi_bvalid <= 1'b0;
      end

      if (rd_hs) begin
        s_axi_rdata  <= bank[ar_idx];
        s_axi_rvalid <= 1'b1;
      end else if (s_axi_rvalid && s_axi_rready) begin
        s_axi_rvalid <= 1'b0;
      end
    end
  end
endmodule
```

**Code review:** Write path: AW+W valid with ready; `bank[aw_idx] <= wdata`; assert `bvalid`. Read path: AR handshake loads `rdata`, asserts `rvalid`. Indices: `aw_idx`, `ar_idx` from addr bits `[3:2]`. Back-pressure: `awready`/`wready` low while `bvalid`; `arready` low while `rvalid`. Responses: `bresp`, `rresp` tied OKAY.

## tb/tb_axil.v

**Purpose:** Testbench `tb_axil` — elaborates `axi_lite_min` and captures waves under `sim/`.

Lines 8–96 — Module `tb_axil`

```verilog
`timescale 1ns/1ps
/* Cycle-based only — avoid wait(...) (can stall ivl/vvp in WASM). */
module tb_axil;
  reg aclk, aresetn;
  reg [7:0]  s_axi_awaddr, s_axi_araddr;
  reg s_axi_awvalid, s_axi_awready;
  reg [31:0] s_axi_wdata;
  reg s_axi_wvalid, s_axi_wready;
  wire [1:0]  s_axi_bresp;
  reg s_axi_bvalid, s_axi_bready;
  reg s_axi_arvalid, s_axi_arready;
  wire [31:0] s_axi_rdata;
  wire [1:0]  s_axi_rresp;
  reg s_axi_rvalid, s_axi_rready;

  axi_lite_min u_dut (
    .aclk(aclk),
    .aresetn(aresetn),
    .s_axi_awaddr(s_axi_awaddr),
    .s_axi_awvalid(s_axi_awvalid),
    .s_axi_awready(s_axi_awready),
    .s_axi_wdata(s_axi_wdata),
    .s_axi_wvalid(s_axi_wvalid),
    .s_axi_wready(s_axi_wready),
    .s_axi_bresp(s_axi_bresp),
    .s_axi_bvalid(s_axi_bvalid),
    .s_axi_bready(s_axi_bready),
    .s_axi_araddr(s_axi_araddr),
    .s_axi_arvalid(s_axi_arvalid),
    .s_axi_arready(s_axi_arready),
    .s_axi_rdata(s_axi_rdata),
    .s_axi_rresp(s_axi_rresp),
    .s_axi_rvalid(s_axi_rvalid),
    .s_axi_rready(s_axi_rready)
  );

  initial aclk = 0;
  always #5 aclk = ~aclk;

  initial begin
    $dumpfile("sim/dump.vcd");
    $dumpvars(0, tb_axil);
    aresetn = 0;
    s_axi_awvalid = 0; s_axi_wvalid = 0; s_axi_bready = 0;
    s_axi_arvalid = 0; s_axi_rready = 0;
    s_axi_awaddr = 0; s_axi_araddr = 0; s_axi_wdata = 0;
    #30 aresetn = 1;

    // Write reg0
    @(posedge aclk);
    s_axi_awaddr = 8'h00; s_axi_wdata = 32'hA5A5_0001;
    s_axi_awvalid = 1; s_axi_wvalid = 1; s_axi_bready = 1;
    @(posedge aclk);
    s_axi_awvalid = 0; s_axi_wvalid = 0;
    @(posedge aclk);
    @(posedge aclk);
    s_axi_bready = 0;

    // Write reg1
    @(posedge aclk);
    s_axi_awaddr = 8'h04; s_axi_wdata = 32'hDEAD_BEEF;
    s_axi_awvalid = 1; s_axi_wvalid = 1; s_axi_bready = 1;
    @(posedge aclk);
    s_axi_awvalid = 0; s_axi_wvalid = 0;
    @(posedge aclk);
    @(posedge aclk);
    s_axi_bready = 0;

    // Read reg0
    @(posedge aclk);
    s_axi_araddr = 8'h00; s_axi_arvalid = 1; s_axi_rready = 1;
    @(posedge aclk);
    s_axi_arvalid = 0;
    @(posedge aclk);
    $display("RD0=%h", s_axi_rdata);
    @(posedge aclk);
    s_axi_rready = 0;

    // Read reg1
    @(posedge aclk);
    s_axi_araddr = 8'h04; s_axi_arvalid = 1; s_axi_rready = 1;
    @(posedge aclk);
    s_axi_arvalid = 0;
    @(posedge aclk);
    $display("RD1=%h", s_axi_rdata);
    @(posedge aclk);
    s_axi_rready = 0;

    #40 $finish;
  end
endmodule
```

**Code review:** `tb_axil` instantiates `axi_lite_min`. Look for `$dumpfile` / `$dumpvars` in an `initial` block and any loops or `$display` checks that report PASS/FAIL.

---

© 2026 RtlStudio. Licensed under [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/). Commercial use and modifications by third parties are not permitted.

© 2026 RtlStudio. All rights reserved. RTL Studio is proprietary software.