Skip to content

Commit

Permalink
autowrap and network construction features (#212)
Browse files Browse the repository at this point in the history
  • Loading branch information
sgherbst authored May 9, 2024
1 parent 8771fec commit b0a8be8
Show file tree
Hide file tree
Showing 49 changed files with 2,600 additions and 901 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ jobs:
- name: Check out Git repository
uses: actions/checkout@v4

# note that "sb_loopback.v" is not included in linting. verible doesn't
# seem to be able to take into account the macro definitions in the port
# list, so linting unfortunately needs to be disabled for that file.

- name: Lint with Verible
run: |
find . \( \
Expand All @@ -64,6 +68,7 @@ jobs:
-or -path "./examples/deps/*" \
-or -name "axil_interconnect_wrap_1x2.v" \
-or -name "picorv32.v" \
-or -name "sb_loopback.v" \
\) > files.txt
cat files.txt
verible-verilog-lint \
Expand Down
2 changes: 2 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ The [python](python) example is similar to the [minimal](minimal) example, excep
We also provide a mechanism for bridging switchboard connections over TCP, which may be useful if you're running a simulation or FPGA-based emulator on one machine, but want to interact with it from another machine. The [tcp example](tcp) shows how to set this up; it's mostly a matter of calling `start_tcp_bridge` on the server and client sides.

Switchboard also supports mixed-signal simulation by using Xyce in conjunction with a digital simulator. The [xyce example](xyce) shows how this can be set up by instantiating a SPICE subcircuit as an ordinary Verilog module and calling `SbDut.input_analog()` to configure analog/digital interfaces.

For a preview of switchboard's new features to automatically generate Verilog wrappers and construct networks of simulations dynamically, have a look at the [network](network) example.
20 changes: 8 additions & 12 deletions examples/axi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,20 @@ PASS!

`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 manager.
In the Python script [test.py](test.py), an AXI interface is specified using the `interfaces` argument of `SbDut`. The name of the interface is `s_axi`, corresponding to the AXI port prefix in the `axi_ram` implementation. After simulation starts, the AXI interface object is retrieved from the `SbDut.intfs` dictionary.

```verilog
`include "switchboard.vh"
```python
interfaces = {
's_axi': dict(type='axi', dw=dw, aw=aw, idw=idw, direction='subordinate')
}

...

`SB_AXI_M(axi, DATA_WIDTH, ADDR_WIDTH, ID_WIDTH, "axi");
```

Based on the first argument, `axi`, the module instance is called `axi_sb_inst` and it connects to AXI signals starting with the prefix `axi`. The next three arguments specify the widths of the data, address, and ID buses, respectively. The last argument indicates that the switchboard queues to be used start with the prefix `axi`: `axi-aw.q`, `axi-w.q`, `axi-b.q`, `axi-ar.q`, `axi-r.q`.
dut = SbDut('axi_ram', ..., interfaces=interfaces, ...)

Various optional macro arguments can fine-tune the behavior, for example changing the clock signal name, which defaults to `clk`.

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

```python
axi = AxiTxRx('axi', data_width=..., addr_width=..., id_width=...)
axi = dut.intfs['s_axi'] # type: AxiTxRx
```

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.
Expand Down
28 changes: 23 additions & 5 deletions examples/axi/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,23 @@
import random
import numpy as np

from switchboard import SbDut, AxiTxRx
from switchboard import SbDut


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

# create the queues
axi = AxiTxRx('axi', data_width=32, addr_width=13, id_width=8, max_beats=dut.args.max_beats)
# wire up max-beats argument
dut.intf_defs['s_axi']['max_beats'] = dut.args.max_beats

# launch the simulation
dut.simulate()

# run the test: write to random addresses and read back in a random order

axi = dut.intfs['s_axi']

addr_bytes = (axi.addr_width + 7) // 8

model = np.zeros((1 << axi.addr_width,), dtype=np.uint8)
Expand Down Expand Up @@ -70,6 +72,22 @@ def main():


def build_testbench():
dw = 32
aw = 13
idw = 8

parameters = dict(
DATA_WIDTH=dw,
ADDR_WIDTH=aw,
ID_WIDTH=idw,
)

interfaces = {
's_axi': dict(type='axi', dw=dw, aw=aw, idw=idw, direction='subordinate')
}

resets = [dict(name='rst', delay=8)]

extra_args = {
'-n': dict(type=int, default=10000, help='Number of'
' words to write as part of the test.'),
Expand All @@ -79,7 +97,8 @@ def build_testbench():
' number of beats to use in AXI transfers.')
}

dut = SbDut(cmdline=True, extra_args=extra_args)
dut = SbDut('axi_ram', autowrap=True, cmdline=True, extra_args=extra_args,
parameters=parameters, interfaces=interfaces, resets=resets)

