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/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))