Skip to content

Commit

Permalink
Make it possible to connect() to TCP interfaces (#235)
Browse files Browse the repository at this point in the history
  • Loading branch information
sgherbst authored May 31, 2024
1 parent 2292247 commit 28cfd85
Show file tree
Hide file tree
Showing 15 changed files with 696 additions and 122 deletions.
4 changes: 2 additions & 2 deletions examples/network-fifo-chain/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@

.PHONY: verilator
verilator:
./test.py --tool verilator --start-delay 5 --max-rate 1e3
./test.py --tool verilator --max-rate 1e3

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

.PHONY: icarus
icarus:
./test.py --tool icarus --start-delay 2 --max-rate 1e3 --fifos 125
./test.py --tool icarus --max-rate 1e3

.PHONY: icarus-single-netlist
icarus-single-netlist:
Expand Down
96 changes: 81 additions & 15 deletions examples/network-fifo-chain/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,74 @@
# Copyright (c) 2024 Zero ASIC Corporation
# This code is licensed under Apache License 2.0 (see LICENSE for details)

import os
import umi
from switchboard import SbNetwork, umi_loopback
from copy import deepcopy

from switchboard import SbNetwork, umi_loopback, TcpIntf
from switchboard.cmdline import get_cmdline_args

from pathlib import Path
THIS_DIR = Path(__file__).resolve().parent


def main():
# environment parameters used when there is TCP bridging

tcp_in_port = os.environ.get('SB_TCP_IN_PORT', '5555')
tcp_in_port = int(tcp_in_port)

tcp_in_host = os.environ.get('SB_TCP_IN_HOST', 'localhost')

tcp_out_port = os.environ.get('SB_TCP_OUT_PORT', '5556')
tcp_out_port = int(tcp_out_port)

tcp_out_host = os.environ.get('SB_TCP_OUT_HOST', 'localhost')

max_rate = float(os.environ.get('SB_MAX_RATE', '-1'))

last_fifo = os.environ.get('SB_LAST_FIFO', '1')
last_fifo = bool(int(last_fifo))

# create network

extra_args = {
'--packets': dict(type=int, default=1000, help='Number of'
' transactions to send into the FIFO during the test.'),
'--fifos': dict(type=int, default=500, help='Number of'
' FIFOs to instantiate in series for this test.'),
'--fifos-per-sim': dict(type=int, default=1, help='Number of'
' FIFOs to include in each simulation.')
' transactions to send into the FIFO during the test.'),
'--fifos': dict(type=int, default=200, help='Number of'
' FIFOs to instantiate in series for this test.'),
'--fifos-per-sim': dict(type=int, default=100, help='Number of'
' FIFOs to include in each simulation.'),
'--tcp': dict(action='store_true', help='Run the simulation with UMI ports'
' made available over TCP'),
'--quiet': dict(action='store_true', help="Don't print debugging information"
" for TCP bridges.")
}

# workaround - need to see what type of simulation we're running
# (network of simulations, network of networks, single netlist)

args = get_cmdline_args(extra_args=extra_args)
args = get_cmdline_args(max_rate=max_rate, trace=False, extra_args=extra_args)

assert args.fifos % args.fifos_per_sim == 0, \
'Number of FIFOs must be divisible by the number of FIFOs per simulation'

if args.fifos_per_sim in [1, args.fifos]:
# single network
net = SbNetwork(cmdline=True, single_netlist=args.fifos_per_sim == args.fifos)
args.single_netlist = (args.fifos_per_sim == args.fifos)
net = SbNetwork(args=args)
subnet = net
n = args.fifos
else:
# network of networks
net = SbNetwork(cmdline=True, single_netlist=False)
subnet = SbNetwork(name='subnet', cmdline=True, single_netlist=True)
# top level network
args.single_netlist = False
net = SbNetwork(args=args)

# subnetwork
subnet_args = deepcopy(args)
subnet_args.single_netlist = True
subnet = SbNetwork(name='subnet', args=subnet_args)

n = args.fifos_per_sim

subblock = make_umi_fifo(subnet)
Expand All @@ -62,8 +93,30 @@ def main():
else:
blocks = subblocks

net.external(blocks[0].umi_in, txrx='umi')
net.external(blocks[-1].umi_out, txrx='umi')
# external connection depends on whether TCP bridging is being used

if not args.tcp:
net.external(blocks[0].umi_in, txrx='umi')
net.external(blocks[-1].umi_out, txrx='umi')
else:
net.connect(
blocks[0].umi_in,
TcpIntf(
port=tcp_in_port,
host=tcp_in_host,
mode='server',
quiet=args.quiet
)
)
net.connect(
blocks[-1].umi_out,
TcpIntf(
port=tcp_out_port,
host=tcp_out_host,
mode='client' if not last_fifo else 'server',
quiet=args.quiet
)
)

# build simulator

Expand All @@ -73,9 +126,22 @@ def main():

net.simulate()

# interact with the simulation
if not args.tcp:
# interact with the simulation

umi_loopback(net.intfs['umi'], packets=args.packets)
else:
# wait for SIGINT

import sys
import signal

def signal_handler(signum, frame):
sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)

umi_loopback(net.intfs['umi'], packets=args.packets)
signal.pause()