dut.register_package_source(
'verilog-axi',
Expand All @@ -88,7 +107,6 @@ def build_testbench():
)

dut.input('rtl/axi_ram.v', package='verilog-axi')
dut.input('testbench.sv')

dut.add('tool', 'verilator', 'task', 'compile', 'warningoff',
['WIDTHEXPAND', 'CASEINCOMPLETE', 'WIDTHTRUNC', 'TIMESCALEMOD'])
Expand Down
69 changes: 0 additions & 69 deletions examples/axi/testbench.sv

This file was deleted.

20 changes: 8 additions & 12 deletions examples/axil/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,20 @@ PASS!

`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.
In the Python script [test.py](test.py), an AXI-Lite interface is specified using the `interfaces` argument of `SbDut`. The name of the interface is `s_axil`, corresponding to the AXI-Lite port prefix in the `axil_ram` implementation. After simulation starts, the AXI-Lite interface object is retrieved from the `SbDut.intfs` dictionary.

```verilog
`include "switchboard.vh"
```python
interfaces = {
's_axil': dict(type='axil', dw=dw, aw=aw, direction='subordinate')
}

...

`SB_AXIL_M(axil, DATA_WIDTH, ADDR_WIDTH, "axil");
```

Based on the first argument, `axil`, the module instance is called `axil_sb_inst` and it connects to AXI-Lite signals starting with the prefix `axil`. The next two arguments specify the widths of the data and address buses, respectively. The last argument indicates that the switchboard queues to be used start with the prefix `axil`: `axil-aw.q`, `axil-w.q`, `axil-b.q`, `axil-ar.q`, `axil-r.q`.
dut = SbDut('axill_ram', ..., interfaces=interfaces, ...)

Various optional macro arguments can fine-tune the behavior, for example changing the clock signal name, which defaults to `clk`.

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('axil', data_width=..., addr_width=...)
axil = dut.intfs['s_axil'] # type: AxiLiteTxRx
```

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.
Expand Down
29 changes: 20 additions & 9 deletions examples/axil/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,20 @@
import random
import numpy as np

from switchboard import SbDut, AxiLiteTxRx
from switchboard import SbDut


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

# create the queues
axil = AxiLiteTxRx('axil', data_width=32, addr_width=13)

# launch the simulation
dut.simulate()

# run the test: write to random addresses and read back in a random order

axil = dut.intfs['s_axil']

addr_bytes = (axil.addr_width + 7) // 8

model = np.zeros((1 << axil.addr_width,), dtype=np.uint8)
Expand Down Expand Up @@ -70,16 +69,29 @@ def main():


def build_testbench():
dw = 32
aw = 13

parameters = dict(
DATA_WIDTH=dw,
ADDR_WIDTH=aw
)

interfaces = {
's_axil': dict(type='axil', dw=dw, aw=aw, direction='subordinate')
}

resets = [dict(name='rst', delay=8)]

extra_args = {
'-n': dict(type=int, default=10000, help='Number of'
' words to write as part of the test.'),
'--max-bytes': dict(type=int, default=10, help='Maximum'
' number of bytes in any single read/write.'),
'--max-beats': dict(type=int, default=256, help='Maximum'
' number of beats to use in AXI transfers.')
' number of bytes in any single read/write.')
}

dut = SbDut(cmdline=True, extra_args=extra_args)
dut = SbDut('axil_ram', autowrap=True, cmdline=True, extra_args=extra_args,
parameters=parameters, interfaces=interfaces, resets=resets)

dut.register_package_source(
'verilog-axi',
Expand All @@ -88,7 +100,6 @@ def build_testbench():
)

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

dut.add('tool', 'verilator', 'task', 'compile', 'warningoff',
['WIDTHTRUNC', 'TIMESCALEMOD'])
Expand Down
67 changes: 0 additions & 67 deletions examples/axil/testbench.sv

This file was deleted.

34 changes: 34 additions & 0 deletions examples/common/verilog/sb_loopback.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) 2024 Zero ASIC Corporation
// This code is licensed under Apache License 2.0 (see LICENSE for details)

`default_nettype none

`include "switchboard.vh"

module sb_loopback #(
parameter DW=256,
parameter [7:0] INCREMENT=1
) (
input clk,

`SB_INPUT(in, DW),
`SB_OUTPUT(out, DW)
);

// loopback with increment

genvar i;
generate
for (i=0; i<(DW/8); i=i+1) begin
assign out_data[(i*8) +: 8] = in_data[(i*8) +: 8] + INCREMENT;
end
endgenerate

assign out_dest = in_dest;
assign out_last = in_last;
assign out_valid = in_valid;
assign in_ready = out_ready;

endmodule

`default_nettype wire
Loading

0 comments on commit b0a8be8

Please sign in to comment.