eth: Add support for synchronous gearbox to PHY, MAC+PHY, and GT wrappers

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2025-05-30 21:14:54 -07:00
parent f31ba113d2
commit e4762b7a8c
76 changed files with 3853 additions and 1090 deletions

View File

@@ -24,19 +24,24 @@ from cocotbext.eth import XgmiiFrame
class BaseRSerdesSource():
def __init__(self, data, hdr, clock, enable=None, slip=None, scramble=True, reverse=False, *args, **kwargs):
def __init__(self, data, hdr, clock, enable=None, slip=None, data_valid=None, hdr_valid=None,
gbx_start=None, scramble=True, reverse=False, gbx_cfg=None, *args, **kwargs):
self.log = logging.getLogger(f"cocotb.{data._path}")
self.data = data
self.hdr = hdr
self.clock = clock
self.enable = enable
self.slip = slip
self.data_valid = data_valid
self.hdr_valid = hdr_valid
self.gbx_start = gbx_start
self.scramble = scramble
self.reverse = reverse
self.log.info("BASE-R serdes source")
self.log.info("Copyright (c) 2021 Alex Forencich")
self.log.info("https://github.com/alexforencich/verilog-ethernet")
self.log.info("Copyright (c) 2021-2025 FPGA Ninja, LLC")
self.log.info("https://github.com/fpganinja/taxi")
super().__init__(*args, **kwargs)
@@ -53,6 +58,13 @@ class BaseRSerdesSource():
self.bit_offset = 0
self.gbx_seq = 0
self.gbx_seq_len = None
self.gbx_seq_stall = None
self.gbx_in_bits = 66
self.gbx_out_bits = 66
self.gbx_bit_cnt = 0
self.queue_occupancy_bytes = 0
self.queue_occupancy_frames = 0
@@ -71,11 +83,64 @@ class BaseRSerdesSource():
self.log.info(" Enable scrambler: %s", self.scramble)
self.log.info(" Bit reverse: %s", self.reverse)
if gbx_cfg:
self.set_gbx_cfg(*gbx_cfg)
self.data.setimmediatevalue(0)
if self.data_valid is not None:
self.data_valid.setimmediatevalue(0)
self.hdr.setimmediatevalue(0)
if self.hdr_valid is not None:
self.hdr_valid.setimmediatevalue(0)
if self.gbx_start is not None:
self.gbx_start.setimmediatevalue(0)
self._run_cr = cocotb.start_soon(self._run())
def set_gbx_cfg(self, seq_len=None, seq_stall=None):
self.log.info("Set gearbox configuration")
if seq_len is None:
self.log.info("Gearbox disabled")
self.gbx_bit_cnt = 0
self.gbx_seq_len = None
self.gbx_seq_stall = None
self.gbx_in_bits = 66
self.gbx_out_bits = 66
self.gbx_seq = 0
seq_stall = sorted(list(set(seq_stall)))
for x in seq_stall:
assert 0 <= x < seq_len
self.log.info(" Sequence length: %d cycles", seq_len)
self.log.info(" Stall cycles: %s", seq_stall)
out_bits = 66
in_cycles = seq_len
out_cycles = in_cycles - len(seq_stall)
in_bits = (out_bits * out_cycles) // in_cycles
self.log.info(" Input: %d bits (%d cycles)", in_bits, in_cycles)
self.log.info(" Output: %d bits (%d cycles)", out_bits, out_cycles)
self.log.info(" Gearbox ratio: %d:%d", in_bits, out_bits)
assert in_cycles*in_bits == out_cycles*out_bits
self.gbx_seq = 0
self.gbx_seq_len = seq_len
self.gbx_seq_stall = set(seq_stall)
self.gbx_in_bits = in_bits
self.gbx_out_bits = out_bits
self.gbx_bit_cnt = 0
for k in range(self.gbx_seq_len):
self.gbx_bit_cnt += in_bits
if k in self.gbx_seq_stall:
continue
self.gbx_bit_cnt = max(self.gbx_bit_cnt - out_bits, 0)
async def send(self, frame):
while self.full():
self.dequeue_event.clear()
@@ -134,13 +199,52 @@ class BaseRSerdesSource():
last_d = 0
self.active = False
clk_period = 0
last_clk = 0
gbx_delay = 0
while True:
await RisingEdge(self.clock)
if not clk_period:
if last_clk:
clk_period = get_sim_time() - last_clk
else:
last_clk = get_sim_time()
# clock enable
if self.enable is not None and not self.enable.value:
continue
# gearbox sequence
if self.gbx_seq_len:
self.gbx_seq = (self.gbx_seq + 1) % self.gbx_seq_len
if self.gbx_start is not None:
self.gbx_start.value = (self.gbx_seq == 0)
self.gbx_bit_cnt += self.gbx_in_bits
# stall cycle
if self.gbx_seq in self.gbx_seq_stall:
self.data.value = 0
if self.data_valid is not None:
self.data_valid.value = 0
self.hdr.value = 0
if self.hdr_valid is not None:
self.hdr_valid.value = 0
continue
self.gbx_bit_cnt = max(self.gbx_bit_cnt - self.gbx_out_bits, 0)
gbx_delay = (self.gbx_bit_cnt * clk_period) // self.gbx_in_bits
else:
self.gbx_seq = 0
self.gbx_bit_cnt = 0
gbx_delay = 0
if self.gbx_start is not None:
self.gbx_start.value = 0
if ifg_cnt + deficit_idle_cnt > self.byte_lanes-1 or (not self.enable_dic and ifg_cnt > 4):
# in IFG
ifg_cnt = ifg_cnt - self.byte_lanes
@@ -158,7 +262,7 @@ class BaseRSerdesSource():
self.queue_occupancy_bytes -= len(frame)
self.queue_occupancy_frames -= 1
self.current_frame = frame
frame.sim_time_start = get_sim_time()
frame.sim_time_start = get_sim_time() - gbx_delay
frame.sim_time_sfd = None
frame.sim_time_end = None
self.log.info("TX frame: %s", frame)
@@ -201,14 +305,14 @@ class BaseRSerdesSource():
if frame is not None:
d = frame.data[frame_offset]
if frame.sim_time_sfd is None and d == EthPre.SFD:
frame.sim_time_sfd = get_sim_time()
frame.sim_time_sfd = get_sim_time() - gbx_delay
dl.append(d)
cl.append(frame.ctrl[frame_offset])
frame_offset += 1
if frame_offset >= len(frame.data):
ifg_cnt = max(self.ifg - (self.byte_lanes-k), 0)
frame.sim_time_end = get_sim_time()
frame.sim_time_end = get_sim_time() - gbx_delay
frame.handle_tx_complete()
frame = None
self.current_frame = None
@@ -349,23 +453,35 @@ class BaseRSerdesSource():
hdr = sum(1 << (1-i) for i in range(2) if (hdr >> i) & 1)
self.data.value = data
if self.data_valid is not None:
self.data_valid.value = 1
self.hdr.value = hdr
if self.hdr_valid is not None:
self.hdr_valid.value = 1
class BaseRSerdesSink:
def __init__(self, data, hdr, clock, enable=None, scramble=True, reverse=False, *args, **kwargs):
def __init__(self, data, hdr, clock, enable=None, data_valid=None, hdr_valid=None,
gbx_req_start=None, gbx_req_stall=None, gbx_start=None,
scramble=True, reverse=False, gbx_cfg=None, *args, **kwargs):
self.log = logging.getLogger(f"cocotb.{data._path}")
self.data = data
self.hdr = hdr
self.clock = clock
self.enable = enable
self.data_valid = data_valid
self.hdr_valid = hdr_valid
self.gbx_req_start = gbx_req_start
self.gbx_req_stall = gbx_req_stall
self.gbx_start = gbx_start
self.scramble = scramble
self.reverse = reverse
self.log.info("BASE-R serdes sink")
self.log.info("Copyright (c) 2021 Alex Forencich")
self.log.info("https://github.com/alexforencich/verilog-ethernet")
self.log.info("Copyright (c) 2021-2025 FPGA Ninja, LLC")
self.log.info("https://github.com/fpganinja/taxi")
super().__init__(*args, **kwargs)
@@ -373,6 +489,14 @@ class BaseRSerdesSink:
self.queue = Queue()
self.active_event = Event()
self.gbx_seq = 0
self.gbx_seq_gen = 0
self.gbx_seq_len = None
self.gbx_seq_stall = None
self.gbx_in_bits = 66
self.gbx_out_bits = 66
self.gbx_bit_cnt = 0
self.queue_occupancy_bytes = 0
self.queue_occupancy_frames = 0
@@ -388,8 +512,57 @@ class BaseRSerdesSink:
self.log.info(" Enable scrambler: %s", self.scramble)
self.log.info(" Bit reverse: %s", self.reverse)
if gbx_cfg:
self.set_gbx_cfg(*gbx_cfg)
if self.gbx_req_start is not None:
self.gbx_req_start.setimmediatevalue(0)
if self.gbx_req_stall is not None:
self.gbx_req_stall.setimmediatevalue(0)
self._run_cr = cocotb.start_soon(self._run())
def set_gbx_cfg(self, seq_len=None, seq_stall=None):
self.log.info("Set gearbox configuration")
if seq_len is None:
self.log.info("Gearbox disabled")
self.gbx_seq_len = None
self.gbx_seq_stall = None
seq_stall = sorted(list(set(seq_stall)))
for x in seq_stall:
assert 0 <= x < seq_len
self.log.info(" Sequence length: %d cycles", seq_len)
self.log.info(" Stall cycles: %s", seq_stall)
in_bits = 66
out_cycles = seq_len
in_cycles = out_cycles - len(seq_stall)
out_bits = (in_bits * in_cycles) // out_cycles
self.log.info(" Input: %d bits (%d cycles)", in_bits, in_cycles)
self.log.info(" Output: %d bits (%d cycles)", out_bits, out_cycles)
self.log.info(" Gearbox ratio: %d:%d", in_bits, out_bits)
assert in_cycles*in_bits == out_cycles*out_bits
self.gbx_seq = 0
self.gbx_seq_gen = 0
self.gbx_seq_len = seq_len
self.gbx_seq_stall = set(seq_stall)
self.gbx_in_bits = in_bits
self.gbx_out_bits = out_bits
self.gbx_bit_cnt = 0
for k in range(self.gbx_seq_len):
self.gbx_bit_cnt = max(self.gbx_bit_cnt - out_bits, 0)
if k in self.gbx_seq_stall:
continue
self.gbx_bit_cnt += in_bits
def _recv(self, frame, compact=True):
if self.queue.empty():
self.active_event.clear()
@@ -436,13 +609,69 @@ class BaseRSerdesSink:
scrambler_state = 0
self.active = False
clk_period = 0
last_clk = 0
gbx_delay = 0
sync_bad = True
while True:
await RisingEdge(self.clock)
if not clk_period:
if last_clk:
clk_period = get_sim_time() - last_clk
else:
last_clk = get_sim_time()
# clock enable
if self.enable is not None and not self.enable.value:
continue
# gearbox sequence
if self.gbx_seq_len:
# generation
self.gbx_seq_gen = (self.gbx_seq_gen + 1) % self.gbx_seq_len
if self.gbx_req_start is not None:
self.gbx_req_start.value = (self.gbx_seq_gen == 0)
# stall cycle
if self.gbx_req_stall is not None:
self.gbx_req_stall.value = (self.gbx_seq_gen in self.gbx_seq_stall)
# sync
self.gbx_seq = (self.gbx_seq + 1) % self.gbx_seq_len
if self.gbx_start is not None:
if self.gbx_start.value.integer:
self.gbx_seq = 0
self.gbx_bit_cnt = max(self.gbx_bit_cnt - self.gbx_out_bits, 0)
if self.gbx_seq in self.gbx_seq_stall:
continue
self.gbx_bit_cnt += self.gbx_in_bits
gbx_delay = (self.gbx_bit_cnt * clk_period) // self.gbx_out_bits
else:
self.gbx_seq = 0
self.gbx_seq_gen = 0
self.gbx_bit_cnt = 0
gbx_delay = 0
if self.gbx_start is not None:
self.gbx_start.value = 1
if self.data_valid is not None:
if not self.data_valid.value.integer:
# stall
if self.gbx_seq_len and not sync_bad:
sync_bad = True
self.log.warning("Data not valid outside of gearbox stall cycle")
continue
sync_bad = False
data = self.data.value.integer
hdr = self.hdr.value.integer
@@ -590,7 +819,7 @@ class BaseRSerdesSink:
if c_val and d_val == XgmiiCtrl.START:
# start
frame = XgmiiFrame(bytearray([EthPre.PRE]), [0])
frame.sim_time_start = get_sim_time()
frame.sim_time_start = get_sim_time() + gbx_delay
frame.start_lane = offset
else:
if c_val:
@@ -601,7 +830,7 @@ class BaseRSerdesSink:
frame.ctrl.append(c_val)
frame.compact()
frame.sim_time_end = get_sim_time()
frame.sim_time_end = get_sim_time() + gbx_delay
self.log.info("RX frame: %s", frame)
self.queue_occupancy_bytes += len(frame)
@@ -613,7 +842,7 @@ class BaseRSerdesSink:
frame = None
else:
if frame.sim_time_sfd is None and d_val == EthPre.SFD:
frame.sim_time_sfd = get_sim_time()
frame.sim_time_sfd = get_sim_time() + gbx_delay
frame.data.append(d_val)
frame.ctrl.append(c_val)

