switchboard-hw 0.3.0__cp314-cp314-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- _switchboard.cpython-314-darwin.so +0 -0
- switchboard/__init__.py +24 -0
- switchboard/ams.py +668 -0
- switchboard/apb.py +278 -0
- switchboard/autowrap.py +1000 -0
- switchboard/axi.py +571 -0
- switchboard/axil.py +348 -0
- switchboard/bitvector.py +112 -0
- switchboard/cmdline.py +142 -0
- switchboard/cpp/Makefile +13 -0
- switchboard/cpp/bitutil.h +39 -0
- switchboard/cpp/pagemap.h +91 -0
- switchboard/cpp/pciedev.h +86 -0
- switchboard/cpp/router.cc +89 -0
- switchboard/cpp/spsc_queue.h +267 -0
- switchboard/cpp/switchboard.hpp +257 -0
- switchboard/cpp/switchboard_pcie.hpp +234 -0
- switchboard/cpp/switchboard_tlm.hpp +98 -0
- switchboard/cpp/umilib.h +144 -0
- switchboard/cpp/umilib.hpp +113 -0
- switchboard/cpp/umisb.hpp +364 -0
- switchboard/cpp/xyce.hpp +90 -0
- switchboard/deps/__init__.py +0 -0
- switchboard/deps/verilog_axi.py +23 -0
- switchboard/dpi/__init__.py +0 -0
- switchboard/dpi/switchboard_dpi.cc +119 -0
- switchboard/dpi/switchboard_dpi.py +13 -0
- switchboard/dpi/xyce_dpi.cc +43 -0
- switchboard/gpio.py +108 -0
- switchboard/icarus.py +85 -0
- switchboard/loopback.py +157 -0
- switchboard/network.py +714 -0
- switchboard/pytest_plugin.py +11 -0
- switchboard/sbdesign.py +55 -0
- switchboard/sbdut.py +744 -0
- switchboard/sbtcp.py +345 -0
- switchboard/sc/__init__.py +0 -0
- switchboard/sc/morty/__init__.py +0 -0
- switchboard/sc/morty/uniquify.py +67 -0
- switchboard/sc/sed/__init__.py +0 -0
- switchboard/sc/sed/sed_remove.py +47 -0
- switchboard/sc/standalone_netlist_flow.py +25 -0
- switchboard/switchboard.py +53 -0
- switchboard/test_util.py +46 -0
- switchboard/uart_xactor.py +66 -0
- switchboard/umi.py +793 -0
- switchboard/util.py +131 -0
- switchboard/verilator/__init__.py +0 -0
- switchboard/verilator/config.vlt +13 -0
- switchboard/verilator/testbench.cc +143 -0
- switchboard/verilator/verilator.py +13 -0
- switchboard/verilator_run.py +31 -0
- switchboard/verilog/__init__.py +0 -0
- switchboard/verilog/common/__init__.py +0 -0
- switchboard/verilog/common/common.py +26 -0
- switchboard/verilog/common/switchboard.vh +429 -0
- switchboard/verilog/common/uart_xactor.sv +247 -0
- switchboard/verilog/common/umi_gpio.v +236 -0
- switchboard/verilog/fpga/__init__.py +0 -0
- switchboard/verilog/fpga/axi_reader.sv +82 -0
- switchboard/verilog/fpga/axi_writer.sv +111 -0
- switchboard/verilog/fpga/config_registers.sv +249 -0
- switchboard/verilog/fpga/fpga.py +21 -0
- switchboard/verilog/fpga/include/sb_queue_regmap.vh +21 -0
- switchboard/verilog/fpga/include/spsc_queue.vh +7 -0
- switchboard/verilog/fpga/memory_fault.sv +40 -0
- switchboard/verilog/fpga/sb_fpga_queues.sv +416 -0
- switchboard/verilog/fpga/sb_rx_fpga.sv +303 -0
- switchboard/verilog/fpga/sb_tx_fpga.sv +294 -0
- switchboard/verilog/fpga/umi_fpga_queues.sv +146 -0
- switchboard/verilog/sim/__init__.py +0 -0
- switchboard/verilog/sim/auto_stop_sim.sv +25 -0
- switchboard/verilog/sim/perf_meas_sim.sv +97 -0
- switchboard/verilog/sim/queue_to_sb_sim.sv +176 -0
- switchboard/verilog/sim/queue_to_umi_sim.sv +66 -0
- switchboard/verilog/sim/sb_apb_m.sv +146 -0
- switchboard/verilog/sim/sb_axi_m.sv +199 -0
- switchboard/verilog/sim/sb_axil_m.sv +180 -0
- switchboard/verilog/sim/sb_axil_s.sv +180 -0
- switchboard/verilog/sim/sb_clk_gen.sv +89 -0
- switchboard/verilog/sim/sb_jtag_rbb_sim.sv +148 -0
- switchboard/verilog/sim/sb_rx_sim.sv +55 -0
- switchboard/verilog/sim/sb_to_queue_sim.sv +196 -0
- switchboard/verilog/sim/sb_tx_sim.sv +55 -0
- switchboard/verilog/sim/switchboard_sim.py +49 -0
- switchboard/verilog/sim/umi_rx_sim.sv +61 -0
- switchboard/verilog/sim/umi_to_queue_sim.sv +66 -0
- switchboard/verilog/sim/umi_tx_sim.sv +61 -0
- switchboard/verilog/sim/xyce_intf.sv +67 -0
- switchboard/vpi/switchboard_vpi.cc +431 -0
- switchboard/vpi/xyce_vpi.cc +200 -0
- switchboard/warn.py +14 -0
- switchboard/xyce.py +27 -0
- switchboard_hw-0.3.0.dist-info/METADATA +303 -0
- switchboard_hw-0.3.0.dist-info/RECORD +99 -0
- switchboard_hw-0.3.0.dist-info/WHEEL +6 -0
- switchboard_hw-0.3.0.dist-info/entry_points.txt +6 -0
- switchboard_hw-0.3.0.dist-info/licenses/LICENSE +190 -0
- switchboard_hw-0.3.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from siliconcompiler import Design
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class VerilogAxi(Design):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
super().__init__('verilog_axis')
|
|
7
|
+
|
|
8
|
+
self.set_dataroot(
|
|
9
|
+
name="verilog_axis",
|
|
10
|
+
path="git+https://github.com/alexforencich/verilog-axis.git@master",
|
|
11
|
+
tag="25912d48fec2abbf3565bbefe402c1cff99fe470"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
path = "rtl"
|
|
15
|
+
|
|
16
|
+
files = [
|
|
17
|
+
f"{path}/arbiter.v",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
with self.active_fileset('rtl'):
|
|
21
|
+
for item in files:
|
|
22
|
+
self.add_file(item)
|
|
23
|
+
|
|
File without changes
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Copyright (c) 2024 Zero ASIC Corporation
|
|
2
|
+
// This code is licensed under Apache License 2.0 (see LICENSE for details)
|
|
3
|
+
|
|
4
|
+
#include <assert.h>
|
|
5
|
+
#include <chrono>
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <vector>
|
|
8
|
+
|
|
9
|
+
#include "svdpi.h"
|
|
10
|
+
#include "switchboard.hpp"
|
|
11
|
+
|
|
12
|
+
// function definitions
|
|
13
|
+
#ifdef __cplusplus
|
|
14
|
+
extern "C" {
|
|
15
|
+
#endif
|
|
16
|
+
extern void pi_sb_rx_init(int* id, const char* uri, int width);
|
|
17
|
+
extern void pi_sb_tx_init(int* id, const char* uri, int width);
|
|
18
|
+
extern void pi_sb_recv(int id, svBitVecVal* rdata, svBitVecVal* rdest, svBit* rlast, int* success);
|
|
19
|
+
extern void pi_sb_send(int id, const svBitVecVal* sdata, const svBitVecVal* sdest, svBit slast,
|
|
20
|
+
int* success);
|
|
21
|
+
extern void pi_time_taken(double* t);
|
|
22
|
+
#ifdef __cplusplus
|
|
23
|
+
}
|
|
24
|
+
#endif
|
|
25
|
+
|
|
26
|
+
static std::vector<std::unique_ptr<SBRX>> rxconn;
|
|
27
|
+
static std::vector<std::unique_ptr<SBTX>> txconn;
|
|
28
|
+
static std::vector<int> rxwidth;
|
|
29
|
+
static std::vector<int> txwidth;
|
|
30
|
+
|
|
31
|
+
void pi_sb_rx_init(int* id, const char* uri, int width) {
|
|
32
|
+
rxconn.push_back(std::unique_ptr<SBRX>(new SBRX()));
|
|
33
|
+
rxconn.back()->init(uri);
|
|
34
|
+
|
|
35
|
+
// record the width of this connection
|
|
36
|
+
rxwidth.push_back(width);
|
|
37
|
+
|
|
38
|
+
// assign the ID of this connection
|
|
39
|
+
*id = rxconn.size() - 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
void pi_sb_tx_init(int* id, const char* uri, int width) {
|
|
43
|
+
txconn.push_back(std::unique_ptr<SBTX>(new SBTX()));
|
|
44
|
+
txconn.back()->init(uri);
|
|
45
|
+
|
|
46
|
+
// record the width of this connection
|
|
47
|
+
txwidth.push_back(width);
|
|
48
|
+
|
|
49
|
+
// assign the ID of this connection
|
|
50
|
+
*id = txconn.size() - 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
void pi_sb_recv(int id, svBitVecVal* rdata, svBitVecVal* rdest, svBit* rlast, int* success) {
|
|
54
|
+
// make sure this is a valid id
|
|
55
|
+
assert(id < rxconn.size());
|
|
56
|
+
|
|
57
|
+
// try to receive an inbound packet
|
|
58
|
+
sb_packet p;
|
|
59
|
+
if (rxconn[id]->recv(p)) {
|
|
60
|
+
memcpy(rdata, p.data, rxwidth[id]);
|
|
61
|
+
*rdest = p.destination;
|
|
62
|
+
*rlast = p.last ? 1 : 0;
|
|
63
|
+
*success = 1;
|
|
64
|
+
} else {
|
|
65
|
+
*success = 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
void pi_sb_send(int id, const svBitVecVal* sdata, const svBitVecVal* sdest, svBit slast,
|
|
70
|
+
int* success) {
|
|
71
|
+
// make sure this is a valid id
|
|
72
|
+
assert(id < txconn.size());
|
|
73
|
+
|
|
74
|
+
// form the outbound packet
|
|
75
|
+
sb_packet p;
|
|
76
|
+
memcpy(p.data, sdata, txwidth[id]);
|
|
77
|
+
p.destination = *sdest;
|
|
78
|
+
p.last = slast;
|
|
79
|
+
|
|
80
|
+
// try to send the packet
|
|
81
|
+
if (txconn[id]->send(p)) {
|
|
82
|
+
*success = 1;
|
|
83
|
+
} else {
|
|
84
|
+
*success = 0;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
void pi_time_taken(double* t) {
|
|
89
|
+
static std::chrono::steady_clock::time_point start_time;
|
|
90
|
+
static std::chrono::steady_clock::time_point stop_time;
|
|
91
|
+
|
|
92
|
+
// compute time taken in seconds
|
|
93
|
+
stop_time = std::chrono::steady_clock::now();
|
|
94
|
+
*t = 1.0e-6 *
|
|
95
|
+
(std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time).count());
|
|
96
|
+
start_time = std::chrono::steady_clock::now();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
void pi_start_delay(double value) {
|
|
100
|
+
// WARNING: not tested yet since Icarus Verilog uses VPI and Verilator
|
|
101
|
+
// uses start_delay in main(), not through DPI
|
|
102
|
+
start_delay(value);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
void pi_max_rate_tick(svBitVecVal* t_us_vec, svBitVecVal* min_period_us_vec) {
|
|
106
|
+
// WARNING: not tested yet since Icarus Verilog uses VPI and Verilator
|
|
107
|
+
// uses max_rate_tick in main(), not through DPI
|
|
108
|
+
|
|
109
|
+
// retrieve the previous timestamp and minimum period
|
|
110
|
+
long t_us, min_period_us;
|
|
111
|
+
memcpy(&t_us, t_us_vec, 8);
|
|
112
|
+
memcpy(&min_period_us, min_period_us_vec, 8);
|
|
113
|
+
|
|
114
|
+
// call the underlying switchboard function
|
|
115
|
+
max_rate_tick(t_us, min_period_us);
|
|
116
|
+
|
|
117
|
+
// store the new timestamp
|
|
118
|
+
memcpy(t_us_vec, &t_us, 8);
|
|
119
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from siliconcompiler import Design
|
|
2
|
+
|
|
3
|
+
from switchboard import sb_path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SwitchboardDPI(Design):
|
|
7
|
+
def __init__(self):
|
|
8
|
+
super().__init__("switchboard_dpi")
|
|
9
|
+
|
|
10
|
+
self.set_dataroot('localroot', sb_path() / "dpi")
|
|
11
|
+
|
|
12
|
+
with self.active_fileset('sim'):
|
|
13
|
+
self.add_file("switchboard_dpi.cc")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Copyright (c) 2024 Zero ASIC Corporation
|
|
2
|
+
// This code is licensed under Apache License 2.0 (see LICENSE for details)
|
|
3
|
+
|
|
4
|
+
#include "svdpi.h"
|
|
5
|
+
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <vector>
|
|
9
|
+
|
|
10
|
+
#include <stdio.h>
|
|
11
|
+
#include <stdlib.h>
|
|
12
|
+
|
|
13
|
+
#include "xyce.hpp"
|
|
14
|
+
|
|
15
|
+
// function definitions
|
|
16
|
+
#ifdef __cplusplus
|
|
17
|
+
extern "C" {
|
|
18
|
+
#endif
|
|
19
|
+
extern void pi_sb_xyce_init(int* id, char* file);
|
|
20
|
+
extern void pi_sb_xyce_put(int id, char* name, double time, double value);
|
|
21
|
+
extern void pi_sb_xyce_get(int id, char* name, double time, double* val);
|
|
22
|
+
#ifdef __cplusplus
|
|
23
|
+
}
|
|
24
|
+
#endif
|
|
25
|
+
|
|
26
|
+
static std::vector<std::unique_ptr<XyceIntf>> xyceIntfs;
|
|
27
|
+
|
|
28
|
+
void pi_sb_xyce_init(int* id, char* file) {
|
|
29
|
+
// add a new Xyce interface
|
|
30
|
+
xyceIntfs.push_back(std::unique_ptr<XyceIntf>(new XyceIntf()));
|
|
31
|
+
xyceIntfs.back()->init(std::string(file));
|
|
32
|
+
|
|
33
|
+
// set ID
|
|
34
|
+
*id = xyceIntfs.size() - 1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void pi_sb_xyce_put(int id, char* name, double time, double value) {
|
|
38
|
+
xyceIntfs[id]->put(std::string(name), time, value);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
void pi_sb_xyce_get(int id, char* name, double time, double* value) {
|
|
42
|
+
xyceIntfs[id]->get(std::string(name), time, value);
|
|
43
|
+
}
|
switchboard/gpio.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Copyright (c) 2024 Zero ASIC Corporation
|
|
2
|
+
# This code is licensed under Apache License 2.0 (see LICENSE for details)
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
from .bitvector import BitVector
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class UmiGpioInput:
|
|
9
|
+
def __init__(self, width, dstaddr, srcaddr, max_bytes, umi):
|
|
10
|
+
self.width = width
|
|
11
|
+
self.dstaddr = dstaddr
|
|
12
|
+
self.srcaddr = srcaddr
|
|
13
|
+
self.max_bytes = max_bytes
|
|
14
|
+
self.umi = umi
|
|
15
|
+
|
|
16
|
+
def _read(self):
|
|
17
|
+
# determine the number of bytes to read
|
|
18
|
+
nbytes = (self.width // 8)
|
|
19
|
+
if (self.width % 8) != 0:
|
|
20
|
+
nbytes += 1
|
|
21
|
+
|
|
22
|
+
# read the bytes
|
|
23
|
+
return BitVector.frombytes(
|
|
24
|
+
self.umi.read(
|
|
25
|
+
addr=self.dstaddr,
|
|
26
|
+
num_or_dtype=nbytes,
|
|
27
|
+
dtype=np.uint8,
|
|
28
|
+
srcaddr=self.srcaddr,
|
|
29
|
+
max_bytes=self.max_bytes
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def __str__(self):
|
|
34
|
+
return str(self._read())
|
|
35
|
+
|
|
36
|
+
def __int__(self):
|
|
37
|
+
return int(self[:])
|
|
38
|
+
|
|
39
|
+
def __getitem__(self, key):
|
|
40
|
+
bv = self._read()
|
|
41
|
+
return bv.__getitem__(key=key)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class UmiGpioOutput:
|
|
45
|
+
def __init__(self, width, init, dstaddr, srcaddr, posted, max_bytes, umi):
|
|
46
|
+
self.width = width
|
|
47
|
+
self.dstaddr = dstaddr
|
|
48
|
+
self.srcaddr = srcaddr
|
|
49
|
+
self.posted = posted
|
|
50
|
+
self.max_bytes = max_bytes
|
|
51
|
+
self.umi = umi
|
|
52
|
+
|
|
53
|
+
self.bv = BitVector(init)
|
|
54
|
+
|
|
55
|
+
if init is not None:
|
|
56
|
+
self._write()
|
|
57
|
+
|
|
58
|
+
def _write(self):
|
|
59
|
+
# determine the number of bytes to write
|
|
60
|
+
nbytes = (self.width // 8)
|
|
61
|
+
if (self.width % 8) != 0:
|
|
62
|
+
nbytes += 1
|
|
63
|
+
|
|
64
|
+
# write the bytes
|
|
65
|
+
self.umi.write(
|
|
66
|
+
addr=self.dstaddr,
|
|
67
|
+
data=self.bv.tobytes(n=nbytes),
|
|
68
|
+
srcaddr=self.srcaddr,
|
|
69
|
+
max_bytes=self.max_bytes,
|
|
70
|
+
posted=self.posted
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def __setitem__(self, key, value):
|
|
74
|
+
self.bv.__setitem__(key=key, value=value)
|
|
75
|
+
self._write()
|
|
76
|
+
|
|
77
|
+
# read functions provided for convenience
|
|
78
|
+
|
|
79
|
+
def __str__(self):
|
|
80
|
+
return str(self.bv)
|
|
81
|
+
|
|
82
|
+
def __int__(self):
|
|
83
|
+
return int(self[:])
|
|
84
|
+
|
|
85
|
+
def __getitem__(self, key):
|
|
86
|
+
return self.bv.__getitem__(key=key)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class UmiGpio(object):
|
|
90
|
+
def __init__(self, iwidth, owidth, init, dstaddr, srcaddr, posted, max_bytes, umi):
|
|
91
|
+
|
|
92
|
+
self.i = UmiGpioInput(
|
|
93
|
+
width=iwidth,
|
|
94
|
+
dstaddr=dstaddr,
|
|
95
|
+
srcaddr=srcaddr,
|
|
96
|
+
max_bytes=max_bytes,
|
|
97
|
+
umi=umi
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
self.o = UmiGpioOutput(
|
|
101
|
+
width=owidth,
|
|
102
|
+
init=init,
|
|
103
|
+
dstaddr=dstaddr,
|
|
104
|
+
srcaddr=srcaddr,
|
|
105
|
+
posted=posted,
|
|
106
|
+
max_bytes=max_bytes,
|
|
107
|
+
umi=umi
|
|
108
|
+
)
|
switchboard/icarus.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Utilities for working with Icarus Verilog
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2024 Zero ASIC Corporation
|
|
4
|
+
# This code is licensed under Apache License 2.0 (see LICENSE for details)
|
|
5
|
+
|
|
6
|
+
# TODO: replace with SiliconCompiler functionality
|
|
7
|
+
|
|
8
|
+
from typing import Union, List
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from .util import plusargs_to_args, binary_run
|
|
12
|
+
from .switchboard import path as sb_path
|
|
13
|
+
from subprocess import check_output, STDOUT, CalledProcessError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run(command: list, cwd: str = None) -> str:
|
|
17
|
+
return check_output(command, cwd=cwd, stderr=STDOUT).decode()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def icarus_build_vpi(
|
|
21
|
+
cwd: str = None,
|
|
22
|
+
name: str = 'switchboard',
|
|
23
|
+
cincludes: List[str] = None,
|
|
24
|
+
ldflags: List[str] = None
|
|
25
|
+
) -> str:
|
|
26
|
+
if cincludes is None:
|
|
27
|
+
cincludes = []
|
|
28
|
+
|
|
29
|
+
if ldflags is None:
|
|
30
|
+
ldflags = []
|
|
31
|
+
|
|
32
|
+
sbdir = sb_path()
|
|
33
|
+
incdirs = cincludes + [f'{sbdir}/cpp']
|
|
34
|
+
|
|
35
|
+
cmd = []
|
|
36
|
+
cmd += ['iverilog-vpi']
|
|
37
|
+
cmd += [f'-I{incdir}' for incdir in incdirs]
|
|
38
|
+
cmd += ldflags
|
|
39
|
+
cmd += [str(sbdir / 'vpi' / f'{name}_vpi.cc')]
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
run(cmd, cwd)
|
|
43
|
+
except CalledProcessError as e:
|
|
44
|
+
print(e.output)
|
|
45
|
+
raise
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def icarus_find_vpi(cwd: Union[str, Path] = None, name: str = 'switchboard') -> Path:
|
|
49
|
+
path = Path(f'{name}_vpi.vpi')
|
|
50
|
+
|
|
51
|
+
if cwd is not None:
|
|
52
|
+
path = Path(cwd) / path
|
|
53
|
+
|
|
54
|
+
if path.exists():
|
|
55
|
+
return path
|
|
56
|
+
else:
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def icarus_run(vvp, plusargs=None, modules=None, extra_args=None, **kwargs):
|
|
61
|
+
args = []
|
|
62
|
+
|
|
63
|
+
args += ['-n']
|
|
64
|
+
|
|
65
|
+
mdirs = set()
|
|
66
|
+
|
|
67
|
+
if modules is not None:
|
|
68
|
+
if not isinstance(modules, list):
|
|
69
|
+
raise TypeError('modules must be a list')
|
|
70
|
+
for module in modules:
|
|
71
|
+
mdirs.add(str(Path(module.resolve().parent)))
|
|
72
|
+
args += ['-m', Path(module).stem]
|
|
73
|
+
|
|
74
|
+
for mdir in mdirs:
|
|
75
|
+
args += [f'-M{mdir}']
|
|
76
|
+
|
|
77
|
+
args += [vvp]
|
|
78
|
+
args += plusargs_to_args(plusargs)
|
|
79
|
+
|
|
80
|
+
if extra_args is not None:
|
|
81
|
+
if not isinstance(modules, list):
|
|
82
|
+
raise TypeError('extra_args must be a list')
|
|
83
|
+
args += extra_args
|
|
84
|
+
|
|
85
|
+
return binary_run(bin='vvp', args=args, **kwargs)
|
switchboard/loopback.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Loopback test to check the behavior of blocks that split/merge UMI packets
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2024 Zero ASIC Corporation
|
|
4
|
+
# This code is licensed under Apache License 2.0 (see LICENSE for details)
|
|
5
|
+
|
|
6
|
+
from numbers import Integral
|
|
7
|
+
from typing import Iterable, Iterator, Union
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from tqdm import tqdm
|
|
11
|
+
except ModuleNotFoundError:
|
|
12
|
+
tqdm = None
|
|
13
|
+
|
|
14
|
+
from .umi import UmiTxRx, random_umi_packet
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def umi_loopback(
|
|
18
|
+
umi: UmiTxRx,
|
|
19
|
+
packets: Union[Integral, Iterable, Iterator] = 10,
|
|
20
|
+
**kwargs
|
|
21
|
+
):
|
|
22
|
+
"""
|
|
23
|
+
Performs a loopback test by sending packets into a block and checking that
|
|
24
|
+
the packets received back are equivalent under the UMI split/merge rules.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
umi: UmiTxRx
|
|
29
|
+
packets:
|
|
30
|
+
Can be a number, a list of packets, or a generator.
|
|
31
|
+
|
|
32
|
+
- If this is a number, it represents the number of packets to send,
|
|
33
|
+
which are generated with random_umi_packet. Any remaining arguments
|
|
34
|
+
are passed directly to random_umi_packet.
|
|
35
|
+
- If this is an iterable (list, tuple, etc.), then it represents a list
|
|
36
|
+
of packets to use for the test. This is helpful if you want to use a very
|
|
37
|
+
specific sequence of transactions.
|
|
38
|
+
- This can also be an iterator, which might be convenient if you want to
|
|
39
|
+
send a very large number of packets without having to store them
|
|
40
|
+
all in memory at once, e.g. (random_umi_packet() for _ in range(1000000))
|
|
41
|
+
|
|
42
|
+
Raises
|
|
43
|
+
------
|
|
44
|
+
ValueError
|
|
45
|
+
If the number of packets is not positive or if the `packets` argument is empty
|
|
46
|
+
Exception
|
|
47
|
+
If a received packet does not match the corresponding transmitted packet
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
# input validation
|
|
52
|
+
|
|
53
|
+
if isinstance(packets, Integral):
|
|
54
|
+
if packets <= 0:
|
|
55
|
+
raise ValueError(f'The number of packets must be positive (got packets={packets}).')
|
|
56
|
+
else:
|
|
57
|
+
total = packets
|
|
58
|
+
packets = (random_umi_packet(**kwargs) for _ in range(packets))
|
|
59
|
+
elif isinstance(packets, Iterable):
|
|
60
|
+
if isinstance(packets, (list, tuple)):
|
|
61
|
+
total = len(packets)
|
|
62
|
+
else:
|
|
63
|
+
total = float('inf')
|
|
64
|
+
packets = iter(packets)
|
|
65
|
+
elif isinstance(packets, Iterator):
|
|
66
|
+
total = float('inf')
|
|
67
|
+
else:
|
|
68
|
+
raise TypeError(f'Unsupported type for packets: {type(packets)}')
|
|
69
|
+
|
|
70
|
+
tx_sets = [] # kept for debug purposes
|
|
71
|
+
tx_hist = []
|
|
72
|
+
|
|
73
|
+
tx_set = None
|
|
74
|
+
tx_partial = None
|
|
75
|
+
|
|
76
|
+
rx_set = None # kept for debug purposes
|
|
77
|
+
rx_partial = None
|
|
78
|
+
|
|
79
|
+
if tqdm is not None:
|
|
80
|
+
pbar = tqdm(total=total)
|
|
81
|
+
else:
|
|
82
|
+
pbar = None
|
|
83
|
+
|
|
84
|
+
# get the first element
|
|
85
|
+
try:
|
|
86
|
+
txp = next(packets)
|
|
87
|
+
if pbar is not None:
|
|
88
|
+
pbar.update(0)
|
|
89
|
+
except StopIteration:
|
|
90
|
+
raise ValueError('The argument "packets" is empty.')
|
|
91
|
+
|
|
92
|
+
while (txp is not None) or (len(tx_hist) > 0):
|
|
93
|
+
# send data
|
|
94
|
+
if txp is not None:
|
|
95
|
+
if umi.send(txp, blocking=False):
|
|
96
|
+
if tx_partial is not None:
|
|
97
|
+
if not tx_partial.merge(txp):
|
|
98
|
+
tx_hist.append(tx_partial)
|
|
99
|
+
tx_sets.append(tx_set)
|
|
100
|
+
tx_start_new = True
|
|
101
|
+
else:
|
|
102
|
+
tx_set.append(txp)
|
|
103
|
+
tx_start_new = False
|
|
104
|
+
else:
|
|
105
|
+
tx_start_new = True
|
|
106
|
+
|
|
107
|
+
if tx_start_new:
|
|
108
|
+
tx_partial = txp
|
|
109
|
+
tx_set = [txp]
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
txp = next(packets)
|
|
113
|
+
except StopIteration:
|
|
114
|
+
txp = None
|
|
115
|
+
|
|
116
|
+
if txp is None:
|
|
117
|
+
# if this is the last packet, add it to the history
|
|
118
|
+
# even if the merge was successful
|
|
119
|
+
tx_hist.append(tx_partial)
|
|
120
|
+
tx_sets.append(tx_set)
|
|
121
|
+
|
|
122
|
+
# receive data
|
|
123
|
+
if len(tx_hist) > 0:
|
|
124
|
+
rxp = umi.recv(blocking=False)
|
|
125
|
+
if rxp is not None:
|
|
126
|
+
# try to merge into an existing partial packet
|
|
127
|
+
if rx_partial is not None:
|
|
128
|
+
if not rx_partial.merge(rxp):
|
|
129
|
+
print('=== Mismatch detected ===')
|
|
130
|
+
for i, p in enumerate(tx_sets[0]):
|
|
131
|
+
print(f'* TX[{i}] *')
|
|
132
|
+
print(p)
|
|
133
|
+
print('---')
|
|
134
|
+
for i, p in enumerate(rx_set):
|
|
135
|
+
print(f'* RX[{i}] *')
|
|
136
|
+
print(p)
|
|
137
|
+
print('=========================')
|
|
138
|
+
raise Exception('Mismatch!')
|
|
139
|
+
else:
|
|
140
|
+
rx_set.append(rxp)
|
|
141
|
+
else:
|
|
142
|
+
rx_partial = rxp
|
|
143
|
+
rx_set = [rxp]
|
|
144
|
+
|
|
145
|
+
# at this point it is guaranteed there is something in
|
|
146
|
+
# rx_partial, so compare it to the expected outbound packet
|
|
147
|
+
if rx_partial == tx_hist[0]:
|
|
148
|
+
tx_hist.pop(0)
|
|
149
|
+
tx_sets.pop(0)
|
|
150
|
+
rx_partial = None
|
|
151
|
+
rx_set = None
|
|
152
|
+
|
|
153
|
+
if pbar is not None:
|
|
154
|
+
pbar.update()
|
|
155
|
+
|
|
156
|
+
if pbar is not None:
|
|
157
|
+
pbar.close()
|