-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Simple axil_ram testbench example (#274)
* Added axil_ram example with testbench * Update README * Update README
- Loading branch information
Showing
4 changed files
with
309 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |