eth: Support ordered sets in 10GBASE-R simulation model

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2026-06-18 21:59:43 -07:00
parent e39ac2fd9c
commit 3c2c86e35f

View File

@@ -71,6 +71,9 @@ class BaseRSerdesSource():
self.queue_occupancy_limit_bytes = -1
self.queue_occupancy_limit_frames = -1
self.os = None
self.os_sig = False
self.width = len(self.data)
self.byte_size = 8
self.byte_lanes = self.width // self.byte_size
@@ -191,10 +194,22 @@ class BaseRSerdesSource():
self.idle_event.set()
self.queue_occupancy_bytes = 0
self.queue_occupancy_frames = 0
self.os = None
self.os_sig = False
async def wait(self):
await self.idle_event.wait()
def set_seq_os(self, os=None):
self.set_os(os, False)
def set_sig_os(self, os=None):
self.set_os(os, True)
def set_os(self, os=None, sig=False):
self.os = os
self.os_sig = sig
async def _run(self):
frame = None
frame_offset = 0
@@ -326,132 +341,141 @@ class BaseRSerdesSource():
# clear counters
deficit_idle_cnt = 0
ifg_cnt = 0
self.active = False
self.idle_event.set()
if frame is not None:
dl = bytearray()
cl = []
dl = bytearray()
cl = []
for k in range(8):
if frame is not None:
d = frame.data[frame_offset]
if frame.sim_time_sfd is None and not in_pre:
frame.sim_time_sfd = sim_time + (clk_period // self.byte_lanes * k) - gbx_delay
if d == EthPre.SFD:
in_pre = False
dl.append(d)
cl.append(frame.ctrl[frame_offset])
frame_offset += 1
for k in range(8):
if frame is not None:
d = frame.data[frame_offset]
if frame.sim_time_sfd is None and not in_pre:
frame.sim_time_sfd = sim_time + (clk_period // self.byte_lanes * k) - gbx_delay
if d == EthPre.SFD:
in_pre = False
dl.append(d)
cl.append(frame.ctrl[frame_offset])
frame_offset += 1
if frame_offset >= len(frame.data):
ifg_cnt = max(self.ifg - (8-k), 0)
frame.sim_time_end = sim_time + (clk_period // self.byte_lanes * k) - gbx_delay
frame.handle_tx_complete()
frame = None
self.current_frame = None
else:
dl.append(XgmiiCtrl.IDLE)
cl.append(1)
# remap control characters
ctrl = sum(xgmii_ctrl_to_baser_mapping.get(d, BaseRCtrl.ERROR) << i*7 for i, d in enumerate(dl))
if not any(cl):
# data
hdr = BaseRSync.DATA
data = int.from_bytes(dl, 'little')
if frame_offset >= len(frame.data):
ifg_cnt = max(self.ifg - (8-k), 0)
frame.sim_time_end = sim_time + (clk_period // self.byte_lanes * k) - gbx_delay
frame.handle_tx_complete()
frame = None
self.current_frame = None
else:
# control
hdr = BaseRSync.CTRL
if cl[0] and dl[0] == XgmiiCtrl.START and not any(cl[1:]):
# start in lane 0
data = BaseRBlockType.START_0
for i in range(1, 8):
data |= dl[i] << i*8
elif cl[4] and dl[4] == XgmiiCtrl.START and not any(cl[5:]):
# start in lane 4
if cl[0] and (dl[0] == XgmiiCtrl.SEQ_OS or dl[0] == XgmiiCtrl.SIG_OS) and not any(cl[1:4]):
# ordered set in lane 0
data = BaseRBlockType.OS_START
for i in range(1, 4):
data |= dl[i] << i*8
if dl[0] == XgmiiCtrl.SIG_OS:
# signal ordered set
data |= BaseRO.SIG_OS << 32
else:
# other control
data = BaseRBlockType.START_4 | (ctrl & 0xfffffff) << 8
dl.append(XgmiiCtrl.IDLE)
cl.append(1)
for i in range(5, 8):
data |= dl[i] << i*8
elif cl[0] and (dl[0] == XgmiiCtrl.SEQ_OS or dl[0] == XgmiiCtrl.SIG_OS) and not any(cl[1:4]):
# ordered set in lane 0
if cl[4] and (dl[4] == XgmiiCtrl.SEQ_OS or dl[4] == XgmiiCtrl.SIG_OS) and not any(cl[5:8]):
# ordered set in lane 4
data = BaseRBlockType.OS_04
for i in range(5, 8):
data |= dl[i] << i*8
if dl[4] == XgmiiCtrl.SIG_OS:
# signal ordered set
data |= BaseRO.SIG_OS << 36
# replace idles with ordered sets
if self.os is not None:
for k in [0, 4]:
if all(cl[k:k+4]) and all(d == XgmiiCtrl.IDLE for d in dl[k:k+4]):
if self.os_sig:
self.log.info("TX signal ordered set: 0x%06x", self.os)
dl[k] = XgmiiCtrl.SIG_OS
else:
data = BaseRBlockType.OS_0 | (ctrl & 0xfffffff) << 40
self.log.info("TX sequence ordered set: 0x%06x", self.os)
dl[k] = XgmiiCtrl.SEQ_OS
dl[k+1:k+4] = self.os.to_bytes(3, 'big')
cl[k+1:k+4] = [0, 0, 0]
# remap control characters
ctrl = sum(xgmii_ctrl_to_baser_mapping.get(d, BaseRCtrl.ERROR) << i*7 for i, d in enumerate(dl))
if not any(cl):
# data
hdr = BaseRSync.DATA
data = int.from_bytes(dl, 'little')
else:
# control
hdr = BaseRSync.CTRL
if cl[0] and dl[0] == XgmiiCtrl.START and not any(cl[1:]):
# start in lane 0
data = BaseRBlockType.START_0
for i in range(1, 8):
data |= dl[i] << i*8
elif cl[4] and dl[4] == XgmiiCtrl.START and not any(cl[5:]):
# start in lane 4
if cl[0] and (dl[0] == XgmiiCtrl.SEQ_OS or dl[0] == XgmiiCtrl.SIG_OS) and not any(cl[1:4]):
# ordered set in lane 0
data = BaseRBlockType.OS_START
for i in range(1, 4):
data |= dl[i] << i*8
if dl[0] == XgmiiCtrl.SIG_OS:
# signal ordered set
data |= BaseRO.SIG_OS << 32
elif cl[4] and (dl[4] == XgmiiCtrl.SEQ_OS or dl[4] == XgmiiCtrl.SIG_OS) and not any(cl[5:8]):
else:
# other control
data = BaseRBlockType.START_4 | (ctrl & 0xfffffff) << 8
for i in range(5, 8):
data |= dl[i] << i*8
elif cl[0] and (dl[0] == XgmiiCtrl.SEQ_OS or dl[0] == XgmiiCtrl.SIG_OS) and not any(cl[1:4]):
# ordered set in lane 0
if cl[4] and (dl[4] == XgmiiCtrl.SEQ_OS or dl[4] == XgmiiCtrl.SIG_OS) and not any(cl[5:8]):
# ordered set in lane 4
data = BaseRBlockType.OS_4 | (ctrl & 0xfffffff) << 8
data = BaseRBlockType.OS_04
for i in range(5, 8):
data |= dl[i] << i*8
if dl[4] == XgmiiCtrl.SIG_OS:
# signal ordered set
data |= BaseRO.SIG_OS << 36
elif cl[0] and dl[0] == XgmiiCtrl.TERM:
# terminate in lane 0
data = BaseRBlockType.TERM_0 | (ctrl & 0xffffffffffff80) << 8
elif cl[1] and dl[1] == XgmiiCtrl.TERM and not cl[0]:
# terminate in lane 1
data = BaseRBlockType.TERM_1 | (ctrl & 0xffffffffffc000) << 8 | dl[0] << 8
elif cl[2] and dl[2] == XgmiiCtrl.TERM and not any(cl[0:2]):
# terminate in lane 2
data = BaseRBlockType.TERM_2 | (ctrl & 0xffffffffe00000) << 8
for i in range(2):
data |= dl[i] << ((i+1)*8)
elif cl[3] and dl[3] == XgmiiCtrl.TERM and not any(cl[0:3]):
# terminate in lane 3
data = BaseRBlockType.TERM_3 | (ctrl & 0xfffffff0000000) << 8
for i in range(3):
data |= dl[i] << ((i+1)*8)
elif cl[4] and dl[4] == XgmiiCtrl.TERM and not any(cl[0:4]):
# terminate in lane 4
data = BaseRBlockType.TERM_4 | (ctrl & 0xfffff800000000) << 8
for i in range(4):
data |= dl[i] << ((i+1)*8)
elif cl[5] and dl[5] == XgmiiCtrl.TERM and not any(cl[0:5]):
# terminate in lane 5
data = BaseRBlockType.TERM_5 | (ctrl & 0xfffc0000000000) << 8
for i in range(5):
data |= dl[i] << ((i+1)*8)
elif cl[6] and dl[6] == XgmiiCtrl.TERM and not any(cl[0:6]):
# terminate in lane 6
data = BaseRBlockType.TERM_6 | (ctrl & 0xfe000000000000) << 8
for i in range(6):
data |= dl[i] << ((i+1)*8)
elif cl[7] and dl[7] == XgmiiCtrl.TERM and not any(cl[0:7]):
# terminate in lane 7
data = BaseRBlockType.TERM_7
for i in range(7):
data |= dl[i] << ((i+1)*8)
else:
# all control
data = BaseRBlockType.CTRL | ctrl << 8
else:
data = BaseRBlockType.CTRL
hdr = BaseRSync.CTRL
self.active = False
self.idle_event.set()
data = BaseRBlockType.OS_0 | (ctrl & 0xfffffff) << 40
for i in range(1, 4):
data |= dl[i] << i*8
if dl[0] == XgmiiCtrl.SIG_OS:
# signal ordered set
data |= BaseRO.SIG_OS << 32
elif cl[4] and (dl[4] == XgmiiCtrl.SEQ_OS or dl[4] == XgmiiCtrl.SIG_OS) and not any(cl[5:8]):
# ordered set in lane 4
data = BaseRBlockType.OS_4 | (ctrl & 0xfffffff) << 8
for i in range(5, 8):
data |= dl[i] << i*8
if dl[4] == XgmiiCtrl.SIG_OS:
# signal ordered set
data |= BaseRO.SIG_OS << 36
elif cl[0] and dl[0] == XgmiiCtrl.TERM:
# terminate in lane 0
data = BaseRBlockType.TERM_0 | (ctrl & 0xffffffffffff80) << 8
elif cl[1] and dl[1] == XgmiiCtrl.TERM and not cl[0]:
# terminate in lane 1
data = BaseRBlockType.TERM_1 | (ctrl & 0xffffffffffc000) << 8 | dl[0] << 8
elif cl[2] and dl[2] == XgmiiCtrl.TERM and not any(cl[0:2]):
# terminate in lane 2
data = BaseRBlockType.TERM_2 | (ctrl & 0xffffffffe00000) << 8
for i in range(2):
data |= dl[i] << ((i+1)*8)
elif cl[3] and dl[3] == XgmiiCtrl.TERM and not any(cl[0:3]):
# terminate in lane 3
data = BaseRBlockType.TERM_3 | (ctrl & 0xfffffff0000000) << 8
for i in range(3):
data |= dl[i] << ((i+1)*8)
elif cl[4] and dl[4] == XgmiiCtrl.TERM and not any(cl[0:4]):
# terminate in lane 4
data = BaseRBlockType.TERM_4 | (ctrl & 0xfffff800000000) << 8
for i in range(4):
data |= dl[i] << ((i+1)*8)
elif cl[5] and dl[5] == XgmiiCtrl.TERM and not any(cl[0:5]):
# terminate in lane 5
data = BaseRBlockType.TERM_5 | (ctrl & 0xfffc0000000000) << 8
for i in range(5):
data |= dl[i] << ((i+1)*8)
elif cl[6] and dl[6] == XgmiiCtrl.TERM and not any(cl[0:6]):
# terminate in lane 6
data = BaseRBlockType.TERM_6 | (ctrl & 0xfe000000000000) << 8
for i in range(6):
data |= dl[i] << ((i+1)*8)
elif cl[7] and dl[7] == XgmiiCtrl.TERM and not any(cl[0:7]):
# terminate in lane 7
data = BaseRBlockType.TERM_7
for i in range(7):
data |= dl[i] << ((i+1)*8)
else:
# all control
data = BaseRBlockType.CTRL | ctrl << 8
if self.scramble:
# 64b/66b scrambler
@@ -537,6 +561,9 @@ class BaseRSerdesSink:
self.queue_occupancy_bytes = 0
self.queue_occupancy_frames = 0
self.os = None
self.os_sig = False
self.width = len(self.data)
self.byte_size = 8
self.byte_lanes = self.width // self.byte_size
@@ -604,6 +631,11 @@ class BaseRSerdesSink:
continue
self.gbx_bit_cnt += in_bits
def get_os(self):
ret = (self.os, self.os_sig)
self.os = None
return ret
def _recv(self, frame, compact=True):
if self.queue.empty():
self.active_event.clear()
@@ -764,6 +796,7 @@ class BaseRSerdesSink:
dl = bytearray()
cl = []
os = False
if hdr == BaseRSync.DATA:
# data
dl = db
@@ -777,11 +810,13 @@ class BaseRSerdesSink:
# D7 D6 D5 O4 C3 C2 C1 C0 BT
dl = ctrl[0:4]
cl = [1]*4
os = True
if (db[4] >> 4) & 0xf == BaseRO.SEQ_OS:
dl.append(XgmiiCtrl.SEQ_OS)
elif (db[4] >> 4) & 0xf == BaseRO.SIG_OS:
dl.append(XgmiiCtrl.SIG_OS)
else:
self.log.warning("Invalid O code")
dl.append(XgmiiCtrl.ERROR)
cl.append(1)
dl += db[5:]
@@ -796,11 +831,13 @@ class BaseRSerdesSink:
cl += [0]*3
elif db[0] == BaseRBlockType.OS_START:
# D7 D6 D5 O0 D3 D2 D1 BT
os = True
if db[4] & 0xf == BaseRO.SEQ_OS:
dl.append(XgmiiCtrl.SEQ_OS)
elif db[4] & 0xf == BaseRO.SIG_OS:
dl.append(XgmiiCtrl.SIG_OS)
else:
self.log.warning("Invalid O code")
dl.append(XgmiiCtrl.ERROR)
cl.append(1)
dl += db[1:4]
@@ -811,11 +848,13 @@ class BaseRSerdesSink:
cl += [0]*3
elif db[0] == BaseRBlockType.OS_04:
# D7 D6 D5 O4 O0 D3 D2 D1 BT
os = True
if db[4] & 0xf == BaseRO.SEQ_OS:
dl.append(XgmiiCtrl.SEQ_OS)
elif db[4] & 0xf == BaseRO.SIG_OS:
dl.append(XgmiiCtrl.SIG_OS)
else:
self.log.warning("Invalid O code")
dl.append(XgmiiCtrl.ERROR)
cl.append(1)
dl += db[1:4]
@@ -825,6 +864,7 @@ class BaseRSerdesSink:
elif (db[4] >> 4) & 0xf == BaseRO.SIG_OS:
dl.append(XgmiiCtrl.SIG_OS)
else:
self.log.warning("Invalid O code")
dl.append(XgmiiCtrl.ERROR)
cl.append(1)
dl += db[5:]
@@ -837,11 +877,13 @@ class BaseRSerdesSink:
cl += [0]*7
elif db[0] == BaseRBlockType.OS_0:
# C7 C6 C5 C4 O0 D3 D2 D1 BT
os = True
if db[4] & 0xf == BaseRO.SEQ_OS:
dl.append(XgmiiCtrl.SEQ_OS)
elif db[4] & 0xf == BaseRO.SIG_OS:
dl.append(XgmiiCtrl.SEQ_OS)
dl.append(XgmiiCtrl.SIG_OS)
else:
self.log.warning("Invalid O code")
dl.append(XgmiiCtrl.ERROR)
cl.append(1)
dl += db[1:4]
@@ -877,6 +919,18 @@ class BaseRSerdesSink:
dl = [XgmiiCtrl.ERROR]*8
cl = [1]*8
# extract ordered sets
if os:
for k in [0, 4]:
if cl[k] and dl[k] == XgmiiCtrl.SEQ_OS:
self.os = int.from_bytes(db[k+1:k+4], 'big')
self.os_sig = False
self.log.info("RX sequence ordered set: 0x%06x", self.os)
elif cl[k] and dl[k] == XgmiiCtrl.SIG_OS:
self.os = int.from_bytes(db[k+1:k+4], 'big')
self.os_sig = True
self.log.info("RX signal ordered set: 0x%06x", self.os)
for k in range(8):
d_val = dl[k]
c_val = cl[k]