Skip to content

Commit 48a02cd

Browse files
committed
Add support for optional axis_tstrb
1 parent e816d6a commit 48a02cd

File tree

2 files changed

+51
-11
lines changed

2 files changed

+51
-11
lines changed

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ To receive data with an `AxiStreamSink` or `AxiStreamMonitor`, call `recv()`/`re
253253
* `tready`: indicates sink is ready for data; optional, assumed `1` when absent
254254
* `tlast`: marks the last cycle of a frame; optional, assumed `1` when absent
255255
* `tkeep`: qualifies data byte, data bus width must be evenly divisible by `tkeep` signal width; optional, assumed `1` when absent
256+
* `tstrb`: qualifies data byte, data bus width must be evenly divisible by `tstrb` signal width; optional, assumed equal to `tkeep` when absent
256257
* `tid`: ID signal, can be used for routing; optional, assumed `0` when absent
257258
* `tdest`: destination signal, can be used for routing; optional, assumed `0` when absent
258259
* `tuser`: additional user data; optional, assumed `0` when absent
@@ -266,7 +267,7 @@ To receive data with an `AxiStreamSink` or `AxiStreamMonitor`, call `recv()`/`re
266267
* _byte_size_: byte size (optional)
267268
* _byte_lanes_: byte lane count (optional)
268269

269-
Note: _byte_size_, _byte_lanes_, `len(tdata)`, and `len(tkeep)` are all related, in that _byte_lanes_ is set from `tkeep` if it is connected, and `byte_size*byte_lanes == len(tdata)`. So, if `tkeep` is connected, both _byte_size_ and _byte_lanes_ will be computed internally and cannot be overridden. If `tkeep` is not connected, then either _byte_size_ or _byte_lanes_ can be specified, and the other will be computed such that `byte_size*byte_lanes == len(tdata)`.
270+
Note: _byte_size_, _byte_lanes_, `len(tdata)`, `len(tkeep)`, and `len(tstrb)` are all related, in that _byte_lanes_ is set from `tkeep` or `tstrb` if either is connected, and `byte_size*byte_lanes == len(tdata)`. So, if either `tkeep` or `tstrb` is connected, both _byte_size_ and _byte_lanes_ will be computed internally and cannot be overridden. If `tkeep` and `tstrb` is not connected, then either _byte_size_ or _byte_lanes_ can be specified, and the other will be computed such that `byte_size*byte_lanes == len(tdata)`.
270271

271272
#### Attributes:
272273

@@ -302,12 +303,13 @@ The `AxiStreamBus` object is a container for the interface signals. Currently,
302303

303304
#### `AxiStreamFrame` object
304305

305-
The `AxiStreamFrame` object is a container for a frame to be transferred via AXI stream. The `tdata` field contains the packet data in the form of a list of bytes, which is either a `bytearray` if the byte size is 8 bits or a `list` of `int`s otherwise. `tkeep`, `tid`, `tdest`, and `tuser` can either be `None`, an `int`, or a `list` of `int`s.
306+
The `AxiStreamFrame` object is a container for a frame to be transferred via AXI stream. The `tdata` field contains the packet data in the form of a list of bytes, which is either a `bytearray` if the byte size is 8 bits or a `list` of `int`s otherwise. `tkeep`, `tstrb`, `tid`, `tdest`, and `tuser` can either be `None`, an `int`, or a `list` of `int`s.
306307

307308
Attributes:
308309

309310
* `tdata`: bytes, bytearray, or list
310311
* `tkeep`: tkeep field, optional; list, each entry qualifies the corresponding entry in `tdata`. Can be used to insert gaps on the source side.
312+
* `tstrb`: tstrb field, optional; list, each entry qualifies the corresponding entry in `tdata`. Used to signal padding bytes.
311313
* `tid`: tid field, optional; int or list with one entry per `tdata`, last value used per cycle when sending.
312314
* `tdest`: tdest field, optional; int or list with one entry per `tdata`, last value used per cycle when sending.
313315
* `tuser`: tuser field, optional; int or list with one entry per `tdata`, last value used per cycle when sending.
@@ -317,8 +319,8 @@ Attributes:
317319

