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.
- _switchboard.cpython-314-darwin.so +0 -0
- switchboard/__init__.py +24 -0
- switchboard/ams.py +668 -0
- switchboard/apb.py +278 -0
- switchboard/autowrap.py +1000 -0
- switchboard/axi.py +571 -0
- switchboard/axil.py +348 -0
- switchboard/bitvector.py +112 -0
- switchboard/cmdline.py +142 -0
- switchboard/cpp/Makefile +13 -0
- switchboard/cpp/bitutil.h +39 -0
- switchboard/cpp/pagemap.h +91 -0
- switchboard/cpp/pciedev.h +86 -0
- switchboard/cpp/router.cc +89 -0
- switchboard/cpp/spsc_queue.h +267 -0
- switchboard/cpp/switchboard.hpp +257 -0
- switchboard/cpp/switchboard_pcie.hpp +234 -0
- switchboard/cpp/switchboard_tlm.hpp +98 -0
- switchboard/cpp/umilib.h +144 -0
- switchboard/cpp/umilib.hpp +113 -0
- switchboard/cpp/umisb.hpp +364 -0
- switchboard/cpp/xyce.hpp +90 -0
- switchboard/deps/__init__.py +0 -0
- switchboard/deps/verilog_axi.py +23 -0
- switchboard/dpi/__init__.py +0 -0
- switchboard/dpi/switchboard_dpi.cc +119 -0
- switchboard/dpi/switchboard_dpi.py +13 -0
- switchboard/dpi/xyce_dpi.cc +43 -0
- switchboard/gpio.py +108 -0
- switchboard/icarus.py +85 -0
- switchboard/loopback.py +157 -0
- switchboard/network.py +714 -0
- switchboard/pytest_plugin.py +11 -0
- switchboard/sbdesign.py +55 -0
- switchboard/sbdut.py +744 -0
- switchboard/sbtcp.py +345 -0
- switchboard/sc/__init__.py +0 -0
- switchboard/sc/morty/__init__.py +0 -0
- switchboard/sc/morty/uniquify.py +67 -0
- switchboard/sc/sed/__init__.py +0 -0
- switchboard/sc/sed/sed_remove.py +47 -0
- switchboard/sc/standalone_netlist_flow.py +25 -0
- switchboard/switchboard.py +53 -0
- switchboard/test_util.py +46 -0
- switchboard/uart_xactor.py +66 -0
- switchboard/umi.py +793 -0
- switchboard/util.py +131 -0
- switchboard/verilator/__init__.py +0 -0
- switchboard/verilator/config.vlt +13 -0
- switchboard/verilator/testbench.cc +143 -0
- switchboard/verilator/verilator.py +13 -0
- switchboard/verilator_run.py +31 -0
- switchboard/verilog/__init__.py +0 -0
- switchboard/verilog/common/__init__.py +0 -0
- switchboard/verilog/common/common.py +26 -0
- switchboard/verilog/common/switchboard.vh +429 -0
- switchboard/verilog/common/uart_xactor.sv +247 -0
- switchboard/verilog/common/umi_gpio.v +236 -0
- switchboard/verilog/fpga/__init__.py +0 -0
- switchboard/verilog/fpga/axi_reader.sv +82 -0
- switchboard/verilog/fpga/axi_writer.sv +111 -0
- switchboard/verilog/fpga/config_registers.sv +249 -0
- switchboard/verilog/fpga/fpga.py +21 -0
- switchboard/verilog/fpga/include/sb_queue_regmap.vh +21 -0
- switchboard/verilog/fpga/include/spsc_queue.vh +7 -0
- switchboard/verilog/fpga/memory_fault.sv +40 -0
- switchboard/verilog/fpga/sb_fpga_queues.sv +416 -0
- switchboard/verilog/fpga/sb_rx_fpga.sv +303 -0
- switchboard/verilog/fpga/sb_tx_fpga.sv +294 -0
- switchboard/verilog/fpga/umi_fpga_queues.sv +146 -0
- switchboard/verilog/sim/__init__.py +0 -0
- switchboard/verilog/sim/auto_stop_sim.sv +25 -0
- switchboard/verilog/sim/perf_meas_sim.sv +97 -0
- switchboard/verilog/sim/queue_to_sb_sim.sv +176 -0
- switchboard/verilog/sim/queue_to_umi_sim.sv +66 -0
- switchboard/verilog/sim/sb_apb_m.sv +146 -0
- switchboard/verilog/sim/sb_axi_m.sv +199 -0
- switchboard/verilog/sim/sb_axil_m.sv +180 -0
- switchboard/verilog/sim/sb_axil_s.sv +180 -0
- switchboard/verilog/sim/sb_clk_gen.sv +89 -0
- switchboard/verilog/sim/sb_jtag_rbb_sim.sv +148 -0
- switchboard/verilog/sim/sb_rx_sim.sv +55 -0
- switchboard/verilog/sim/sb_to_queue_sim.sv +196 -0
- switchboard/verilog/sim/sb_tx_sim.sv +55 -0
- switchboard/verilog/sim/switchboard_sim.py +49 -0
- switchboard/verilog/sim/umi_rx_sim.sv +61 -0
- switchboard/verilog/sim/umi_to_queue_sim.sv +66 -0
- switchboard/verilog/sim/umi_tx_sim.sv +61 -0
- switchboard/verilog/sim/xyce_intf.sv +67 -0
- switchboard/vpi/switchboard_vpi.cc +431 -0
- switchboard/vpi/xyce_vpi.cc +200 -0
- switchboard/warn.py +14 -0
- switchboard/xyce.py +27 -0
- switchboard_hw-0.3.0.dist-info/METADATA +303 -0
- switchboard_hw-0.3.0.dist-info/RECORD +99 -0
- switchboard_hw-0.3.0.dist-info/WHEEL +6 -0
- switchboard_hw-0.3.0.dist-info/entry_points.txt +6 -0
- switchboard_hw-0.3.0.dist-info/licenses/LICENSE +190 -0
- switchboard_hw-0.3.0.dist-info/top_level.txt +2 -0
switchboard/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
|
+
]
|