diff --git a/src/eth/rtl/taxi_eth_phy_1g_basex.f b/src/eth/rtl/taxi_eth_phy_1g_basex.f new file mode 100644 index 0000000..08680fa --- /dev/null +++ b/src/eth/rtl/taxi_eth_phy_1g_basex.f @@ -0,0 +1,3 @@ +taxi_eth_phy_1g_basex.sv +taxi_eth_phy_1g_basex_rx.f +taxi_eth_phy_1g_basex_tx.f diff --git a/src/eth/rtl/taxi_eth_phy_1g_basex.sv b/src/eth/rtl/taxi_eth_phy_1g_basex.sv new file mode 100644 index 0000000..e74f6d2 --- /dev/null +++ b/src/eth/rtl/taxi_eth_phy_1g_basex.sv @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: CERN-OHL-S-2.0 +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * 1000BASE-X Ethernet PHY + */ +module taxi_eth_phy_1g_basex # +( + parameter DATA_W = 16, + parameter CTRL_W = (DATA_W/8), + parameter logic TX_GBX_IF_EN = 1'b0, + parameter logic RX_GBX_IF_EN = TX_GBX_IF_EN, + parameter logic BIT_REVERSE = 1'b0, + parameter logic ENC_8B10B_EN = 1'b0, + parameter logic DEC_8B10B_EN = ENC_8B10B_EN, + parameter logic PRBS31_EN = 1'b0, + parameter TX_SERDES_PIPELINE = 0, + parameter RX_SERDES_PIPELINE = 0, + parameter COUNT_125US = 125000/6.4 +) +( + input wire logic rx_clk, + input wire logic rx_rst, + input wire logic tx_clk, + input wire logic tx_rst, + + /* + * GMII interface + */ + input wire logic [DATA_W-1:0] gmii_txd, + input wire logic [CTRL_W-1:0] gmii_tx_en, + input wire logic [CTRL_W-1:0] gmii_tx_er, + input wire logic gmii_tx_valid = 1'b1, + output wire logic [DATA_W-1:0] gmii_rxd, + output wire logic [CTRL_W-1:0] gmii_rx_dv, + output wire logic [CTRL_W-1:0] gmii_rx_er, + output wire logic gmii_rx_valid, + output wire logic tx_gbx_req_sync, + output wire logic tx_gbx_req_stall, + input wire logic tx_gbx_sync = 1'b0, + + /* + * SERDES interface + */ + output wire logic [DATA_W-1:0] serdes_tx_data, + output wire logic [CTRL_W-1:0] serdes_tx_data_k, + output wire logic [CTRL_W-1:0] serdes_tx_data_dm, + output wire logic [CTRL_W-1:0] serdes_tx_data_dv, + output wire logic serdes_tx_data_valid, + input wire logic serdes_tx_gbx_req_sync = 1'b0, + input wire logic serdes_tx_gbx_req_stall = 1'b0, + output wire logic serdes_tx_gbx_sync, + input wire logic [DATA_W-1:0] serdes_rx_data, + input wire logic [CTRL_W-1:0] serdes_rx_data_k, + input wire logic serdes_rx_data_valid = 1'b1, + output wire logic serdes_rx_reset_req, + + /* + * Status + */ + output wire logic [4:0] rx_error_count, + output wire logic stat_rx_err_bad_block, + output wire logic stat_rx_err_framing, + output wire logic rx_block_lock, + output wire logic rx_high_ber, + output wire logic rx_status, + + /* + * Configuration + */ + input wire logic cfg_tx_prbs31_enable = 1'b0, + input wire logic cfg_rx_prbs31_enable = 1'b0 +); + +taxi_eth_phy_1g_basex_rx #( + .DATA_W(DATA_W), + .CTRL_W(CTRL_W), + .GBX_IF_EN(RX_GBX_IF_EN), + .BIT_REVERSE(BIT_REVERSE), + .DEC_8B10B_EN(DEC_8B10B_EN), + .PRBS31_EN(PRBS31_EN), + .SERDES_PIPELINE(RX_SERDES_PIPELINE), + .COUNT_125US(COUNT_125US) +) +rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + + /* + * GMII interface + */ + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .gmii_rx_valid(gmii_rx_valid), + + /* + * SERDES interface + */ + .serdes_rx_data(serdes_rx_data), + .serdes_rx_data_k(serdes_rx_data_k), + .serdes_rx_data_valid(serdes_rx_data_valid), + .serdes_rx_reset_req(serdes_rx_reset_req), + + /* + * Status + */ + .rx_error_count(rx_error_count), + .stat_rx_err_bad_block(stat_rx_err_bad_block), + .stat_rx_err_framing(stat_rx_err_framing), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .rx_status(rx_status), + + /* + * Configuration + */ + .cfg_rx_prbs31_enable(cfg_rx_prbs31_enable) +); + +taxi_eth_phy_1g_basex_tx #( + .DATA_W(DATA_W), + .CTRL_W(CTRL_W), + .GBX_IF_EN(TX_GBX_IF_EN), + .BIT_REVERSE(BIT_REVERSE), + .ENC_8B10B_EN(ENC_8B10B_EN), + .PRBS31_EN(PRBS31_EN), + .SERDES_PIPELINE(TX_SERDES_PIPELINE) +) +tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + + /* + * GMII interface + */ + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .gmii_tx_valid(gmii_tx_valid), + .tx_gbx_req_sync(tx_gbx_req_sync), + .tx_gbx_req_stall(tx_gbx_req_stall), + .tx_gbx_sync(tx_gbx_sync), + + /* + * SERDES interface + */ + .serdes_tx_data(serdes_tx_data), + .serdes_tx_data_k(serdes_tx_data_k), + .serdes_tx_data_dm(serdes_tx_data_dm), + .serdes_tx_data_dv(serdes_tx_data_dv), + .serdes_tx_data_valid(serdes_tx_data_valid), + .serdes_tx_gbx_req_sync(serdes_tx_gbx_req_sync), + .serdes_tx_gbx_req_stall(serdes_tx_gbx_req_stall), + .serdes_tx_gbx_sync(serdes_tx_gbx_sync), + + /* + * Configuration + */ + .cfg_tx_prbs31_enable(cfg_tx_prbs31_enable) +); + +endmodule + +`resetall diff --git a/src/eth/rtl/taxi_eth_phy_1g_basex_rx.f b/src/eth/rtl/taxi_eth_phy_1g_basex_rx.f new file mode 100644 index 0000000..0ca354b --- /dev/null +++ b/src/eth/rtl/taxi_eth_phy_1g_basex_rx.f @@ -0,0 +1,3 @@ +taxi_eth_phy_1g_basex_rx.sv +taxi_eth_phy_1g_basex_rx_if.f +taxi_gmii_basex_dec.sv diff --git a/src/eth/rtl/taxi_eth_phy_1g_basex_rx.sv b/src/eth/rtl/taxi_eth_phy_1g_basex_rx.sv new file mode 100644 index 0000000..ee5f4e5 --- /dev/null +++ b/src/eth/rtl/taxi_eth_phy_1g_basex_rx.sv @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: CERN-OHL-S-2.0 +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * 1000BASE-X Ethernet PHY RX + */ +module taxi_eth_phy_1g_basex_rx # +( + parameter DATA_W = 16, + parameter CTRL_W = (DATA_W/8), + parameter logic GBX_IF_EN = 1'b0, + parameter logic BIT_REVERSE = 1'b0, + parameter logic DEC_8B10B_EN = 1'b0, + parameter logic PRBS31_EN = 1'b0, + parameter SERDES_PIPELINE = 0, + parameter COUNT_125US = 125000/6.4 +) +( + input wire logic clk, + input wire logic rst, + + /* + * GMII interface + */ + output wire logic [DATA_W-1:0] gmii_rxd, + output wire logic [CTRL_W-1:0] gmii_rx_dv, + output wire logic [CTRL_W-1:0] gmii_rx_er, + output wire logic gmii_rx_valid, + + /* + * SERDES interface + */ + input wire logic [DATA_W-1:0] serdes_rx_data, + input wire logic [CTRL_W-1:0] serdes_rx_data_k, + input wire logic serdes_rx_data_valid = 1'b1, + output wire logic serdes_rx_reset_req, + + /* + * Status + */ + output wire logic [4:0] rx_error_count, + output wire logic stat_rx_err_bad_block, + output wire logic stat_rx_err_framing, + output wire logic rx_block_lock, + output wire logic rx_high_ber, + output wire logic rx_status, + + /* + * Configuration + */ + input wire logic cfg_rx_prbs31_enable +); + +wire [DATA_W-1:0] encoded_rx_data; +wire [CTRL_W-1:0] encoded_rx_data_k; +wire encoded_rx_data_valid; + +taxi_eth_phy_1g_basex_rx_if #( + .DATA_W(DATA_W), + .CTRL_W(CTRL_W), + .GBX_IF_EN(GBX_IF_EN), + .BIT_REVERSE(BIT_REVERSE), + .DEC_8B10B_EN(DEC_8B10B_EN), + .PRBS31_EN(PRBS31_EN), + .SERDES_PIPELINE(SERDES_PIPELINE), + .COUNT_125US(COUNT_125US) +) +rx_if_inst ( + .clk(clk), + .rst(rst), + + /* + * 1000GBASE-X encoded interface + */ + .encoded_rx_data(encoded_rx_data), + .encoded_rx_data_k(encoded_rx_data_k), + .encoded_rx_data_valid(encoded_rx_data_valid), + + /* + * SERDES interface + */ + .serdes_rx_data(serdes_rx_data), + .serdes_rx_data_k(serdes_rx_data_k), + .serdes_rx_data_valid(serdes_rx_data_valid), + .serdes_rx_reset_req(serdes_rx_reset_req), + + /* + * Status + */ + .rx_bad_block(stat_rx_err_bad_block), + .rx_sequence_error(stat_rx_err_framing), + .rx_error_count(rx_error_count), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .rx_status(rx_status), + + /* + * Configuration + */ + .cfg_rx_prbs31_enable(cfg_rx_prbs31_enable) +); + +taxi_gmii_basex_dec #( + .DATA_W(DATA_W), + .CTRL_W(CTRL_W), + .GBX_IF_EN(GBX_IF_EN) +) +dec_inst ( + .clk(clk), + .rst(rst), + + /* + * 1000GBASE-X encoded input + */ + .encoded_rx_data(encoded_rx_data), + .encoded_rx_data_k(encoded_rx_data_k), + .encoded_rx_data_valid(encoded_rx_data_valid), + + /* + * GMII interface + */ + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .gmii_rx_valid(gmii_rx_valid), + + /* + * Status + */ + .stat_rx_err_bad_block(stat_rx_err_bad_block), + .stat_rx_err_framing(stat_rx_err_framing) +); + +endmodule + +`resetall diff --git a/src/eth/rtl/taxi_eth_phy_1g_basex_tx.f b/src/eth/rtl/taxi_eth_phy_1g_basex_tx.f new file mode 100644 index 0000000..7dec0e5 --- /dev/null +++ b/src/eth/rtl/taxi_eth_phy_1g_basex_tx.f @@ -0,0 +1,3 @@ +taxi_eth_phy_1g_basex_tx.sv +taxi_eth_phy_1g_basex_tx_if.f +taxi_gmii_basex_enc.sv diff --git a/src/eth/rtl/taxi_eth_phy_1g_basex_tx.sv b/src/eth/rtl/taxi_eth_phy_1g_basex_tx.sv new file mode 100644 index 0000000..f1ec6f9 --- /dev/null +++ b/src/eth/rtl/taxi_eth_phy_1g_basex_tx.sv @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: CERN-OHL-S-2.0 +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * 1000BASE-X Ethernet PHY TX + */ +module taxi_eth_phy_1g_basex_tx # +( + parameter DATA_W = 16, + parameter CTRL_W = (DATA_W/8), + parameter logic GBX_IF_EN = 1'b0, + parameter logic BIT_REVERSE = 1'b0, + parameter logic ENC_8B10B_EN = 1'b0, + parameter logic PRBS31_EN = 1'b0, + parameter SERDES_PIPELINE = 0 +) +( + input wire logic clk, + input wire logic rst, + + /* + * GMII interface + */ + input wire logic [DATA_W-1:0] gmii_txd, + input wire logic [CTRL_W-1:0] gmii_tx_en, + input wire logic [CTRL_W-1:0] gmii_tx_er, + input wire logic gmii_tx_valid = 1'b1, + output wire logic tx_gbx_req_sync, + output wire logic tx_gbx_req_stall, + input wire logic tx_gbx_sync = 1'b0, + + /* + * SERDES interface + */ + output wire logic [DATA_W-1:0] serdes_tx_data, + output wire logic [CTRL_W-1:0] serdes_tx_data_k, + output wire logic [CTRL_W-1:0] serdes_tx_data_dm, + output wire logic [CTRL_W-1:0] serdes_tx_data_dv, + output wire logic serdes_tx_data_valid, + input wire logic serdes_tx_gbx_req_sync = 1'b0, + input wire logic serdes_tx_gbx_req_stall = 1'b0, + output wire logic serdes_tx_gbx_sync, + + /* + * Configuration + */ + input wire logic cfg_tx_prbs31_enable +); + +wire [DATA_W-1:0] encoded_tx_data; +wire [CTRL_W-1:0] encoded_tx_data_k; +wire [CTRL_W-1:0] encoded_tx_data_dm; +wire [CTRL_W-1:0] encoded_tx_data_dv; +wire encoded_tx_data_valid; + +wire tx_gbx_sync_int; + +taxi_gmii_basex_enc #( + .DATA_W(DATA_W), + .CTRL_W(CTRL_W), + .GBX_IF_EN(GBX_IF_EN), + .GBX_CNT(1) +) +enc_inst ( + .clk(clk), + .rst(rst), + + /* + * GMII interface + */ + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .gmii_tx_valid(gmii_tx_valid), + .tx_gbx_sync_in(tx_gbx_sync), + + /* + * 1000GBASE-X encoded interface + */ + .encoded_tx_data(encoded_tx_data), + .encoded_tx_data_k(encoded_tx_data_k), + .encoded_tx_data_dm(encoded_tx_data_dm), + .encoded_tx_data_dv(encoded_tx_data_dv), + .encoded_tx_data_valid(encoded_tx_data_valid), + .tx_gbx_sync_out(tx_gbx_sync_int) +); + +taxi_eth_phy_1g_basex_tx_if #( + .DATA_W(DATA_W), + .CTRL_W(CTRL_W), + .GBX_IF_EN(GBX_IF_EN), + .BIT_REVERSE(BIT_REVERSE), + .ENC_8B10B_EN(ENC_8B10B_EN), + .PRBS31_EN(PRBS31_EN), + .SERDES_PIPELINE(SERDES_PIPELINE) +) +tx_if_inst ( + .clk(clk), + .rst(rst), + + /* + * 1000GBASE-X encoded interface + */ + .encoded_tx_data(encoded_tx_data), + .encoded_tx_data_k(encoded_tx_data_k), + .encoded_tx_data_dm(encoded_tx_data_dm), + .encoded_tx_data_dv(encoded_tx_data_dv), + .encoded_tx_data_valid(encoded_tx_data_valid), + .tx_gbx_req_sync(tx_gbx_req_sync), + .tx_gbx_req_stall(tx_gbx_req_stall), + .tx_gbx_sync(tx_gbx_sync_int), + + /* + * SERDES interface + */ + .serdes_tx_data(serdes_tx_data), + .serdes_tx_data_k(serdes_tx_data_k), + .serdes_tx_data_dm(serdes_tx_data_dm), + .serdes_tx_data_dv(serdes_tx_data_dv), + .serdes_tx_data_valid(serdes_tx_data_valid), + .serdes_tx_gbx_req_sync(serdes_tx_gbx_req_sync), + .serdes_tx_gbx_req_stall(serdes_tx_gbx_req_stall), + .serdes_tx_gbx_sync(serdes_tx_gbx_sync), + + /* + * Configuration + */ + .cfg_tx_prbs31_enable(cfg_tx_prbs31_enable) +); + +endmodule + +`resetall diff --git a/src/eth/tb/taxi_eth_phy_1g_basex/Makefile b/src/eth/tb/taxi_eth_phy_1g_basex/Makefile new file mode 100644 index 0000000..0aa52dc --- /dev/null +++ b/src/eth/tb/taxi_eth_phy_1g_basex/Makefile @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: CERN-OHL-S-2.0 +# +# Copyright (c) 2026 FPGA Ninja, LLC +# +# Authors: +# - Alex Forencich + +TOPLEVEL_LANG = verilog + +SIM ?= verilator +WAVES ?= 0 + +COCOTB_HDL_TIMEUNIT = 1ns +COCOTB_HDL_TIMEPRECISION = 1ps + +RTL_DIR = ../../rtl +LIB_DIR = ../../lib +TAXI_SRC_DIR = $(LIB_DIR)/taxi/src + +DUT = taxi_eth_phy_1g_basex +COCOTB_TEST_MODULES = test_$(DUT) +COCOTB_TOPLEVEL = $(DUT) +MODULE = $(COCOTB_TEST_MODULES) +TOPLEVEL = $(COCOTB_TOPLEVEL) +VERILOG_SOURCES += $(RTL_DIR)/$(DUT).f + +# handle file list files +process_f_file = $(call process_f_files,$(addprefix $(dir $1),$(shell cat $1))) +process_f_files = $(foreach f,$1,$(if $(filter %.f,$f),$(call process_f_file,$f),$f)) +uniq_base = $(if $1,$(call uniq_base,$(foreach f,$1,$(if $(filter-out $(notdir $(lastword $1)),$(notdir $f)),$f,))) $(lastword $1)) +VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES))) + +# module parameters +export PARAM_DATA_W := 16 +export PARAM_CTRL_W := $(shell expr $(PARAM_DATA_W) / 8 ) +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_ENC_8B10B_EN := "1'b0" +export PARAM_DEC_8B10B_EN := $(PARAM_ENC_8B10B_EN) +export PARAM_PRBS31_EN := "1'b1" +export PARAM_TX_SERDES_PIPELINE := 2 +export PARAM_RX_SERDES_PIPELINE := 2 +export PARAM_COUNT_125US := 195 + +ifeq ($(SIM), icarus) + PLUSARGS += -fst + + COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v))) +else ifeq ($(SIM), verilator) + COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v))) + + ifeq ($(WAVES), 1) + COMPILE_ARGS += --trace-fst + VERILATOR_TRACE = 1 + endif +endif + +include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/src/eth/tb/taxi_eth_phy_1g_basex/basex.py b/src/eth/tb/taxi_eth_phy_1g_basex/basex.py new file mode 120000 index 0000000..306569f --- /dev/null +++ b/src/eth/tb/taxi_eth_phy_1g_basex/basex.py @@ -0,0 +1 @@ +../basex.py \ No newline at end of file diff --git a/src/eth/tb/taxi_eth_phy_1g_basex/test_taxi_eth_phy_1g_basex.py b/src/eth/tb/taxi_eth_phy_1g_basex/test_taxi_eth_phy_1g_basex.py new file mode 100644 index 0000000..8742041 --- /dev/null +++ b/src/eth/tb/taxi_eth_phy_1g_basex/test_taxi_eth_phy_1g_basex.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: CERN-OHL-S-2.0 +""" + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +""" + +import itertools +import logging +import os +import sys + +import cocotb_test.simulator + +import pytest +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge +from cocotb.regression import TestFactory + +from cocotbext.eth import GmiiSource, GmiiSink, GmiiFrame + +try: + from basex import BaseXSerdesSource, BaseXSerdesSink +except ImportError: + # attempt import from current directory + sys.path.insert(0, os.path.join(os.path.dirname(__file__))) + try: + from basex import BaseXSerdesSource, BaseXSerdesSink + finally: + del sys.path[0] + + +class TB: + def __init__(self, dut, gbx_cfg=None): + self.dut = dut + + self.log = logging.getLogger("cocotb.tb") + self.log.setLevel(logging.DEBUG) + + if len(dut.gmii_txd) == 16: + self.clk_period = 16 + else: + self.clk_period = 8 + + cocotb.start_soon(Clock(dut.tx_clk, self.clk_period, units="ns").start()) + cocotb.start_soon(Clock(dut.rx_clk, self.clk_period, units="ns").start()) + + self.gmii_source = GmiiSource(dut.gmii_txd, dut.gmii_tx_er, dut.gmii_tx_en, + dut.tx_clk, dut.tx_rst) + self.gmii_sink = GmiiSink(dut.gmii_rxd, dut.gmii_rx_er, dut.gmii_rx_dv, + dut.rx_clk, dut.rx_rst) + + self.serdes_source = BaseXSerdesSource( + data=dut.serdes_rx_data, + data_k=dut.serdes_rx_data_k, + data_valid=dut.serdes_rx_data_valid, + clock=dut.rx_clk, + enc_8b10b=False, + gbx_cfg=gbx_cfg + ) + self.serdes_sink = BaseXSerdesSink( + data=dut.serdes_tx_data, + data_k=dut.serdes_tx_data_k, + data_valid=dut.serdes_tx_data_valid, + gbx_req_sync=dut.serdes_tx_gbx_req_sync, + gbx_req_stall=dut.serdes_tx_gbx_req_stall, + gbx_sync=dut.serdes_tx_gbx_sync, + clock=dut.tx_clk, + dec_8b10b=False, + gbx_cfg=gbx_cfg + ) + + dut.cfg_tx_prbs31_enable.setimmediatevalue(0) + dut.cfg_rx_prbs31_enable.setimmediatevalue(0) + + async def reset(self): + self.dut.tx_rst.setimmediatevalue(0) + self.dut.rx_rst.setimmediatevalue(0) + await RisingEdge(self.dut.tx_clk) + await RisingEdge(self.dut.tx_clk) + self.dut.tx_rst.value = 1 + self.dut.rx_rst.value = 1 + await RisingEdge(self.dut.tx_clk) + await RisingEdge(self.dut.tx_clk) + self.dut.tx_rst.value = 0 + self.dut.rx_rst.value = 0 + await RisingEdge(self.dut.tx_clk) + await RisingEdge(self.dut.tx_clk) + + +async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12): + + tb = TB(dut) + + tb.gmii_source.ifg = ifg + tb.serdes_source.ifg = ifg + + await tb.reset() + + tb.log.info("Wait for block lock") + while not int(dut.rx_block_lock.value): + await RisingEdge(dut.rx_clk) + + # clear out sink buffer + tb.gmii_sink.clear() + + test_frames = [payload_data(x) for x in payload_lengths()] + + for test_data in test_frames: + test_frame = GmiiFrame.from_payload(test_data) + await tb.serdes_source.send(test_frame) + + for test_data in test_frames: + rx_frame = await tb.gmii_sink.recv() + + assert rx_frame.get_payload() == test_data + assert rx_frame.check_fcs() + + assert tb.gmii_sink.empty() + + await RisingEdge(dut.rx_clk) + await RisingEdge(dut.rx_clk) + + +async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12): + + tb = TB(dut) + + tb.gmii_source.ifg = ifg + tb.serdes_source.ifg = ifg + + await tb.reset() + + test_frames = [payload_data(x) for x in payload_lengths()] + + for test_data in test_frames: + test_frame = GmiiFrame.from_payload(test_data) + await tb.gmii_source.send(test_frame) + + for test_data in test_frames: + rx_frame = await tb.serdes_sink.recv() + + assert rx_frame.get_payload() == test_data + assert rx_frame.check_fcs() + + assert tb.serdes_sink.empty() + + await RisingEdge(dut.tx_clk) + await RisingEdge(dut.tx_clk) + + +def size_list(): + return list(range(60, 128)) + [512, 1514, 9214] + [60]*10 + + +def incrementing_payload(length): + return bytearray(itertools.islice(itertools.cycle(range(256)), length)) + + +def cycle_en(): + return itertools.cycle([0, 0, 0, 1]) + + +if getattr(cocotb, 'top', None) is not None: + + 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]) + factory.generate_tests() + + +# cocotb-test + +tests_dir = os.path.abspath(os.path.dirname(__file__)) +rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl')) +lib_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'lib')) +taxi_src_dir = os.path.abspath(os.path.join(lib_dir, 'taxi', 'src')) + + +def process_f_files(files): + lst = {} + for f in files: + if f[-2:].lower() == '.f': + with open(f, 'r') as fp: + l = fp.read().split() + for f in process_f_files([os.path.join(os.path.dirname(f), x) for x in l]): + lst[os.path.basename(f)] = f + else: + lst[os.path.basename(f)] = f + return list(lst.values()) + + +@pytest.mark.parametrize("data_w", [8, 16]) +def test_taxi_eth_phy_1g_basex(request, data_w): + dut = "taxi_eth_phy_1g_basex" + module = os.path.splitext(os.path.basename(__file__))[0] + toplevel = dut + + verilog_sources = [ + os.path.join(tests_dir, f"{toplevel}.sv"), + os.path.join(rtl_dir, f"{dut}.f"), + ] + + verilog_sources = process_f_files(verilog_sources) + + parameters = {} + + parameters['DATA_W'] = data_w + parameters['CTRL_W'] = parameters['DATA_W'] // 8 + parameters['TX_GBX_IF_EN'] = 0 + parameters['RX_GBX_IF_EN'] = parameters['TX_GBX_IF_EN'] + parameters['BIT_REVERSE'] = "1'b0" + parameters['ENC_8B10B_EN'] = "1'b0" + parameters['DEC_8B10B_EN'] = parameters['ENC_8B10B_EN'] + parameters['PRBS31_EN'] = "1'b1" + parameters['TX_SERDES_PIPELINE'] = 2 + parameters['RX_SERDES_PIPELINE'] = 2 + parameters['COUNT_125US'] = int(1250/6.4) + + extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()} + + extra_env['COCOTB_RESOLVE_X'] = 'RANDOM' + + sim_build = os.path.join(tests_dir, "sim_build", + request.node.name.replace('[', '-').replace(']', '')) + + cocotb_test.simulator.run( + simulator="verilator", + python_search=[tests_dir], + verilog_sources=verilog_sources, + toplevel=toplevel, + module=module, + parameters=parameters, + sim_build=sim_build, + extra_env=extra_env, + )