View File

@@ -36,6 +36,7 @@ VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
# module parameters
export PARAM_DATA_W := 64
export PARAM_HDR_W := 2
export PARAM_GBX_IF_EN := 1
export PARAM_PTP_TS_EN := 1
export PARAM_PTP_TS_FMT_TOD := 1

View File

@@ -15,6 +15,7 @@ import os
import sys
import cocotb_test.simulator
import pytest
import cocotb
from cocotb.clock import Clock
@@ -37,15 +38,28 @@ except ImportError:
class TB:
def __init__(self, dut):
def __init__(self, dut, gbx_cfg=None):
self.dut = dut
self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG)
cocotb.start_soon(Clock(dut.clk, 6.4, units="ns").start())
if gbx_cfg:
self.clk_period = 6.206
else:
self.clk_period = 6.4
self.source = BaseRSerdesSource(dut.encoded_rx_data, dut.encoded_rx_hdr, dut.clk, scramble=False)
cocotb.start_soon(Clock(dut.clk, self.clk_period, units="ns").start())
self.source = BaseRSerdesSource(
data=dut.encoded_rx_data,
data_valid=dut.encoded_rx_data_valid,
hdr=dut.encoded_rx_hdr,
hdr_valid=dut.encoded_rx_hdr_valid,
clock=dut.clk,
scramble=False,
gbx_cfg=gbx_cfg
)
self.sink = AxiStreamSink(AxiStreamBus.from_entity(dut.m_axis_rx), dut.clk, dut.rst)
self.ptp_clock = PtpClockSimTime(ts_tod=dut.ptp_ts, clock=dut.clk)
@@ -96,9 +110,9 @@ class TB:
self.stats[stat] += int(getattr(self.dut, stat).value)
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12):
async def run_test(dut, gbx_cfg=None, payload_lengths=None, payload_data=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.source.ifg = ifg
tb.dut.cfg_rx_max_pkt_len.value = 9218
@@ -138,7 +152,8 @@ async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12):
assert rx_frame.tdata == test_data
assert frame_error == 0
assert abs(ptp_ts_ns - tx_frame_sfd_ns - 6.4) < 0.01
if gbx_cfg is None:
assert abs(ptp_ts_ns - tx_frame_sfd_ns - 6.4) < 0.01
assert tb.sink.empty()
@@ -161,13 +176,13 @@ async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12):
assert tb.stats["stat_rx_err_framing"] == 0
assert tb.stats["stat_rx_err_preamble"] == 0
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
for k in range(10):
await RisingEdge(dut.clk)
async def run_test_oversize(dut, ifg=12):
async def run_test_oversize(dut, gbx_cfg=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.source.ifg = ifg
tb.dut.cfg_rx_max_pkt_len.value = 1518
@@ -250,8 +265,8 @@ async def run_test_oversize(dut, ifg=12):
assert tb.stats["stat_rx_err_framing"] == 0
assert tb.stats["stat_rx_err_preamble"] == 0
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
for k in range(10):
await RisingEdge(dut.clk)
def size_list():
@@ -268,14 +283,22 @@ def cycle_en():
if cocotb.SIM_NAME:
gbx_cfgs = [None]
if cocotb.top.GBX_IF_EN.value:
gbx_cfgs.append((33, [32]))
gbx_cfgs.append((66, [64, 65]))
factory = TestFactory(run_test)
factory.add_option("payload_lengths", [size_list])
factory.add_option("payload_data", [incrementing_payload])
factory.add_option("ifg", list(range(0, 13)))
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
factory = TestFactory(run_test_oversize)
factory.add_option("ifg", list(range(0, 13)))
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
@@ -300,7 +323,8 @@ def process_f_files(files):
return list(lst.values())
def test_taxi_axis_baser_rx_64(request):
@pytest.mark.parametrize("gbx_en", [1, 0])
def test_taxi_axis_baser_rx_64(request, gbx_en):
dut = "taxi_axis_baser_rx_64"
module = os.path.splitext(os.path.basename(__file__))[0]
toplevel = module
@@ -318,6 +342,7 @@ def test_taxi_axis_baser_rx_64(request):
parameters['DATA_W'] = 64
parameters['HDR_W'] = 2
parameters['GBX_IF_EN'] = gbx_en
parameters['PTP_TS_EN'] = 1
parameters['PTP_TS_FMT_TOD'] = 1
parameters['PTP_TS_W'] = 96 if parameters['PTP_TS_FMT_TOD'] else 64

View File

@@ -20,6 +20,7 @@ module test_taxi_axis_baser_rx_64 #
/* verilator lint_off WIDTHTRUNC */
parameter DATA_W = 64,
parameter HDR_W = 2,
parameter logic GBX_IF_EN = 1'b0,
parameter logic PTP_TS_EN = 1'b0,
parameter logic PTP_TS_FMT_TOD = 1'b1,
parameter PTP_TS_W = PTP_TS_FMT_TOD ? 96 : 64
@@ -33,7 +34,9 @@ logic clk;
logic rst;
logic [DATA_W-1:0] encoded_rx_data;
logic encoded_rx_data_valid;
logic [HDR_W-1:0] encoded_rx_hdr;
logic encoded_rx_hdr_valid;
taxi_axis_if #(.DATA_W(DATA_W), .USER_EN(1), .USER_W(USER_W)) m_axis_rx();
@@ -62,6 +65,7 @@ logic stat_rx_err_preamble;
taxi_axis_baser_rx_64 #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.GBX_IF_EN(GBX_IF_EN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
.PTP_TS_W(PTP_TS_W)
@@ -74,7 +78,9 @@ uut (
* 10GBASE-R encoded input
*/
.encoded_rx_data(encoded_rx_data),
.encoded_rx_data_valid(encoded_rx_data_valid),
.encoded_rx_hdr(encoded_rx_hdr),
.encoded_rx_hdr_valid(encoded_rx_hdr_valid),
/*
* AXI4-Stream output (source)

View File

@@ -36,6 +36,8 @@ VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
# module parameters
export PARAM_DATA_W := 64
export PARAM_HDR_W := 2
export PARAM_GBX_IF_EN := 1
export PARAM_GBX_CNT := 1
export PARAM_PADDING_EN := 1
export PARAM_DIC_EN := 1
export PARAM_MIN_FRAME_LEN := 64

View File

@@ -38,16 +38,32 @@ except ImportError:
class TB:
def __init__(self, dut):
def __init__(self, dut, gbx_cfg=None):
self.dut = dut
self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG)
cocotb.start_soon(Clock(dut.clk, 6.4, units="ns").start())
if gbx_cfg:
self.clk_period = 6.206
else:
self.clk_period = 6.4
cocotb.start_soon(Clock(dut.clk, self.clk_period, units="ns").start())
self.source = AxiStreamSource(AxiStreamBus.from_entity(dut.s_axis_tx), dut.clk, dut.rst)
self.sink = BaseRSerdesSink(dut.encoded_tx_data, dut.encoded_tx_hdr, dut.clk, scramble=False)
self.sink = BaseRSerdesSink(
data=dut.encoded_tx_data,
data_valid=dut.encoded_tx_data_valid,
hdr=dut.encoded_tx_hdr,
hdr_valid=dut.encoded_tx_hdr_valid,
gbx_req_start=dut.tx_gbx_req_start,
gbx_req_stall=dut.tx_gbx_req_stall,
gbx_start=dut.tx_gbx_start,
clock=dut.clk,
scramble=False,
gbx_cfg=gbx_cfg
)
self.ptp_clock = PtpClockSimTime(ts_tod=dut.ptp_ts, clock=dut.clk)
self.tx_cpl_sink = AxiStreamSink(AxiStreamBus.from_entity(dut.m_axis_tx_cpl), dut.clk, dut.rst)
@@ -95,16 +111,20 @@ class TB:
self.stats[stat] += int(getattr(self.dut, stat).value)
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12):
async def run_test(dut, gbx_cfg=None, payload_lengths=None, payload_data=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.dut.cfg_tx_max_pkt_len.value = 9218
tb.dut.cfg_tx_ifg.value = ifg
tb.dut.cfg_tx_enable.value = 1
await tb.reset()
for k in range(100):
await RisingEdge(dut.clk)
tb.dut.cfg_tx_enable.value = 1
test_frames = [payload_data(x) for x in payload_lengths()]
total_bytes = 0
@@ -134,7 +154,8 @@ async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12):
assert rx_frame.get_payload() == test_data
assert rx_frame.check_fcs()
assert rx_frame.ctrl is None
assert abs(rx_frame_sfd_ns - ptp_ts_ns - 12.8) < 0.01
if gbx_cfg is None:
assert abs(rx_frame_sfd_ns - ptp_ts_ns - 12.8) < 0.01
assert tb.sink.empty()
@@ -153,24 +174,28 @@ async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12):
assert tb.stats["stat_tx_err_user"] == 0
assert tb.stats["stat_tx_err_underflow"] == 0
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
for k in range(10):
await RisingEdge(dut.clk)
async def run_test_alignment(dut, payload_data=None, ifg=12):
async def run_test_alignment(dut, gbx_cfg=None, payload_data=None, ifg=12):
enable_dic = int(dut.DIC_EN.value)
tb = TB(dut)
tb = TB(dut, gbx_cfg)
byte_width = tb.source.width // 8
tb.dut.cfg_tx_max_pkt_len.value = 9218
tb.dut.cfg_tx_ifg.value = ifg
tb.dut.cfg_tx_enable.value = 1
await tb.reset()
for k in range(100):
await RisingEdge(dut.clk)
tb.dut.cfg_tx_enable.value = 1
total_bytes = 0
total_pkts = 0
@@ -206,7 +231,8 @@ async def run_test_alignment(dut, payload_data=None, ifg=12):
assert rx_frame.get_payload() == test_data
assert rx_frame.check_fcs()
assert rx_frame.ctrl is None
assert abs(rx_frame_sfd_ns - ptp_ts_ns - 12.8) < 0.01
if gbx_cfg is None:
assert abs(rx_frame_sfd_ns - ptp_ts_ns - 12.8) < 0.01
start_lane.append(rx_frame.start_lane)
@@ -261,20 +287,24 @@ async def run_test_alignment(dut, payload_data=None, ifg=12):
assert tb.stats["stat_tx_err_user"] == 0
assert tb.stats["stat_tx_err_underflow"] == 0
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
for k in range(10):
await RisingEdge(dut.clk)
async def run_test_underrun(dut, ifg=12):
async def run_test_underrun(dut, gbx_cfg=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.dut.cfg_tx_max_pkt_len.value = 9218
tb.dut.cfg_tx_ifg.value = ifg
tb.dut.cfg_tx_enable.value = 1
await tb.reset()
for k in range(100):
await RisingEdge(dut.clk)
tb.dut.cfg_tx_enable.value = 1
test_data = bytes(x for x in range(60))
for k in range(3):
@@ -319,20 +349,24 @@ async def run_test_underrun(dut, ifg=12):
assert tb.stats["stat_tx_err_user"] == 0
assert tb.stats["stat_tx_err_underflow"] == 1
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
for k in range(10):
await RisingEdge(dut.clk)
async def run_test_error(dut, ifg=12):
async def run_test_error(dut, gbx_cfg=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.dut.cfg_tx_max_pkt_len.value = 9218
tb.dut.cfg_tx_ifg.value = ifg
tb.dut.cfg_tx_enable.value = 1
await tb.reset()
for k in range(100):
await RisingEdge(dut.clk)
tb.dut.cfg_tx_enable.value = 1
test_data = bytes(x for x in range(60))
for k in range(3):
@@ -369,20 +403,24 @@ async def run_test_error(dut, ifg=12):
assert tb.stats["stat_tx_err_user"] == 1
assert tb.stats["stat_tx_err_underflow"] == 0
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
for k in range(10):
await RisingEdge(dut.clk)
async def run_test_oversize(dut, ifg=12):
async def run_test_oversize(dut, gbx_cfg=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.dut.cfg_tx_max_pkt_len.value = 1518
tb.dut.cfg_tx_ifg.value = ifg
tb.dut.cfg_tx_enable.value = 1
await tb.reset()
for k in range(100):
await RisingEdge(dut.clk)
tb.dut.cfg_tx_enable.value = 1
for max_len in range(128-4-8, 128-4+9):
tb.stats_reset()
@@ -454,8 +492,8 @@ async def run_test_oversize(dut, ifg=12):
assert tb.stats["stat_tx_err_user"] == 0
assert tb.stats["stat_tx_err_underflow"] == 0
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
for k in range(10):
await RisingEdge(dut.clk)
def size_list():
@@ -472,15 +510,23 @@ def cycle_en():
if cocotb.SIM_NAME:
gbx_cfgs = [None]
if cocotb.top.GBX_IF_EN.value:
gbx_cfgs.append((33, [32]))
gbx_cfgs.append((66, [64, 65]))
factory = TestFactory(run_test)
factory.add_option("payload_lengths", [size_list])
factory.add_option("payload_data", [incrementing_payload])
factory.add_option("ifg", [12])
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
factory = TestFactory(run_test_alignment)
factory.add_option("payload_data", [incrementing_payload])
factory.add_option("ifg", [12])
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
for test in [
@@ -491,6 +537,7 @@ if cocotb.SIM_NAME:
factory = TestFactory(test)
factory.add_option("ifg", [12])
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
@@ -516,7 +563,8 @@ def process_f_files(files):
@pytest.mark.parametrize("dic_en", [1, 0])
def test_taxi_axis_baser_tx_64(request, dic_en):
@pytest.mark.parametrize("gbx_en", [1, 0])
def test_taxi_axis_baser_tx_64(request, gbx_en, dic_en):
dut = "taxi_axis_baser_tx_64"
module = os.path.splitext(os.path.basename(__file__))[0]
toplevel = module
@@ -534,6 +582,8 @@ def test_taxi_axis_baser_tx_64(request, dic_en):
parameters['DATA_W'] = 64
parameters['HDR_W'] = 2
parameters['GBX_IF_EN'] = gbx_en
parameters['GBX_CNT'] = 1
parameters['PADDING_EN'] = 1
parameters['DIC_EN'] = dic_en
parameters['MIN_FRAME_LEN'] = 64

View File

@@ -20,6 +20,8 @@ module test_taxi_axis_baser_tx_64 #
/* verilator lint_off WIDTHTRUNC */
parameter DATA_W = 64,
parameter HDR_W = 2,
parameter logic GBX_IF_EN = 1'b0,
parameter GBX_CNT = 1,
parameter logic PADDING_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
@@ -41,7 +43,12 @@ taxi_axis_if #(.DATA_W(DATA_W), .USER_EN(1), .USER_W(USER_W), .ID_EN(1), .ID_W(T
taxi_axis_if #(.DATA_W(PTP_TS_W), .KEEP_W(1), .ID_EN(1), .ID_W(TX_TAG_W)) m_axis_tx_cpl();
logic [DATA_W-1:0] encoded_tx_data;
logic encoded_tx_data_valid;
logic [HDR_W-1:0] encoded_tx_hdr;
logic encoded_tx_hdr_valid;
logic [GBX_CNT-1:0] tx_gbx_req_start;
logic tx_gbx_req_stall;
logic [GBX_CNT-1:0] tx_gbx_start;
logic [PTP_TS_W-1:0] ptp_ts;
@@ -65,6 +72,7 @@ logic stat_tx_err_underflow;
taxi_axis_baser_tx_64 #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.GBX_IF_EN(GBX_IF_EN),
.PADDING_EN(PADDING_EN),
.DIC_EN(DIC_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
@@ -86,7 +94,12 @@ uut (
* 10GBASE-R encoded interface
*/
.encoded_tx_data(encoded_tx_data),
.encoded_tx_data_valid(encoded_tx_data_valid),
.encoded_tx_hdr(encoded_tx_hdr),
.encoded_tx_hdr_valid(encoded_tx_hdr_valid),
.tx_gbx_req_start(tx_gbx_req_start),
.tx_gbx_req_stall(tx_gbx_req_stall),
.tx_gbx_start(tx_gbx_start),
/*
* PTP

View File

@@ -34,6 +34,8 @@ VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
# module parameters
export PARAM_DATA_W := 64
export PARAM_HDR_W := 2
export PARAM_TX_GBX_IF_EN := 1
export PARAM_RX_GBX_IF_EN := $(PARAM_TX_GBX_IF_EN)
export PARAM_PADDING_EN := 1
export PARAM_DIC_EN := 1
export PARAM_MIN_FRAME_LEN := 64

View File

@@ -41,23 +41,47 @@ except ImportError:
class TB:
def __init__(self, dut):
def __init__(self, dut, gbx_cfg=None):
self.dut = dut
self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG)
if len(dut.serdes_tx_data) == 64:
self.clk_period = 6.4
if gbx_cfg:
self.clk_period = 6.206
else:
self.clk_period = 6.4
else:
self.clk_period = 3.2
if gbx_cfg:
self.clk_period = 3.102
else:
self.clk_period = 3.2
cocotb.start_soon(Clock(dut.rx_clk, self.clk_period, units="ns").start())
cocotb.start_soon(Clock(dut.tx_clk, self.clk_period, units="ns").start())
cocotb.start_soon(Clock(dut.stat_clk, self.clk_period, units="ns").start())
self.serdes_source = BaseRSerdesSource(dut.serdes_rx_data, dut.serdes_rx_hdr, dut.rx_clk, slip=dut.serdes_rx_bitslip)
self.serdes_sink = BaseRSerdesSink(dut.serdes_tx_data, dut.serdes_tx_hdr, dut.tx_clk)
self.serdes_source = BaseRSerdesSource(
data=dut.serdes_rx_data,
data_valid=dut.serdes_rx_data_valid,
hdr=dut.serdes_rx_hdr,
hdr_valid=dut.serdes_rx_hdr_valid,
clock=dut.rx_clk,
slip=dut.serdes_rx_bitslip,
gbx_cfg=gbx_cfg
)
self.serdes_sink = BaseRSerdesSink(
data=dut.serdes_tx_data,
data_valid=dut.serdes_tx_data_valid,
hdr=dut.serdes_tx_hdr,
hdr_valid=dut.serdes_tx_hdr_valid,
gbx_req_start=dut.serdes_tx_gbx_req_start,
gbx_req_stall=dut.serdes_tx_gbx_req_stall,
gbx_start=dut.serdes_tx_gbx_start,
clock=dut.tx_clk,
gbx_cfg=gbx_cfg
)
self.axis_source = AxiStreamSource(AxiStreamBus.from_entity(dut.s_axis_tx), dut.tx_clk, dut.tx_rst)
self.tx_cpl_sink = AxiStreamSink(AxiStreamBus.from_entity(dut.m_axis_tx_cpl), dut.tx_clk, dut.tx_rst)
@@ -127,9 +151,9 @@ class TB:
await RisingEdge(self.dut.rx_clk)
async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12):
async def run_test_rx(dut, gbx_cfg=None, payload_lengths=None, payload_data=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.serdes_source.ifg = ifg
tb.dut.cfg_tx_ifg.value = ifg
@@ -171,20 +195,22 @@ async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12):
tb.log.info("RX frame PTP TS: %f ns", ptp_ts_ns)
tb.log.info("TX frame SFD sim time: %f ns", tx_frame_sfd_ns)
tb.log.info("Difference: %f ns", abs(ptp_ts_ns - tx_frame_sfd_ns))
tb.log.info("Error: %f ns", abs(ptp_ts_ns - tx_frame_sfd_ns - tb.clk_period*4))
assert rx_frame.tdata == test_data
assert frame_error == 0
assert abs(ptp_ts_ns - tx_frame_sfd_ns - tb.clk_period*4) < 0.01
if gbx_cfg is None:
assert abs(ptp_ts_ns - tx_frame_sfd_ns - tb.clk_period*4) < 0.01
assert tb.axis_sink.empty()
await RisingEdge(dut.rx_clk)
await RisingEdge(dut.rx_clk)
for k in range(10):
await RisingEdge(dut.rx_clk)
async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12):
async def run_test_tx(dut, gbx_cfg=None, payload_lengths=None, payload_data=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.serdes_source.ifg = ifg
tb.dut.cfg_tx_max_pkt_len.value = 9218
@@ -192,6 +218,9 @@ async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12):
await tb.reset()
for k in range(100):
await RisingEdge(dut.tx_clk)
tb.dut.cfg_tx_enable.value = 1
test_frames = [payload_data(x) for x in payload_lengths()]
@@ -214,23 +243,25 @@ async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12):
tb.log.info("TX frame PTP TS: %f ns", ptp_ts_ns)
tb.log.info("RX frame SFD sim time: %f ns", rx_frame_sfd_ns)
tb.log.info("Difference: %f ns", abs(rx_frame_sfd_ns - ptp_ts_ns))
tb.log.info("Error: %f ns", abs(rx_frame_sfd_ns - ptp_ts_ns - tb.clk_period*5))
assert rx_frame.get_payload() == test_data
assert rx_frame.check_fcs()
assert rx_frame.ctrl is None
assert abs(rx_frame_sfd_ns - ptp_ts_ns - tb.clk_period*5) < 0.01
if gbx_cfg is None:
assert abs(rx_frame_sfd_ns - ptp_ts_ns - tb.clk_period*5) < 0.01
assert tb.serdes_sink.empty()
await RisingEdge(dut.tx_clk)
await RisingEdge(dut.tx_clk)
for k in range(10):
await RisingEdge(dut.tx_clk)
async def run_test_tx_alignment(dut, payload_data=None, ifg=12):
async def run_test_tx_alignment(dut, gbx_cfg=None, payload_data=None, ifg=12):
dic_en = int(cocotb.top.DIC_EN.value)
tb = TB(dut)
tb = TB(dut, gbx_cfg)
byte_width = tb.axis_source.width // 8
@@ -240,6 +271,9 @@ async def run_test_tx_alignment(dut, payload_data=None, ifg=12):
await tb.reset()
for k in range(100):
await RisingEdge(dut.tx_clk)
tb.dut.cfg_tx_enable.value = 1
for length in range(60, 92):
@@ -272,7 +306,8 @@ async def run_test_tx_alignment(dut, payload_data=None, ifg=12):
assert rx_frame.get_payload() == test_data
assert rx_frame.check_fcs()
assert rx_frame.ctrl is None
assert abs(rx_frame_sfd_ns - ptp_ts_ns - tb.clk_period*5) < 0.01
if gbx_cfg is None:
assert abs(rx_frame_sfd_ns - ptp_ts_ns - tb.clk_period*5) < 0.01
start_lane.append(rx_frame.start_lane)
@@ -312,13 +347,13 @@ async def run_test_tx_alignment(dut, payload_data=None, ifg=12):
assert tb.serdes_sink.empty()
await RisingEdge(dut.tx_clk)
await RisingEdge(dut.tx_clk)
for k in range(10):
await RisingEdge(dut.tx_clk)
async def run_test_tx_underrun(dut, ifg=12):
async def run_test_tx_underrun(dut, gbx_cfg=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.serdes_source.ifg = ifg
tb.dut.cfg_tx_max_pkt_len.value = 9218
@@ -326,6 +361,9 @@ async def run_test_tx_underrun(dut, ifg=12):
await tb.reset()
for k in range(100):
await RisingEdge(dut.tx_clk)
tb.dut.cfg_tx_enable.value = 1
test_data = bytes(x for x in range(60))
@@ -357,13 +395,13 @@ async def run_test_tx_underrun(dut, ifg=12):
assert tb.serdes_sink.empty()
await RisingEdge(dut.tx_clk)
await RisingEdge(dut.tx_clk)
for k in range(10):
await RisingEdge(dut.tx_clk)
async def run_test_tx_error(dut, ifg=12):
async def run_test_tx_error(dut, gbx_cfg=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.serdes_source.ifg = ifg
tb.dut.cfg_tx_max_pkt_len.value = 9218
@@ -371,6 +409,9 @@ async def run_test_tx_error(dut, ifg=12):
await tb.reset()
for k in range(100):
await RisingEdge(dut.tx_clk)
tb.dut.cfg_tx_enable.value = 1
test_data = bytes(x for x in range(60))
@@ -394,13 +435,13 @@ async def run_test_tx_error(dut, ifg=12):
assert tb.serdes_sink.empty()
await RisingEdge(dut.tx_clk)
await RisingEdge(dut.tx_clk)
for k in range(10):
await RisingEdge(dut.tx_clk)
async def run_test_rx_frame_sync(dut):
async def run_test_rx_frame_sync(dut, gbx_cfg=None):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
await tb.reset()
@@ -432,13 +473,13 @@ async def run_test_rx_frame_sync(dut):
tb.log.info("Check for high BER deassert")
assert not int(dut.rx_high_ber.value)
await RisingEdge(dut.rx_clk)
await RisingEdge(dut.rx_clk)
for k in range(10):
await RisingEdge(dut.rx_clk)
async def run_test_lfc(dut, ifg=12):
async def run_test_lfc(dut, gbx_cfg=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.serdes_source.ifg = ifg
tb.dut.cfg_tx_max_pkt_len.value = 9218
@@ -447,6 +488,9 @@ async def run_test_lfc(dut, ifg=12):
await tb.reset()
for k in range(100):
await RisingEdge(dut.tx_clk)
tb.log.info("Wait for block lock")
while not int(dut.rx_block_lock.value):
await RisingEdge(dut.rx_clk)
@@ -584,13 +628,13 @@ async def run_test_lfc(dut, ifg=12):
assert tb.axis_sink.empty()
assert tb.serdes_sink.empty()
await RisingEdge(dut.tx_clk)
await RisingEdge(dut.tx_clk)
for k in range(10):
await RisingEdge(dut.tx_clk)
async def run_test_pfc(dut, ifg=12):
async def run_test_pfc(dut, gbx_cfg=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.serdes_source.ifg = ifg
tb.dut.cfg_tx_max_pkt_len.value = 9218
@@ -599,6 +643,9 @@ async def run_test_pfc(dut, ifg=12):
await tb.reset()
for k in range(100):
await RisingEdge(dut.tx_clk)
tb.log.info("Wait for block lock")
while not int(dut.rx_block_lock.value):
await RisingEdge(dut.rx_clk)
@@ -726,8 +773,8 @@ async def run_test_pfc(dut, ifg=12):
assert tb.axis_sink.empty()
assert tb.serdes_sink.empty()
await RisingEdge(dut.tx_clk)
await RisingEdge(dut.tx_clk)
for k in range(10):
await RisingEdge(dut.tx_clk)
def size_list():
@@ -744,23 +791,32 @@ def cycle_en():
if cocotb.SIM_NAME:
gbx_cfgs = [None]
if cocotb.top.RX_GBX_IF_EN.value:
gbx_cfgs.append((33, [32]))
gbx_cfgs.append((66, [64, 65]))
for test in [run_test_rx, run_test_tx]:
factory = TestFactory(test)
factory.add_option("payload_lengths", [size_list])
factory.add_option("payload_data", [incrementing_payload])
factory.add_option("ifg", [12, 0])
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
factory = TestFactory(run_test_tx_alignment)
factory.add_option("payload_data", [incrementing_payload])
factory.add_option("ifg", [12])
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
for test in [run_test_tx_underrun, run_test_tx_error]:
factory = TestFactory(test)
factory.add_option("ifg", [12])
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
factory = TestFactory(run_test_rx_frame_sync)
@@ -770,6 +826,7 @@ if cocotb.SIM_NAME:
for test in [run_test_lfc, run_test_pfc]:
factory = TestFactory(test)
factory.add_option("ifg", [12])
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
@@ -795,8 +852,9 @@ def process_f_files(files):
@pytest.mark.parametrize(("dic_en", "pfc_en"), [(1, 1), (1, 0), (0, 0)])
@pytest.mark.parametrize("gbx_en", [1, 0])
@pytest.mark.parametrize("data_w", [64])
def test_taxi_eth_mac_phy_10g(request, data_w, dic_en, pfc_en):
def test_taxi_eth_mac_phy_10g(request, data_w, gbx_en, dic_en, pfc_en):
dut = "taxi_eth_mac_phy_10g"
module = os.path.splitext(os.path.basename(__file__))[0]
toplevel = module
@@ -812,6 +870,8 @@ def test_taxi_eth_mac_phy_10g(request, data_w, dic_en, pfc_en):
parameters['DATA_W'] = data_w
parameters['HDR_W'] = 2
parameters['TX_GBX_IF_EN'] = gbx_en
parameters['RX_GBX_IF_EN'] = parameters['TX_GBX_IF_EN']
parameters['PADDING_EN'] = 1
parameters['DIC_EN'] = dic_en
parameters['MIN_FRAME_LEN'] = 64

View File

@@ -20,6 +20,8 @@ module test_taxi_eth_mac_phy_10g #
/* verilator lint_off WIDTHTRUNC */
parameter DATA_W = 64,
parameter HDR_W = 2,
parameter logic TX_GBX_IF_EN = 1'b0,
parameter logic RX_GBX_IF_EN = TX_GBX_IF_EN,
parameter logic PADDING_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
@@ -61,9 +63,16 @@ taxi_axis_if #(.DATA_W(PTP_TS_W), .KEEP_W(1), .ID_EN(1), .ID_W(TX_TAG_W)) m_axis
taxi_axis_if #(.DATA_W(DATA_W), .USER_EN(1), .USER_W(RX_USER_W)) m_axis_rx();
logic [DATA_W-1:0] serdes_tx_data;
logic serdes_tx_data_valid;
logic [HDR_W-1:0] serdes_tx_hdr;
logic serdes_tx_hdr_valid;
logic serdes_tx_gbx_req_start;
logic serdes_tx_gbx_req_stall;
logic serdes_tx_gbx_start;
logic [DATA_W-1:0] serdes_rx_data;
logic serdes_rx_data_valid;
logic [HDR_W-1:0] serdes_rx_hdr;
logic serdes_rx_hdr_valid;
logic serdes_rx_bitslip;
logic serdes_rx_reset_req;
@@ -184,6 +193,8 @@ logic cfg_rx_pfc_en;
taxi_eth_mac_phy_10g #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.TX_GBX_IF_EN(TX_GBX_IF_EN),
.RX_GBX_IF_EN(RX_GBX_IF_EN),
.PADDING_EN(PADDING_EN),
.DIC_EN(DIC_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
@@ -229,9 +240,16 @@ uut (
* SERDES interface
*/
.serdes_tx_data(serdes_tx_data),
.serdes_tx_data_valid(serdes_tx_data_valid),
.serdes_tx_hdr(serdes_tx_hdr),
.serdes_tx_hdr_valid(serdes_tx_hdr_valid),
.serdes_tx_gbx_req_start(serdes_tx_gbx_req_start),
.serdes_tx_gbx_req_stall(serdes_tx_gbx_req_stall),
.serdes_tx_gbx_start(serdes_tx_gbx_start),
.serdes_rx_data(serdes_rx_data),
.serdes_rx_data_valid(serdes_rx_data_valid),
.serdes_rx_hdr(serdes_rx_hdr),
.serdes_rx_hdr_valid(serdes_rx_hdr_valid),
.serdes_rx_bitslip(serdes_rx_bitslip),
.serdes_rx_reset_req(serdes_rx_reset_req),

View File

@@ -34,6 +34,8 @@ VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
# module parameters
export PARAM_DATA_W := 64
export PARAM_HDR_W := 2
export PARAM_TX_GBX_IF_EN := 1
export PARAM_RX_GBX_IF_EN := $(PARAM_TX_GBX_IF_EN)
export PARAM_AXIS_DATA_W := $(PARAM_DATA_W)
export PARAM_PADDING_EN := 1
export PARAM_DIC_EN := 1

View File

@@ -38,16 +38,22 @@ except ImportError:
class TB:
def __init__(self, dut):
def __init__(self, dut, gbx_cfg=None):
self.dut = dut
self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG)
if len(dut.serdes_tx_data) == 64:
self.clk_period = 6.4
if gbx_cfg:
self.clk_period = 6.206
else:
self.clk_period = 6.4
else:
self.clk_period = 3.2
if gbx_cfg:
self.clk_period = 3.102
else:
self.clk_period = 3.2
cocotb.start_soon(Clock(dut.logic_clk, self.clk_period, units="ns").start())
cocotb.start_soon(Clock(dut.rx_clk, self.clk_period, units="ns").start())
@@ -55,8 +61,26 @@ class TB:
cocotb.start_soon(Clock(dut.stat_clk, self.clk_period, units="ns").start())
cocotb.start_soon(Clock(dut.ptp_sample_clk, 9.9, units="ns").start())
self.serdes_source = BaseRSerdesSource(dut.serdes_rx_data, dut.serdes_rx_hdr, dut.rx_clk, slip=dut.serdes_rx_bitslip)
self.serdes_sink = BaseRSerdesSink(dut.serdes_tx_data, dut.serdes_tx_hdr, dut.tx_clk)
self.serdes_source = BaseRSerdesSource(
data=dut.serdes_rx_data,
data_valid=dut.serdes_rx_data_valid,
hdr=dut.serdes_rx_hdr,
hdr_valid=dut.serdes_rx_hdr_valid,
clock=dut.rx_clk,
slip=dut.serdes_rx_bitslip,
gbx_cfg=gbx_cfg
)
self.serdes_sink = BaseRSerdesSink(
data=dut.serdes_tx_data,
data_valid=dut.serdes_tx_data_valid,
hdr=dut.serdes_tx_hdr,
hdr_valid=dut.serdes_tx_hdr_valid,
gbx_req_start=dut.serdes_tx_gbx_req_start,
gbx_req_stall=dut.serdes_tx_gbx_req_stall,
gbx_start=dut.serdes_tx_gbx_start,
clock=dut.tx_clk,
gbx_cfg=gbx_cfg
)
self.axis_source = AxiStreamSource(AxiStreamBus.from_entity(dut.s_axis_tx), dut.logic_clk, dut.logic_rst)
self.tx_cpl_sink = AxiStreamSink(AxiStreamBus.from_entity(dut.m_axis_tx_cpl), dut.logic_clk, dut.logic_rst)
@@ -97,9 +121,9 @@ class TB:
await RisingEdge(self.dut.logic_clk)
async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12):
async def run_test_rx(dut, gbx_cfg=None, payload_lengths=None, payload_data=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.serdes_source.ifg = ifg
tb.dut.cfg_tx_ifg.value = ifg
@@ -148,17 +172,18 @@ async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12):
assert rx_frame.tdata == test_data
assert frame_error == 0
assert abs(ptp_ts_ns - tx_frame_sfd_ns - tb.clk_period*4) < tb.clk_period*2
if gbx_cfg is None:
assert abs(ptp_ts_ns - tx_frame_sfd_ns - tb.clk_period*4) < tb.clk_period*2
assert tb.axis_sink.empty()
await RisingEdge(dut.logic_clk)
await RisingEdge(dut.logic_clk)
for k in range(10):
await RisingEdge(dut.logic_clk)
async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12):
async def run_test_tx(dut, gbx_cfg=None, payload_lengths=None, payload_data=None, ifg=12):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
tb.serdes_source.ifg = ifg
tb.dut.cfg_tx_max_pkt_len.value = 9218
@@ -197,19 +222,20 @@ async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12):
assert rx_frame.get_payload() == test_data
assert rx_frame.check_fcs()
assert rx_frame.ctrl is None
assert abs(rx_frame_sfd_ns - ptp_ts_ns - tb.clk_period*5) < tb.clk_period*2
if gbx_cfg is None:
assert abs(rx_frame_sfd_ns - ptp_ts_ns - tb.clk_period*5) < tb.clk_period*2
assert tb.serdes_sink.empty()
await RisingEdge(dut.logic_clk)
await RisingEdge(dut.logic_clk)
for k in range(10):
await RisingEdge(dut.logic_clk)
async def run_test_tx_alignment(dut, payload_data=None, ifg=12):
async def run_test_tx_alignment(dut, gbx_cfg=None, payload_data=None, ifg=12):
dic_en = int(cocotb.top.DIC_EN.value)
tb = TB(dut)
tb = TB(dut, gbx_cfg)
byte_width = tb.axis_source.width // 8
@@ -256,7 +282,8 @@ async def run_test_tx_alignment(dut, payload_data=None, ifg=12):
assert rx_frame.get_payload() == test_data
assert rx_frame.check_fcs()
assert rx_frame.ctrl is None
assert abs(rx_frame_sfd_ns - ptp_ts_ns - tb.clk_period*5) < tb.clk_period*2
if gbx_cfg is None:
assert abs(rx_frame_sfd_ns - ptp_ts_ns - tb.clk_period*5) < tb.clk_period*2
start_lane.append(rx_frame.start_lane)
@@ -296,13 +323,13 @@ async def run_test_tx_alignment(dut, payload_data=None, ifg=12):
assert tb.serdes_sink.empty()
await RisingEdge(dut.logic_clk)
await RisingEdge(dut.logic_clk)
for k in range(10):
await RisingEdge(dut.logic_clk)
async def run_test_rx_frame_sync(dut):
async def run_test_rx_frame_sync(dut, gbx_cfg=None):
tb = TB(dut)
tb = TB(dut, gbx_cfg)
await tb.reset()
@@ -352,20 +379,29 @@ def cycle_en():
if cocotb.SIM_NAME:
gbx_cfgs = [None]
if cocotb.top.RX_GBX_IF_EN.value:
gbx_cfgs.append((33, [32]))
gbx_cfgs.append((66, [64, 65]))
for test in [run_test_rx, run_test_tx]:
factory = TestFactory(test)
factory.add_option("payload_lengths", [size_list])
factory.add_option("payload_data", [incrementing_payload])
factory.add_option("ifg", [12, 0])
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
factory = TestFactory(run_test_tx_alignment)
factory.add_option("payload_data", [incrementing_payload])
factory.add_option("ifg", [12])
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
factory = TestFactory(run_test_rx_frame_sync)
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
@@ -391,8 +427,9 @@ def process_f_files(files):
@pytest.mark.parametrize("dic_en", [1, 0])
@pytest.mark.parametrize("gbx_en", [1, 0])
@pytest.mark.parametrize("data_w", [64])
def test_taxi_eth_mac_phy_10g_fifo(request, data_w, dic_en):
def test_taxi_eth_mac_phy_10g_fifo(request, data_w, gbx_en, dic_en):
dut = "taxi_eth_mac_phy_10g_fifo"
module = os.path.splitext(os.path.basename(__file__))[0]
toplevel = module
@@ -408,6 +445,8 @@ def test_taxi_eth_mac_phy_10g_fifo(request, data_w, dic_en):
parameters['DATA_W'] = data_w
parameters['HDR_W'] = 2
parameters['TX_GBX_IF_EN'] = gbx_en
parameters['RX_GBX_IF_EN'] = parameters['TX_GBX_IF_EN']
parameters['AXIS_DATA_W'] = parameters['DATA_W']
parameters['PADDING_EN'] = 1
parameters['DIC_EN'] = dic_en

View File

@@ -18,8 +18,10 @@ Authors:
module test_taxi_eth_mac_phy_10g_fifo #
(
/* verilator lint_off WIDTHTRUNC */
parameter DATA_W = 8,
parameter DATA_W = 64,
parameter HDR_W = 2,
parameter logic TX_GBX_IF_EN = 1'b0,
parameter logic RX_GBX_IF_EN = TX_GBX_IF_EN,
parameter AXIS_DATA_W = 8,
parameter logic PADDING_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
@@ -76,9 +78,16 @@ taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_EN(1), .ID_W(TX_TAG_W)) m_axis_tx_cp
taxi_axis_if #(.DATA_W(AXIS_DATA_W), .USER_EN(1), .USER_W(RX_USER_W)) m_axis_rx();
logic [DATA_W-1:0] serdes_tx_data;
logic serdes_tx_data_valid;
logic [HDR_W-1:0] serdes_tx_hdr;
logic serdes_tx_hdr_valid;
logic serdes_tx_gbx_req_start;
logic serdes_tx_gbx_req_stall;
logic serdes_tx_gbx_start;
logic [DATA_W-1:0] serdes_rx_data;
logic serdes_rx_data_valid;
logic [HDR_W-1:0] serdes_rx_hdr;
logic serdes_rx_hdr_valid;
logic serdes_rx_bitslip;
logic serdes_rx_reset_req;
@@ -115,6 +124,8 @@ logic cfg_rx_prbs31_enable;
taxi_eth_mac_phy_10g_fifo #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.TX_GBX_IF_EN(TX_GBX_IF_EN),
.RX_GBX_IF_EN(RX_GBX_IF_EN),
.PADDING_EN(PADDING_EN),
.DIC_EN(DIC_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
@@ -174,9 +185,16 @@ uut (
* SERDES interface
*/
.serdes_tx_data(serdes_tx_data),
.serdes_tx_data_valid(serdes_tx_data_valid),
.serdes_tx_hdr(serdes_tx_hdr),
.serdes_tx_hdr_valid(serdes_tx_hdr_valid),
.serdes_tx_gbx_req_start(serdes_tx_gbx_req_start),
.serdes_tx_gbx_req_stall(serdes_tx_gbx_req_stall),
.serdes_tx_gbx_start(serdes_tx_gbx_start),
.serdes_rx_data(serdes_rx_data),
.serdes_rx_data_valid(serdes_rx_data_valid),
.serdes_rx_hdr(serdes_rx_hdr),
.serdes_rx_hdr_valid(serdes_rx_hdr_valid),
.serdes_rx_bitslip(serdes_rx_bitslip),
.serdes_rx_reset_req(serdes_rx_reset_req),

View File

@@ -34,6 +34,8 @@ VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
export PARAM_DATA_W := 64
export PARAM_CTRL_W := $(shell expr $(PARAM_DATA_W) / 8 )
export PARAM_HDR_W := 2
export PARAM_TX_GBX_IF_EN := 0
export PARAM_RX_GBX_IF_EN := $(PARAM_TX_GBX_IF_EN)
export PARAM_BIT_REVERSE := "1'b0"
export PARAM_SCRAMBLER_DISABLE := "1'b0"
export PARAM_PRBS31_EN := "1'b1"

View File

@@ -35,7 +35,7 @@ except ImportError:
class TB:
def __init__(self, dut):
def __init__(self, dut, gbx_cfg=None):
self.dut = dut
self.log = logging.getLogger("cocotb.tb")
@@ -47,8 +47,26 @@ class TB:
self.xgmii_source = XgmiiSource(dut.xgmii_txd, dut.xgmii_txc, dut.tx_clk, dut.tx_rst)
self.xgmii_sink = XgmiiSink(dut.xgmii_rxd, dut.xgmii_rxc, dut.rx_clk, dut.rx_rst)
self.serdes_source = BaseRSerdesSource(dut.serdes_rx_data, dut.serdes_rx_hdr, dut.rx_clk, slip=dut.serdes_rx_bitslip)
self.serdes_sink = BaseRSerdesSink(dut.serdes_tx_data, dut.serdes_tx_hdr, dut.tx_clk)
self.serdes_source = BaseRSerdesSource(
data=dut.serdes_rx_data,
data_valid=dut.serdes_rx_data_valid,
hdr=dut.serdes_rx_hdr,
hdr_valid=dut.serdes_rx_hdr_valid,
clock=dut.rx_clk,
slip=dut.serdes_rx_bitslip,
gbx_cfg=gbx_cfg
)
self.serdes_sink = BaseRSerdesSink(
data=dut.serdes_tx_data,
data_valid=dut.serdes_tx_data_valid,
hdr=dut.serdes_tx_hdr,
hdr_valid=dut.serdes_tx_hdr_valid,
gbx_req_start=dut.serdes_tx_gbx_req_start,
gbx_req_stall=dut.serdes_tx_gbx_req_stall,
gbx_start=dut.serdes_tx_gbx_start,
clock=dut.tx_clk,
gbx_cfg=gbx_cfg
)
dut.cfg_tx_prbs31_enable.setimmediatevalue(0)
dut.cfg_rx_prbs31_enable.setimmediatevalue(0)
@@ -231,6 +249,8 @@ def test_taxi_eth_phy_10g(request):
parameters['DATA_W'] = 64
parameters['CTRL_W'] = parameters['DATA_W'] // 8
parameters['HDR_W'] = 2
parameters['TX_GBX_IF_EN'] = 0
parameters['RX_GBX_IF_EN'] = parameters['TX_GBX_IF_EN']
parameters['BIT_REVERSE'] = "1'b0"
parameters['SCRAMBLER_DISABLE'] = "1'b0"
parameters['PRBS31_EN'] = "1'b1"

View File

@@ -34,6 +34,7 @@ VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
export PARAM_DATA_W := 64
export PARAM_CTRL_W := $(shell expr $(PARAM_DATA_W) / 8 )
export PARAM_HDR_W := 2
export PARAM_GBX_IF_EN := 0
ifeq ($(SIM), icarus)
PLUSARGS += -fst

View File

@@ -46,6 +46,9 @@ class TB:
self.source = BaseRSerdesSource(dut.encoded_rx_data, dut.encoded_rx_hdr, dut.clk, scramble=False)
self.sink = XgmiiSink(dut.xgmii_rxd, dut.xgmii_rxc, dut.clk, dut.rst)
dut.encoded_rx_data_valid.setimmediatevalue(1)
dut.encoded_rx_hdr_valid.setimmediatevalue(1)
async def reset(self):
self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk)
@@ -227,6 +230,7 @@ def test_taxi_xgmii_baser_dec_64(request):
parameters['DATA_W'] = 64
parameters['CTRL_W'] = parameters['DATA_W'] // 8
parameters['HDR_W'] = 2
parameters['GBX_IF_EN'] = 0
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}

View File

@@ -34,6 +34,7 @@ VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
export PARAM_DATA_W := 64
export PARAM_CTRL_W := $(shell expr $(PARAM_DATA_W) / 8 )
export PARAM_HDR_W := 2
export PARAM_GBX_IF_EN := 0
ifeq ($(SIM), icarus)
PLUSARGS += -fst

View File

@@ -46,6 +46,9 @@ class TB:
self.source = XgmiiSource(dut.xgmii_txd, dut.xgmii_txc, dut.clk, dut.rst)
self.sink = BaseRSerdesSink(dut.encoded_tx_data, dut.encoded_tx_hdr, dut.clk, scramble=False)
dut.xgmii_tx_valid.setimmediatevalue(1)
dut.tx_gbx_start_in.setimmediatevalue(0)
async def reset(self):
self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk)
@@ -227,6 +230,7 @@ def test_taxi_xgmii_baser_enc_64(request):
parameters['DATA_W'] = 64
parameters['CTRL_W'] = parameters['DATA_W'] // 8
parameters['HDR_W'] = 2
parameters['GBX_IF_EN'] = 0
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}