318320
Methods:
319321

320-
* `normalize()`: pack `tkeep`, `tid`, `tdest`, and `tuser` to the same length as `tdata`, replicating last element if necessary, initialize `tkeep` to list of `1` and `tid`, `tdest`, and `tuser` to list of `0` if not specified.
321-
* `compact()`: remove `tdata`, `tid`, `tdest`, and `tuser` values based on `tkeep`, remove `tkeep`, compact `tid`, `tdest`, and `tuser` to an int if all values are identical.
322+
* `normalize()`: pack `tkeep`, `tstrb`, `tid`, `tdest`, and `tuser` to the same length as `tdata`, replicating last element if necessary, initialize `tkeep` to list of `1`, `tstrb` == `tkeep`, and `tid`, `tdest`, and `tuser` to list of `0` if not specified.
323+
* `compact()`: remove `tdata`, `tstrb`, `tid`, `tdest`, and `tuser` values based on `tkeep`, remove `tkeep`, compact `tid`, `tdest`, and `tuser` to an int if all values are identical.
322324

323325
### Address space abstraction
324326

cocotbext/axi/axis.py

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@
3535

3636

3737
class AxiStreamFrame:
38-
def __init__(self, tdata=b'', tkeep=None, tid=None, tdest=None, tuser=None, tx_complete=None):
38+
def __init__(self, tdata=b'', tkeep=None, tstrb=None, tid=None, tdest=None, tuser=None, tx_complete=None):
3939
self.tdata = bytearray()
4040
self.tkeep = None
41+
self.tstrb = None
4142
self.tid = None
4243
self.tdest = None
4344
self.tuser = None
@@ -52,6 +53,8 @@ def __init__(self, tdata=b'', tkeep=None, tid=None, tdest=None, tuser=None, tx_c
5253
self.tdata = list(tdata.tdata)
5354
if tdata.tkeep is not None:
5455
self.tkeep = list(tdata.tkeep)
56+
if tdata.tstrb is not None:
57+
self.tstrb = list(tdata.tstrb)
5558
if tdata.tid is not None:
5659
if type(tdata.tid) in (int, bool):
5760
self.tid = tdata.tid
@@ -73,12 +76,14 @@ def __init__(self, tdata=b'', tkeep=None, tid=None, tdest=None, tuser=None, tx_c
7376
elif type(tdata) in (bytes, bytearray):
7477
self.tdata = bytearray(tdata)
7578
self.tkeep = tkeep
79+
self.tstrb = tstrb
7680
self.tid = tid
7781
self.tdest = tdest
7882
self.tuser = tuser
7983
else:
8084
self.tdata = list(tdata)
8185
self.tkeep = tkeep
86+
self.tstrb = tstrb
8287
self.tid = tid
8388
self.tdest = tdest
8489
self.tuser = tuser
@@ -95,6 +100,11 @@ def normalize(self):
95100
else:
96101
self.tkeep = [1]*n
97102

103+
if self.tstrb is not None:
104+
self.tstrb = self.tstrb[:n] + [self.tstrb[-1]]*(n-len(self.tstrb))
105+
else:
106+
self.tstrb = self.tkeep
107+
98108
if self.tid is not None:
99109
if type(self.tid) in (int, bool):
100110
self.tid = [self.tid]*n
@@ -128,6 +138,8 @@ def compact(self):
128138
del self.tdata[k]
129139
if k < len(self.tkeep):
130140
del self.tkeep[k]
141+
if k < len(self.tstrb):
142+
del self.tstrb[k]
131143
if k < len(self.tid):
132144
del self.tid[k]
133145
if k < len(self.tdest):
@@ -172,6 +184,10 @@ def __eq__(self, other):
172184
if self.tkeep != other.tkeep:
173185
return False
174186

187+
if self.tstrb is not None and other.tstrb is not None:
188+
if self.tstrb != other.tstrb:
189+
return False
190+
175191
if self.tid is not None and other.tid is not None:
176192
if type(self.tid) in (int, bool) and type(other.tid) is list:
177193
for k in other.tid:
@@ -214,6 +230,7 @@ def __repr__(self):
214230
return (
215231
f"{type(self).__name__}(tdata={self.tdata!r}, "
216232
f"tkeep={self.tkeep!r}, "
233+
f"tstrb={self.tstrb!r}, "
217234
f"tid={self.tid!r}, "
218235
f"tdest={self.tdest!r}, "
219236
f"tuser={self.tuser!r}, "
@@ -234,7 +251,7 @@ def __bytes__(self):
234251
class AxiStreamBus(Bus):
235252

236253
_signals = ["tdata"]
237-
_optional_signals = ["tvalid", "tready", "tlast", "tkeep", "tid", "tdest", "tuser"]
254+
_optional_signals = ["tvalid", "tready", "tlast", "tkeep", "tstrb", "tid", "tdest", "tuser"]
238255

239256
def __init__(self, entity=None, prefix=None, **kwargs):
240257
super().__init__(entity, prefix, self._signals, optional_signals=self._optional_signals, **kwargs)
@@ -251,7 +268,7 @@ def from_prefix(cls, entity, prefix, **kwargs):
251268
class AxiStreamBase(Reset):
252269

253270
_signals = ["tdata"]
254-
_optional_signals = ["tvalid", "tready", "tlast", "tkeep", "tid", "tdest", "tuser"]
271+
_optional_signals = ["tvalid", "tready", "tlast", "tkeep", "tstrb", "tid", "tdest", "tuser"]
255272

256273
_type = "base"
257274

@@ -306,6 +323,14 @@ def __init__(self, bus, clock, reset=None, reset_active_level=True,
306323
self.byte_lanes = len(self.bus.tkeep)
307324
if byte_size is not None or byte_lanes is not None:
308325
raise ValueError("Cannot specify byte_size or byte_lanes if tkeep is connected")
326+
if hasattr(self.bus, "tstrb") and (len(self.bus.tstrb) != self.byte_lanes):
327+
raise ValueError("tstrb must be the same width as tkeep")
328+
329+
elif hasattr(self.bus, "tstrb"):
330+
self.byte_lanes = len(self.bus.tstrb)
331+
if byte_size is not None or byte_lanes is not None:
332+
raise ValueError("Cannot specify byte_size or byte_lanes if tstrb is connected")
333+
309334
else:
310335
if byte_lanes is not None:
311336
self.byte_lanes = byte_lanes
@@ -484,6 +509,8 @@ def _handle_reset(self, state):
484509
self.bus.tlast.value = 0
485510
if hasattr(self.bus, "tkeep"):
486511
self.bus.tkeep.value = 0
512+
if hasattr(self.bus, "tstrb"):
513+
self.bus.tstrb.value = 0
487514
if hasattr(self.bus, "tid"):
488515
self.bus.tid.value = 0
489516
if hasattr(self.bus, "tdest"):
@@ -505,6 +532,7 @@ async def _run(self):
505532
has_tvalid = hasattr(self.bus, "tvalid")
506533
has_tlast = hasattr(self.bus, "tlast")
507534
has_tkeep = hasattr(self.bus, "tkeep")
535+
has_tstrb = hasattr(self.bus, "tstrb")
508536
has_tid = hasattr(self.bus, "tid")
509537
has_tdest = hasattr(self.bus, "tdest")
510538
has_tuser = hasattr(self.bus, "tuser")
@@ -536,13 +564,15 @@ async def _run(self):
536564
tdata_val = 0
537565
tlast_val = 0
538566
tkeep_val = 0
567+
tstrb_val = 0
539568
tid_val = 0
540569
tdest_val = 0
541570
tuser_val = 0
542571

543572
for offset in range(self.byte_lanes):
544573
tdata_val |= (frame.tdata[frame_offset] & self.byte_mask) << (offset * self.byte_size)
545574
tkeep_val |= (frame.tkeep[frame_offset] & 1) << offset
575+
tstrb_val |= (frame.tstrb[frame_offset] & 1) << offset
546576
tid_val = frame.tid[frame_offset]
547577
tdest_val = frame.tdest[frame_offset]
548578
tuser_val = frame.tuser[frame_offset]
@@ -563,6 +593,8 @@ async def _run(self):
563593
self.bus.tlast.value = tlast_val
564594
if has_tkeep:
565595
self.bus.tkeep.value = tkeep_val
596+
if has_tstrb:
597+
self.bus.tstrb.value = tstrb_val
566598
if has_tid:
567599
self.bus.tid.value = tid_val
568600
if has_tdest:
@@ -673,6 +705,7 @@ async def _run(self):
673705
has_tvalid = hasattr(self.bus, "tvalid")
674706
has_tlast = hasattr(self.bus, "tlast")
675707
has_tkeep = hasattr(self.bus, "tkeep")
708+
has_tstrb = hasattr(self.bus, "tstrb")
676709
has_tid = hasattr(self.bus, "tid")
677710
has_tdest = hasattr(self.bus, "tdest")
678711
has_tuser = hasattr(self.bus, "tuser")
@@ -691,16 +724,18 @@ async def _run(self):
691724
if tready_sample and tvalid_sample:
692725
if not frame:
693726
if self.byte_size == 8:
694-
frame = AxiStreamFrame(bytearray(), [], [], [], [])
727+
frame = AxiStreamFrame(bytearray(), [], [], [], [], [])
695728
else:
696-
frame = AxiStreamFrame([], [], [], [], [])
729+
frame = AxiStreamFrame([], [], [], [], [], [])
697730
frame.sim_time_start = get_sim_time()
698731
self.active = True
699732

700733
for offset in range(self.byte_lanes):
701734
frame.tdata.append((self.bus.tdata.value.integer >> (offset * self.byte_size)) & self.byte_mask)
702735
if has_tkeep:
703736
frame.tkeep.append((self.bus.tkeep.value.integer >> offset) & 1)
737+
if has_tstrb:
738+
frame.tstrb.append((self.bus.tstrb.value.integer >> offset) & 1)
704739
if has_tid:
705740
frame.tid.append(self.bus.tid.value.integer)
706741
if has_tdest:
@@ -772,6 +807,7 @@ async def _run(self):
772807
has_tvalid = hasattr(self.bus, "tvalid")
773808
has_tlast = hasattr(self.bus, "tlast")
774809
has_tkeep = hasattr(self.bus, "tkeep")
810+
has_tstrb = hasattr(self.bus, "tstrb")
775811
has_tid = hasattr(self.bus, "tid")
776812
has_tdest = hasattr(self.bus, "tdest")
777813
has_tuser = hasattr(self.bus, "tuser")
@@ -792,16 +828,18 @@ async def _run(self):
792828
if tready_sample and tvalid_sample:
793829
if not frame:
794830
if self.byte_size == 8:
795-
frame = AxiStreamFrame(bytearray(), [], [], [], [])
831+
frame = AxiStreamFrame(bytearray(), [], [], [], [], [])
796832
else:
797-
frame = AxiStreamFrame([], [], [], [], [])
833+
frame = AxiStreamFrame([], [], [], [], [], [])
798834
frame.sim_time_start = get_sim_time()
799835
self.active = True
800836

801837
for offset in range(self.byte_lanes):
802838
frame.tdata.append((self.bus.tdata.value.integer >> (offset * self.byte_size)) & self.byte_mask)
803839
if has_tkeep:
804840
frame.tkeep.append((self.bus.tkeep.value.integer >> offset) & 1)
841+
if has_tstrb:
842+
frame.tstrb.append((self.bus.tstrb.value.integer >> offset) & 1)
805843
if has_tid:
806844
frame.tid.append(self.bus.tid.value.integer)
807845
if has_tdest:

0 commit comments

Comments
 (0)