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
@@ -0,0 +1,1000 @@
1
+ # Tool for automatically wrapping a DUT with switchboard interfaces
2
+
3
+ # Copyright (c) 2024 Zero ASIC Corporation
4
+ # This code is licensed under Apache License 2.0 (see LICENSE for details)
5
+
6
+ from pathlib import Path
7
+ from copy import deepcopy
8
+
9
+ from .umi import UmiTxRx
10
+ from .axi import AxiTxRx
11
+ from .axil import AxiLiteTxRx
12
+ from switchboard.apb import ApbTxRx
13
+ from .bitvector import slice_to_msb_lsb
14
+
15
+ from _switchboard import PySbTx, PySbRx
16
+
17
+
18
+ class WireExpr:
19
+ def __init__(self, width):
20
+ self.width = width
21
+ self.bindings = []
22
+
23
+ def bind(self, slice, wire):
24
+ # extract msb, lsb
25
+ msb, lsb = slice_to_msb_lsb(start=slice.start, stop=slice.stop, step=slice.step)
26
+
27
+ # make sure that the slice fits in the width
28
+ assert 0 <= lsb <= self.width - 1
29
+ assert 0 <= msb <= self.width - 1
30
+
31
+ if len(self.bindings) == 0:
32
+ self.bindings.append(((msb, lsb), wire))
33
+ return
34
+
35
+ for idx in range(len(self.bindings) - 1, -1, -1):
36
+ (msb_i, lsb_i), _ = self.bindings[idx]
37
+ if lsb < lsb_i:
38
+ assert msb < lsb_i, \
39
+ f'bit assignments {msb_i}:{lsb_i} and {msb}:{lsb} overlap'
40
+ self.bindings.insert(idx + 1, ((msb, lsb), wire))
41
+ break
42
+ else:
43
+ (msb_i, lsb_i), _ = self.bindings[0]
44
+ assert lsb > msb_i, \
45
+ f'bit assignments {msb_i}:{lsb_i} and {msb}:{lsb} overlap'
46
+ self.bindings.insert(0, ((msb, lsb), wire))
47
+
48
+ def padded(self):
49
+ retval = []
50
+
51
+ for idx, ((msb, lsb), wire) in enumerate(self.bindings):
52
+ if idx == 0:
53
+ if msb != self.width - 1:
54
+ msb_pad = (self.width - 1) - msb
55
+ retval.append(f"{msb_pad}'b0")
56
+
57
+ retval.append(wire)
58
+
59
+ if idx < len(self.bindings) - 1:
60
+ lsb_pad = (lsb - 1) - self.bindings[idx + 1][0][0]
61
+ else:
62
+ lsb_pad = lsb
63
+
64
+ if lsb_pad > 0:
65
+ retval.append(f"{lsb_pad}'b0")
66
+
67
+ return retval
68
+
69
+ def __str__(self):
70
+ padded = self.padded()
71
+
72
+ if len(padded) == 1:
73
+ return padded[0]
74
+ else:
75
+ return '{' + ', '.join(padded) + '}'
76
+
77
+
78
+ def normalize_interface(name, value):
79
+ # copy before modifying
80
+ value = deepcopy(value)
81
+
82
+ assert isinstance(value, dict)
83
+
84
+ if 'type' not in value:
85
+ value['type'] = 'sb'
86
+
87
+ if 'wire' not in value:
88
+ value['wire'] = name
89
+
90
+ assert 'type' in value
91
+ value['type'] = normalize_intf_type(value['type'])
92
+ type = value['type']
93
+
94
+ if 'external' not in value:
95
+ if type == 'plusarg':
96
+ value['external'] = False
97
+ else:
98
+ value['external'] = True
99
+
100
+ if (type == 'plusarg') and ('direction' not in value):
101
+ value['direction'] = 'input'
102
+
103
+ assert 'direction' in value
104
+ value['direction'] = normalize_direction(type=type, direction=value['direction'])
105
+
106
+ if type == 'sb':
107
+ if 'dw' not in value:
108
+ value['dw'] = 256
109
+ if 'uri' not in value:
110
+ value['uri'] = f'{name}.q'
111
+ elif type == 'umi':
112
+ if 'dw' not in value:
113
+ value['dw'] = 256
114
+ if 'aw' not in value:
115
+ value['aw'] = 64
116
+ if 'cw' not in value:
117
+ value['cw'] = 32
118
+ if 'txrx' not in value:
119
+ value['txrx'] = None
120
+ if 'uri' not in value:
121
+ value['uri'] = f'{name}.q'
122
+ elif type in ['axi', 'axil', 'apb']:
123
+ if 'dw' not in value:
124
+ value['dw'] = 32
125
+ if 'aw' not in value:
126
+ value['aw'] = 16
127
+ if 'uri' not in value:
128
+ value['uri'] = name
129
+
130
+ if type == 'axi':
131
+ # settings that only apply to AXI, not AXI-Lite
132
+
133
+ if 'idw' not in value:
134
+ value['idw'] = 8
135
+ elif type == 'gpio':
136
+ if 'width' not in value:
137
+ value['width'] = 1
138
+ elif type == 'plusarg':
139
+ if 'width' not in value:
140
+ value['width'] = 1
141
+ if 'default' not in value:
142
+ value['default'] = 0
143
+ if 'plusarg' not in value:
144
+ value['plusarg'] = name
145
+ else:
146
+ raise ValueError(f'Unsupported interface type: "{type}"')
147
+
148
+ return name, value
149
+
150
+
151
+ def normalize_interfaces(interfaces):
152
+ if interfaces is None:
153
+ interfaces = {}
154
+
155
+ retval = {}
156
+
157
+ for name, value in interfaces.items():
158
+ name, value = normalize_interface(name, value)
159
+ retval[name] = value
160
+
161
+ return retval
162
+
163
+
164
+ def normalize_clock(clock):
165
+ # copy before modifying
166
+ clock = deepcopy(clock)
167
+
168
+ if isinstance(clock, str):
169
+ clock = dict(name=clock)
170
+
171
+ assert isinstance(clock, dict)
172
+
173
+ return clock
174
+
175
+
176
+ def normalize_clocks(clocks):
177
+ if clocks is None:
178
+ clocks = ['clk']
179
+
180
+ if isinstance(clocks, str):
181
+ clocks = [clocks]
182
+
183
+ retval = []
184
+
185
+ for clock in clocks:
186
+ clock = normalize_clock(clock)
187
+ retval.append(clock)
188
+
189
+ return retval
190
+
191
+
192
+ def normalize_reset(reset):
193
+ # copy before modifying
194
+ reset = deepcopy(reset)
195
+
196
+ if isinstance(reset, str):
197
+ reset = {'name': reset}
198
+
199
+ assert 'name' in reset
200
+
201
+ name = reset['name']
202
+
203
+ if 'polarity' not in reset:
204
+ if (('nreset' in name) or ('resetn' in name)
205
+ or ('nrst' in name) or ('rstn' in name)):
206
+ reset['polarity'] = 'negative'
207
+ else:
208
+ reset['polarity'] = 'positive'
209
+ else:
210
+ reset['polarity'] = normalize_polarity(reset['polarity'])
211
+
212
+ if 'delay' not in reset:
213
+ reset['delay'] = 0
214
+
215
+ return reset
216
+
217
+
218
+ def normalize_resets(resets):
219
+ if resets is None:
220
+ resets = []
221
+
222
+ if isinstance(resets, str):
223
+ resets = [resets]
224
+
225
+ retval = []
226
+
227
+ for reset in resets:
228
+ reset = normalize_reset(reset)
229
+ retval.append(reset)
230
+
231
+ return retval
232
+
233
+
234
+ def normalize_tieoff(key, value):
235
+ if isinstance(value, dict):
236
+ value = deepcopy(value)
237
+ else:
238
+ value = {'value': value}
239
+
240
+ if 'width' not in value:
241
+ value['width'] = 1
242
+
243
+ if 'wire' not in value:
244
+ value['wire'] = None
245
+
246
+ return key, value
247
+
248
+
249
+ def normalize_tieoffs(tieoffs):
250
+ if tieoffs is None:
251
+ tieoffs = {}
252
+
253
+ retval = {}
254
+
255
+ for key, value in tieoffs.items():
256
+ key, value = normalize_tieoff(key, value)
257
+ retval[key] = value
258
+
259
+ return retval
260
+
261
+
262
+ def normalize_parameter(key, value):
263
+ # placeholder for doing more interesting things in the future
264
+ return key, value
265
+
266
+
267
+ def normalize_parameters(parameters):
268
+ if parameters is None:
269
+ parameters = {}
270
+
271
+ retval = {}
272
+
273
+ for key, value in parameters.items():
274
+ key, value = normalize_parameter(key, value)
275
+ retval[key] = value
276
+
277
+ return retval
278
+
279
+
280
+ def autowrap(
281
+ instances,
282
+ toplevel='testbench',
283
+ parameters=None,
284
+ interfaces=None,
285
+ clocks=None,
286
+ resets=None,
287
+ tieoffs=None,
288
+ filename=None,
289
+ nl='\n',
290
+ tab=' '
291
+ ):
292
+ # normalize inputs
293
+
294
+ parameters = {k: normalize_parameters(v) for k, v in parameters.items()}
295
+ interfaces = {k: normalize_interfaces(v) for k, v in interfaces.items()}
296
+ clocks = {k: normalize_clocks(v) for k, v in clocks.items()}
297
+ resets = {k: normalize_resets(v) for k, v in resets.items()}
298
+ tieoffs = {k: normalize_tieoffs(v) for k, v in tieoffs.items()}
299
+
300
+ # build up output lines
301
+
302
+ lines = []
303
+
304
+ lines += [
305
+ '`default_nettype none',
306
+ '',
307
+ '`include "switchboard.vh"',
308
+ '',
309
+ f'module {toplevel} (',
310
+ tab + '`ifdef VERILATOR',
311
+ (2 * tab) + 'input clk',
312
+ tab + '`endif',
313
+ ');',
314
+ tab + '`ifndef VERILATOR',
315
+ (2 * tab) + '`SB_CREATE_CLOCK(clk)',
316
+ tab + '`endif',
317
+ ''
318
+ ]
319
+
320
+ # wire declarations
321
+
322
+ wires = {}
323
+
324
+ lines += ['']
325
+
326
+ # declare all GPIO output wires (makes things easier when an output is
327
+ # sent to multiple places or slices of it are used)
328
+
329
+ wires['gpio'] = set()
330
+
331
+ for instance in instances:
332
+ for name, value in interfaces[instance].items():
333
+ type = value['type']
334
+ direction = value['direction']
335
+
336
+ if not ((type == 'gpio') and (direction == 'output')):
337
+ continue
338
+
339
+ wire = value['wire']
340
+
341
+ if wire is None:
342
+ # means that the output is unused
343
+ continue
344
+
345
+ assert wire not in wires['gpio']
346
+
347
+ width = value['width']
348
+
349
+ lines += [tab + f'wire [{width - 1}:0] {wire};']
350
+
351
+ wires['gpio'].add(wire)
352
+
353
+ lines += ['']
354
+
355
+ for instance in instances:
356
+ # declare wires for tieoffs
357
+
358
+ for key, value in tieoffs[instance].items():
359
+ if value['value'] is None:
360
+ continue
361
+
362
+ if value['wire'] is None:
363
+ value['wire'] = f'{instance}_tieoff_{key}'
364
+
365
+ width = value['width']
366
+ wire = value["wire"]
367
+
368
+ lines += [tab + f'wire [{width - 1}:0] {wire};']
369
+
370
+ lines += [tab + f'assign {wire} = {value["value"]};']
371
+
372
+ lines += ['']
373
+
374
+ # declare wires for interfaces
375
+
376
+ for name, value in interfaces[instance].items():
377
+ type = value['type']
378
+
379
+ if type not in wires:
380
+ wires[type] = set()
381
+
382
+ wire = value['wire']
383
+
384
+ if (type != 'gpio') and (wire not in wires[type]):
385
+ decl_wire = True
386
+ wires[type].add(wire)
387
+ else:
388
+ decl_wire = False
389
+
390
+ direction = value['direction']
391
+
392
+ external = value['external']
393
+
394
+ if type == 'sb':
395
+ dw = value['dw']
396
+
397
+ if decl_wire:
398
+ lines += [tab + f'`SB_WIRES({wire}, {dw});']
399
+
400
+ if external:
401
+ if direction_is_input(direction):
402
+ lines += [tab + f'`QUEUE_TO_SB_SIM({wire}, {dw}, "");']
403
+ elif direction_is_output(direction):
404
+ lines += [tab + f'`SB_TO_QUEUE_SIM({wire}, {dw}, "");']
405
+ else:
406
+ raise Exception(f'Unsupported SB direction: {direction}')
407
+ elif type == 'umi':
408
+ dw = value['dw']
409
+ cw = value['cw']
410
+ aw = value['aw']
411
+
412
+ if decl_wire and (wire is not None):
413
+ lines += [tab + f'`SB_UMI_WIRES({wire}, {dw}, {cw}, {aw});']
414
+
415
+ if external:
416
+ if direction_is_input(direction):
417
+ lines += [tab + f'`QUEUE_TO_UMI_SIM({wire}, {dw}, {cw}, {aw}, "");']
418
+ elif direction_is_output(direction):
419
+ lines += [tab + f'`UMI_TO_QUEUE_SIM({wire}, {dw}, {cw}, {aw}, "");']
420
+ else:
421
+ raise Exception(f'Unsupported UMI direction: {direction}')
422
+ elif type == 'axi':
423
+ dw = value['dw']
424
+ aw = value['aw']
425
+ idw = value['idw']
426
+
427
+ if decl_wire:
428
+ lines += [tab + f'`SB_AXI_WIRES({wire}, {dw}, {aw}, {idw});']
429
+
430
+ if external:
431
+ if direction_is_subordinate(direction):
432
+ lines += [tab + f'`SB_AXI_M({wire}, {dw}, {aw}, {idw}, "");']
433
+ elif direction_is_manager(direction):
434
+ lines += [tab + f'`SB_AXI_S({wire}, {dw}, {aw}, "");']
435
+ else:
436
+ raise Exception(f'Unsupported AXI direction: {direction}')
437
+ elif type == 'axil':
438
+ dw = value['dw']
439
+ aw = value['aw']
440
+
441
+ if decl_wire:
442
+ lines += [tab + f'`SB_AXIL_WIRES({wire}, {dw}, {aw});']
443
+
444
+ if external:
445
+ if direction_is_subordinate(direction):
446
+ lines += [tab + f'`SB_AXIL_M({wire}, {dw}, {aw}, "");']
447
+ elif direction_is_manager(direction):
448
+ lines += [tab + f'`SB_AXIL_S({wire}, {dw}, {aw}, "");']
449
+ else:
450
+ raise Exception(f'Unsupported AXI-Lite direction: {direction}')
451
+ elif type == 'gpio':
452
+ if direction == 'input':
453
+ width = value['width']
454
+ new_wire = f'{instance}_input_{name}'
455
+ lines += [
456
+ tab + f'wire [{width - 1}:0] {new_wire};',
457
+ tab + f'assign {new_wire} = {wire};'
458
+ ]
459
+ value['wire'] = new_wire
460
+ else:
461
+ pass
462
+ elif type == 'plusarg':
463
+ width = value['width']
464
+
465
+ plusarg = value['plusarg']
466
+ assert plusarg is not None
467
+
468
+ plusarg_wire = f'{wire}_plusarg'
469
+ plusarg_width = 32 # TODO use long or another format?
470
+
471
+ lines += [
472
+ tab + f'reg [{plusarg_width - 1}:0] {plusarg_wire} = {value["default"]};',
473
+ tab + 'initial begin',
474
+ 2 * tab + f"void'($value$plusargs(\"{plusarg}=%d\", {plusarg_wire}));",
475
+ tab + 'end'
476
+ ]
477
+
478
+ lines += [tab + f'wire [{width - 1}:0] {wire};']
479
+
480
+ if width <= plusarg_width:
481
+ lines += [tab + f'assign {wire} = {plusarg_wire}[{width - 1}:0];']
482
+ else:
483
+ lines += [tab + f'assign {wire}[{plusarg_width - 1}:0] = {plusarg_wire};']
484
+ else:
485
+ raise Exception(f'Unsupported interface type: "{type}"')
486
+
487
+ lines += ['']
488
+
489
+ max_rst_dly = None
490
+
491
+ for inst_resets in resets.values():
492
+ if len(inst_resets) > 0:
493
+ # find the max reset delay for this instance
494
+ inst_max_rst_dly = max(reset['delay'] for reset in inst_resets)
495
+
496
+ # update the overall max reset delay
497
+ if (max_rst_dly is None) or (inst_max_rst_dly > max_rst_dly):
498
+ max_rst_dly = inst_max_rst_dly
499
+
500
+ if max_rst_dly is not None:
501
+ lines += [
502
+ tab + f"reg [{max_rst_dly}:0] rstvec = '1;"
503
+ '',
504
+ tab + 'always @(posedge clk) begin'
505
+ ]
506
+
507
+ if max_rst_dly > 0:
508
+ lines += [(2 * tab) + f"rstvec <= {{rstvec[{max_rst_dly - 1}:0], 1'b0}};"]
509
+ else:
510
+ lines += [(2 * tab) + "rstvec <= 1'b0;"]
511
+
512
+ lines += [
513
+ tab + 'end',
514
+ ''
515
+ ]
516
+
517
+ for instance, module in instances.items():
518
+ # start of the instantiation
519
+
520
+ if len(parameters[instance]) > 0:
521
+ lines += [tab + f'{module} #(']
522
+ for n, (key, value) in enumerate(parameters[instance].items()):
523
+ line = (2 * tab) + f'.{key}({value})'
524
+
525
+ if n != len(parameters[instance]) - 1:
526
+ line += ','
527
+
528
+ lines += [line]
529
+ lines += [tab + f') {instance} (']
530
+ else:
531
+ lines += [tab + f'{module} {instance} (']
532
+
533
+ connections = []
534
+
535
+ # interfaces
536
+
537
+ for name, value in interfaces[instance].items():
538
+ type = value['type']
539
+ wire = value['wire']
540
+
541
+ if type_is_sb(type):
542
+ assert wire is not None
543
+ connections += [f'`SB_CONNECT({name}, {wire})']
544
+ elif type_is_umi(type):
545
+ if wire is None:
546
+ if value['direction'] == 'input':
547
+ connections += [f'`SB_TIEOFF_UMI_INPUT({name})']
548
+ elif value['direction'] == 'output':
549
+ connections += [f'`SB_TIEOFF_UMI_OUTPUT({name})']
550
+ else:
551
+ raise Exception(f'Unsupported UMI direction: {value["direction"]}')
552
+ else:
553
+ connections += [f'`SB_UMI_CONNECT({name}, {wire})']
554
+ elif type_is_axi(type):
555
+ assert wire is not None
556
+ connections += [f'`SB_AXI_CONNECT({name}, {wire})']
557
+ elif type_is_axil(type):
558
+ assert wire is not None
559
+ connections += [f'`SB_AXIL_CONNECT({name}, {wire})']
560
+ elif type_is_gpio(type) or type_is_plusarg(type):
561
+ if wire is None:
562
+ # unused output
563
+ connections += [f'.{name}()']
564
+ else:
565
+ connections += [f'.{name}({wire})']
566
+
567
+ # clocks
568
+
569
+ for clock in clocks[instance]:
570
+ connections += [f'.{clock["name"]}(clk)']
571
+
572
+ # resets
573
+
574
+ for reset in resets[instance]:
575
+ name = reset['name']
576
+ polarity = reset['polarity']
577
+ delay = reset['delay']
578
+
579
+ if polarity_is_positive(polarity):
580
+ value = f'rstvec[{delay}]'
581
+ elif polarity_is_negative(polarity):
582
+ value = f'~rstvec[{delay}]'
583
+ else:
584
+ raise ValueError(f'Unsupported reset polarity: "{polarity}"')
585
+
586
+ connections += [f'.{name}({value})']
587
+
588
+ # tieoffs
589
+
590
+ for key, value in tieoffs[instance].items():
591
+ wire = value.get('wire')
592
+
593
+ if wire is None:
594
+ wire = ''
595
+
596
+ connections += [f'.{key}({wire})']
597
+
598
+ for n, connection in enumerate(connections):
599
+ if n != len(connections) - 1:
600
+ connection += ','
601
+ lines += [(2 * tab) + connection]
602
+
603
+ lines += [tab + ');']
604
+ lines += ['']
605
+
606
+ # initialize queue connections for this instance
607
+
608
+ lines += [
609
+ tab + 'string uri_sb_value;',
610
+ '',
611
+ tab + 'initial begin',
612
+ (2 * tab) + '/* verilator lint_off IGNOREDRETURN */'
613
+ ]
614
+
615
+ for inst_interfaces in interfaces.values():
616
+ for value in inst_interfaces.values():
617
+ external = value['external']
618
+
619
+ if not external:
620
+ continue
621
+
622
+ wire = value['wire']
623
+
624
+ lines += [
625
+ (2 * tab) + f'if($value$plusargs("{wire}=%s", uri_sb_value)) begin',
626
+ (3 * tab) + f'{wire}_sb_inst.init(uri_sb_value);',
627
+ (2 * tab) + 'end'
628
+ ]
629
+
630
+ lines += [
631
+ (2 * tab) + '/* verilator lint_on IGNOREDRETURN */',
632
+ tab + 'end'
633
+ ]
634
+
635
+ lines += ['']
636
+
637
+ lines += [tab + '`SB_SETUP_PROBES();']
638
+ lines += ['']
639
+
640
+ lines += ['endmodule']
641
+
642
+ lines += ['']
643
+
644
+ lines += ['`default_nettype wire']
645
+
646
+ if filename is None:
647
+ filename = 'testbench.sv'
648
+
649
+ filename = Path(filename).resolve()
650
+
651
+ with open(filename, 'w') as f:
652
+ for line in lines:
653
+ f.write(line + nl)
654
+
655
+ return filename
656
+
657
+
658
+ def direction_is_input(direction):
659
+ return direction.lower() in ['i', 'in', 'input']
660
+
661
+
662
+ def direction_is_output(direction):
663
+ return direction.lower() in ['o', 'out', 'output']
664
+
665
+
666
+ def direction_is_inout(direction):
667
+ return direction.lower() in ['inout']
668
+
669
+
670
+ def direction_is_manager(direction):
671
+ return direction.lower() in ['m', 'manager', 'master', 'indicator']
672
+
673
+
674
+ def direction_is_subordinate(direction):
675
+ return direction.lower() in ['s', 'subordinate', 'slave', 'target']
676
+
677
+
678
+ def normalize_direction(type, direction):
679
+ if type_is_const(type):
680
+ if direction_is_output(direction):
681
+ return 'output'
682
+ else:
683
+ raise Exception(f'Unsupported direction for interface type "{type}": "{direction}"')
684
+ elif type_is_plusarg(type):
685
+ if direction_is_input(direction):
686
+ return 'input'
687
+ else:
688
+ raise Exception(f'Unsupported direction for interface type "{type}": "{direction}"')
689
+ elif type_is_sb(type) or type_is_umi(type) or type_is_gpio(type):
690
+ if direction_is_input(direction):
691
+ return 'input'
692
+ elif direction_is_output(direction):
693
+ return 'output'
694
+ elif direction_is_inout(direction):
695
+ return 'inout'
696
+ else:
697
+ raise Exception(f'Unsupported direction for interface type "{type}": "{direction}"')
698
+ elif type_is_axi(type) or type_is_axil(type) or type_is_apb(type):
699
+ if direction_is_manager(direction):
700
+ return 'manager'
701
+ elif direction_is_subordinate(direction):
702
+ return 'subordinate'
703
+ else:
704
+ raise Exception(f'Unsupported direction for interface type "{type}": "{direction}"')
705
+ else:
706
+ raise Exception(f'Unsupported interface type: "{type}"')
707
+
708
+
709
+ def directions_are_compatible(type_a, a, type_b, b):
710
+ a = normalize_direction(type_a, a)
711
+ b = normalize_direction(type_b, b)
712
+
713
+ if a == 'input':
714
+ return b in ['output', 'inout']
715
+ elif a == 'output':
716
+ return b in ['input', 'inout']
717
+ elif a == 'inout':
718
+ return b in ['input', 'output', 'inout']
719
+ elif a == 'manager':
720
+ return b == 'subordinate'
721
+ elif a == 'subordinate':
722
+ return b == 'manager'
723
+ else:
724
+ raise Exception(f'Cannot determine if directions are compatible: {a} and {b}')
725
+
726
+
727
+ def types_are_compatible(a, b):
728
+ a = normalize_intf_type(a)
729
+ b = normalize_intf_type(b)
730
+
731
+ if type_is_const(a):
732
+ return type_is_gpio(b)
733
+ elif type_is_const(b):
734
+ return type_is_gpio(a)
735
+ else:
736
+ return a == b
737
+
738
+
739
+ def flip_intf(a):
740
+ type = normalize_intf_type(a['type'])
741
+ direction = normalize_direction(type=type, direction=a['direction'])
742
+
743
+ retval = deepcopy(a)
744
+
745
+ if type_is_sb(type) or type_is_umi(type):
746
+ if direction == 'input':
747
+ retval['direction'] = 'output'
748
+ elif direction == 'output':
749
+ retval['direction'] = 'input'
750
+ else:
751
+ raise Exception(f'Unsupported direction: {direction}')
752
+ elif type_is_axi(type) or type_is_axil(type):
753
+ if direction == 'manager':
754
+ retval['direction'] = 'subordinate'
755
+ elif direction == 'subordinate':
756
+ retval['direction'] = 'manager'
757
+ else:
758
+ raise Exception(f'Unsupported direction: {direction}')
759
+ else:
760
+ raise Exception(f'Unsupported interface type: "{type}"')
761
+
762
+ return retval
763
+
764
+
765
+ def polarity_is_positive(polarity):
766
+ return polarity.lower() in ['+', 'p', 'plus', 'positive']
767
+
768
+
769
+ def polarity_is_negative(polarity):
770
+ return polarity.lower() in ['-', 'n', 'minus', 'negative']
771
+
772
+
773
+ def normalize_polarity(polarity):
774
+ if polarity_is_positive(polarity):
775
+ return 'positive'
776
+ elif polarity_is_negative(polarity):
777
+ return 'negative'
778
+ else:
779
+ raise ValueError(f'Unsupported reset polarity: "{polarity}"')
780
+
781
+
782
+ def type_is_sb(type):
783
+ return type.lower() in ['sb', 'switchboard']
784
+
785
+
786
+ def type_is_umi(type):
787
+ return type.lower() in ['umi']
788
+
789
+
790
+ def type_is_axi(type):
791
+ return type.lower() in ['axi']
792
+
793
+
794
+ def type_is_axil(type):
795
+ return type.lower() in ['axil']
796
+
797
+
798
+ def type_is_apb(type):
799
+ return type.lower() in ['apb']
800
+
801
+
802
+ def type_is_input(type):
803
+ return type.lower() in ['i', 'in', 'input']
804
+
805
+
806
+ def type_is_output(type):
807
+ return type.lower() in ['o', 'out', 'output']
808
+
809
+
810
+ def type_is_gpio(type):
811
+ return type.lower() in ['gpio']
812
+
813
+
814
+ def type_is_const(type):
815
+ return type.lower() in ['const', 'constant']
816
+
817
+
818
+ def type_is_plusarg(type):
819
+ return type.lower() in ['plusarg']
820
+
821
+
822
+ def normalize_intf_type(type):
823
+ if type_is_sb(type):
824
+ return 'sb'
825
+ elif type_is_umi(type):
826
+ return 'umi'
827
+ elif type_is_axi(type):
828
+ return 'axi'
829
+ elif type_is_axil(type):
830
+ return 'axil'
831
+ elif type_is_apb(type):
832
+ return 'apb'
833
+ elif type_is_input(type):
834
+ return 'input'
835
+ elif type_is_output(type):
836
+ return 'output'
837
+ elif type_is_gpio(type):
838
+ return 'gpio'
839
+ elif type_is_const(type):
840
+ return 'const'
841
+ elif type_is_plusarg(type):
842
+ return 'plusarg'
843
+ else:
844
+ raise ValueError(f'Unsupported interface type: "{type}"')
845
+
846
+
847
+ def create_intf_objs(intf_defs, fresh=True, max_rate=-1):
848
+ intf_objs = {}
849
+
850
+ umi_txrx = {}
851
+
852
+ for name, value in intf_defs.items():
853
+ type = value['type']
854
+
855
+ if type == 'plusarg':
856
+ continue
857
+
858
+ if type.lower() in ['umi']:
859
+ txrx = value['txrx']
860
+
861
+ if txrx is not None:
862
+ if txrx not in umi_txrx:
863
+ umi_txrx[txrx] = dict(tx_uri=None, rx_uri=None)
864
+
865
+ if 'srcaddr' in value:
866
+ umi_txrx[txrx]['srcaddr'] = value['srcaddr']
867
+
868
+ if 'posted' in value:
869
+ umi_txrx[txrx]['posted'] = value['posted']
870
+
871
+ if 'max_bytes' in value:
872
+ umi_txrx[txrx]['max_bytes'] = value['max_bytes']
873
+
874
+ if 'max_rate' in value:
875
+ umi_txrx[txrx]['max_rate'] = value['max_rate']
876
+ else:
877
+ # use default if not set for this particular interface
878
+ umi_txrx[txrx]['max_rate'] = max_rate
879
+
880
+ direction = value['direction']
881
+
882
+ if direction.lower() in ['i', 'in', 'input']:
883
+ umi_txrx[txrx]['tx_uri'] = value['uri']
884
+ elif direction.lower() in ['o', 'out', 'output']:
885
+ umi_txrx[txrx]['rx_uri'] = value['uri']
886
+ else:
887
+ raise Exception(f'Unsupported UMI direction: {direction}')
888
+ else:
889
+ intf_objs[name] = create_intf_obj(value, fresh=fresh, max_rate=max_rate)
890
+ else:
891
+ intf_objs[name] = create_intf_obj(value, fresh=fresh, max_rate=max_rate)
892
+
893
+ for key, value in umi_txrx.items():
894
+ intf_objs[key] = UmiTxRx(**value, fresh=fresh)
895
+
896
+ return intf_objs
897
+
898
+
899
+ def create_intf_obj(value, fresh=True, max_rate=-1):
900
+ type = value['type']
901
+ direction = value['direction']
902
+
903
+ if type_is_sb(type):
904
+ kwargs = {}
905
+
906
+ if 'max_rate' in value:
907
+ kwargs['max_rate'] = value['max_rate']
908
+ else:
909
+ # use default if not set for this particular interface
910
+ kwargs['max_rate'] = max_rate
911
+
912
+ if direction_is_input(direction):
913
+ obj = PySbTx(value['uri'], fresh=fresh, **kwargs)
914
+ elif direction_is_output(direction):
915
+ obj = PySbRx(value['uri'], fresh=fresh, **kwargs)
916
+ else:
917
+ raise Exception(f'Unsupported SB direction: "{direction}"')
918
+ elif type_is_umi(type):
919
+ kwargs = {}
920
+
921
+ if 'max_rate' in value:
922
+ kwargs['max_rate'] = value['max_rate']
923
+ else:
924
+ # use default if not set for this particular interface
925
+ kwargs['max_rate'] = max_rate
926
+
927
+ if direction_is_input(direction):
928
+ obj = UmiTxRx(tx_uri=value['uri'], fresh=fresh, **kwargs)
929
+ elif direction_is_output(direction):
930
+ obj = UmiTxRx(rx_uri=value['uri'], fresh=fresh, **kwargs)
931
+ else:
932
+ raise Exception(f'Unsupported UMI direction: "{direction}"')
933
+ elif type_is_axi(type):
934
+ kwargs = {}
935
+
936
+ if 'prot' in value:
937
+ kwargs['prot'] = value['prot']
938
+
939
+ if 'id' in value:
940
+ kwargs['id'] = value['id']
941
+
942
+ if 'size' in value:
943
+ kwargs['size'] = value['size']
944
+
945
+ if 'max_beats' in value:
946
+ kwargs['max_beats'] = value['max_beats']
947
+
948
+ if 'max_rate' in value:
949
+ kwargs['max_rate'] = value['max_rate']
950
+ else:
951
+ # use default if not set for this particular interface
952
+ kwargs['max_rate'] = max_rate
953
+
954
+ if direction_is_subordinate(direction):
955
+ obj = AxiTxRx(uri=value['uri'], data_width=value['dw'],
956
+ addr_width=value['aw'], id_width=value['idw'], **kwargs)
957
+ else:
958
+ raise Exception(f'Unsupported AXI direction: "{direction}"')
959
+ elif type_is_axil(type):
960
+ kwargs = {}
961
+
962
+ if 'prot' in value:
963
+ kwargs['prot'] = value['prot']
964
+
965
+ if 'max_rate' in value:
966
+ kwargs['max_rate'] = value['max_rate']
967
+ else:
968
+ # use default if not set for this particular interface
969
+ kwargs['max_rate'] = max_rate
970
+
971
+ if direction_is_subordinate(direction):
972
+ obj = AxiLiteTxRx(uri=value['uri'], data_width=value['dw'],
973
+ addr_width=value['aw'], **kwargs)
974
+ else:
975
+ raise Exception(f'Unsupported AXI-Lite direction: "{direction}"')
976
+ elif type_is_apb(type):
977
+ kwargs = {}
978
+
979
+ if 'prot' in value:
980
+ kwargs['prot'] = value['prot']
981
+
982
+ if 'max_rate' in value:
983
+ kwargs['max_rate'] = value['max_rate']
984
+ else:
985
+ # use default if not set for this particular interface
986
+ kwargs['max_rate'] = max_rate
987
+
988
+ if direction_is_subordinate(direction):
989
+ obj = ApbTxRx(
990
+ uri=value['uri'],
991
+ data_width=value['dw'],
992
+ addr_width=value['aw'],
993
+ **kwargs
994
+ )
995
+ else:
996
+ raise Exception(f'Unsupported APB direction: "{direction}"')
997
+ else:
998
+ raise Exception(f'Unsupported interface type: "{type}"')
999
+
1000
+ return obj