Skip to content

Commit

Permalink
Simple axil_ram testbench example (#274)
Browse files Browse the repository at this point in the history
* Added axil_ram example with testbench

* Update README

* Update README
  • Loading branch information
azaidy authored Jan 15, 2025
1 parent 31c9bb7 commit 02fe750
Show file tree
Hide file tree
Showing 4 changed files with 309 additions and 0 deletions.
17 changes: 17 additions & 0 deletions examples/axil_ram/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright (c) 2024 Zero ASIC Corporation
# This code is licensed under Apache License 2.0 (see LICENSE for details)

.PHONY: verilator
verilator:
./test.py --tool verilator

.PHONY: icarus
icarus:
./test.py --tool icarus

.PHONY: clean
clean:
rm -f queue-* *.q
rm -f *.vcd *.fst *.fst.hier
rm -rf obj_dir build
rm -f *.o *.vpi
41 changes: 41 additions & 0 deletions examples/axil_ram/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# axil example

NOTE: This example is similar to the one in axil. However, while axil auto generates the testbenches using the `interfaces` feature of `SbDut`, this example demonstrates a manual testbench using the switchboard axil module.

This example shows how to interact with AXI-Lite subordinates using switchboard. A Python script writes various sized data to an AXI-Lite memory implementation ([this one](https://github.com/alexforencich/verilog-axi/blob/master/rtl/axil_ram.v)). Data is then read back from the memory and result is printed to standard output.

To run the example, type `make`. You'll see a Verilator build, followed by output like this:

```text
Read addr=0 data=[0xef 0x0 0x0 0x0]
Read addr=0 data=[0xef 0xbe 0x0 0x0]
Read addr=0 data=[0xef 0xbe 0xad 0xde]
Read addr=200 data=[0xa0 0xa0 0xa0 0xa0]
Read addr=0 data=[0xef 0xbe 0xad 0xde]
```

`make icarus` runs the example using Icarus Verilog as the digital simulator.

In the Verilog implementation, [testbench.sv](testbench.sv) instantiates a switchboard module that acts as an AXI-Lite manager.

The init method defines that the switchboard queues to be used start with the prefix `sb_axil_m`: `sb_axil_m-aw.q`, `sb_axil_m-w.q`, `sb_axil_m-b.q`, `sb_axil_m-ar.q`, `sb_axil_m-r.q`.

In the Python script [test.py](test.py), a corresponding `AxiLiteTxRx` object is created, using the same shorthand for connecting to all 5 queues.

```python
axil = AxiLiteTxRx('sb_axil_m', data_width=..., addr_width=...)
```

As with `UmiTxRx`, this object may be used to issue read and write transactions involving numpy scalars and arrays. Under the hood, each transaction may be converted to multiple cycles of AXI transactions, with the write strobe automatically calculated in each cycle.

```python
axil.write(0, np.uint8(0xef))
read_data = axil.read(0, 4) # read_data will contain 0xef
```

The `dtype` can be something other than a byte (even if the data type is wider than `data_width`)

```python
axil.write(0, np.uint32(0xdeadbeef))
read_data = axil.read(0, 4) # read_data will contain 0xdeadbeef
```
80 changes: 80 additions & 0 deletions examples/axil_ram/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env python3

# Copyright (C) 2025 Zero ASIC
# This code is licensed under Apache License 2.0 (see LICENSE for details)

import numpy as np
from switchboard import SbDut, AxiLiteTxRx


def main():
# build the simulator
dut = build_testbench()

# launch the simulation
dut.simulate(
plusargs=[
('valid_mode', dut.args.vldmode),
('ready_mode', dut.args.rdymode)
]
)

# Switchboard queue initialization
axil = AxiLiteTxRx('sb_axil_m',
data_width=256,
addr_width=8,
fresh=True)

np.set_printoptions(formatter={'int': hex})

axil.write(0, np.uint8(0xef))
read_data = axil.read(0, 4)
print(f'Read addr=0 data={read_data}')

axil.write(0, np.uint16(0xbeef))
read_data = axil.read(0, 4)
print(f'Read addr=0 data={read_data}')

axil.write(0, np.uint32(0xdeadbeef))
read_data = axil.read(0, 4)
print(f'Read addr=0 data={read_data}')

axil.write(200, np.uint32(0xa0a0a0a0))
read_data = axil.read(200, 4)
print(f'Read addr=200 data={read_data}')

read_data = axil.read(0, 4)
print(f'Read addr=0 data={read_data}')


def build_testbench(fast=False):

extra_args = {
'--vldmode': dict(type=int, default=1, help='Valid mode'),
'--rdymode': dict(type=int, default=1, help='Ready mode'),
}

dut = SbDut('testbench', cmdline=True, extra_args=extra_args,
trace=False, trace_type='fst', default_main=True)

dut.register_source(
'verilog-axi',
'git+https://github.com/alexforencich/verilog-axi.git',
'38915fb'
)

# Set up inputs
dut.input('testbench.sv')
dut.input('rtl/axil_ram.v', package='verilog-axi')

# Verilator configuration
dut.add('tool', 'verilator', 'task', 'compile', 'warningoff',
['WIDTHTRUNC', 'TIMESCALEMOD'])

dut.build()

return dut


if __name__ == '__main__':
main()
171 changes: 171 additions & 0 deletions examples/axil_ram/testbench.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*******************************************************************************
* Copyright 2025 Zero ASIC Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ----
*
* Documentation:
* - Testbench for AXIL Ram using switchboard
*
******************************************************************************/

`default_nettype none

`include "switchboard.vh"

module testbench (
`ifdef VERILATOR
input clk
`endif
);

localparam ADDR_WIDTH = 8;
localparam DATA_WIDTH = 256;
localparam STRB_WIDTH = (DATA_WIDTH/8);

localparam PERIOD_CLK = 10;
localparam RST_CYCLES = 16;

`ifndef VERILATOR
// Generate clock for non verilator sim tools
reg clk;

initial
clk = 1'b0;
always #(PERIOD_CLK/2) clk = ~clk;
`endif

// Reset control
reg [RST_CYCLES:0] nreset_vec;
wire nreset;
wire initdone;

assign nreset = nreset_vec[RST_CYCLES-1];
assign initdone = nreset_vec[RST_CYCLES];

initial
nreset_vec = 'b1;
always @(negedge clk) nreset_vec <= {nreset_vec[RST_CYCLES-1:0], 1'b1};

wire [ADDR_WIDTH-1:0] s_axil_awaddr;
wire [2:0] s_axil_awprot;
wire s_axil_awvalid;
wire s_axil_awready;

wire [DATA_WIDTH-1:0] s_axil_wdata;
wire [STRB_WIDTH-1:0] s_axil_wstrb;
wire s_axil_wvalid;
wire s_axil_wready;

wire [1:0] s_axil_bresp;
wire s_axil_bvalid;
wire s_axil_bready;

wire [ADDR_WIDTH-1:0] s_axil_araddr;
wire [2:0] s_axil_arprot;
wire s_axil_arvalid;
wire s_axil_arready;

wire [DATA_WIDTH-1:0] s_axil_rdata;
wire [1:0] s_axil_rresp;
wire s_axil_rvalid;
wire s_axil_rready;

axil_ram #(
.DATA_WIDTH (DATA_WIDTH),
.ADDR_WIDTH (ADDR_WIDTH))
dut (
.clk (clk),
.rst (~nreset),

.s_axil_awaddr (s_axil_awaddr),
.s_axil_awprot (s_axil_awprot),
.s_axil_awvalid (s_axil_awvalid),
.s_axil_awready (s_axil_awready),

.s_axil_wdata (s_axil_wdata),
.s_axil_wstrb (s_axil_wstrb),
.s_axil_wvalid (s_axil_wvalid),
.s_axil_wready (s_axil_wready),

.s_axil_bresp (s_axil_bresp),
.s_axil_bvalid (s_axil_bvalid),
.s_axil_bready (s_axil_bready),

.s_axil_araddr (s_axil_araddr),
.s_axil_arprot (s_axil_arprot),
.s_axil_arvalid (s_axil_arvalid),
.s_axil_arready (s_axil_arready),

.s_axil_rdata (s_axil_rdata),
.s_axil_rresp (s_axil_rresp),
.s_axil_rvalid (s_axil_rvalid),
.s_axil_rready (s_axil_rready));

sb_axil_m #(
.DATA_WIDTH (DATA_WIDTH),
.ADDR_WIDTH (ADDR_WIDTH))
sb_axil_m_i (
.clk (clk),

.m_axil_awaddr (s_axil_awaddr),
.m_axil_awprot (s_axil_awprot),
.m_axil_awvalid (s_axil_awvalid),
.m_axil_awready (s_axil_awready),

.m_axil_wdata (s_axil_wdata),
.m_axil_wstrb (s_axil_wstrb),
.m_axil_wvalid (s_axil_wvalid),
.m_axil_wready (s_axil_wready),

.m_axil_bresp (s_axil_bresp),
.m_axil_bvalid (s_axil_bvalid),
.m_axil_bready (s_axil_bready),

.m_axil_araddr (s_axil_araddr),
.m_axil_arprot (s_axil_arprot),
.m_axil_arvalid (s_axil_arvalid),
.m_axil_arready (s_axil_arready),

.m_axil_rdata (s_axil_rdata),
.m_axil_rresp (s_axil_rresp),
.m_axil_rvalid (s_axil_rvalid),
.m_axil_rready (s_axil_rready));

// Initialize queues/modes
integer valid_mode, ready_mode;

initial begin
if (!$value$plusargs("valid_mode=%d", valid_mode)) begin
valid_mode = 2; // default if not provided as a plusarg
end

if (!$value$plusargs("ready_mode=%d", ready_mode)) begin
ready_mode = 2; // default if not provided as a plusarg
end

sb_axil_m_i.init("sb_axil_m");
sb_axil_m_i.set_valid_mode(valid_mode);
sb_axil_m_i.set_ready_mode(ready_mode);
end

// control block
`SB_SETUP_PROBES

// auto-stop
auto_stop_sim auto_stop_sim_i (.clk(clk));

endmodule

`default_nettype wire

0 comments on commit 02fe750

Please sign in to comment.