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
switchboard/axil.py
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# Python interface for AXI-Lite reads and writes
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2024 Zero ASIC Corporation
|
|
4
|
+
# This code is licensed under Apache License 2.0 (see LICENSE for details)
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from math import floor, ceil, log2
|
|
9
|
+
from numbers import Integral
|
|
10
|
+
|
|
11
|
+
from _switchboard import PySbPacket, PySbTx, PySbRx
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AxiLiteTxRx:
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
uri: str,
|
|
18
|
+
fresh: bool = True,
|
|
19
|
+
data_width: int = 32,
|
|
20
|
+
addr_width: int = 16,
|
|
21
|
+
prot: int = 0,
|
|
22
|
+
resp_expected: str = 'OKAY',
|
|
23
|
+
queue_suffix: str = '.q',
|
|
24
|
+
max_rate: float = -1
|
|
25
|
+
):
|
|
26
|
+
"""
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
uri: str
|
|
30
|
+
Base name of for switchboard queues used to convey AXI transactions. Five
|
|
31
|
+
queues are used: write address (aw), write data (w), write response (b),
|
|
32
|
+
read address (ar), and read response (r). If the base name provided is
|
|
33
|
+
"axil", the correponding queues will be "axil-aw.q", "axil-w.q", "axil-b.q",
|
|
34
|
+
"axil-ar.q" and "axil-r.q". The suffix used can be changed via the
|
|
35
|
+
"queue_suffix" argument if needed.
|
|
36
|
+
fresh: bool, optional
|
|
37
|
+
If True (default), the queue specified by the uri parameter will get cleared
|
|
38
|
+
before executing the simulation.
|
|
39
|
+
data_width: int, optional
|
|
40
|
+
Width the write and read data buses, in bits.
|
|
41
|
+
addr_width: int, optional
|
|
42
|
+
Width the write and read address buses, in bits.
|
|
43
|
+
prot: int, optional
|
|
44
|
+
Default value of PROT to use for read and write transactions. Can be
|
|
45
|
+
overridden on a transaction-by-transaction basis.
|
|
46
|
+
resp_expected: str, optional
|
|
47
|
+
Default response to expect from reads and writes. Options are 'OKAY',
|
|
48
|
+
'EXOKAY', 'SLVERR', 'DECERR'. None means "don't check the response".
|
|
49
|
+
This default can be overridden on a transaction-by-transaction basis.
|
|
50
|
+
queue_suffix: str, optional
|
|
51
|
+
File extension/suffix to use when naming switchboard queues that carry
|
|
52
|
+
AXI transactions. For example, if set to ".queue", the write address
|
|
53
|
+
queue name will be "{uri}-aw.queue"
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
# check data types
|
|
57
|
+
assert isinstance(data_width, Integral), 'data_width must be an integer'
|
|
58
|
+
assert isinstance(addr_width, Integral), 'addr_width must be an integer'
|
|
59
|
+
|
|
60
|
+
# check that data width is a multiple of a byte
|
|
61
|
+
data_width_choices = [8, 16, 32, 64, 128, 256, 512, 1024]
|
|
62
|
+
assert data_width in data_width_choices, \
|
|
63
|
+
f'data_width must be in {data_width_choices}'
|
|
64
|
+
|
|
65
|
+
# check that data and address widths are supported
|
|
66
|
+
SBDW = 416
|
|
67
|
+
assert 0 < data_width <= floor(SBDW / (1 + (1 / 8))), 'data_width out of range'
|
|
68
|
+
assert 0 < addr_width <= SBDW - 3, 'addr_width out of range'
|
|
69
|
+
|
|
70
|
+
# save settings
|
|
71
|
+
self.data_width = data_width
|
|
72
|
+
self.addr_width = addr_width
|
|
73
|
+
self.default_prot = prot
|
|
74
|
+
self.default_resp_expected = resp_expected
|
|
75
|
+
|
|
76
|
+
# create the queues
|
|
77
|
+
self.aw = PySbTx(f'{uri}-aw{queue_suffix}', fresh=fresh, max_rate=max_rate)
|
|
78
|
+
self.w = PySbTx(f'{uri}-w{queue_suffix}', fresh=fresh, max_rate=max_rate)
|
|
79
|
+
self.b = PySbRx(f'{uri}-b{queue_suffix}', fresh=fresh, max_rate=max_rate)
|
|
80
|
+
self.ar = PySbTx(f'{uri}-ar{queue_suffix}', fresh=fresh, max_rate=max_rate)
|
|
81
|
+
self.r = PySbRx(f'{uri}-r{queue_suffix}', fresh=fresh, max_rate=max_rate)
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def strb_width(self):
|
|
85
|
+
return self.data_width // 8
|
|
86
|
+
|
|
87
|
+
def write(
|
|
88
|
+
self,
|
|
89
|
+
addr: Integral,
|
|
90
|
+
data,
|
|
91
|
+
prot: Integral = None,
|
|
92
|
+
resp_expected: str = None
|
|
93
|
+
):
|
|
94
|
+
"""
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
addr: int
|
|
98
|
+
Address to write to
|
|
99
|
+
|
|
100
|
+
data: np.uint8, np.uint16, np.uint32, np.uint64, or np.array
|
|
101
|
+
Data to write
|
|
102
|
+
|
|
103
|
+
prot: Integral
|
|
104
|
+
Value of PROT for this transaction. Defaults to the value provided in the
|
|
105
|
+
AxiLiteTxRx constructor if not provided, which in turn defaults to 0.
|
|
106
|
+
|
|
107
|
+
resp_expected: str, optional
|
|
108
|
+
Response to expect for this transaction. Options are 'OKAY', 'EXOKAY', 'SLVERR',
|
|
109
|
+
'DECERR', and None. None means, "don't check the response". Defaults to the
|
|
110
|
+
value provided in the AxiLiteTxRx constructor if not provided, which in turn
|
|
111
|
+
defaults to 'OKAY'
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
-------
|
|
115
|
+
str
|
|
116
|
+
String representation of the response code, which may be 'OKAY', 'EXOKAY',
|
|
117
|
+
'SLVERR', or 'DECERR'.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
# set defaults
|
|
121
|
+
|
|
122
|
+
if prot is None:
|
|
123
|
+
prot = self.default_prot
|
|
124
|
+
|
|
125
|
+
if resp_expected is None:
|
|
126
|
+
resp_expected = self.default_resp_expected
|
|
127
|
+
|
|
128
|
+
# check/standardize data types
|
|
129
|
+
|
|
130
|
+
assert isinstance(addr, Integral), 'addr must be an integer'
|
|
131
|
+
addr = int(addr)
|
|
132
|
+
|
|
133
|
+
assert isinstance(prot, Integral), 'prot must be an integer'
|
|
134
|
+
prot = int(prot)
|
|
135
|
+
|
|
136
|
+
if isinstance(data, np.ndarray):
|
|
137
|
+
if data.ndim == 0:
|
|
138
|
+
write_data = np.atleast_1d(data)
|
|
139
|
+
elif data.ndim == 1:
|
|
140
|
+
write_data = data
|
|
141
|
+
else:
|
|
142
|
+
raise ValueError(f'Can only write 1D arrays (got ndim={data.ndim})')
|
|
143
|
+
|
|
144
|
+
if not np.issubdtype(write_data.dtype, np.integer):
|
|
145
|
+
raise ValueError('Can only write integer dtypes such as uint8, uint16, etc.'
|
|
146
|
+
f' (got dtype "{data.dtype}")')
|
|
147
|
+
elif isinstance(data, np.integer):
|
|
148
|
+
write_data = np.array(data, ndmin=1)
|
|
149
|
+
else:
|
|
150
|
+
raise TypeError(f"Unknown data type: {type(data)}")
|
|
151
|
+
|
|
152
|
+
write_data = write_data.view(np.uint8)
|
|
153
|
+
bytes_to_send = write_data.size
|
|
154
|
+
|
|
155
|
+
# range validation
|
|
156
|
+
|
|
157
|
+
assert 0 <= addr < (1 << self.addr_width), 'addr out of range'
|
|
158
|
+
assert addr + bytes_to_send <= (1 << self.addr_width), \
|
|
159
|
+
"transaction exceeds the address space."
|
|
160
|
+
|
|
161
|
+
assert 0 <= prot < (1 << 3), 'prot out of range'
|
|
162
|
+
|
|
163
|
+
# loop until all data is sent
|
|
164
|
+
# TODO: move to C++?
|
|
165
|
+
|
|
166
|
+
bytes_sent = 0
|
|
167
|
+
|
|
168
|
+
data_bytes = self.data_width // 8
|
|
169
|
+
strb_bytes = (self.strb_width + 7) // 8
|
|
170
|
+
|
|
171
|
+
addr_mask = (1 << self.addr_width) - 1
|
|
172
|
+
addr_mask >>= ceil(log2(data_bytes))
|
|
173
|
+
addr_mask <<= ceil(log2(data_bytes))
|
|
174
|
+
|
|
175
|
+
while bytes_sent < bytes_to_send:
|
|
176
|
+
# find the offset into the data bus for this cycle. bytes below
|
|
177
|
+
# the offset will have write strobe de-asserted.
|
|
178
|
+
offset = addr % data_bytes
|
|
179
|
+
|
|
180
|
+
# determine how many bytes we're sending in this cycle
|
|
181
|
+
bytes_this_cycle = min(bytes_to_send - bytes_sent, data_bytes - offset)
|
|
182
|
+
|
|
183
|
+
# extract those bytes from the whole input data array, picking
|
|
184
|
+
# up where we left off from the last iteration
|
|
185
|
+
data_this_cycle = write_data[bytes_sent:bytes_sent + bytes_this_cycle]
|
|
186
|
+
|
|
187
|
+
# calculate strobe value based on the offset and number
|
|
188
|
+
# of bytes that we're writing.
|
|
189
|
+
strb = ((1 << bytes_this_cycle) - 1) << offset
|
|
190
|
+
strb = strb.to_bytes(strb_bytes, 'little')
|
|
191
|
+
strb = np.frombuffer(strb, dtype=np.uint8)
|
|
192
|
+
|
|
193
|
+
# transmit the write address
|
|
194
|
+
pack = (prot << self.addr_width) | (addr & addr_mask)
|
|
195
|
+
pack = pack.to_bytes((self.addr_width + 3 + 7) // 8, 'little')
|
|
196
|
+
pack = np.frombuffer(pack, dtype=np.uint8)
|
|
197
|
+
pack = PySbPacket(data=pack, flags=1, destination=0)
|
|
198
|
+
self.aw.send(pack, True)
|
|
199
|
+
|
|
200
|
+
# write data and strobe
|
|
201
|
+
pack = np.empty((data_bytes + strb_bytes,), dtype=np.uint8)
|
|
202
|
+
pack[offset:offset + bytes_this_cycle] = data_this_cycle
|
|
203
|
+
pack[data_bytes:data_bytes + strb_bytes] = strb
|
|
204
|
+
pack = PySbPacket(data=pack, flags=1, destination=0)
|
|
205
|
+
self.w.send(pack, True)
|
|
206
|
+
|
|
207
|
+
# wait for response
|
|
208
|
+
pack = self.b.recv(True)
|
|
209
|
+
pack = pack.data.tobytes()
|
|
210
|
+
pack = int.from_bytes(pack, 'little')
|
|
211
|
+
|
|
212
|
+
# decode the response
|
|
213
|
+
resp = decode_resp(pack & 0b11)
|
|
214
|
+
|
|
215
|
+
# check the response if desired
|
|
216
|
+
if resp_expected is not None:
|
|
217
|
+
assert resp.upper() == resp_expected.upper(), f'Unexpected response: {resp}'
|
|
218
|
+
|
|
219
|
+
# increment pointers
|
|
220
|
+
bytes_sent += bytes_this_cycle
|
|
221
|
+
addr += bytes_this_cycle
|
|
222
|
+
|
|
223
|
+
# return the last reponse
|
|
224
|
+
return resp
|
|
225
|
+
|
|
226
|
+
def read(
|
|
227
|
+
self,
|
|
228
|
+
addr: Integral,
|
|
229
|
+
num_or_dtype,
|
|
230
|
+
dtype=np.uint8,
|
|
231
|
+
prot: Integral = None,
|
|
232
|
+
resp_expected: str = None
|
|
233
|
+
):
|
|
234
|
+
"""
|
|
235
|
+
Parameters
|
|
236
|
+
----------
|
|
237
|
+
addr: int
|
|
238
|
+
Address to read from
|
|
239
|
+
|
|
240
|
+
num_or_dtype: int or numpy integer datatype
|
|
241
|
+
If a plain int, `num_or_datatype` specifies the number of bytes to be read.
|
|
242
|
+
If a numpy integer datatype (np.uint8, np.uint16, etc.), num_or_datatype
|
|
243
|
+
specifies the data type to be returned.
|
|
244
|
+
|
|
245
|
+
dtype: numpy integer datatype, optional
|
|
246
|
+
If num_or_dtype is a plain integer, the value returned by this function
|
|
247
|
+
will be a numpy array of type "dtype". On the other hand, if num_or_dtype
|
|
248
|
+
is a numpy datatype, the value returned will be a scalar of that datatype.
|
|
249
|
+
|
|
250
|
+
prot: Integral
|
|
251
|
+
Value of PROT for this transaction. Defaults to the value provided in the
|
|
252
|
+
AxiLiteTxRx constructor if not provided, which in turn defaults to 0.
|
|
253
|
+
|
|
254
|
+
resp_expected: str, optional
|
|
255
|
+
Response to expect for this transaction. Options are 'OKAY', 'EXOKAY', 'SLVERR',
|
|
256
|
+
'DECERR', and None. None means, "don't check the response". Defaults to the
|
|
257
|
+
value provided in the AxiLiteTxRx constructor if not provided, which in turn
|
|
258
|
+
defaults to 'OKAY'
|
|
259
|
+
|
|
260
|
+
Returns
|
|
261
|
+
-------
|
|
262
|
+
int
|
|
263
|
+
Value read, as an arbitrary-size Python integer.
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
# set defaults
|
|
267
|
+
|
|
268
|
+
if prot is None:
|
|
269
|
+
prot = self.default_prot
|
|
270
|
+
|
|
271
|
+
if resp_expected is None:
|
|
272
|
+
resp_expected = self.default_resp_expected
|
|
273
|
+
|
|
274
|
+
# check/standardize data types
|
|
275
|
+
|
|
276
|
+
assert isinstance(addr, Integral), 'addr must be an integer'
|
|
277
|
+
addr = int(addr)
|
|
278
|
+
|
|
279
|
+
assert isinstance(prot, Integral), 'prot must be an integer'
|
|
280
|
+
prot = int(prot)
|
|
281
|
+
|
|
282
|
+
if isinstance(num_or_dtype, (type, np.dtype)):
|
|
283
|
+
bytes_to_read = np.dtype(num_or_dtype).itemsize
|
|
284
|
+
else:
|
|
285
|
+
bytes_to_read = num_or_dtype * np.dtype(dtype).itemsize
|
|
286
|
+
|
|
287
|
+
# range validation
|
|
288
|
+
|
|
289
|
+
assert 0 <= addr < (1 << self.addr_width), 'addr out of range'
|
|
290
|
+
assert addr + bytes_to_read <= (1 << self.addr_width), \
|
|
291
|
+
"transaction exceeds the address space."
|
|
292
|
+
|
|
293
|
+
assert 0 <= prot < (1 << 3), 'prot out of range'
|
|
294
|
+
|
|
295
|
+
# loop until all data is read
|
|
296
|
+
# TODO: move to C++?
|
|
297
|
+
|
|
298
|
+
bytes_read = 0
|
|
299
|
+
data_bytes = self.data_width // 8
|
|
300
|
+
|
|
301
|
+
addr_mask = (1 << self.addr_width) - 1
|
|
302
|
+
addr_mask >>= ceil(log2(data_bytes))
|
|
303
|
+
addr_mask <<= ceil(log2(data_bytes))
|
|
304
|
+
|
|
305
|
+
retval = np.empty((bytes_to_read,), dtype=np.uint8)
|
|
306
|
+
|
|
307
|
+
while bytes_read < bytes_to_read:
|
|
308
|
+
# find the offset into the data bus for this cycle
|
|
309
|
+
offset = addr % data_bytes
|
|
310
|
+
|
|
311
|
+
# determine what data we're reading this cycle
|
|
312
|
+
bytes_this_cycle = min(bytes_to_read - bytes_read, data_bytes - offset)
|
|
313
|
+
|
|
314
|
+
# transmit read address
|
|
315
|
+
pack = (prot << self.addr_width) | (addr & addr_mask)
|
|
316
|
+
pack = pack.to_bytes((self.addr_width + 3 + 7) // 8, 'little')
|
|
317
|
+
pack = np.frombuffer(pack, dtype=np.uint8)
|
|
318
|
+
pack = PySbPacket(data=pack, flags=1, destination=0)
|
|
319
|
+
self.ar.send(pack, True)
|
|
320
|
+
|
|
321
|
+
# wait for response
|
|
322
|
+
pack = self.r.recv(True)
|
|
323
|
+
data = pack.data[offset:offset + bytes_this_cycle]
|
|
324
|
+
resp = pack.data[data_bytes] & 0b11
|
|
325
|
+
|
|
326
|
+
# check the reponse
|
|
327
|
+
if resp_expected is not None:
|
|
328
|
+
resp = decode_resp(resp)
|
|
329
|
+
assert resp.upper() == resp_expected.upper(), f'Unexpected response: {resp}'
|
|
330
|
+
|
|
331
|
+
# add this data to the return value
|
|
332
|
+
retval[bytes_read:bytes_read + bytes_this_cycle] = data
|
|
333
|
+
|
|
334
|
+
# increment pointers
|
|
335
|
+
bytes_read += bytes_this_cycle
|
|
336
|
+
addr += bytes_this_cycle
|
|
337
|
+
|
|
338
|
+
if isinstance(num_or_dtype, (type, np.dtype)):
|
|
339
|
+
return retval.view(num_or_dtype)[0]
|
|
340
|
+
else:
|
|
341
|
+
return retval.view(dtype)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def decode_resp(resp: Integral):
|
|
345
|
+
assert isinstance(resp, Integral), 'response code must be an integer'
|
|
346
|
+
assert 0 <= resp <= 3, 'response code out of range'
|
|
347
|
+
|
|
348
|
+
return ['OKAY', 'EXOKAY', 'SLVERR', 'DECERR'][resp]
|
switchboard/bitvector.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Utilities for working with bit vectors using Verilog-style syntax, i.e. [MSB:LSB]
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2024 Zero ASIC Corporation
|
|
4
|
+
# This code is licensed under Apache License 2.0 (see LICENSE for details)
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BitVector:
|
|
10
|
+
def __init__(self, value: int = 0):
|
|
11
|
+
self.value = int(value)
|
|
12
|
+
|
|
13
|
+
def __int__(self):
|
|
14
|
+
return int(self.value)
|
|
15
|
+
|
|
16
|
+
def __str__(self):
|
|
17
|
+
return f'0x{self.value:x}'
|
|
18
|
+
|
|
19
|
+
def __setitem__(self, key, value):
|
|
20
|
+
if isinstance(key, slice):
|
|
21
|
+
if (key.start is None) and (key.stop is None) and (key.step is None):
|
|
22
|
+
self.value = value
|
|
23
|
+
return
|
|
24
|
+
else:
|
|
25
|
+
msb, lsb = slice_to_msb_lsb(key.start, key.stop, key.step)
|
|
26
|
+
else:
|
|
27
|
+
msb, lsb = slice_to_msb_lsb(key, key)
|
|
28
|
+
|
|
29
|
+
# generate mask with the right width
|
|
30
|
+
mask = (1 << (msb - lsb + 1)) - 1
|
|
31
|
+
|
|
32
|
+
# clear bit field using the mask
|
|
33
|
+
new_value = self.value & (~(mask << lsb))
|
|
34
|
+
|
|
35
|
+
# set bit field
|
|
36
|
+
new_value |= (value & mask) << lsb
|
|
37
|
+
|
|
38
|
+
# set the new value (done here instead of through
|
|
39
|
+
# incremental updates, to prevent the value from
|
|
40
|
+
# being partially updated in case of an exception
|
|
41
|
+
self.value = new_value
|
|
42
|
+
|
|
43
|
+
def __getitem__(self, key):
|
|
44
|
+
if isinstance(key, slice):
|
|
45
|
+
if (key.start is None) and (key.stop is None) and (key.step is None):
|
|
46
|
+
return self.value
|
|
47
|
+
else:
|
|
48
|
+
msb, lsb = slice_to_msb_lsb(key.start, key.stop, key.step)
|
|
49
|
+
else:
|
|
50
|
+
msb, lsb = slice_to_msb_lsb(key, key)
|
|
51
|
+
|
|
52
|
+
# generate mask with the right width
|
|
53
|
+
mask = (1 << (msb - lsb + 1)) - 1
|
|
54
|
+
|
|
55
|
+
# extract the value
|
|
56
|
+
return (self.value >> lsb) & mask
|
|
57
|
+
|
|
58
|
+
def tobytes(self, n=None):
|
|
59
|
+
# convert to a numpy byte array. if "n" is provided,
|
|
60
|
+
# pad result to be "n" bytes. will error out if "n"
|
|
61
|
+
# is less than the number of bytes needed to represent
|
|
62
|
+
# the current value.
|
|
63
|
+
|
|
64
|
+
value = self.value
|
|
65
|
+
bytes = []
|
|
66
|
+
|
|
67
|
+
while value != 0:
|
|
68
|
+
bytes.append(value & 0xff)
|
|
69
|
+
value >>= 8
|
|
70
|
+
|
|
71
|
+
if n is not None:
|
|
72
|
+
if len(bytes) < n:
|
|
73
|
+
bytes += [0] * (n - len(bytes))
|
|
74
|
+
elif len(bytes) > n:
|
|
75
|
+
raise ValueError('Number of bytes needed to represent the current value'
|
|
76
|
+
f' ({self.value}) is {len(bytes)}, but the argument n={n} is smaller.')
|
|
77
|
+
|
|
78
|
+
return np.array(bytes, dtype=np.uint8)
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def frombytes(arr):
|
|
82
|
+
value = 0
|
|
83
|
+
|
|
84
|
+
for i, elem in enumerate(arr):
|
|
85
|
+
if not (0 <= elem <= 255):
|
|
86
|
+
raise ValueError(f'Non-byte value detected at index {i}: {elem}')
|
|
87
|
+
value |= (int(elem) & 0xff) << (i * 8)
|
|
88
|
+
|
|
89
|
+
return BitVector(value)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def slice_to_msb_lsb(start=None, stop=None, step=None):
|
|
93
|
+
# set defaults
|
|
94
|
+
if start is None:
|
|
95
|
+
start = 0
|
|
96
|
+
if stop is None:
|
|
97
|
+
stop = 0
|
|
98
|
+
if step is None:
|
|
99
|
+
step = 1
|
|
100
|
+
|
|
101
|
+
if step != 1:
|
|
102
|
+
raise ValueError('Only step=1 allowed for slice indexing.')
|
|
103
|
+
|
|
104
|
+
msb = start
|
|
105
|
+
lsb = stop
|
|
106
|
+
|
|
107
|
+
if msb < lsb:
|
|
108
|
+
raise ValueError('MSB must be greater than or equal to LSB')
|
|
109
|
+
if lsb < 0:
|
|
110
|
+
raise ValueError('Negative LSB is not allowed.')
|
|
111
|
+
|
|
112
|
+
return msb, lsb
|
switchboard/cmdline.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Copyright (c) 2024 Zero ASIC Corporation
|
|
2
|
+
# This code is licensed under Apache License 2.0 (see LICENSE for details)
|
|
3
|
+
|
|
4
|
+
def get_cmdline_args(
|
|
5
|
+
tool: str = 'verilator',
|
|
6
|
+
trace: bool = True,
|
|
7
|
+
trace_type: str = 'vcd',
|
|
8
|
+
frequency: float = 100e6,
|
|
9
|
+
period: float = None,
|
|
10
|
+
max_rate: float = -1,
|
|
11
|
+
start_delay: float = None,
|
|
12
|
+
fast: bool = False,
|
|
13
|
+
single_netlist: bool = False,
|
|
14
|
+
threads: int = None,
|
|
15
|
+
extra_args: dict = None
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
Sets up and runs a command-line option parser (argparse) using the arguments
|
|
19
|
+
provided as defaults. The object returned is an argparse.Namespace object,
|
|
20
|
+
which is the same object type returned by ArgumentParser.parse_args()
|
|
21
|
+
|
|
22
|
+
This function is used in SbNetwork and SbDut. It should generally be
|
|
23
|
+
called only once, at the top level of the simulation.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
tool: string, optional
|
|
28
|
+
Which tool to use to compile simulator. Options are "verilator" or
|
|
29
|
+
"icarus".
|
|
30
|
+
|
|
31
|
+
trace: bool, optional
|
|
32
|
+
If true, a waveform dump file will be produced using the file type
|
|
33
|
+
specified by `trace_type`.
|
|
34
|
+
|
|
35
|
+
trace_type: str, optional
|
|
36
|
+
File type for the waveform dump file. Defaults to vcd.
|
|
37
|
+
|
|
38
|
+
frequency: float, optional
|
|
39
|
+
If provided, the default frequency of the clock generated in the testbench,
|
|
40
|
+
in seconds.
|
|
41
|
+
|
|
42
|
+
period: float, optional
|
|
43
|
+
If provided, the default period of the clock generated in the testbench,
|
|
44
|
+
in seconds.
|
|
45
|
+
|
|
46
|
+
max_rate: float, optional
|
|
47
|
+
If provided, the maximum real-world rate that the simulation is allowed to run
|
|
48
|
+
at, in Hz. Can be useful to encourage time-sharing between many processes and
|
|
49
|
+
for performance modeling when latencies are large and/or variable. A value of
|
|
50
|
+
"-1" means that the rate-limiting feature is disabled.
|
|
51
|
+
|
|
52
|
+
start_delay: float, optional
|
|
53
|
+
If provided, the real-world time to delay before the first clock tick in the
|
|
54
|
+
simulation. Can be useful to make sure that programs start at approximately
|
|
55
|
+
the same time and to prevent simulations from stepping on each other's toes
|
|
56
|
+
when starting up.
|
|
57
|
+
|
|
58
|
+
fast: bool, optional
|
|
59
|
+
If True, the simulation binary will not be rebuilt if an existing one is found.
|
|
60
|
+
The setting here can be overridden when build() is called by setting its argument
|
|
61
|
+
with the same name.
|
|
62
|
+
|
|
63
|
+
extra_args: dict, optional
|
|
64
|
+
If provided and cmdline=True, a dictionary of additional command line arguments
|
|
65
|
+
to be made available. The keys of the dictionary are the arguments ("-n", "--test",
|
|
66
|
+
etc.) and the values are themselves dictionaries that contain keyword arguments
|
|
67
|
+
accepted by argparse ("action": "store_true", "default": 42, etc.)
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
from argparse import ArgumentParser
|
|
71
|
+
|
|
72
|
+
parser = ArgumentParser()
|
|
73
|
+
|
|
74
|
+
if not trace:
|
|
75
|
+
parser.add_argument('--trace', action='store_true', help='Probe'
|
|
76
|
+
' waveforms during simulation.')
|
|
77
|
+
else:
|
|
78
|
+
parser.add_argument('--no-trace', action='store_true', help='Do not'
|
|
79
|
+
' probe waveforms during simulation. This can improve build time'
|
|
80
|
+
' and run time, but reduces visibility.')
|
|
81
|
+
|
|
82
|
+
parser.add_argument('--trace-type', type=str, choices=['vcd', 'fst'],
|
|
83
|
+
default=trace_type, help='File type for waveform probing.')
|
|
84
|
+
|
|
85
|
+
if not fast:
|
|
86
|
+
parser.add_argument('--fast', action='store_true', help='Do not build'
|
|
87
|
+
' the simulator binary if it has already been built.')
|
|
88
|
+
else:
|
|
89
|
+
parser.add_argument('--rebuild', action='store_true', help='Build the'
|
|
90
|
+
' simulator binary even if it has already been built.')
|
|
91
|
+
|
|
92
|
+
parser.add_argument('--tool', type=str, choices=['verilator', 'icarus'],
|
|
93
|
+
default=tool, help='Name of the simulator to use.')
|
|
94
|
+
|
|
95
|
+
group = parser.add_mutually_exclusive_group()
|
|
96
|
+
group.add_argument('--period', type=float, default=period,
|
|
97
|
+
help='Period of the clk signal in seconds. Automatically set if'
|
|
98
|
+
' --frequency is provided.')
|
|
99
|
+
group.add_argument('--frequency', type=float, default=frequency,
|
|
100
|
+
help='Frequency of the clk signal in Hz. Automatically set if'
|
|
101
|
+
' --period is provided.')
|
|
102
|
+
|
|
103
|
+
parser.add_argument('--max-rate', type=float, default=max_rate,
|
|
104
|
+
help='Maximum real-world rate that the simulation is allowed to run at, in Hz.')
|
|
105
|
+
|
|
106
|
+
parser.add_argument('--start-delay', type=float, default=start_delay,
|
|
107
|
+
help='Delay before starting simulation, in seconds. Can be useful to prevent'
|
|
108
|
+
' simulations from stepping on each others toes when starting up.')
|
|
109
|
+
|
|
110
|
+
if not single_netlist:
|
|
111
|
+
parser.add_argument('--single-netlist', action='store_true', help='Run in single-netlist'
|
|
112
|
+
' mode, where the network is constructed in Verilog and run in a single simulator.')
|
|
113
|
+
else:
|
|
114
|
+
parser.add_argument('--distributed', action='store_true', help='Run in distributed'
|
|
115
|
+
' simulation mode, rather than single-netlist mode.')
|
|
116
|
+
|
|
117
|
+
parser.add_argument('--threads', type=int, default=threads,
|
|
118
|
+
help='Number of threads to use when running a simulation.')
|
|
119
|
+
|
|
120
|
+
if extra_args is not None:
|
|
121
|
+
for k, v in extra_args.items():
|
|
122
|
+
parser.add_argument(k, **v)
|
|
123
|
+
|
|
124
|
+
args, _ = parser.parse_known_args()
|
|
125
|
+
|
|
126
|
+
# standardize boolean flags
|
|
127
|
+
|
|
128
|
+
if trace:
|
|
129
|
+
args.trace = not args.no_trace
|
|
130
|
+
del args.no_trace
|
|
131
|
+
|
|
132
|
+
if fast:
|
|
133
|
+
args.fast = not args.rebuild
|
|
134
|
+
del args.rebuild
|
|
135
|
+
|
|
136
|
+
if single_netlist:
|
|
137
|
+
args.single_netlist = not args.distributed
|
|
138
|
+
del args.distributed
|
|
139
|
+
|
|
140
|
+
# return arguments
|
|
141
|
+
|
|
142
|
+
return args
|
switchboard/cpp/Makefile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright (c) 2024 Zero ASIC Corporation
|
|
2
|
+
# This code is licensed under Apache License 2.0 (see LICENSE for details)
|
|
3
|
+
|
|
4
|
+
TARGETS = umidriver old_umidriver router old2new
|
|
5
|
+
|
|
6
|
+
all: $(TARGETS)
|
|
7
|
+
|
|
8
|
+
%: %.cc switchboard.hpp
|
|
9
|
+
g++ -std=c++11 -I. $< -o $@ $(CPP_LIBS)
|
|
10
|
+
|
|
11
|
+
.PHONY: clean
|
|
12
|
+
clean:
|
|
13
|
+
rm -f $(TARGETS)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Copyright (c) 2024 Zero ASIC Corporation
|
|
2
|
+
// This code is licensed under Apache License 2.0 (see LICENSE for details)
|
|
3
|
+
|
|
4
|
+
#ifndef __BITUTIL_H__
|
|
5
|
+
#define __BITUTIL_H__
|
|
6
|
+
|
|
7
|
+
#include <stddef.h>
|
|
8
|
+
|
|
9
|
+
// highest_bit: determine the index of the most significant non-zero
|
|
10
|
+
// bit in a number.
|
|
11
|
+
|
|
12
|
+
static inline size_t highest_bit(size_t x) {
|
|
13
|
+
size_t retval = 0;
|
|
14
|
+
while ((x >>= 1) != 0) {
|
|
15
|
+
retval++;
|
|
16
|
+
}
|
|
17
|
+
return retval;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// lowest_bit: determine index of the least significant non-zero
|
|
21
|
+
// bit in a number.
|
|
22
|
+
|
|
23
|
+
static inline size_t lowest_bit(size_t x) {
|
|
24
|
+
if (x == 0) {
|
|
25
|
+
// if the input is zero, it is convenient to return a value
|
|
26
|
+
// that is larger than the return value for any non-zero
|
|
27
|
+
// input value, which is (sizeof(size_t)*8)-1.
|
|
28
|
+
return sizeof(size_t) * 8;
|
|
29
|
+
} else {
|
|
30
|
+
size_t retval = 0;
|
|
31
|
+
while ((x & 1) == 0) {
|
|
32
|
+
x >>= 1;
|
|
33
|
+
retval++;
|
|
34
|
+
}
|
|
35
|
+
return retval;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#endif // #ifndef __BITUTIL_H__
|