Ready-to-run snippets

Code Examples

Ready-to-run Verilog, SystemVerilog, and UVM examples. Click any to open it live in the IDE.

← All examples

AXI4-Lite interconnect with a default slave

Verilog Bus Protocols

One master, two register slaves and a DECERR default slave behind a registered AXI4-Lite interconnect.

Open in IDE
design.v
// A one-master, two-slave AXI4-Lite interconnect with a default slave.
//   0x000 - 0x3FF  slave 0
//   0x400 - 0x7FF  slave 1
//   anything else  default slave: DECERR
// The address is decoded on AW and AR, the transaction is passed to the selected slave with the
// region bits removed, and the response is routed back. One write and one read can be in flight.
// Every output comes from a register, so there is no combinational path through the interconnect.
module axil_xbar (
  input             ACLK,
  input             ARESETn,
  // from the master
  input      [11:0] S_AWADDR,
  input             S_AWVALID,
  output            S_AWREADY,
  input      [31:0] S_WDATA,
  input      [3:0]  S_WSTRB,
  input             S_WVALID,
  output            S_WREADY,
  output     [1:0]  S_BRESP,
  output            S_BVALID,
  input             S_BREADY,
  input      [11:0] S_ARADDR,
  input             S_ARVALID,
  output            S_ARREADY,
  output     [31:0] S_RDATA,
  output     [1:0]  S_RRESP,
  output            S_RVALID,
  input             S_RREADY,
  // to the two slaves (index 0 and 1 packed into vectors)
  output     [23:0] M_AWADDR,
  output     [1:0]  M_AWVALID,
  input      [1:0]  M_AWREADY,
  output     [31:0] M_WDATA,
  output     [3:0]  M_WSTRB,
  output     [1:0]  M_WVALID,
  input      [1:0]  M_WREADY,
  input      [3:0]  M_BRESP,
  input      [1:0]  M_BVALID,
  output     [1:0]  M_BREADY,
  output     [23:0] M_ARADDR,
  output     [1:0]  M_ARVALID,
  input      [1:0]  M_ARREADY,
  input      [63:0] M_RDATA,
  input      [3:0]  M_RRESP,
  input      [1:0]  M_RVALID,
  output     [1:0]  M_RREADY
);
  localparam DECERR = 2'b11;

  // Address decoder: 0 = slave 0, 1 = slave 1, 2 = default slave
  function [1:0] decode(input [11:0] a);
    decode = (a[11:10] == 2'b00) ? 2'd0 : (a[11:10] == 2'b01) ? 2'd1 : 2'd2;
  endfunction

  // ---- Write path: every signal towards either side comes from a register ----
  reg        w_busy;                     // a write transaction is in progress
  reg [1:0]  w_sel;                      // where it goes
  reg [9:0]  w_addr;
  reg        aw_sent;                    // the address has been passed on
  reg        w_have;                     // write data held, not yet passed on
  reg        w_sent;                     // the data has been passed on
  reg [31:0] w_data;
  reg [3:0]  w_strb;
  reg        b_have;                     // a write response is held for the master
  reg [1:0]  b_resp;

  assign S_AWREADY = !w_busy;
  assign S_WREADY  = w_busy && !w_have && !w_sent;     // data is taken once the destination is known
  assign S_BVALID  = b_have;
  assign S_BRESP   = b_resp;

  assign M_AWADDR  = {2'b00, w_addr, 2'b00, w_addr};
  assign M_AWVALID = {w_busy && !aw_sent && w_sel == 2'd1, w_busy && !aw_sent && w_sel == 2'd0};
  assign M_WDATA   = w_data;
  assign M_WSTRB   = w_strb;
  assign M_WVALID  = {w_have && w_sel == 2'd1, w_have && w_sel == 2'd0};
  assign M_BREADY  = {w_busy && !b_have && w_sel == 2'd1, w_busy && !b_have && w_sel == 2'd0};

  always @(posedge ACLK) begin
    if (!ARESETn) begin
      w_busy  <= 1'b0;
      w_sel   <= 2'd0;
      aw_sent <= 1'b0;
      w_have  <= 1'b0;
      w_sent  <= 1'b0;
      b_have  <= 1'b0;
    end
    else begin
      if (S_AWVALID && S_AWREADY) begin
        w_busy  <= 1'b1;
        w_sel   <= decode(S_AWADDR);
        w_addr  <= S_AWADDR[9:0];
        aw_sent <= 1'b0;
        w_sent  <= 1'b0;
      end
      if (w_busy && !aw_sent && (w_sel == 2'd2 || M_AWREADY[w_sel]))
        aw_sent <= 1'b1;
      if (S_WVALID && S_WREADY) begin
        w_have <= 1'b1;
        w_data <= S_WDATA;
        w_strb <= S_WSTRB;
      end
      if (w_have && (w_sel == 2'd2 || M_WREADY[w_sel])) begin
        w_have <= 1'b0;
        w_sent <= 1'b1;
        if (w_sel == 2'd2) begin
          b_have <= 1'b1;                // the default slave answers once it has the data
          b_resp <= DECERR;
        end
      end
      if (w_sel != 2'd2 && w_busy && !b_have && M_BVALID[w_sel]) begin
        b_have <= 1'b1;                  // take the slave's response
        b_resp <= (w_sel == 2'd1) ? M_BRESP[3:2] : M_BRESP[1:0];
      end
      if (S_BVALID && S_BREADY) begin
        b_have <= 1'b0;
        w_busy <= 1'b0;
      end
    end
  end

  // ---- Read path ----
  reg        r_busy;
  reg [1:0]  r_sel;
  reg [9:0]  r_addr;
  reg        ar_sent;
  reg        r_have;                     // read data held for the master
  reg [31:0] r_data;
  reg [1:0]  r_resp;

  assign S_ARREADY = !r_busy;
  assign S_RVALID  = r_have;
  assign S_RDATA   = r_data;
  assign S_RRESP   = r_resp;

  assign M_ARADDR  = {2'b00, r_addr, 2'b00, r_addr};
  assign M_ARVALID = {r_busy && !ar_sent && r_sel == 2'd1, r_busy && !ar_sent && r_sel == 2'd0};
  assign M_RREADY  = {r_busy && !r_have && r_sel == 2'd1, r_busy && !r_have && r_sel == 2'd0};

  always @(posedge ACLK) begin
    if (!ARESETn) begin
      r_busy  <= 1'b0;
      r_sel   <= 2'd0;
      ar_sent <= 1'b0;
      r_have  <= 1'b0;
    end
    else begin
      if (S_ARVALID && S_ARREADY) begin
        r_busy  <= 1'b1;
        r_sel   <= decode(S_ARADDR);
        r_addr  <= S_ARADDR[9:0];
        ar_sent <= 1'b0;
      end
      if (r_busy && !ar_sent && (r_sel == 2'd2 || M_ARREADY[r_sel])) begin
        ar_sent <= 1'b1;
        if (r_sel == 2'd2) begin
          r_have <= 1'b1;                // the default slave answers at once
          r_data <= 32'h0;
          r_resp <= DECERR;
        end
      end
      if (r_sel != 2'd2 && r_busy && !r_have && M_RVALID[r_sel]) begin
        r_have <= 1'b1;                  // take the slave's read data
        r_data <= (r_sel == 2'd1) ? M_RDATA[63:32] : M_RDATA[31:0];
        r_resp <= (r_sel == 2'd1) ? M_RRESP[3:2] : M_RRESP[1:0];
      end
      if (S_RVALID && S_RREADY) begin
        r_have <= 1'b0;
        r_busy <= 1'b0;
      end
    end
  end
endmodule

// The interconnect with two copies of the AXI4-Lite register slave behind it
module axil_sys (
  input         ACLK,
  input         ARESETn,
  input  [11:0] AWADDR,
  input         AWVALID,
  output        AWREADY,
  input  [31:0] WDATA,
  input  [3:0]  WSTRB,
  input         WVALID,
  output        WREADY,
  output [1:0]  BRESP,
  output        BVALID,
  input         BREADY,
  input  [11:0] ARADDR,
  input         ARVALID,
  output        ARREADY,
  output [31:0] RDATA,
  output [1:0]  RRESP,
  output        RVALID,
  input         RREADY
);
  wire [23:0] awaddr;
  wire [1:0]  awvalid, awready, wvalid, wready, bvalid, bready, arvalid, arready, rvalid, rready;
  wire [31:0] wdata;
  wire [3:0]  wstrb;
  wire [3:0]  bresp;
  wire [3:0]  rresp;
  wire [23:0] araddr;
  wire [63:0] rdata;

  axil_xbar xbar (
    .ACLK      (ACLK),
    .ARESETn   (ARESETn),
    .S_AWADDR  (AWADDR),
    .S_AWVALID (AWVALID),
    .S_AWREADY (AWREADY),
    .S_WDATA   (WDATA),
    .S_WSTRB   (WSTRB),
    .S_WVALID  (WVALID),
    .S_WREADY  (WREADY),
    .S_BRESP   (BRESP),
    .S_BVALID  (BVALID),
    .S_BREADY  (BREADY),
    .S_ARADDR  (ARADDR),
    .S_ARVALID (ARVALID),
    .S_ARREADY (ARREADY),
    .S_RDATA   (RDATA),
    .S_RRESP   (RRESP),
    .S_RVALID  (RVALID),
    .S_RREADY  (RREADY),
    .M_AWADDR  (awaddr),
    .M_AWVALID (awvalid),
    .M_AWREADY (awready),
    .M_WDATA   (wdata),
    .M_WSTRB   (wstrb),
    .M_WVALID  (wvalid),
    .M_WREADY  (wready),
    .M_BRESP   (bresp),
    .M_BVALID  (bvalid),
    .M_BREADY  (bready),
    .M_ARADDR  (araddr),
    .M_ARVALID (arvalid),
    .M_ARREADY (arready),
    .M_RDATA   (rdata),
    .M_RRESP   (rresp),
    .M_RVALID  (rvalid),
    .M_RREADY  (rready)
  );

  genvar k;
  generate
    for (k = 0; k < 2; k = k + 1) begin : slave
      axil_regs regs (
        .ACLK    (ACLK),
        .ARESETn (ARESETn),
        .AWADDR  (awaddr[12*k +: 12]),
        .AWPROT  (3'b000),
        .AWVALID (awvalid[k]),
        .AWREADY (awready[k]),
        .WDATA   (wdata),
        .WSTRB   (wstrb),
        .WVALID  (wvalid[k]),
        .WREADY  (wready[k]),
        .BRESP   (bresp[2*k +: 2]),
        .BVALID  (bvalid[k]),
        .BREADY  (bready[k]),
        .ARADDR  (araddr[12*k +: 12]),
        .ARPROT  (3'b000),
        .ARVALID (arvalid[k]),
        .ARREADY (arready[k]),
        .RDATA   (rdata[32*k +: 32]),
        .RRESP   (rresp[2*k +: 2]),
        .RVALID  (rvalid[k]),
        .RREADY  (rready[k])
      );
    end
  endgenerate
endmodule
// An AXI4-Lite register slave with four 32-bit registers.
//   0x00  CTRL    read/write
//   0x04  DATA    read/write, byte writes through WSTRB
//   0x08  COUNT   read-only: number of writes accepted so far
//   0x0C  ID      read-only: 0x41584C31 ("AXL1")
//   anything else: SLVERR. Writes to the read-only registers are ignored.
// The write address and write data are accepted independently, in either order.
module axil_regs (
  input             ACLK,
  input             ARESETn,
  // write address channel
  input      [11:0] AWADDR,
  input      [2:0]  AWPROT,
  input             AWVALID,
  output            AWREADY,
  // write data channel
  input      [31:0] WDATA,
  input      [3:0]  WSTRB,
  input             WVALID,
  output            WREADY,
  // write response channel
  output reg [1:0]  BRESP,
  output reg        BVALID,
  input             BREADY,
  // read address channel
  input      [11:0] ARADDR,
  input      [2:0]  ARPROT,
  input             ARVALID,
  output            ARREADY,
  // read data channel
  output reg [31:0] RDATA,
  output reg [1:0]  RRESP,
  output reg        RVALID,
  input             RREADY
);
  localparam OKAY = 2'b00, SLVERR = 2'b10;

  reg [31:0] ctrl;
  reg [31:0] data;
  reg [31:0] count;

  // Holding registers: an address or data that arrived before its partner
  reg        aw_full;
  reg [11:0] aw_addr;
  reg        w_full;
  reg [31:0] w_data;
  reg [3:0]  w_strb;

  assign AWREADY = !aw_full;
  assign WREADY  = !w_full;

  // The write happens once both halves are here and the response channel is free
  wire do_write = aw_full && w_full && (!BVALID || BREADY);

  always @(posedge ACLK) begin
    if (!ARESETn) begin
      aw_full <= 1'b0;
      w_full  <= 1'b0;
      BVALID  <= 1'b0;
      ctrl    <= 32'h0;
      data    <= 32'h0;
      count   <= 32'h0;
    end
    else begin
      if (AWVALID && AWREADY) begin
        aw_full <= 1'b1;
        aw_addr <= AWADDR;
      end
      if (WVALID && WREADY) begin
        w_full <= 1'b1;
        w_data <= WDATA;
        w_strb <= WSTRB;
      end
      if (BVALID && BREADY)
        BVALID <= 1'b0;
      if (do_write) begin
        aw_full <= 1'b0;
        w_full  <= 1'b0;
        BVALID  <= 1'b1;
        BRESP   <= (aw_addr[11:4] == 8'h00) ? OKAY : SLVERR;
        if (aw_addr[11:4] == 8'h00)
          count <= count + 32'd1;
        if (aw_addr == 12'h000)
          ctrl <= w_data;
        if (aw_addr == 12'h004) begin
          if (w_strb[0])
            data[7:0]   <= w_data[7:0];
          if (w_strb[1])
            data[15:8]  <= w_data[15:8];
          if (w_strb[2])
            data[23:16] <= w_data[23:16];
          if (w_strb[3])
            data[31:24] <= w_data[31:24];
        end
      end
    end
  end

  // Reads: one at a time, the address is accepted when the read data channel is free
  assign ARREADY = !RVALID;

  always @(posedge ACLK) begin
    if (!ARESETn) begin
      RVALID <= 1'b0;
    end
    else begin
      if (RVALID && RREADY)
        RVALID <= 1'b0;
      if (ARVALID && ARREADY) begin
        RVALID <= 1'b1;
        RRESP  <= (ARADDR[11:4] == 8'h00) ? OKAY : SLVERR;
        case (ARADDR)
          12'h000: RDATA <= ctrl;
          12'h004: RDATA <= data;
          12'h008: RDATA <= count;
          12'h00C: RDATA <= 32'h41584C31;
          default: RDATA <= 32'h0;
        endcase
      end
    end
  end
endmodule
testbench.v
// Testbench: one master, an AXI4-Lite interconnect, two register slaves and a default slave
module tb;
  reg         ACLK    = 0;
  reg         ARESETn = 0;
  // write address channel
  wire [3:0]  AWID;
  wire [11:0] AWADDR;
  wire [7:0]  AWLEN;
  wire [2:0]  AWSIZE;
  wire [1:0]  AWBURST;
  wire        AWLOCK;
  wire        AWVALID;
  wire        AWREADY;
  // write data channel
  wire [31:0] WDATA;
  wire [3:0]  WSTRB;
  wire        WLAST;
  wire        WVALID;
  wire        WREADY;
  // write response channel
  wire [3:0]  BID;
  wire [1:0]  BRESP;
  wire        BVALID;
  wire        BREADY;
  // read address channel
  wire [3:0]  ARID;
  wire [11:0] ARADDR;
  wire [7:0]  ARLEN;
  wire [2:0]  ARSIZE;
  wire [1:0]  ARBURST;
  wire        ARLOCK;
  wire        ARVALID;
  wire        ARREADY;
  // read data channel
  wire [3:0]  RID;
  wire [31:0] RDATA;
  wire [1:0]  RRESP;
  wire        RLAST;
  wire        RVALID;
  wire        RREADY;

  localparam FIXED = 2'b00, INCR = 2'b01, WRAP = 2'b10;
  localparam BYTE = 3'd0, HALF = 3'd1, WORD = 3'd2;

  // ---- Command lists ----
  reg  [3:0]  aw_id    [0:63];
  reg  [11:0] aw_addr  [0:63];
  reg  [7:0]  aw_len   [0:63];
  reg  [2:0]  aw_size  [0:63];
  reg  [1:0]  aw_burst [0:63];
  reg         aw_lock  [0:63];
  integer     aw_delay [0:63];           // idle cycles before AWVALID
  integer     aw_after [0:63];           // read bursts that must complete first
  reg  [31:0] w_data   [0:255];
  reg  [3:0]  w_strb   [0:255];
  reg         w_last   [0:255];
  integer     w_delay  [0:255];          // idle cycles before WVALID
  reg  [3:0]  ar_id    [0:63];
  reg  [11:0] ar_addr  [0:63];
  reg  [7:0]  ar_len   [0:63];
  reg  [2:0]  ar_size  [0:63];
  reg  [1:0]  ar_burst [0:63];
  reg         ar_lock  [0:63];
  integer     ar_delay [0:63];           // idle cycles before ARVALID
  integer     ar_after [0:63];           // write responses that must arrive first
  integer     awn = 0, wn = 0, arn = 0;  // list lengths
  integer     awi = 0, wi = 0, ari = 0;  // next entry to send
  integer     aw_cnt = 0, w_cnt = 0, ar_cnt = 0;
  integer     bn = 0;                    // write responses received
  integer     rlasts = 0;                // read bursts completed
  integer     max_reads = 4;             // read transactions allowed in flight
  reg  [31:0] bready_pat = 32'hFFFFFFFF; // BREADY pattern, one bit per cycle
  reg  [31:0] rready_pat = 32'hFFFFFFFF; // RREADY pattern, one bit per cycle
  integer     cyc = 0;
  reg         run = 0;                   // VALIDs may rise only after reset
  integer     after_r = 0;               // ordering between channels for the next write ...
  integer     after_b = 0;               // ... and the next read

  // Address of the next beat (FIXED, INCR or WRAP)
  function [11:0] next_addr(input [11:0] a, input [2:0] size, input [1:0] burst, input [7:0] len);
    reg [11:0] bytes;
    reg [11:0] incr;
    reg [11:0] wrap_bytes;
    begin
      bytes = 12'd1 << size;
      incr  = (a & ~(bytes - 12'd1)) + bytes;
      if (burst == FIXED) begin
        next_addr = a;
      end
      else if (burst == WRAP) begin
        wrap_bytes = bytes * (len + 12'd1);
        next_addr  = (a & ~(wrap_bytes - 12'd1)) | (incr & (wrap_bytes - 12'd1));
      end
      else begin
        next_addr = incr;
      end
    end
  endfunction

  // A write burst: one AW entry and len+1 W entries. Beat k carries d0 + k on its byte lanes;
  // with pattern = 1, every byte carries the low 8 bits of its own address instead.
  task write(input [3:0] id, input [11:0] a0, input [7:0] len, input [2:0] size, input [1:0] burst,
             input [31:0] d0, input pattern, input lock, input integer delay);
    integer    k;
    integer    b;
    reg [11:0] a;
    reg [11:0] bytes;
    reg [1:0]  lo;
    reg [1:0]  hi;
    begin
      aw_id[awn]    = id;
      aw_addr[awn]  = a0;
      aw_len[awn]   = len;
      aw_size[awn]  = size;
      aw_burst[awn] = burst;
      aw_lock[awn]  = lock;
      aw_delay[awn] = delay;
      aw_after[awn] = after_r;
      awn = awn + 1;
      bytes = 12'd1 << size;
      a = a0;
      for (k = 0; k <= len; k = k + 1) begin
        lo = a[1:0];                                     // lowest byte lane of this beat
        hi = ((a & ~(bytes - 12'd1)) + bytes - 12'd1) & 12'd3;   // highest byte lane
        w_strb[wn] = 4'b0000;
        w_data[wn] = 32'h0;
        for (b = 0; b < 4; b = b + 1) begin
          if (b >= lo && b <= hi) begin
            w_strb[wn][b] = 1'b1;
            if (pattern)
              w_data[wn][8*b +: 8] = (a & ~12'd3) + b;
          end
        end
        if (!pattern)
          w_data[wn] = (d0 + k) << (8 * ((a & ~(bytes - 12'd1)) & 12'd3));
        w_last[wn]  = (k == len);
        w_delay[wn] = 0;
        wn = wn + 1;
        a = next_addr(a, size, burst, len);
      end
    end
  endtask

  task read(input [3:0] id, input [11:0] a0, input [7:0] len, input [2:0] size, input [1:0] burst,
            input lock, input integer delay);
    begin
      ar_id[arn]    = id;
      ar_addr[arn]  = a0;
      ar_len[arn]   = len;
      ar_size[arn]  = size;
      ar_burst[arn] = burst;
      ar_lock[arn]  = lock;
      ar_delay[arn] = delay;
      ar_after[arn] = after_b;
      arn = arn + 1;
    end
  endtask

  // ---- Channel outputs: VALID never depends on READY ----
  assign AWVALID = run && awi < awn && aw_cnt >= aw_delay[awi] && rlasts >= aw_after[awi];
  assign AWID    = aw_id[awi];
  assign AWADDR  = aw_addr[awi];
  assign AWLEN   = aw_len[awi];
  assign AWSIZE  = aw_size[awi];
  assign AWBURST = aw_burst[awi];
  assign AWLOCK  = aw_lock[awi];
  assign WVALID  = run && wi < wn && w_cnt >= w_delay[wi];
  assign WDATA   = WVALID ? w_data[wi] : 32'h0;
  assign WSTRB   = WVALID ? w_strb[wi] : 4'h0;
  assign WLAST   = WVALID ? w_last[wi] : 1'b0;
  assign ARVALID = run && ari < arn && ar_cnt >= ar_delay[ari] && bn >= ar_after[ari] && (ari - rlasts) < max_reads;
  assign ARID    = ar_id[ari];
  assign ARADDR  = ar_addr[ari];
  assign ARLEN   = ar_len[ari];
  assign ARSIZE  = ar_size[ari];
  assign ARBURST = ar_burst[ari];
  assign ARLOCK  = ar_lock[ari];
  assign BREADY  = run && bready_pat[cyc % 32];
  assign RREADY  = run && rready_pat[cyc % 32];

  function [8*6-1:0] resp_name(input [1:0] r);
    resp_name = (r == 2'b00) ? "OKAY" : (r == 2'b01) ? "EXOKAY" : (r == 2'b10) ? "SLVERR" : "DECERR";
  endfunction

  function [8*5-1:0] burst_name(input [1:0] b);
    burst_name = (b == FIXED) ? "FIXED" : (b == INCR) ? "INCR" : "WRAP";
  endfunction

  // ---- Handshakes: log them and move each list forward ----
  always @(posedge ACLK) begin
    if (ARESETn) begin
      run <= 1'b1;
      cyc <= cyc + 1;
      aw_cnt <= aw_cnt + 1;
      w_cnt  <= w_cnt + 1;
      ar_cnt <= ar_cnt + 1;
      if (AWVALID && AWREADY) begin
        $display("%0t  AW  id=%0d addr=0x%h len=%0d size=%0d %0s%0s", $time, AWID, AWADDR, AWLEN, 1 << AWSIZE,
                 burst_name(AWBURST), AWLOCK ? " exclusive" : "");
        awi <= awi + 1;
        aw_cnt <= 0;
      end
      if (WVALID && WREADY) begin
        $display("%0t  W   data=0x%h strb=%b%0s", $time, WDATA, WSTRB, WLAST ? " last" : "");
        wi <= wi + 1;
        w_cnt <= 0;
      end
      if (BVALID && BREADY) begin
        $display("%0t  B   id=%0d %0s", $time, BID, resp_name(BRESP));
        bn <= bn + 1;
      end
      if (ARVALID && ARREADY) begin
        $display("%0t  AR  id=%0d addr=0x%h len=%0d size=%0d %0s%0s", $time, ARID, ARADDR, ARLEN, 1 << ARSIZE,
                 burst_name(ARBURST), ARLOCK ? " exclusive" : "");
        ari <= ari + 1;
        ar_cnt <= 0;
      end
      if (RVALID && RREADY) begin
        $display("%0t  R   id=%0d data=0x%h %0s%0s", $time, RID, RDATA, resp_name(RRESP), RLAST ? " last" : "");
        if (RLAST)
          rlasts <= rlasts + 1;
      end
    end
  end

  // The AXI4-Lite slave has no IDs and no RLAST: tie them for the full AXI master
  assign BID   = 4'd0;
  assign RID   = 4'd0;
  assign RLAST = 1'b1;

  axil_sys dut (
    .ACLK    (ACLK),
    .ARESETn (ARESETn),
    .AWADDR  (AWADDR),
    .AWVALID (AWVALID),
    .AWREADY (AWREADY),
    .WDATA   (WDATA),
    .WSTRB   (WSTRB),
    .WVALID  (WVALID),
    .WREADY  (WREADY),
    .BRESP   (BRESP),
    .BVALID  (BVALID),
    .BREADY  (BREADY),
    .ARADDR  (ARADDR),
    .ARVALID (ARVALID),
    .ARREADY (ARREADY),
    .RDATA   (RDATA),
    .RRESP   (RRESP),
    .RVALID  (RVALID),
    .RREADY  (RREADY)
  );

  always #5 ACLK = ~ACLK;

  initial begin
    $dumpfile("dump.vcd");
    $dumpvars(0, tb);
    write(0, 12'h000, 0, WORD, INCR, 32'h0000_0A00, 0, 0, 0);   // slave 0, CTRL
    write(0, 12'h400, 0, WORD, INCR, 32'h0000_0B00, 0, 0, 0);   // slave 1, CTRL
    write(0, 12'h800, 0, WORD, INCR, 32'h0000_0C00, 0, 0, 0);   // nobody: DECERR
    after_b = 3;
    read(0, 12'h000, 0, WORD, INCR, 0, 0);                      // slave 0, CTRL
    read(0, 12'h400, 0, WORD, INCR, 0, 0);                      // slave 1, CTRL
    read(0, 12'h40C, 0, WORD, INCR, 0, 0);                      // slave 1, ID
    read(0, 12'h408, 0, WORD, INCR, 0, 0);                      // slave 1, COUNT
    read(0, 12'hC00, 0, WORD, INCR, 0, 0);                      // nobody: DECERR
    #22 ARESETn = 1;
    wait (bn == awn && rlasts == arn && awi == awn && wi == wn);
    #20 $finish;
  end
endmodule
Admin mode — edit and delete controls are visible on each row. Use the IDE to add new snippets via File → Save as Snippet.