switchboard-hw 0.3.0__cp314-cp314-macosx_10_15_x86_64.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/network.py ADDED
@@ -0,0 +1,714 @@
1
+ # Copyright (c) 2024 Zero ASIC Corporation
2
+ # This code is licensed under Apache License 2.0 (see LICENSE for details)
3
+
4
+ from typing import Set
5
+
6
+ from pathlib import Path
7
+ from copy import deepcopy
8
+ from itertools import count
9
+ from numbers import Integral
10
+
11
+ from .sbdut import SbDut
12
+ from .axi import axi_uris
13
+ from .apb import apb_uris
14
+ from .autowrap import (directions_are_compatible, normalize_intf_type,
15
+ type_is_umi, type_is_sb, create_intf_objs, type_is_axi, type_is_axil, type_is_apb,
16
+ autowrap, flip_intf, normalize_direction, WireExpr, types_are_compatible)
17
+ from .cmdline import get_cmdline_args
18
+ from .sbtcp import start_tcp_bridge
19
+ from .util import ProcessCollection
20
+
21
+ from _switchboard import delete_queues
22
+
23
+ from siliconcompiler import Design
24
+
25
+
26
+ class SbIntf:
27
+ def __init__(self, inst, name, width=None, indices=None):
28
+ self.inst = inst
29
+ self.name = name
30
+ self.width = width
31
+
32
+ if (indices is None) and (width is not None):
33
+ indices = slice(width - 1, 0, 1)
34
+
35
+ self.slice = indices
36
+
37
+ @property
38
+ def intf_def(self):
39
+ return self.inst.block.intf_defs[self.name]
40
+
41
+ @property
42
+ def wire_name(self):
43
+ return f'{self.inst.name}_{self.name}'
44
+
45
+ def __getitem__(self, key):
46
+ if not isinstance(key, slice):
47
+ key = slice(key, key)
48
+
49
+ return SbIntf(inst=self.inst, name=self.name, indices=key)
50
+
51
+ def slice_as_str(self):
52
+ if self.slice is None:
53
+ return ''
54
+ else:
55
+ return f'[{self.slice.start}:{self.slice.stop}]'
56
+
57
+ def compute_slice_width(self):
58
+ if self.slice.start is not None:
59
+ start = self.slice.start
60
+ else:
61
+ start = self.width - 1
62
+
63
+ if self.slice.stop is not None:
64
+ stop = self.slice.stop
65
+ else:
66
+ stop = 0
67
+
68
+ return start - stop + 1
69
+
70
+
71
+ class ConstIntf:
72
+ def __init__(self, value):
73
+ self.value = value
74
+
75
+ @property
76
+ def intf_def(self):
77
+ return dict(
78
+ type='const',
79
+ direction='output'
80
+ )
81
+
82
+ def value_as_str(self, width=None, format='decimal'):
83
+ if width is None:
84
+ width = ''
85
+ else:
86
+ width = str(width)
87
+
88
+ if format.lower() == 'decimal':
89
+ return f"{width}'d{self.value}"
90
+ elif format.lower() == 'hex':
91
+ return f"{width}'h{hex(self.value)[2:]}"
92
+ elif format.lower() == 'hex':
93
+ return f"{width}'b{bin(self.value)[2:]}"
94
+ else:
95
+ raise Exception(f'Unsupported format: {format}')
96
+
97
+
98
+ class TcpIntf:
99
+ def __init__(self, intf_def=None, destination=None, **kwargs):
100
+ self.intf_def = intf_def
101
+ self.destination = destination
102
+ self.kwargs = kwargs
103
+
104
+ @property
105
+ def wire_name(self):
106
+ if 'port' in self.kwargs:
107
+ retval = f'port_{self.kwargs["port"]}'
108
+ if self.destination is not None:
109
+ retval += f'_{self.destination}'
110
+ return retval
111
+
112
+
113
+ class SbInst:
114
+ def __init__(self, name, block):
115
+ self.name = name
116
+ self.block = block
117
+ self.mapping = {}
118
+ self.external = set()
119
+
120
+ for name, value in block.intf_defs.items():
121
+ if value['type'] != 'plusarg':
122
+ self.mapping[name] = dict(uri=None, wire=None)
123
+ width = block.intf_defs[name].get('width', None)
124
+ self.__setattr__(name, SbIntf(inst=self, name=name, width=width))
125
+
126
+
127
+ class SingleNetlistNetwork(Design):
128
+ def __init__(
129
+ self,
130
+ unique_blocks: Set[SbInst],
131
+ wrapper_filename: str,
132
+ fileset: str,
133
+ insts
134
+ ):
135
+ super().__init__("SNDesign")
136
+ unique_blocks = unique_blocks
137
+
138
+ # Pickle each block in the network
139
+ pickled_sources = [block.package() for block in unique_blocks]
140
+
141
+ # populate the interfaces dictionary
142
+ interfaces = {}
143
+
144
+ for inst_name, inst in insts.items():
145
+ # make a copy of the interface definitions for this block
146
+ intf_defs = deepcopy(inst.block.intf_defs)
147
+
148
+ # wiring
149
+ for intf_name, props in inst.mapping.items():
150
+ intf_defs[intf_name]['wire'] = props['wire']
151
+ intf_defs[intf_name]['external'] = intf_name in inst.external
152
+
153
+ # prepend instance name to init interfaces
154
+ for value in intf_defs.values():
155
+ if value['type'] == 'plusarg':
156
+ value['wire'] = f"{inst_name}_{value['wire']}"
157
+ value['plusarg'] = f"{inst_name}_{value['plusarg']}"
158
+
159
+ interfaces[inst_name] = intf_defs
160
+
161
+ # generate netlist that connects everything together, and input() it
162
+ top_lvl = autowrap(
163
+ instances={inst.name: inst.block.get_topmodule_name() for inst in insts.values()},
164
+ toplevel='testbench',
165
+ parameters={inst.name: inst.block.parameters for inst in insts.values()},
166
+ interfaces=interfaces,
167
+ clocks={inst.name: inst.block.clocks for inst in insts.values()},
168
+ resets={inst.name: inst.block.resets for inst in insts.values()},
169
+ tieoffs={inst.name: inst.block.tieoffs for inst in insts.values()},
170
+ filename=wrapper_filename
171
+ )
172
+
173
+ sources = pickled_sources + [str(top_lvl)]
174
+
175
+ from switchboard.verilog.sim.switchboard_sim import SwitchboardSim
176
+
177
+ with self.active_fileset(fileset):
178
+ self.set_topmodule("testbench")
179
+ self.add_depfileset(SwitchboardSim())
180
+ for file in sources:
181
+ self.add_file(file)
182
+
183
+
184
+ class SbNetwork:
185
+ def __init__(
186
+ self,
187
+ cmdline=False,
188
+ tool: str = 'verilator',
189
+ trace: bool = False,
190
+ trace_type: str = 'vcd',
191
+ frequency: float = 100e6,
192
+ period: float = None,
193
+ max_rate: float = -1,
194
+ start_delay: float = None,
195
+ fast: bool = False,
196
+ extra_args: dict = None,
197
+ cleanup: bool = True,
198
+ args=None,
199
+ single_netlist: bool = False,
200
+ threads: int = None,
201
+ name: str = None
202
+ ):
203
+
204
+ self.insts = {}
205
+
206
+ self.inst_name_set = set()
207
+ self.inst_name_counters = {}
208
+
209
+ self.uri_set = set()
210
+ self.uri_counters = {}
211
+
212
+ self.tcp_intfs = {}
213
+
214
+ if cmdline:
215
+ self.args = get_cmdline_args(tool=tool, trace=trace, trace_type=trace_type,
216
+ frequency=frequency, period=period, fast=fast, max_rate=max_rate,
217
+ start_delay=start_delay, single_netlist=single_netlist, threads=threads,
218
+ extra_args=extra_args)
219
+ elif args is not None:
220
+ self.args = args
221
+
222
+ if hasattr(self, 'args'):
223
+ trace = self.args.trace
224
+ trace_type = self.args.trace_type
225
+ fast = self.args.fast
226
+ tool = self.args.tool
227
+ frequency = self.args.frequency
228
+ period = self.args.period
229
+ max_rate = self.args.max_rate
230
+ start_delay = self.args.start_delay
231
+ single_netlist = self.args.single_netlist
232
+ else:
233
+ # create args object to pass down to SbDut
234
+ from types import SimpleNamespace
235
+ self.args = SimpleNamespace(
236
+ trace=trace,
237
+ trace_type=trace_type,
238
+ fast=fast,
239
+ tool=tool,
240
+ frequency=frequency,
241
+ period=period,
242
+ max_rate=max_rate,
243
+ start_delay=start_delay,
244
+ threads=threads
245
+ )
246
+
247
+ # save settings
248
+
249
+ self.tool = tool
250
+ self.trace = trace
251
+ self.trace_type = trace_type
252
+ self.fast = fast
253
+
254
+ if (period is None) and (frequency is not None):
255
+ period = 1 / frequency
256
+ self.period = period
257
+ self.max_rate = max_rate
258
+ self.start_delay = start_delay
259
+
260
+ self.single_netlist = single_netlist
261
+
262
+ if single_netlist:
263
+ self.single_netlist_dut = SbDut(design="single_netlist_network", args=self.args)
264
+ else:
265
+ self._intf_defs = {}
266
+
267
+ self.name = name
268
+
269
+ # keep track of processes started
270
+ self.process_collection = ProcessCollection()
271
+
272
+ if cleanup:
273
+ import atexit
274
+
275
+ def cleanup_func(uri_set=self.uri_set):
276
+ if len(uri_set) > 0:
277
+ delete_queues(list(uri_set))
278
+
279
+ atexit.register(cleanup_func)
280
+
281
+ @property
282
+ def intf_defs(self):
283
+ if self.single_netlist:
284
+ return self.single_netlist_dut.intf_defs
285
+ else:
286
+ return self._intf_defs
287
+
288
+ def instantiate(self, block, name: str = None):
289
+ # generate a name if needed
290
+ if name is None:
291
+ if isinstance(block, SbDut):
292
+ prefix = block.design.name
293
+ else:
294
+ prefix = block.name
295
+
296
+ assert prefix is not None, ('Cannot generate name for this instance.'
297
+ ' When block is an SbNetwork, make sure that its constructor set'
298
+ ' "name" if you want name generation to work here.')
299
+
300
+ name = self.generate_inst_name(prefix=prefix)
301
+
302
+ # make sure the name hasn't been used already
303
+ assert name not in self.inst_name_set
304
+
305
+ # add the name to the set of names in use
306
+ self.inst_name_set.add(name)
307
+
308
+ # create the instance object
309
+ self.insts[name] = SbInst(name=name, block=block)
310
+
311
+ # return the instance object
312
+ return self.insts[name]
313
+
314
+ def connect(self, a, b, uri=None, wire=None):
315
+ # convert integer inputs into constant datatype
316
+ if isinstance(a, Integral):
317
+ a = ConstIntf(value=a)
318
+ if isinstance(b, Integral):
319
+ b = ConstIntf(value=b)
320
+
321
+ # retrieve the two interface definitions
322
+ intf_def_a = a.intf_def
323
+ intf_def_b = b.intf_def
324
+
325
+ if intf_def_a is None:
326
+ assert intf_def_b is not None, 'Cannot infer interface type'
327
+ intf_def_a = flip_intf(intf_def_b)
328
+
329
+ if intf_def_b is None:
330
+ assert intf_def_a is not None, 'Cannot infer interface type'
331
+ intf_def_b = flip_intf(intf_def_a)
332
+
333
+ # make sure that the interfaces are compatible
334
+ type_a = normalize_intf_type(intf_def_a['type'])
335
+ type_b = normalize_intf_type(intf_def_b['type'])
336
+ assert types_are_compatible(type_a, type_b)
337
+
338
+ # make sure that the directions are compatible
339
+ direction_a = normalize_direction(type_a, intf_def_a['direction'])
340
+ direction_b = normalize_direction(type_b, intf_def_b['direction'])
341
+ assert directions_are_compatible(
342
+ type_a=type_a, a=direction_a,
343
+ type_b=type_b, b=direction_b
344
+ )
345
+
346
+ # indicate which is input vs. output. we have to look at both type a and
347
+ # type b since one may be a constant
348
+ if (type_a == 'gpio') or (type_b == 'gpio'):
349
+ if (direction_a == 'input') or (direction_b == 'output'):
350
+ input, output = a, b
351
+ direction_a, direction_b = 'input', 'output'
352
+ elif (direction_b == 'input') or (direction_a == 'output'):
353
+ input, output = b, a
354
+ direction_b, direction_a = 'input', 'output'
355
+ else:
356
+ raise Exception(f'Cannot infer connection direction with direction_a={direction_a}'
357
+ f' and direction_b={direction_b}')
358
+
359
+ intf_def_a['direction'] = direction_a
360
+ intf_def_b['direction'] = direction_b
361
+
362
+ # determine what the queue will be called that connects the two
363
+
364
+ if wire is None:
365
+ if (type_a != 'gpio') and (type_b != 'gpio'):
366
+ wire = f'{a.wire_name}_conn_{b.wire_name}'
367
+ elif not isinstance(output, ConstIntf):
368
+ wire = f'{output.inst.name}_{output.name}'
369
+
370
+ if (uri is None) and (wire is not None):
371
+ uri = wire
372
+
373
+ if type_is_sb(type_a) or type_is_umi(type_a):
374
+ uri = uri + '.q'
375
+
376
+ if (not self.single_netlist) and (type_a != 'gpio') and (type_b != 'gpio'):
377
+ self.register_uri(type=type_a, uri=uri)
378
+
379
+ # tell both instances what they are connected to
380
+
381
+ if (type_a != 'gpio') and (type_b != 'gpio'):
382
+ if not isinstance(a, TcpIntf):
383
+ a.inst.mapping[a.name]['wire'] = wire
384
+ a.inst.mapping[a.name]['uri'] = uri
385
+
386
+ if not isinstance(b, TcpIntf):
387
+ b.inst.mapping[b.name]['wire'] = wire
388
+ b.inst.mapping[b.name]['uri'] = uri
389
+ else:
390
+ if input.inst.mapping[input.name]['wire'] is None:
391
+ expr = WireExpr(input.intf_def['width'])
392
+ input.inst.mapping[input.name]['wire'] = expr
393
+
394
+ if isinstance(output, ConstIntf):
395
+ input.inst.mapping[input.name]['wire'].bind(
396
+ input.slice, output.value_as_str(width=input.compute_slice_width()))
397
+ else:
398
+ input.inst.mapping[input.name]['wire'].bind(
399
+ input.slice, f'{wire}{output.slice_as_str()}')
400
+ output.inst.mapping[output.name]['wire'] = wire
401
+
402
+ # make a note of TCP bridges that need to be started
403
+
404
+ for intf, intf_def in [(a, intf_def_a), (b, intf_def_b)]:
405
+ if isinstance(intf, TcpIntf):
406
+ self.add_tcp_intf(intf=intf, intf_def=intf_def, uri=uri)
407
+
408
+ def add_tcp_intf(self, intf, intf_def, uri):
409
+ tcp_kwargs = deepcopy(intf.kwargs)
410
+
411
+ if 'host' not in tcp_kwargs:
412
+ tcp_kwargs['host'] = 'localhost'
413
+
414
+ if 'port' not in tcp_kwargs:
415
+ tcp_kwargs['port'] = 5555
416
+
417
+ if 'mode' not in tcp_kwargs:
418
+ tcp_kwargs['mode'] = 'auto'
419
+
420
+ if 'quiet' not in tcp_kwargs:
421
+ tcp_kwargs['quiet'] = True
422
+
423
+ if 'max_rate' not in intf.kwargs:
424
+ tcp_kwargs['max_rate'] = self.max_rate
425
+
426
+ if 'run_once' not in tcp_kwargs:
427
+ tcp_kwargs['run_once'] = False
428
+
429
+ tcp_intfs_key = (tcp_kwargs['host'], tcp_kwargs['port'], tcp_kwargs['mode'])
430
+
431
+ if tcp_intfs_key not in self.tcp_intfs:
432
+ self.tcp_intfs[tcp_intfs_key] = tcp_kwargs
433
+ else:
434
+ for key, val in self.tcp_intfs[tcp_intfs_key].items():
435
+ if key not in ['inputs', 'outputs']:
436
+ if val != tcp_kwargs[key]:
437
+ raise ValueError(f'Mismatch on TCP interface property "{key}".'
438
+ f' New value of property is "{tcp_kwargs[key]}"'
439
+ f' but it was previously set to "{val}".')
440
+
441
+ tcp_intf = self.tcp_intfs[tcp_intfs_key]
442
+
443
+ tcp_direction = intf_def['direction']
444
+
445
+ if tcp_direction == 'input':
446
+ assert 'outputs' not in tcp_intf
447
+
448
+ if 'inputs' not in tcp_intf:
449
+ tcp_intf['inputs'] = []
450
+
451
+ if intf.destination is None:
452
+ input = uri
453
+ else:
454
+ input = (intf.destination, uri)
455
+
456
+ tcp_intf['inputs'].append(input)
457
+ elif tcp_direction == 'output':
458
+ assert 'inputs' not in tcp_intf
459
+
460
+ if 'outputs' not in tcp_intf:
461
+ tcp_intf['outputs'] = []
462
+
463
+ if intf.destination is None:
464
+ destination = '*'
465
+ else:
466
+ destination = intf.destination
467
+
468
+ output = (destination, uri)
469
+
470
+ tcp_intf['outputs'].append(output)
471
+ else:
472
+ raise Exception(f'Unsupported direction: {tcp_direction}')
473
+
474
+ def build(self):
475
+ unique_blocks: Set[SbInst] = set(inst.block for inst in self.insts.values())
476
+
477
+ if self.single_netlist:
478
+
479
+ filename = Path(
480
+ self.single_netlist_dut.option.get_builddir()
481
+ ).resolve() / 'testbench.sv'
482
+ filename.parent.mkdir(exist_ok=True, parents=True)
483
+
484
+ self.single_netlist_dut.set_design(
485
+ SingleNetlistNetwork(
486
+ unique_blocks=unique_blocks,
487
+ fileset=self.tool,
488
+ wrapper_filename=filename,
489
+ insts=self.insts
490
+ )
491
+ )
492
+ self.single_netlist_dut.add_fileset(self.tool)
493
+
494
+ # build the single-netlist simulation
495
+ self.single_netlist_dut.build()
496
+ else:
497
+ for block in unique_blocks:
498
+ block.build()
499
+
500
+ def external(self, intf, name=None, txrx=None, uri=None, wire=None):
501
+ # make a copy of the interface definition since we will be modifying it
502
+
503
+ assert intf.intf_def is not None, 'Cannot infer interface type'
504
+ intf_def = deepcopy(intf.intf_def)
505
+
506
+ # generate URI if needed
507
+
508
+ type = intf_def['type']
509
+
510
+ if wire is None:
511
+ wire = intf.wire_name
512
+
513
+ intf_def['wire'] = wire
514
+
515
+ if uri is None:
516
+ uri = wire
517
+
518
+ if type_is_sb(type) or type_is_umi(type):
519
+ uri = uri + '.q'
520
+
521
+ intf_def['uri'] = uri
522
+
523
+ # register the URI to make sure it doesn't collide with anything else
524
+
525
+ self.register_uri(type=type, uri=uri)
526
+
527
+ # propagate information about the URI mapping
528
+
529
+ if not isinstance(intf, TcpIntf):
530
+ intf.inst.mapping[intf.name] = dict(uri=uri, wire=wire)
531
+ intf.inst.external.add(intf.name)
532
+
533
+ # set txrx
534
+
535
+ intf_def['txrx'] = txrx
536
+
537
+ # set max rate
538
+
539
+ if self.max_rate is not None:
540
+ intf_def['max_rate'] = self.max_rate
541
+
542
+ # save interface
543
+
544
+ if name is None:
545
+ name = intf.wire_name
546
+
547
+ assert name not in self.intf_defs, \
548
+ f'Network already contains an external interface called "{name}".'
549
+
550
+ self.intf_defs[name] = intf_def
551
+
552
+ # make a note of TCP bridges that need to be started
553
+
554
+ if isinstance(intf, TcpIntf):
555
+ self.add_tcp_intf(intf=intf, intf_def=intf_def, uri=uri)
556
+
557
+ return name
558
+
559
+ def simulate(self, start_delay=None, run=None, intf_objs=True, plusargs=None):
560
+
561
+ # set defaults
562
+
563
+ if start_delay is None:
564
+ start_delay = self.start_delay
565
+
566
+ if plusargs is None:
567
+ plusargs = []
568
+
569
+ # create interface objects
570
+
571
+ if self.single_netlist:
572
+ if isinstance(plusargs, dict):
573
+ plusargs_processed = []
574
+ for inst_name, inst_plusargs in plusargs.items():
575
+ if inst_name == '*':
576
+ plusargs_processed += inst_plusargs
577
+ else:
578
+ for inst_plusarg, value in inst_plusargs:
579
+ plusargs_processed.append((f"{inst_name}_{inst_plusarg}", value))
580
+ plusargs = plusargs_processed
581
+
582
+ process = self.single_netlist_dut.simulate(
583
+ start_delay=start_delay,
584
+ run=run,
585
+ intf_objs=intf_objs,
586
+ plusargs=plusargs
587
+ )
588
+
589
+ self.process_collection.add(process)
590
+
591
+ if intf_objs:
592
+ self.intfs = self.single_netlist_dut.intfs
593
+ else:
594
+ if intf_objs:
595
+ self.intfs = create_intf_objs(self.intf_defs)
596
+
597
+ if start_delay is not None:
598
+ import time
599
+ start = time.time()
600
+
601
+ insts = self.insts.values()
602
+
603
+ if len(insts) > 1:
604
+ try:
605
+ from tqdm import tqdm
606
+ insts = tqdm(insts)
607
+ except ModuleNotFoundError:
608
+ pass
609
+
610
+ for inst in insts:
611
+ block = inst.block
612
+
613
+ for intf_name, props in inst.mapping.items():
614
+ block.intf_defs[intf_name]['uri'] = props['uri']
615
+
616
+ # calculate the start delay for this process by measuring the
617
+ # time left until the start delay for the whole network is over
618
+
619
+ if start_delay is not None:
620
+ now = time.time()
621
+ dt = now - start
622
+ if dt < start_delay:
623
+ start_delay = start_delay - dt
624
+ else:
625
+ start_delay = None
626
+ else:
627
+ start_delay = None
628
+
629
+ # launch an instance of simulation
630
+
631
+ if isinstance(plusargs, dict):
632
+ inst_plusargs = []
633
+ inst_plusargs += plusargs.get('*', [])
634
+ inst_plusargs += plusargs.get(inst.name, [])
635
+ else:
636
+ inst_plusargs = plusargs
637
+
638
+ process = block.simulate(start_delay=start_delay, run=inst.name,
639
+ intf_objs=False, plusargs=inst_plusargs)
640
+
641
+ self.process_collection.add(process)
642
+
643
+ # start TCP bridges as needed
644
+ for tcp_kwargs in self.tcp_intfs.values():
645
+ process = start_tcp_bridge(**tcp_kwargs)
646
+ self.process_collection.add(process)
647
+
648
+ return self.process_collection
649
+
650
+ def terminate(
651
+ self,
652
+ stop_timeout=10,
653
+ use_sigint=False
654
+ ):
655
+ self.process_collection.terminate(stop_timeout=stop_timeout, use_sigint=use_sigint)
656
+
657
+ def generate_inst_name(self, prefix):
658
+ if prefix not in self.inst_name_counters:
659
+ self.inst_name_counters[prefix] = count(0)
660
+
661
+ counter = self.inst_name_counters[prefix]
662
+
663
+ while True:
664
+ name = f'{prefix}_{next(counter)}'
665
+
666
+ if name not in self.inst_name_set:
667
+ break
668
+
669
+ return name
670
+
671
+ def register_uri(self, type, uri, fresh=True):
672
+ if type_is_axi(type) or type_is_axil(type):
673
+ uris = axi_uris(uri)
674
+ elif type_is_apb(type):
675
+ uris = apb_uris(uri)
676
+ else:
677
+ uris = [uri]
678
+
679
+ uris = set(uris)
680
+ intersection = self.uri_set.intersection(uris)
681
+
682
+ if len(intersection) > 0:
683
+ raise ValueError('Failed to add the following URI(s) that'
684
+ f' are already in use: {list(intersection)}')
685
+
686
+ self.uri_set.update(uris)
687
+
688
+ if fresh:
689
+ delete_queues(list(uris))
690
+
691
+ def make_dut(self, *args, **kwargs):
692
+ # argument customizations
693
+
694
+ cfg = {}
695
+
696
+ cfg['args'] = self.args
697
+
698
+ if self.single_netlist:
699
+ cfg['autowrap'] = False
700
+ cfg['subcomponent'] = True
701
+ else:
702
+ cfg['autowrap'] = True
703
+ cfg['subcomponent'] = False
704
+
705
+ # add to keyword arguments without clobbering
706
+ # existing entries
707
+
708
+ kwargs = deepcopy(kwargs)
709
+
710
+ for k, v in cfg.items():
711
+ if k not in kwargs:
712
+ kwargs[k] = v
713
+
714
+ return SbDut(*args, **kwargs)