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/sbdut.py
ADDED
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
# Build and simulation automation built on SiliconCompiler
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2024 Zero ASIC Corporation
|
|
4
|
+
# This code is licensed under Apache License 2.0 (see LICENSE for details)
|
|
5
|
+
|
|
6
|
+
"""Class inheriting from the SiliconCompiler Chip class that can be used for building a
|
|
7
|
+
Switchboard-based testbench.
|
|
8
|
+
|
|
9
|
+
This class is meant to be interacted with like a regular Chip object, but it has some parameters
|
|
10
|
+
automatically configured to abstract away setup of files that are required by all Switchboard
|
|
11
|
+
testbenches.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import subprocess
|
|
15
|
+
|
|
16
|
+
from copy import deepcopy
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import List, Dict, Any, Union
|
|
19
|
+
|
|
20
|
+
from .switchboard import path as sb_path
|
|
21
|
+
from .icarus import icarus_build_vpi, icarus_find_vpi, icarus_run
|
|
22
|
+
from .verilator_run import verilator_run
|
|
23
|
+
from .util import plusargs_to_args, binary_run, ProcessCollection
|
|
24
|
+
from .ams import make_ams_spice_wrapper, make_ams_verilog_wrapper, parse_spice_subckts
|
|
25
|
+
from .autowrap import (normalize_clocks, normalize_interfaces, normalize_resets, normalize_tieoffs,
|
|
26
|
+
normalize_parameters, create_intf_objs, type_is_axi, type_is_axil, type_is_apb)
|
|
27
|
+
from .cmdline import get_cmdline_args
|
|
28
|
+
from .apb import apb_uris
|
|
29
|
+
from .axi import axi_uris
|
|
30
|
+
|
|
31
|
+
from siliconcompiler import Design, Sim
|
|
32
|
+
from siliconcompiler.tools import get_task
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
SB_DIR = sb_path()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class AutowrapDesign(Design):
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
design: Design,
|
|
42
|
+
fileset: str,
|
|
43
|
+
parameters=None,
|
|
44
|
+
intf_defs=None,
|
|
45
|
+
clocks=None,
|
|
46
|
+
resets=None,
|
|
47
|
+
tieoffs=None,
|
|
48
|
+
filename=None
|
|
49
|
+
):
|
|
50
|
+
|
|
51
|
+
super().__init__("AutowrapDesign")
|
|
52
|
+
|
|
53
|
+
from switchboard.autowrap import autowrap
|
|
54
|
+
|
|
55
|
+
instance = f'{design.name}_i'
|
|
56
|
+
|
|
57
|
+
autowrap(
|
|
58
|
+
toplevel="testbench",
|
|
59
|
+
instances={instance: design.get_topmodule(fileset=fileset)},
|
|
60
|
+
parameters={instance: parameters},
|
|
61
|
+
interfaces={instance: intf_defs},
|
|
62
|
+
clocks={instance: clocks},
|
|
63
|
+
resets={instance: resets},
|
|
64
|
+
tieoffs={instance: tieoffs},
|
|
65
|
+
filename=filename
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
from switchboard.verilog.sim.switchboard_sim import SwitchboardSim
|
|
69
|
+
|
|
70
|
+
with self.active_fileset(fileset):
|
|
71
|
+
self.set_topmodule("testbench")
|
|
72
|
+
self.add_depfileset(design)
|
|
73
|
+
self.add_depfileset(SwitchboardSim())
|
|
74
|
+
self.add_file(str(filename))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class SbDut(Sim):
|
|
78
|
+
def __init__(
|
|
79
|
+
self,
|
|
80
|
+
design: Union[Design, str] = None,
|
|
81
|
+
tool: str = 'verilator',
|
|
82
|
+
fileset: str = None,
|
|
83
|
+
default_main: bool = True,
|
|
84
|
+
trace: bool = True,
|
|
85
|
+
trace_type: str = 'vcd',
|
|
86
|
+
module: str = None,
|
|
87
|
+
fpga: bool = False,
|
|
88
|
+
xyce: bool = False,
|
|
89
|
+
frequency: float = 100e6,
|
|
90
|
+
period: float = None,
|
|
91
|
+
max_rate: float = -1,
|
|
92
|
+
start_delay: float = None,
|
|
93
|
+
timeunit: str = None,
|
|
94
|
+
timeprecision: str = None,
|
|
95
|
+
warnings: List[str] = None,
|
|
96
|
+
cmdline: bool = False,
|
|
97
|
+
fast: bool = False,
|
|
98
|
+
extra_args: dict = None,
|
|
99
|
+
autowrap: bool = False,
|
|
100
|
+
parameters=None,
|
|
101
|
+
interfaces=None,
|
|
102
|
+
clocks=None,
|
|
103
|
+
resets=None,
|
|
104
|
+
tieoffs=None,
|
|
105
|
+
buildroot=None,
|
|
106
|
+
builddir=None,
|
|
107
|
+
args=None,
|
|
108
|
+
subcomponent=False,
|
|
109
|
+
suffix=None,
|
|
110
|
+
threads=None
|
|
111
|
+
):
|
|
112
|
+
|
|
113
|
+
super().__init__(design)
|
|
114
|
+
|
|
115
|
+
self.option.set_nodashboard(True)
|
|
116
|
+
|
|
117
|
+
##########################################
|
|
118
|
+
# parse command-line options if desired
|
|
119
|
+
##########################################
|
|
120
|
+
if cmdline:
|
|
121
|
+
self.args = get_cmdline_args(
|
|
122
|
+
tool=tool,
|
|
123
|
+
trace=trace,
|
|
124
|
+
trace_type=trace_type,
|
|
125
|
+
frequency=frequency,
|
|
126
|
+
period=period,
|
|
127
|
+
fast=fast,
|
|
128
|
+
max_rate=max_rate,
|
|
129
|
+
start_delay=start_delay,
|
|
130
|
+
threads=threads,
|
|
131
|
+
extra_args=extra_args
|
|
132
|
+
)
|
|
133
|
+
elif args is not None:
|
|
134
|
+
self.args = args
|
|
135
|
+
else:
|
|
136
|
+
self.args = None
|
|
137
|
+
|
|
138
|
+
if self.args is not None:
|
|
139
|
+
trace = self.args.trace
|
|
140
|
+
trace_type = self.args.trace_type
|
|
141
|
+
fast = self.args.fast
|
|
142
|
+
tool = self.args.tool
|
|
143
|
+
frequency = self.args.frequency
|
|
144
|
+
period = self.args.period
|
|
145
|
+
max_rate = self.args.max_rate
|
|
146
|
+
start_delay = self.args.start_delay
|
|
147
|
+
threads = self.args.threads
|
|
148
|
+
|
|
149
|
+
# input validation
|
|
150
|
+
|
|
151
|
+
if trace_type not in ('vcd', 'fst'):
|
|
152
|
+
raise ValueError('Invalid trace_type, expected one of "vcd" or "fst"')
|
|
153
|
+
|
|
154
|
+
# save settings
|
|
155
|
+
|
|
156
|
+
self.tool = tool
|
|
157
|
+
self.trace = trace
|
|
158
|
+
self.trace_type = trace_type
|
|
159
|
+
self.fpga = fpga
|
|
160
|
+
self.xyce = False # is set True by _configure_xyce
|
|
161
|
+
self.warnings = warnings
|
|
162
|
+
self.fast = fast
|
|
163
|
+
|
|
164
|
+
if (period is None) and (frequency is not None):
|
|
165
|
+
period = 1 / frequency
|
|
166
|
+
self.period = period
|
|
167
|
+
self.max_rate = max_rate
|
|
168
|
+
self.start_delay = start_delay
|
|
169
|
+
|
|
170
|
+
self.threads = threads
|
|
171
|
+
|
|
172
|
+
self.timeunit = timeunit
|
|
173
|
+
self.timeprecision = timeprecision
|
|
174
|
+
|
|
175
|
+
self.autowrap = autowrap
|
|
176
|
+
|
|
177
|
+
self.parameters = normalize_parameters(parameters)
|
|
178
|
+
self.intf_defs = normalize_interfaces(interfaces)
|
|
179
|
+
self.clocks = normalize_clocks(clocks)
|
|
180
|
+
self.resets = normalize_resets(resets)
|
|
181
|
+
self.tieoffs = normalize_tieoffs(tieoffs)
|
|
182
|
+
|
|
183
|
+
if not fileset:
|
|
184
|
+
fileset = self.tool
|
|
185
|
+
|
|
186
|
+
self.fileset = fileset
|
|
187
|
+
|
|
188
|
+
self.design_name = None
|
|
189
|
+
if isinstance(design, Design):
|
|
190
|
+
self.design_name = design.name
|
|
191
|
+
else:
|
|
192
|
+
self.design_name = design
|
|
193
|
+
|
|
194
|
+
if (suffix is None) and subcomponent:
|
|
195
|
+
suffix = f'_unq_{self.design_name}'
|
|
196
|
+
|
|
197
|
+
self.suffix = suffix
|
|
198
|
+
|
|
199
|
+
# initialization
|
|
200
|
+
|
|
201
|
+
self.intfs = {}
|
|
202
|
+
|
|
203
|
+
# keep track of processes started
|
|
204
|
+
self.process_collection = ProcessCollection()
|
|
205
|
+
|
|
206
|
+
# simulator-agnostic settings
|
|
207
|
+
|
|
208
|
+
if builddir is None:
|
|
209
|
+
if buildroot is None:
|
|
210
|
+
buildroot = 'build'
|
|
211
|
+
|
|
212
|
+
buildroot = Path(buildroot).resolve()
|
|
213
|
+
|
|
214
|
+
if subcomponent:
|
|
215
|
+
# the subcomponent build flow is tool-agnostic, producing a single Verilog
|
|
216
|
+
# file as output, as opposed to a simulator binary
|
|
217
|
+
builddir = buildroot / metadata_str(
|
|
218
|
+
design=self.design_name,
|
|
219
|
+
parameters=parameters
|
|
220
|
+
)
|
|
221
|
+
else:
|
|
222
|
+
builddir = buildroot / metadata_str(
|
|
223
|
+
design=self.design_name,
|
|
224
|
+
parameters=parameters,
|
|
225
|
+
tool=tool,
|
|
226
|
+
trace=trace,
|
|
227
|
+
trace_type=trace_type,
|
|
228
|
+
threads=threads
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
self.option.set_builddir(str(Path(builddir).resolve()))
|
|
232
|
+
# preserve old behavior
|
|
233
|
+
self.option.set_clean(True)
|
|
234
|
+
|
|
235
|
+
if not subcomponent:
|
|
236
|
+
if self.tool == 'icarus':
|
|
237
|
+
self._configure_icarus()
|
|
238
|
+
elif self.tool == 'verilator':
|
|
239
|
+
self._configure_verilator()
|
|
240
|
+
|
|
241
|
+
else:
|
|
242
|
+
from switchboard.sc.standalone_netlist_flow import StandaloneNetlistFlow
|
|
243
|
+
self.set_flow(StandaloneNetlistFlow())
|
|
244
|
+
|
|
245
|
+
def get_topmodule_name(self):
|
|
246
|
+
top_lvl_module_name = None
|
|
247
|
+
main_filesets = self.option.get_fileset()
|
|
248
|
+
if main_filesets and len(main_filesets) != 0:
|
|
249
|
+
main_fileset = main_filesets[0]
|
|
250
|
+
top_lvl_module_name = self.design.get_topmodule(
|
|
251
|
+
fileset=main_fileset
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
if self.suffix is not None:
|
|
255
|
+
return f'{top_lvl_module_name}{self.suffix}'
|
|
256
|
+
return top_lvl_module_name
|
|
257
|
+
|
|
258
|
+
def _configure_verilator(self):
|
|
259
|
+
from siliconcompiler.flows.dvflow import DVFlow
|
|
260
|
+
|
|
261
|
+
self.set_flow(DVFlow(tool="verilator"))
|
|
262
|
+
from siliconcompiler.tools.verilator.compile import CompileTask
|
|
263
|
+
from siliconcompiler.tools.verilator import VerilatorTask
|
|
264
|
+
|
|
265
|
+
get_task(self, filter=VerilatorTask).add_warningoff("TIMESCALEMOD")
|
|
266
|
+
get_task(self, filter=VerilatorTask).add_warningoff("WIDTHTRUNC")
|
|
267
|
+
|
|
268
|
+
get_task(self, filter=CompileTask).set("var", "cincludes", [SB_DIR / 'cpp'])
|
|
269
|
+
|
|
270
|
+
if self.trace:
|
|
271
|
+
get_task(self, filter=CompileTask).set("var", "trace", True)
|
|
272
|
+
get_task(self, filter=CompileTask).set("var", "trace_type", self.trace_type)
|
|
273
|
+
|
|
274
|
+
# Set up flow that compiles RTL
|
|
275
|
+
self.set('option', 'to', 'compile')
|
|
276
|
+
|
|
277
|
+
def _configure_icarus(self):
|
|
278
|
+
# use dvflow to execute Icarus, but set steplist so we don't run sim
|
|
279
|
+
from siliconcompiler.flows.dvflow import DVFlow
|
|
280
|
+
|
|
281
|
+
self.set_flow(DVFlow(tool="icarus"))
|
|
282
|
+
from siliconcompiler.tools.icarus.compile import CompileTask
|
|
283
|
+
get_task(self, filter=CompileTask).set("var", "verilog_generation", "2012")
|
|
284
|
+
|
|
285
|
+
self.set('option', 'to', 'compile')
|
|
286
|
+
|
|
287
|
+
def find_sim(self):
|
|
288
|
+
if self.tool == 'icarus':
|
|
289
|
+
result_kind = 'vvp'
|
|
290
|
+
else:
|
|
291
|
+
result_kind = 'vexe'
|
|
292
|
+
return self.find_result(result_kind, step='compile')
|
|
293
|
+
|
|
294
|
+
def build(self, cwd: str = None, fast: bool = None):
|
|
295
|
+
"""
|
|
296
|
+
Parameters
|
|
297
|
+
---------
|
|
298
|
+
cwd: str, optional
|
|
299
|
+
Working directory for the simulation build
|
|
300
|
+
|
|
301
|
+
fast: bool, optional
|
|
302
|
+
If True, the simulation binary will not be rebuilt if an existing one
|
|
303
|
+
is found. Defaults to the value provided to the SbDut constructor,
|
|
304
|
+
which in turn defaults to False.
|
|
305
|
+
"""
|
|
306
|
+
|
|
307
|
+
if fast is None:
|
|
308
|
+
fast = self.fast
|
|
309
|
+
|
|
310
|
+
if self.tool == 'icarus':
|
|
311
|
+
if (not fast) or (icarus_find_vpi(cwd, name='switchboard') is None):
|
|
312
|
+
icarus_build_vpi(cwd, name='switchboard')
|
|
313
|
+
|
|
314
|
+
# if "fast" is set, then we can return early if the
|
|
315
|
+
# simulation binary already exists
|
|
316
|
+
if fast:
|
|
317
|
+
self.add_fileset(self.fileset)
|
|
318
|
+
sim = self.find_sim()
|
|
319
|
+
if sim is not None:
|
|
320
|
+
return sim
|
|
321
|
+
|
|
322
|
+
# build the wrapper if needed
|
|
323
|
+
if self.autowrap:
|
|
324
|
+
filename = Path(self.option.get_builddir()).resolve() / 'testbench.sv'
|
|
325
|
+
|
|
326
|
+
filename.parent.mkdir(exist_ok=True, parents=True)
|
|
327
|
+
|
|
328
|
+
wrapped_design = AutowrapDesign(
|
|
329
|
+
design=self.design,
|
|
330
|
+
fileset=self.fileset,
|
|
331
|
+
parameters=self.parameters,
|
|
332
|
+
intf_defs=self.intf_defs,
|
|
333
|
+
clocks=self.clocks,
|
|
334
|
+
resets=self.resets,
|
|
335
|
+
tieoffs=self.tieoffs,
|
|
336
|
+
filename=filename
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
self.set_design(wrapped_design)
|
|
340
|
+
self.add_fileset(self.fileset)
|
|
341
|
+
else:
|
|
342
|
+
self.add_fileset(self.fileset)
|
|
343
|
+
|
|
344
|
+
assert self.run()
|
|
345
|
+
|
|
346
|
+
return self.find_sim()
|
|
347
|
+
|
|
348
|
+
def simulate(
|
|
349
|
+
self,
|
|
350
|
+
plusargs=None,
|
|
351
|
+
args=None,
|
|
352
|
+
extra_args=None,
|
|
353
|
+
cwd: str = None,
|
|
354
|
+
trace: bool = None,
|
|
355
|
+
period: float = None,
|
|
356
|
+
frequency: float = None,
|
|
357
|
+
max_rate: float = None,
|
|
358
|
+
start_delay: float = None,
|
|
359
|
+
run: str = None,
|
|
360
|
+
intf_objs: bool = True
|
|
361
|
+
) -> subprocess.Popen:
|
|
362
|
+
"""
|
|
363
|
+
Parameters
|
|
364
|
+
----------
|
|
365
|
+
plusargs: str or list or tuple, optional
|
|
366
|
+
additional arguments to pass to simulator that must be preceded
|
|
367
|
+
with a +. These are listed after `args`.
|
|
368
|
+
|
|
369
|
+
args: str or list or tuple, optional
|
|
370
|
+
additional arguments to pass to simulator listed before `plusargs` and
|
|
371
|
+
`extra_args`
|
|
372
|
+
|
|
373
|
+
extra_args: str or list or tuple, optional
|
|
374
|
+
additional arguments to pass to simulator listed after `args` and
|
|
375
|
+
`plusargs`
|
|
376
|
+
|
|
377
|
+
cwd: str, optional
|
|
378
|
+
working directory where simulation binary is saved
|
|
379
|
+
|
|
380
|
+
trace: bool, optional
|
|
381
|
+
If true, a waveform dump file will be produced
|
|
382
|
+
|
|
383
|
+
period: float, optional
|
|
384
|
+
If provided, the period of the clock generated in the testbench,
|
|
385
|
+
in seconds.
|
|
386
|
+
"""
|
|
387
|
+
|
|
388
|
+
# set up interfaces if needed
|
|
389
|
+
|
|
390
|
+
if max_rate is None:
|
|
391
|
+
max_rate = self.max_rate
|
|
392
|
+
|
|
393
|
+
if intf_objs:
|
|
394
|
+
self.intfs = create_intf_objs(self.intf_defs, max_rate=max_rate)
|
|
395
|
+
|
|
396
|
+
# set defaults
|
|
397
|
+
|
|
398
|
+
if plusargs is None:
|
|
399
|
+
plusargs = []
|
|
400
|
+
else:
|
|
401
|
+
plusargs = deepcopy(plusargs)
|
|
402
|
+
|
|
403
|
+
if args is None:
|
|
404
|
+
args = []
|
|
405
|
+
|
|
406
|
+
if extra_args is None:
|
|
407
|
+
extra_args = []
|
|
408
|
+
|
|
409
|
+
if trace is None:
|
|
410
|
+
trace = self.trace
|
|
411
|
+
|
|
412
|
+
if (period is None) and (frequency is not None):
|
|
413
|
+
period = 1 / frequency
|
|
414
|
+
|
|
415
|
+
if period is None:
|
|
416
|
+
period = self.period
|
|
417
|
+
|
|
418
|
+
if start_delay is None:
|
|
419
|
+
start_delay = self.start_delay
|
|
420
|
+
|
|
421
|
+
# build the simulation if necessary
|
|
422
|
+
|
|
423
|
+
sim = self.build(cwd=cwd, fast=True)
|
|
424
|
+
|
|
425
|
+
# enable tracing if desired. it's convenient to define +trace
|
|
426
|
+
# when running Icarus Verilog, even though it is not necessary,
|
|
427
|
+
# since logic in the testbench can use that flag to enable/disable
|
|
428
|
+
# waveform dumping in a simulator-agnostic manner.
|
|
429
|
+
|
|
430
|
+
if trace:
|
|
431
|
+
carefully_add_plusarg(key='trace', args=args, plusargs=plusargs)
|
|
432
|
+
|
|
433
|
+
if period is not None:
|
|
434
|
+
carefully_add_plusarg(key='period', value=period, args=args, plusargs=plusargs)
|
|
435
|
+
|
|
436
|
+
if max_rate is not None:
|
|
437
|
+
carefully_add_plusarg(key='max-rate', value=max_rate, args=args, plusargs=plusargs)
|
|
438
|
+
|
|
439
|
+
if start_delay is not None:
|
|
440
|
+
carefully_add_plusarg(
|
|
441
|
+
key='start-delay', value=start_delay, args=args, plusargs=plusargs)
|
|
442
|
+
|
|
443
|
+
# add plusargs that define queue connections
|
|
444
|
+
|
|
445
|
+
for name, value in self.intf_defs.items():
|
|
446
|
+
wire = value.get('wire', None)
|
|
447
|
+
uri = value.get('uri', None)
|
|
448
|
+
|
|
449
|
+
if (wire is not None) and (uri is not None):
|
|
450
|
+
plusargs += [(wire, uri)]
|
|
451
|
+
|
|
452
|
+
# run-specific configurations (if running the same simulator build multiple times
|
|
453
|
+
# in parallel)
|
|
454
|
+
|
|
455
|
+
if run is not None:
|
|
456
|
+
dumpfile = f'{run}.{self.trace_type}'
|
|
457
|
+
plusargs.append(('dumpfile', dumpfile))
|
|
458
|
+
|
|
459
|
+
# run the simulation
|
|
460
|
+
|
|
461
|
+
p = None
|
|
462
|
+
|
|
463
|
+
if self.tool == 'icarus':
|
|
464
|
+
names = ['switchboard']
|
|
465
|
+
modules = []
|
|
466
|
+
|
|
467
|
+
if self.xyce:
|
|
468
|
+
names.append('xyce')
|
|
469
|
+
|
|
470
|
+
for name in names:
|
|
471
|
+
vpi = icarus_find_vpi(cwd=cwd, name=name)
|
|
472
|
+
assert vpi is not None, f'Could not find VPI binary "{name}"'
|
|
473
|
+
modules.append(vpi)
|
|
474
|
+
|
|
475
|
+
# set the trace format
|
|
476
|
+
if self.trace_type == 'fst' and ('-fst' not in extra_args):
|
|
477
|
+
extra_args.append('-fst')
|
|
478
|
+
|
|
479
|
+
p = icarus_run(
|
|
480
|
+
sim,
|
|
481
|
+
plusargs=plusargs,
|
|
482
|
+
modules=modules,
|
|
483
|
+
extra_args=args + extra_args
|
|
484
|
+
)
|
|
485
|
+
else:
|
|
486
|
+
# make sure that the simulator was built with tracing enabled
|
|
487
|
+
if trace and not self.trace:
|
|
488
|
+
raise ValueError('Simulator was built without tracing enabled.'
|
|
489
|
+
' Please set trace=True in the SbDut and try again.')
|
|
490
|
+
|
|
491
|
+
if self.tool == 'verilator':
|
|
492
|
+
p = verilator_run(
|
|
493
|
+
sim,
|
|
494
|
+
plusargs=plusargs,
|
|
495
|
+
args=args
|
|
496
|
+
)
|
|
497
|
+
else:
|
|
498
|
+
p = binary_run(
|
|
499
|
+
sim,
|
|
500
|
+
args=plusargs_to_args(plusargs) + args
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
# Add newly created Popen object to subprocess list
|
|
504
|
+
self.process_collection.add(p)
|
|
505
|
+
|
|
506
|
+
# return a Popen object that one can wait() on
|
|
507
|
+
|
|
508
|
+
return p
|
|
509
|
+
|
|
510
|
+
def remove_queues_on_exit(self):
|
|
511
|
+
import atexit
|
|
512
|
+
from _switchboard import delete_queues
|
|
513
|
+
|
|
514
|
+
def cleanup_func(uris=self.get_uris()):
|
|
515
|
+
if len(uris) > 0:
|
|
516
|
+
delete_queues(uris)
|
|
517
|
+
|
|
518
|
+
atexit.register(cleanup_func)
|
|
519
|
+
|
|
520
|
+
def get_uris(self):
|
|
521
|
+
uris = []
|
|
522
|
+
for _, intf in self.intf_defs.items():
|
|
523
|
+
uri = intf.get('uri', None)
|
|
524
|
+
type = intf.get('type', None)
|
|
525
|
+
if uri is not None:
|
|
526
|
+
if type_is_axi(type) or type_is_axil(type):
|
|
527
|
+
uris.extend(axi_uris(uri))
|
|
528
|
+
elif type_is_apb(type):
|
|
529
|
+
uris.extend(apb_uris(uri))
|
|
530
|
+
else:
|
|
531
|
+
uris.append(uri)
|
|
532
|
+
return uris
|
|
533
|
+
|
|
534
|
+
def terminate(
|
|
535
|
+
self,
|
|
536
|
+
stop_timeout=10,
|
|
537
|
+
use_sigint=False
|
|
538
|
+
):
|
|
539
|
+
self.process_collection.terminate(stop_timeout=stop_timeout, use_sigint=use_sigint)
|
|
540
|
+
|
|
541
|
+
def input_analog(
|
|
542
|
+
self,
|
|
543
|
+
filename: str,
|
|
544
|
+
pins: List[Dict[str, Any]] = None,
|
|
545
|
+
name: str = None,
|
|
546
|
+
check_name: bool = True,
|
|
547
|
+
dir: str = None
|
|
548
|
+
):
|
|
549
|
+
"""
|
|
550
|
+
Specifies a SPICE subcircuit to be used in a mixed-signal simulation. This involves
|
|
551
|
+
providing the path to the SPICE file containing the subcircuit definition and describing
|
|
552
|
+
how real-valued outputs in the SPICE subcircuit should be converted to binary values in
|
|
553
|
+
the Verilog simulation (and vice versa for subcircuit inputs).
|
|
554
|
+
|
|
555
|
+
Each of these conversions is specified as an entry in the "pins" argument, which is a
|
|
556
|
+
list of dictionaries, each representing a single pin of the SPICE subcircuit. Each
|
|
557
|
+
dictionary may have the following keys:
|
|
558
|
+
* "name": name of the pin. Bus notation may be used, e.g. "myBus[7:0]". In that case,
|
|
559
|
+
it is expected that the SPICE subcircuit has pins corresponding to each bit in the bus,
|
|
560
|
+
e.g. "myBus[0]", "myBus[1]", etc.
|
|
561
|
+
* "type": direction of the pin. May be "input", "output", or "constant". If "constant",
|
|
562
|
+
then this pin will not show up the Verilog module definition to be instantiated in user
|
|
563
|
+
code. Instead, the SPICE subcircuit pin with that name will be tied off to a fixed
|
|
564
|
+
voltage specified in the "value" field (below).
|
|
565
|
+
* "vil": low voltage threshold, below which a real-number voltage from the SPICE
|
|
566
|
+
simulation is considered to be a logical "0".
|
|
567
|
+
* "vih": high voltage threshold, above which a real-number voltage from the SPICE
|
|
568
|
+
simulation is considered to be a logical "1".
|
|
569
|
+
* "vol": real-number voltage to pass to a SPICE subcircuit input when the digital value
|
|
570
|
+
driven is "0".
|
|
571
|
+
* "voh": real-number voltage to pass to a SPICE subcircuit input when the digital value
|
|
572
|
+
driven is "1".
|
|
573
|
+
* "tr": time taken in the SPICE simulation to transition from a logic "0" value to a
|
|
574
|
+
logic "1" value.
|
|
575
|
+
* "tf": time taken in the SPICE simulation to transition from a logic "1" value to a
|
|
576
|
+
logic "0" value.
|
|
577
|
+
* "initial": initial value of a SPICE subcircuit pin. Currently only implemented for
|
|
578
|
+
subcircuit outputs. This is sometimes helpful, because there is a slight delay between
|
|
579
|
+
t=0 and the time when the SPICE simulation reports values for its outputs. Specifying
|
|
580
|
+
"initial" for subcircuit outputs prevents the corresponding digital signals from being
|
|
581
|
+
driven to "X" at t=0.
|
|
582
|
+
|
|
583
|
+
Parameters
|
|
584
|
+
----------
|
|
585
|
+
filename: str
|
|
586
|
+
The path of the SPICE file containing the subcircuit definition.
|
|
587
|
+
pins: List[Dict[str, Any]]
|
|
588
|
+
List of dictionaries, each describing a pin of the subcircuit.
|
|
589
|
+
name: str
|
|
590
|
+
Name of the SPICE subcircuit that will be instantiated in the mixed-signal simulation.
|
|
591
|
+
If not provided, Switchboard guesses that the name is filename stem. For example,
|
|
592
|
+
if filename="myCircuit.cir", then Switchboard will guess that the subcircuit name
|
|
593
|
+
is "myCircuit"
|
|
594
|
+
check_name: bool
|
|
595
|
+
If True (default), Switchboard parses the provided file to make sure that there
|
|
596
|
+
is a subcircuit definition matching the given name.
|
|
597
|
+
dir: str
|
|
598
|
+
Running a mixed-signal simulation involves creating SPICE and Verilog wrappers. This
|
|
599
|
+
argument specifies the directory where those wrappers should be written. If not
|
|
600
|
+
provided, defaults to the directory where filename is located.
|
|
601
|
+
"""
|
|
602
|
+
|
|
603
|
+
# automatically configures for Xyce co-simulation if not already configured
|
|
604
|
+
|
|
605
|
+
self._configure_xyce()
|
|
606
|
+
|
|
607
|
+
# set defaults
|
|
608
|
+
|
|
609
|
+
if pins is None:
|
|
610
|
+
pins = []
|
|
611
|
+
|
|
612
|
+
if name is None:
|
|
613
|
+
# guess the name of the subcircuit from the filename
|
|
614
|
+
guessed = True
|
|
615
|
+
name = Path(filename).stem
|
|
616
|
+
else:
|
|
617
|
+
guessed = False
|
|
618
|
+
|
|
619
|
+
if check_name:
|
|
620
|
+
# make sure that a subcircuit matching the provided or guessed
|
|
621
|
+
# name exists in the file provided. this is not foolproof, since
|
|
622
|
+
# the SPICE parser is minimal and won't consider things like
|
|
623
|
+
# .INCLUDE. hence, this feature can be disabled by setting
|
|
624
|
+
# check_name=False
|
|
625
|
+
|
|
626
|
+
subckts = parse_spice_subckts(filename)
|
|
627
|
+
|
|
628
|
+
for subckt in subckts:
|
|
629
|
+
if name.lower() == name.lower():
|
|
630
|
+
break
|
|
631
|
+
else:
|
|
632
|
+
if guessed:
|
|
633
|
+
raise Exception(f'Inferred subckt named "{name}" from the filename,'
|
|
634
|
+
' however a corresponding subckt definition was not found. Please'
|
|
635
|
+
' specify a subckt name via the "name" argument.')
|
|
636
|
+
else:
|
|
637
|
+
raise Exception(f'Could not find a subckt definition for "{name}".')
|
|
638
|
+
|
|
639
|
+
if dir is None:
|
|
640
|
+
dir = Path(filename).resolve().parent
|
|
641
|
+
|
|
642
|
+
spice_wrapper = make_ams_spice_wrapper(
|
|
643
|
+
name=name,
|
|
644
|
+
filename=filename,
|
|
645
|
+
pins=pins,
|
|
646
|
+
dir=dir
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
verilog_wrapper = make_ams_verilog_wrapper(
|
|
650
|
+
name=name,
|
|
651
|
+
filename=spice_wrapper,
|
|
652
|
+
pins=pins,
|
|
653
|
+
dir=dir
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
self.input(verilog_wrapper)
|
|
657
|
+
|
|
658
|
+
def package(self, suffix: str = None, fast: bool = None) -> str:
|
|
659
|
+
# set defaults
|
|
660
|
+
|
|
661
|
+
if suffix is None:
|
|
662
|
+
suffix = self.suffix
|
|
663
|
+
|
|
664
|
+
if fast is None:
|
|
665
|
+
fast = self.fast
|
|
666
|
+
|
|
667
|
+
# see if we can exit early
|
|
668
|
+
|
|
669
|
+
if fast:
|
|
670
|
+
self.add_fileset(self.fileset)
|
|
671
|
+
package = self.find_package(suffix=suffix)
|
|
672
|
+
|
|
673
|
+
if package is not None:
|
|
674
|
+
return package
|
|
675
|
+
|
|
676
|
+
from switchboard.sc.morty.uniquify import UniquifyVerilogModules
|
|
677
|
+
from switchboard.sc.sed.sed_remove import SedRemove
|
|
678
|
+
|
|
679
|
+
# if not, parse with surelog and postprocess with morty
|
|
680
|
+
|
|
681
|
+
if suffix:
|
|
682
|
+
get_task(self, filter=UniquifyVerilogModules).set("var", "suffix", suffix)
|
|
683
|
+
|
|
684
|
+
get_task(self, filter=SedRemove).set("var", "to_remove", "`resetall")
|
|
685
|
+
|
|
686
|
+
self.add_fileset(self.fileset)
|
|
687
|
+
|
|
688
|
+
self.run()
|
|
689
|
+
|
|
690
|
+
# return the path to the output
|
|
691
|
+
return self.find_package(suffix=suffix)
|
|
692
|
+
|
|
693
|
+
def find_package(self, suffix=None) -> str:
|
|
694
|
+
if suffix is None:
|
|
695
|
+
return self.find_result('sv', step='parse')
|
|
696
|
+
else:
|
|
697
|
+
return self.find_result('sv', step='uniquify')
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def metadata_str(design: str, tool: str = None, trace: bool = False,
|
|
701
|
+
trace_type: str = None, threads: int = None, parameters: dict = None) -> Path:
|
|
702
|
+
|
|
703
|
+
opts = []
|
|
704
|
+
|
|
705
|
+
opts += [design]
|
|
706
|
+
|
|
707
|
+
if parameters is not None:
|
|
708
|
+
for k, v in parameters.items():
|
|
709
|
+
opts += [k, v]
|
|
710
|
+
|
|
711
|
+
if tool is not None:
|
|
712
|
+
opts += [tool]
|
|
713
|
+
|
|
714
|
+
if trace:
|
|
715
|
+
assert trace_type is not None
|
|
716
|
+
opts += [trace_type]
|
|
717
|
+
|
|
718
|
+
if threads is not None:
|
|
719
|
+
opts += ['threads', threads]
|
|
720
|
+
|
|
721
|
+
return '-'.join(str(opt) for opt in opts)
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
def carefully_add_plusarg(key, args, plusargs, value=None):
|
|
725
|
+
for plusarg in plusargs:
|
|
726
|
+
if isinstance(plusarg, (list, tuple)):
|
|
727
|
+
if (len(plusarg) >= 1) and (key == plusarg[0]):
|
|
728
|
+
return
|
|
729
|
+
elif key == plusarg:
|
|
730
|
+
return
|
|
731
|
+
|
|
732
|
+
if f'+{key}' in args:
|
|
733
|
+
return
|
|
734
|
+
|
|
735
|
+
if any(elem.startswith(f'+{key}+') for elem in args):
|
|
736
|
+
return
|
|
737
|
+
|
|
738
|
+
if any(elem.startswith(f'+{key}=') for elem in args):
|
|
739
|
+
return
|
|
740
|
+
|
|
741
|
+
if value is None:
|
|
742
|
+
plusargs.append(key)
|
|
743
|
+
else:
|
|
744
|
+
plusargs.append((key, value))
|