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/axi.py ADDED
@@ -0,0 +1,571 @@
1
+ # Python interface for AXI reads and writes
2
+
3
+ # Copyright (c) 2024 Zero ASIC Corporation
4
+ # This code is licensed under Apache License 2.0 (see LICENSE for details)
5
+
6
+ import numpy as np
7
+
8
+ from math import floor, ceil, log2
9
+ from numbers import Integral
10
+
11
+ from _switchboard import PySbPacket, PySbTx, PySbRx
12
+
13
+
14
+ class AxiTxRx:
15
+ def __init__(
16
+ self,
17
+ uri: str,
18
+ fresh: bool = True,
19
+ data_width: int = 32,
20
+ addr_width: int = 16,
21
+ id_width: int = 8,
22
+ prot: int = 0,
23
+ id: int = 0,
24
+ size: int = None,
25
+ max_beats: int = 256,
26
+ resp_expected: str = 'OKAY',
27
+ queue_suffix: str = '.q',
28
+ max_rate: float = -1
29
+ ):
30
+ """
31
+ Parameters
32
+ ----------
33
+ uri: str
34
+ Base name of for switchboard queues used to convey AXI transactions. Five
35
+ queues are used: write address (aw), write data (w), write response (b),
36
+ read address (ar), and read response (r). If the base name provided is
37
+ "axi", the correponding queues will be "axi-aw.q", "axi-w.q", "axi-b.q",
38
+ "axi-ar.q" and "axi-r.q". The suffix used can be changed via the
39
+ "queue_suffix" argument if needed.
40
+ fresh: bool, optional
41
+ If True (default), the queue specified by the uri parameter will get cleared
42
+ before executing the simulation.
43
+ data_width: int, optional
44
+ Width of the write and read data buses, in bits.
45
+ addr_width: int, optional
46
+ Width of the write and read address buses, in bits.
47
+ addr_width: int, optional
48
+ Width of the write and read IDs, in bits.
49
+ prot: int, optional
50
+ Default value of PROT to use for read and write transactions. Can be
51
+ overridden on a transaction-by-transaction basis.
52
+ id: int, option
53
+ Default ID to use for read/write transactions.
54
+ size: int, optional
55
+ AXI SIZE indicating the default width of read/write transactions. This can
56
+ be overridden on a transaction-by-transaction basis via the "size" argument.
57
+ If a value isn't provided here, the default size is set to the full data bus
58
+ width.
59
+ max_beats: int, optional
60
+ Maximum number of beats in a single AXI transaction. Defaults to 256; set to
61
+ 1 to disable bursting. Set to 16 for AXI3 compatibility.
62
+ resp_expected: str, optional
63
+ Default response to expect from reads and writes. Options are 'OKAY',
64
+ 'EXOKAY', 'SLVERR', 'DECERR'. None means "don't check the response".
65
+ This default can be overridden on a transaction-by-transaction basis.
66
+ queue_suffix: str, optional
67
+ File extension/suffix to use when naming switchboard queues that carry
68
+ AXI transactions. For example, if set to ".queue", the write address
69
+ queue name will be "{uri}-aw.queue"
70
+ """
71
+
72
+ # check data types
73
+ assert isinstance(data_width, Integral), 'data_width must be an integer'
74
+ assert isinstance(addr_width, Integral), 'addr_width must be an integer'
75
+
76
+ # check that data width is a multiple of a byte
77
+ data_width_choices = [8, 16, 32, 64, 128, 256, 512, 1024]
78
+ assert data_width in data_width_choices, \
79
+ f'data_width must be in {data_width_choices}'
80
+
81
+ # check that data and address widths are supported
82
+ SBDW = 416
83
+ assert 0 < data_width <= floor(SBDW / (1 + (1 / 8))), 'data_width out of range'
84
+ assert 0 < addr_width <= SBDW - 3, 'addr_width out of range'
85
+
86
+ # determine default size
87
+ if size is None:
88
+ size = ceil(log2(data_width // 8))
89
+
90
+ # save settings
91
+ self.data_width = data_width
92
+ self.addr_width = addr_width
93
+ self.id_width = id_width
94
+ self.default_prot = prot
95
+ self.default_id = id
96
+ self.default_size = size
97
+ self.default_max_beats = max_beats
98
+ self.default_resp_expected = resp_expected
99
+
100
+ # create the queues
101
+ self.aw = PySbTx(f'{uri}-aw{queue_suffix}', fresh=fresh, max_rate=max_rate)
102
+ self.w = PySbTx(f'{uri}-w{queue_suffix}', fresh=fresh, max_rate=max_rate)
103
+ self.b = PySbRx(f'{uri}-b{queue_suffix}', fresh=fresh, max_rate=max_rate)
104
+ self.ar = PySbTx(f'{uri}-ar{queue_suffix}', fresh=fresh, max_rate=max_rate)
105
+ self.r = PySbRx(f'{uri}-r{queue_suffix}', fresh=fresh, max_rate=max_rate)
106
+
107
+ @property
108
+ def strb_width(self):
109
+ return self.data_width // 8
110
+
111
+ def write(
112
+ self,
113
+ addr: Integral,
114
+ data,
115
+ prot: Integral = None,
116
+ id: Integral = None,
117
+ size: Integral = None,
118
+ max_beats: Integral = None,
119
+ resp_expected: str = None
120
+ ):
121
+ """
122
+ Parameters
123
+ ----------
124
+ addr: int
125
+ Address to write to
126
+
127
+ data: np.uint8, np.uint16, np.uint32, np.uint64, or np.array
128
+ Data to write
129
+
130
+ prot: Integral
131
+ Value of PROT for this transaction. Defaults to the value provided in the
132
+ AxiTxRx constructor if not provided, which in turn defaults to 0.
133
+
134
+ id: int, option
135
+ ID to use for write transactions. If not provided, defaults to the value
136
+ given in the constructor, which in turn defaults to 0.
137
+
138
+ size: int, optional
139
+ AXI SIZE indicating the width of write transactions. If not provided, defaults
140
+ to the value given in the constructor, which in turn defaults to the full
141
+ data bus width.
142
+
143
+ max_beats: int, optional
144
+ Maximum number of beats in a single write transaction. If not provided, defaults
145
+ to the value given in the constructor, which in turn defaults to 256.
146
+
147
+ resp_expected: str, optional
148
+ Response to expect for this transaction. Options are 'OKAY', 'EXOKAY', 'SLVERR',
149
+ 'DECERR', and None. None means, "don't check the response". Defaults to the
150
+ value provided in the AxiTxRx constructor if not provided, which in turn defaults
151
+ to 'OKAY'
152
+
153
+ Returns
154
+ -------
155
+ str
156
+ String representation of the response code, which may be 'OKAY', 'EXOKAY',
157
+ 'SLVERR', or 'DECERR'.
158
+ """
159
+
160
+ # set defaults
161
+
162
+ if prot is None:
163
+ prot = self.default_prot
164
+
165
+ if id is None:
166
+ id = self.default_id
167
+
168
+ if size is None:
169
+ size = self.default_size
170
+
171
+ if max_beats is None:
172
+ max_beats = self.default_max_beats
173
+
174
+ if resp_expected is None:
175
+ resp_expected = self.default_resp_expected
176
+
177
+ # check/standardize data types
178
+
179
+ assert isinstance(addr, Integral), 'addr must be an integer'
180
+ addr = int(addr)
181
+
182
+ assert isinstance(prot, Integral), 'prot must be an integer'
183
+ prot = int(prot)
184
+
185
+ if isinstance(data, np.ndarray):
186
+ if data.ndim == 0:
187
+ write_data = np.atleast_1d(data)
188
+ elif data.ndim == 1:
189
+ write_data = data
190
+ else:
191
+ raise ValueError(f'Can only write 1D arrays (got ndim={data.ndim})')
192
+
193
+ if not np.issubdtype(write_data.dtype, np.integer):
194
+ raise ValueError('Can only write integer dtypes such as uint8, uint16, etc.'
195
+ f' (got dtype "{data.dtype}")')
196
+ elif isinstance(data, np.integer):
197
+ write_data = np.array(data, ndmin=1)
198
+ else:
199
+ raise TypeError(f"Unknown data type: {type(data)}")
200
+
201
+ write_data = write_data.view(np.uint8)
202
+ bytes_to_send = write_data.size
203
+
204
+ # range validation
205
+
206
+ assert 0 <= addr < (1 << self.addr_width), 'addr out of range'
207
+ assert addr + bytes_to_send <= (1 << self.addr_width), \
208
+ "transaction exceeds the address space."
209
+
210
+ assert 0 <= prot < (1 << 3), 'prot out of range'
211
+
212
+ # loop until all data is sent
213
+ # TODO: move to C++?
214
+
215
+ bytes_sent = 0
216
+
217
+ data_bytes = self.data_width // 8
218
+ data = np.empty((data_bytes,), dtype=np.uint8)
219
+
220
+ addr_mask = (1 << self.addr_width) - 1
221
+ addr_mask >>= size
222
+ addr_mask <<= size
223
+
224
+ mask_4k = (1 << self.addr_width) - 1
225
+ mask_4k >>= 12
226
+ mask_4k <<= 12
227
+
228
+ increment_4k = 1 << 12
229
+
230
+ while bytes_sent < bytes_to_send:
231
+ top_addr = addr + (bytes_to_send - bytes_sent) - 1
232
+
233
+ # limit transfer to the longest burst possible
234
+ longest_burst = max_beats * (1 << size)
235
+ top_addr = min(top_addr, (addr & addr_mask) + longest_burst - 1)
236
+
237
+ # don't cross a 4k boundary
238
+ next_4k_boundary = (addr & mask_4k) + increment_4k - 1
239
+ top_addr = min(top_addr, next_4k_boundary)
240
+
241
+ # calculate the number of beats
242
+ beats = ceil((top_addr - (addr & addr_mask) + 1) / (1 << size))
243
+
244
+ assert 1 <= beats <= max_beats
245
+
246
+ # transmit the write address
247
+ self.aw.send(self.pack_addr(addr & addr_mask, prot=prot, size=size,
248
+ len=beats - 1, id=id), True)
249
+
250
+ for beat in range(beats):
251
+ # find the offset into the data bus for this beat. bytes below
252
+ # the offset will have write strobe de-asserted.
253
+ offset = addr - (addr & addr_mask)
254
+
255
+ # determine how many bytes we're sending in this cycle
256
+ bytes_this_beat = min(bytes_to_send - bytes_sent, (1 << size) - offset)
257
+
258
+ # extract those bytes from the whole input data array, picking
259
+ # up where we left off from the last beat
260
+ data[offset:offset + bytes_this_beat] = \
261
+ write_data[bytes_sent:bytes_sent + bytes_this_beat]
262
+
263
+ # calculate strobe value based on the offset and number
264
+ # of bytes that we're writing.
265
+ strb = ((1 << bytes_this_beat) - 1) << offset
266
+
267
+ # write data and strobe
268
+ if beat == beats - 1:
269
+ last = 1
270
+ else:
271
+ last = 0
272
+ self.w.send(self.pack_w(data, strb=strb, last=last), True)
273
+
274
+ # increment pointers
275
+ bytes_sent += bytes_this_beat
276
+ addr += bytes_this_beat
277
+
278
+ # wait for response
279
+ resp, id = self.unpack_b(self.b.recv(True))
280
+
281
+ # decode the response
282
+ resp = decode_resp(resp)
283
+
284
+ # check the response if desired
285
+ if resp_expected is not None:
286
+ assert resp.upper() == resp_expected.upper(), f'Unexpected response: {resp}'
287
+
288
+ # return the last reponse
289
+ return resp
290
+
291
+ def read(
292
+ self,
293
+ addr: Integral,
294
+ num_or_dtype,
295
+ dtype=np.uint8,
296
+ prot: Integral = None,
297
+ id: Integral = None,
298
+ size: Integral = None,
299
+ max_beats: Integral = None,
300
+ resp_expected: str = None
301
+ ):
302
+ """
303
+ Parameters
304
+ ----------
305
+ addr: int
306
+ Address to read from
307
+
308
+ num_or_dtype: int or numpy integer datatype
309
+ If a plain int, `num_or_datatype` specifies the number of bytes to be read.
310
+ If a numpy integer datatype (np.uint8, np.uint16, etc.), num_or_datatype
311
+ specifies the data type to be returned.
312
+
313
+ dtype: numpy integer datatype, optional
314
+ If num_or_dtype is a plain integer, the value returned by this function
315
+ will be a numpy array of type "dtype". On the other hand, if num_or_dtype
316
+ is a numpy datatype, the value returned will be a scalar of that datatype.
317
+
318
+ prot: Integral
319
+ Value of PROT for this transaction. Defaults to the value provided in the
320
+ AxiTxRx constructor if not provided, which in turn defaults to 0.
321
+
322
+ id: int, option
323
+ ID to use for read transactions. If not provided, defaults to the value
324
+ given in the constructor, which in turn defaults to 0.
325
+
326
+ size: int, optional
327
+ AXI SIZE indicating the width of read transactions. If not provided, defaults
328
+ to the value given in the constructor, which in turn defaults to the full
329
+ data bus width.
330
+
331
+ max_beats: int, optional
332
+ Maximum number of beats in a single read transaction. If not provided, defaults
333
+ to the value given in the constructor, which in turn defaults to 256.
334
+
335
+ resp_expected: str, optional
336
+ Response to expect for this transaction. Options are 'OKAY', 'EXOKAY', 'SLVERR',
337
+ 'DECERR', and None. None means, "don't check the response". Defaults to the
338
+ value provided in the AxiTxRx constructor if not provided, which in turn defaults
339
+ to 'OKAY'
340
+
341
+ Returns
342
+ -------
343
+ int
344
+ Value read, as an arbitrary-size Python integer.
345
+ """
346
+
347
+ # set defaults
348
+
349
+ if prot is None:
350
+ prot = self.default_prot
351
+
352
+ if id is None:
353
+ id = self.default_id
354
+
355
+ if size is None:
356
+ size = self.default_size
357
+
358
+ if max_beats is None:
359
+ max_beats = self.default_max_beats
360
+
361
+ if resp_expected is None:
362
+ resp_expected = self.default_resp_expected
363
+
364
+ # check/standardize data types
365
+
366
+ assert isinstance(addr, Integral), 'addr must be an integer'
367
+ addr = int(addr)
368
+
369
+ assert isinstance(prot, Integral), 'prot must be an integer'
370
+ prot = int(prot)
371
+
372
+ if isinstance(num_or_dtype, (type, np.dtype)):
373
+ bytes_to_read = np.dtype(num_or_dtype).itemsize
374
+ else:
375
+ bytes_to_read = num_or_dtype * np.dtype(dtype).itemsize
376
+
377
+ # range validation
378
+
379
+ assert 0 <= addr < (1 << self.addr_width), 'addr out of range'
380
+ assert addr + bytes_to_read <= (1 << self.addr_width), \
381
+ "transaction exceeds the address space."
382
+
383
+ assert 0 <= prot < (1 << 3), 'prot out of range'
384
+
385
+ # loop until all data is read
386
+ # TODO: move to C++?
387
+
388
+ bytes_read = 0
389
+
390
+ addr_mask = (1 << self.addr_width) - 1
391
+ addr_mask >>= size
392
+ addr_mask <<= size
393
+
394
+ mask_4k = (1 << self.addr_width) - 1
395
+ mask_4k >>= 12
396
+ mask_4k <<= 12
397
+
398
+ increment_4k = 1 << 12
399
+
400
+ retval = np.empty((bytes_to_read,), dtype=np.uint8)
401
+
402
+ while bytes_read < bytes_to_read:
403
+ top_addr = addr + (bytes_to_read - bytes_read) - 1
404
+
405
+ # limit transfer to the longest burst possible
406
+ longest_burst = max_beats * (1 << size)
407
+ top_addr = min(top_addr, (addr & addr_mask) + longest_burst - 1)
408
+
409
+ # don't cross a 4k boundary
410
+ next_4k_boundary = (addr & mask_4k) + increment_4k - 1
411
+ top_addr = min(top_addr, next_4k_boundary)
412
+
413
+ # calculate the number of beats
414
+ beats = ceil((top_addr - (addr & addr_mask) + 1) / (1 << size))
415
+ assert 1 <= beats <= max_beats
416
+
417
+ # transmit read address
418
+ self.ar.send(self.pack_addr(addr & addr_mask, prot=prot, size=size,
419
+ len=beats - 1, id=id), True)
420
+
421
+ for _ in range(beats):
422
+ # find the offset into the data bus for this beat. bytes below
423
+ # the offset will have write strobe de-asserted.
424
+ offset = addr - (addr & addr_mask)
425
+
426
+ # determine how many bytes we're sending in this cycle
427
+ bytes_this_beat = min(bytes_to_read - bytes_read, (1 << size) - offset)
428
+
429
+ # wait for response
430
+ data, resp, id, last = self.unpack_r(self.r.recv(True))
431
+ retval[bytes_read:bytes_read + bytes_this_beat] = \
432
+ data = data[offset:offset + bytes_this_beat]
433
+
434
+ # check the reponse
435
+ if resp_expected is not None:
436
+ resp = decode_resp(resp)
437
+ assert resp.upper() == resp_expected.upper(), f'Unexpected response: {resp}'
438
+
439
+ # increment pointers
440
+ bytes_read += bytes_this_beat
441
+ addr += bytes_this_beat
442
+
443
+ if isinstance(num_or_dtype, (type, np.dtype)):
444
+ return retval.view(num_or_dtype)[0]
445
+ else:
446
+ return retval.view(dtype)
447
+
448
+ def pack_addr(self, addr, prot=0, id=0, len=0, size=0, burst=0b01, lock=0, cache=0):
449
+ pack = 0
450
+
451
+ # cache
452
+ pack = (pack << 4) | (cache & 0b1111)
453
+
454
+ # lock
455
+ pack = (pack << 1) | (lock & 0b1)
456
+
457
+ # burst
458
+ pack = (pack << 2) | (burst & 0b11)
459
+
460
+ # size
461
+ pack = (pack << 3) | (size & 0b111)
462
+
463
+ # len
464
+ pack = (pack << 8) | (len & 0xff)
465
+
466
+ # id
467
+ pack = (pack << self.id_width) | (id & ((1 << self.id_width) - 1))
468
+
469
+ # prot
470
+ pack = (pack << 3) | (prot & 0b111)
471
+
472
+ # addr
473
+ pack = (pack << self.addr_width) | (addr & ((1 << self.addr_width) - 1))
474
+
475
+ # convert to byte array
476
+ pack = pack.to_bytes(
477
+ (self.addr_width + 3 + self.id_width + 8 + 3 + 2 + 1 + 4 + 7) // 8,
478
+ 'little'
479
+ )
480
+
481
+ # convert to a numpy array
482
+ pack = np.frombuffer(pack, dtype=np.uint8)
483
+
484
+ # convert to an SB packet
485
+ pack = PySbPacket(data=pack, flags=1, destination=0)
486
+
487
+ return pack
488
+
489
+ def pack_w(self, data, strb=None, last=1):
490
+ if strb is None:
491
+ strb = (1 << self.strb_width) - 1
492
+
493
+ # figure out how many bytes the data + rest of the signals take up
494
+ data_bytes = self.data_width // 8
495
+ rest_bytes = (self.strb_width + 1 + 7) // 8
496
+
497
+ # pack non-data signals together
498
+ rest = 0
499
+ rest = (rest << 1) | (last & 1)
500
+ rest = (rest << self.strb_width) | (strb & ((1 << self.strb_width) - 1))
501
+ rest = rest.to_bytes(rest_bytes, 'little')
502
+ rest = np.frombuffer(rest, dtype=np.uint8)
503
+
504
+ # pack everything together in a numpy array
505
+ pack = np.empty((data_bytes + rest_bytes,), dtype=np.uint8)
506
+ pack[:data_bytes] = data
507
+ pack[data_bytes:] = rest
508
+
509
+ # convert to an SB packet
510
+ pack = PySbPacket(data=pack, flags=1, destination=0)
511
+
512
+ return pack
513
+
514
+ def unpack_b(self, pack):
515
+ pack = pack.data.tobytes()
516
+ pack = int.from_bytes(pack, 'little')
517
+
518
+ # resp
519
+ resp = pack & 0b11
520
+ pack >>= 2
521
+
522
+ # id
523
+ id = pack & ((1 << self.id_width) - 1)
524
+ pack >>= self.id_width
525
+
526
+ return resp, id
527
+
528
+ def unpack_r(self, pack):
529
+ data_bytes = self.data_width // 8
530
+
531
+ data = pack.data[:data_bytes]
532
+ rest = pack.data[data_bytes:]
533
+
534
+ rest = rest.tobytes()
535
+ rest = int.from_bytes(rest, 'little')
536
+
537
+ # resp
538
+ resp = rest & 0b11
539
+ rest >>= 2
540
+
541
+ # id
542
+ id = rest & ((1 << self.id_width) - 1)
543
+ rest >>= self.id_width
544
+
545
+ # last
546
+ last = rest & 0b1
547
+ rest >>= 1
548
+
549
+ return data, resp, id, last
550
+
551
+
552
+ def decode_resp(resp: Integral):
553
+ assert isinstance(resp, Integral), 'response code must be an integer'
554
+ assert 0 <= resp <= 3, 'response code out of range'
555
+
556
+ return ['OKAY', 'EXOKAY', 'SLVERR', 'DECERR'][resp]
557
+
558
+
559
+ def axi_uris(prefix, suffix='.q'):
560
+ # returns a list of the URIs associated with a given AXI or AXI-Lite
561
+ # prefix. For example, axi_uris('axi') returns ['axi-aw.q', 'axi-w.q',
562
+ # 'axi-b.q', 'axi-ar.q', 'axi-r.q']. Changing the optional suffix
563
+ # argument changes the file extension assumed in generating this list.
564
+
565
+ return [
566
+ f'{prefix}-aw{suffix}',
567
+ f'{prefix}-w{suffix}',
568
+ f'{prefix}-b{suffix}',
569
+ f'{prefix}-ar{suffix}',
570
+ f'{prefix}-r{suffix}'
571
+ ]