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.
Files changed (99) hide show
  1. _switchboard.cpython-314-darwin.so +0 -0
  2. switchboard/__init__.py +24 -0
  3. switchboard/ams.py +668 -0
  4. switchboard/apb.py +278 -0
  5. switchboard/autowrap.py +1000 -0
  6. switchboard/axi.py +571 -0
  7. switchboard/axil.py +348 -0
  8. switchboard/bitvector.py +112 -0
  9. switchboard/cmdline.py +142 -0
  10. switchboard/cpp/Makefile +13 -0
  11. switchboard/cpp/bitutil.h +39 -0
  12. switchboard/cpp/pagemap.h +91 -0
  13. switchboard/cpp/pciedev.h +86 -0
  14. switchboard/cpp/router.cc +89 -0
  15. switchboard/cpp/spsc_queue.h +267 -0
  16. switchboard/cpp/switchboard.hpp +257 -0
  17. switchboard/cpp/switchboard_pcie.hpp +234 -0
  18. switchboard/cpp/switchboard_tlm.hpp +98 -0
  19. switchboard/cpp/umilib.h +144 -0
  20. switchboard/cpp/umilib.hpp +113 -0
  21. switchboard/cpp/umisb.hpp +364 -0
  22. switchboard/cpp/xyce.hpp +90 -0
  23. switchboard/deps/__init__.py +0 -0
  24. switchboard/deps/verilog_axi.py +23 -0
  25. switchboard/dpi/__init__.py +0 -0
  26. switchboard/dpi/switchboard_dpi.cc +119 -0
  27. switchboard/dpi/switchboard_dpi.py +13 -0
  28. switchboard/dpi/xyce_dpi.cc +43 -0
  29. switchboard/gpio.py +108 -0
  30. switchboard/icarus.py +85 -0
  31. switchboard/loopback.py +157 -0
  32. switchboard/network.py +714 -0
  33. switchboard/pytest_plugin.py +11 -0
  34. switchboard/sbdesign.py +55 -0
  35. switchboard/sbdut.py +744 -0
  36. switchboard/sbtcp.py +345 -0
  37. switchboard/sc/__init__.py +0 -0
  38. switchboard/sc/morty/__init__.py +0 -0
  39. switchboard/sc/morty/uniquify.py +67 -0
  40. switchboard/sc/sed/__init__.py +0 -0
  41. switchboard/sc/sed/sed_remove.py +47 -0
  42. switchboard/sc/standalone_netlist_flow.py +25 -0
  43. switchboard/switchboard.py +53 -0
  44. switchboard/test_util.py +46 -0
  45. switchboard/uart_xactor.py +66 -0
  46. switchboard/umi.py +793 -0
  47. switchboard/util.py +131 -0
  48. switchboard/verilator/__init__.py +0 -0
  49. switchboard/verilator/config.vlt +13 -0
  50. switchboard/verilator/testbench.cc +143 -0
  51. switchboard/verilator/verilator.py +13 -0
  52. switchboard/verilator_run.py +31 -0
  53. switchboard/verilog/__init__.py +0 -0
  54. switchboard/verilog/common/__init__.py +0 -0
  55. switchboard/verilog/common/common.py +26 -0
  56. switchboard/verilog/common/switchboard.vh +429 -0
  57. switchboard/verilog/common/uart_xactor.sv +247 -0
  58. switchboard/verilog/common/umi_gpio.v +236 -0
  59. switchboard/verilog/fpga/__init__.py +0 -0
  60. switchboard/verilog/fpga/axi_reader.sv +82 -0
  61. switchboard/verilog/fpga/axi_writer.sv +111 -0
  62. switchboard/verilog/fpga/config_registers.sv +249 -0
  63. switchboard/verilog/fpga/fpga.py +21 -0
  64. switchboard/verilog/fpga/include/sb_queue_regmap.vh +21 -0
  65. switchboard/verilog/fpga/include/spsc_queue.vh +7 -0
  66. switchboard/verilog/fpga/memory_fault.sv +40 -0
  67. switchboard/verilog/fpga/sb_fpga_queues.sv +416 -0
  68. switchboard/verilog/fpga/sb_rx_fpga.sv +303 -0
  69. switchboard/verilog/fpga/sb_tx_fpga.sv +294 -0
  70. switchboard/verilog/fpga/umi_fpga_queues.sv +146 -0
  71. switchboard/verilog/sim/__init__.py +0 -0
  72. switchboard/verilog/sim/auto_stop_sim.sv +25 -0
  73. switchboard/verilog/sim/perf_meas_sim.sv +97 -0
  74. switchboard/verilog/sim/queue_to_sb_sim.sv +176 -0
  75. switchboard/verilog/sim/queue_to_umi_sim.sv +66 -0
  76. switchboard/verilog/sim/sb_apb_m.sv +146 -0
  77. switchboard/verilog/sim/sb_axi_m.sv +199 -0
  78. switchboard/verilog/sim/sb_axil_m.sv +180 -0
  79. switchboard/verilog/sim/sb_axil_s.sv +180 -0
  80. switchboard/verilog/sim/sb_clk_gen.sv +89 -0
  81. switchboard/verilog/sim/sb_jtag_rbb_sim.sv +148 -0
  82. switchboard/verilog/sim/sb_rx_sim.sv +55 -0
  83. switchboard/verilog/sim/sb_to_queue_sim.sv +196 -0
  84. switchboard/verilog/sim/sb_tx_sim.sv +55 -0
  85. switchboard/verilog/sim/switchboard_sim.py +49 -0
  86. switchboard/verilog/sim/umi_rx_sim.sv +61 -0
  87. switchboard/verilog/sim/umi_to_queue_sim.sv +66 -0
  88. switchboard/verilog/sim/umi_tx_sim.sv +61 -0
  89. switchboard/verilog/sim/xyce_intf.sv +67 -0
  90. switchboard/vpi/switchboard_vpi.cc +431 -0
  91. switchboard/vpi/xyce_vpi.cc +200 -0
  92. switchboard/warn.py +14 -0
  93. switchboard/xyce.py +27 -0
  94. switchboard_hw-0.3.0.dist-info/METADATA +303 -0
  95. switchboard_hw-0.3.0.dist-info/RECORD +99 -0
  96. switchboard_hw-0.3.0.dist-info/WHEEL +6 -0
  97. switchboard_hw-0.3.0.dist-info/entry_points.txt +6 -0
  98. switchboard_hw-0.3.0.dist-info/licenses/LICENSE +190 -0
  99. 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]
@@ -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
@@ -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__