def make_umi_fifo(net):
Expand Down
11 changes: 11 additions & 0 deletions examples/tcp-fifo-chain/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright (c) 2024 Zero ASIC Corporation
# This code is licensed under Apache License 2.0 (see LICENSE for details)

.PHONY: test
test:
SB_MAX_RATE=1e3 ./test.py

.PHONY: clean
clean:
rm -f *.q

84 changes: 84 additions & 0 deletions examples/tcp-fifo-chain/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env python3

# Example showing how to connect simulations over TCP

# Copyright (c) 2024 Zero ASIC Corporation
# This code is licensed under Apache License 2.0 (see LICENSE for details)

import os

from switchboard import binary_run, SbNetwork, TcpIntf, flip_intf, umi_loopback
from switchboard.cmdline import get_cmdline_args


def main():
# environment parameters

max_rate = float(os.environ.get('SB_MAX_RATE', '-1'))

# design parameters

dw = 256
aw = 64
cw = 32

# create network

extra_args = {
'--packets': dict(type=int, default=1000, help='Number of'
' transactions to send into the FIFO during the test.'),
'--fifos': dict(type=int, default=9, help='Number of'
' FIFOs to instantiate in series for this test.'),
'--fifos-per-sim': dict(type=int, default=3, help='Number of'
' FIFOs to include in each simulation.'),
'--tcp-output-host': dict(type=str, default='localhost'),
'--tcp-output-port': dict(type=int, default=5555),
'--tcp-input-host': dict(type=str, default='localhost'),
'--tcp-input-port': dict(type=int, default=5556),
'--standalone': dict(action='store_true'),
}

args = get_cmdline_args(max_rate=max_rate, extra_args=extra_args)

net = SbNetwork(args=args)

if not args.standalone:
import sys

subprocess_args = []

subprocess_args += ['--fast']
subprocess_args += ['--quiet']
subprocess_args += ['--tcp']
subprocess_args += ['--fifos', args.fifos]
subprocess_args += ['--fifos-per-sim', args.fifos_per_sim]

env = dict(SB_TCP_IN_PORT='5555', SB_TCP_OUT_PORT='5556', SB_LAST_FIFO='1')
env.update(os.environ)
binary_run(sys.executable, ['test.py'] + subprocess_args,
cwd='../network-fifo-chain', use_sigint=True, env=env)

intf_i = dict(type='umi', dw=dw, cw=cw, aw=aw, direction='input')
intf_o = flip_intf(intf_i)

net.external(
TcpIntf(intf_i, port=args.tcp_output_port, host=args.tcp_output_host, mode='client'),
txrx='umi'
)

net.external(
TcpIntf(intf_o, port=args.tcp_input_port, host=args.tcp_input_host, mode='client'),
txrx='umi'
)

# launch the simulation

net.simulate()

# interact with the simulation

umi_loopback(net.intfs['umi'], packets=args.packets)


if __name__ == '__main__':
main()
8 changes: 6 additions & 2 deletions examples/tcp/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@

.PHONY: test
test:
./test.py
SB_MAX_RATE=1e3 ./test.py

.PHONY: clean
clean:
rm -f queue-* *.q
rm -f *.q
rm -f ram/*.q
rm -rf ram/build
rm -f fifos/*.q
rm -rf fifos/build
17 changes: 5 additions & 12 deletions examples/tcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,10 @@ This example shows how to bridge a switchboard connection using TCP. This can b
To run the example, type `make`. You'll see output like this:

```text
*** TX packet ***
dest: 123456789
last: 1
data: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
24 25 26 27 28 29 30 31]
*** RX packet ***
dest: 123456789
last: 1
data: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
24 25 26 27 28 29 30 31]
Wrote addr=0x10 data=0xdeadbeef
Read addr=0x10 data=0xdeadbeef
```

Delving into [test.py](test.py), you'll see that the code is similar to the [python](../python) example, using `PySbTx` and `PySbRx` objects and the `send()` and `recv()` methods. The difference is that `PySbTx` and `PySbRx` are two sides of the same connection, but use different queue names (`tx.q` and `rx.q`). We launch two TCP bridge processes, one for each side of the connection, using `start_tcp_bridge()`. It must be the case that one side is the server and one side is the client, but it doesn't matter which one is which, and the launch order doesn't matter either. However, when bridging between two different machines, the client side must specify the IP address of the server in the `host` argument of `start_tcp_bridge()`. Also, if you're running multiple bridges simultaneously, you'll need to specify the `port` argument for each bridge to make sure that they don't interfere with each other.
Delving into [test.py](test.py), you'll see that the script launches two scripts, [ram/ram.py] and [fifos/fifos.py]. These run switchboard simulations for a UMI RAM module and UMI FIFO module, respectively. The `test.py` script connects to the FIFO simulation via TCP, using `SbNetwork.external()` with one of the arguments set to `TcpIntf` to represent a TCP port. `SbNetwork.simulate()` is called even though there is no RTL being simulated at the top level, since this is also where TCP bridges are launched.

In the scripts `ram.py` and `fifos.py`, RTL simulations are specified using `SbNetwork`, with `SbNetwork.connect()` and `SbNetwork.external()` used to specify interactions with switchboard connections being bridged over TCP.
Loading

0 comments on commit 28cfd85

Please sign in to comment.