Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading