From 4a439783f114212e5f6db759c3abdb88e7df4c8f Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Sat, 22 Feb 2025 23:01:52 -0800 Subject: [PATCH] example/KR260: Add support for 10GBASE-R on KR260 Signed-off-by: Alex Forencich --- example/KR260/fpga/README.md | 2 + example/KR260/fpga/fpga_10g/Makefile | 48 ++ example/KR260/fpga/fpga_10g/config.tcl | 21 + example/KR260/fpga/fpga_1g/Makefile | 2 +- example/KR260/fpga/fpga_1g/config.tcl | 21 + example/KR260/fpga/rtl/fpga.sv | 158 ++++--- example/KR260/fpga/rtl/fpga_core.sv | 421 +++++++++++++++--- example/KR260/fpga/tb/fpga_core/Makefile | 2 + example/KR260/fpga/tb/fpga_core/baser.py | 1 + .../KR260/fpga/tb/fpga_core/test_fpga_core.py | 101 ++++- 10 files changed, 633 insertions(+), 144 deletions(-) create mode 100644 example/KR260/fpga/fpga_10g/Makefile create mode 100644 example/KR260/fpga/fpga_10g/config.tcl create mode 100644 example/KR260/fpga/fpga_1g/config.tcl create mode 120000 example/KR260/fpga/tb/fpga_core/baser.py diff --git a/example/KR260/fpga/README.md b/example/KR260/fpga/README.md index 0ed8177..29dd4f5 100644 --- a/example/KR260/fpga/README.md +++ b/example/KR260/fpga/README.md @@ -10,12 +10,14 @@ The design places looped-back MACs on the BASE-T ports and SFP+ cage. * Looped-back MAC via RGMII * SFP+ cage * Looped-back 1000BASE-X via Xilinx PCS/PMA core and GTH transceiver + * Looped-back 10GBASE-R MAC via GTH transceiver ## Board details * FPGA: xck26-sfvc784-2LV-c * 1000BASE-T PHY: TI DP83867CSRGZ via RGMII * 1000BASE-X PHY: Xilinx PCS/PMA core via GTH transceiver +* 10GBASE-R PHY: Soft PCS with GTH transceiver ## Licensing diff --git a/example/KR260/fpga/fpga_10g/Makefile b/example/KR260/fpga/fpga_10g/Makefile new file mode 100644 index 0000000..8ee899e --- /dev/null +++ b/example/KR260/fpga/fpga_10g/Makefile @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: MIT +# +# Copyright (c) 2025 FPGA Ninja, LLC +# +# Authors: +# - Alex Forencich +# + +# FPGA settings +FPGA_PART = xck26-sfvc784-2LV-c +FPGA_TOP = fpga +FPGA_ARCH = zynquplus + +# Files for synthesis +SYN_FILES = ../rtl/fpga.sv +SYN_FILES += ../rtl/fpga_core.sv +SYN_FILES += ../lib/taxi/rtl/eth/us/taxi_eth_mac_25g_us.f +SYN_FILES += ../lib/taxi/rtl/eth/taxi_eth_mac_1g_rgmii_fifo.f +SYN_FILES += ../lib/taxi/rtl/sync/taxi_sync_reset.sv +SYN_FILES += ../lib/taxi/rtl/sync/taxi_sync_signal.sv + +# XDC files +XDC_FILES = ../fpga.xdc +XDC_FILES += ../eth_rgmii.xdc +XDC_FILES += ../lib/taxi/syn/vivado/taxi_rgmii_phy_if.tcl +XDC_FILES += ../lib/taxi/syn/vivado/taxi_eth_mac_1g_rgmii.tcl +XDC_FILES += ../lib/taxi/syn/vivado/taxi_eth_mac_fifo.tcl +XDC_FILES += ../lib/taxi/syn/vivado/taxi_axis_async_fifo.tcl +XDC_FILES += ../lib/taxi/syn/vivado/taxi_sync_reset.tcl + +# IP +IP_TCL_FILES = ../lib/taxi/rtl/eth/us/taxi_eth_mac_25g_us_gth_10g_156.tcl + +# Configuration +CONFIG_TCL_FILES = ./config.tcl + +include ../common/vivado.mk + +program: $(PROJECT).bit + echo "open_hw_manager" > program.tcl + echo "connect_hw_server" >> program.tcl + echo "open_hw_target" >> program.tcl + echo "current_hw_device [lindex [get_hw_devices] 0]" >> program.tcl + echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> program.tcl + echo "set_property PROGRAM.FILE {$(PROJECT).bit} [current_hw_device]" >> program.tcl + echo "program_hw_devices [current_hw_device]" >> program.tcl + echo "exit" >> program.tcl + vivado -nojournal -nolog -mode batch -source program.tcl diff --git a/example/KR260/fpga/fpga_10g/config.tcl b/example/KR260/fpga/fpga_10g/config.tcl new file mode 100644 index 0000000..fe5171b --- /dev/null +++ b/example/KR260/fpga/fpga_10g/config.tcl @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: MIT +# +# Copyright (c) 2025 FPGA Ninja, LLC +# +# Authors: +# - Alex Forencich +# + +set params [dict create] + +# SFP+ rate +# 0 for 1G, 1 for 10G +dict set params SFP_RATE "1" + +# apply parameters to top-level +set param_list {} +dict for {name value} $params { + lappend param_list $name=$value +} + +set_property generic $param_list [get_filesets sources_1] diff --git a/example/KR260/fpga/fpga_1g/Makefile b/example/KR260/fpga/fpga_1g/Makefile index fd05f1a..8fe576d 100644 --- a/example/KR260/fpga/fpga_1g/Makefile +++ b/example/KR260/fpga/fpga_1g/Makefile @@ -32,7 +32,7 @@ XDC_FILES += ../lib/taxi/syn/vivado/taxi_sync_reset.tcl IP_TCL_FILES = ../ip/basex_pcs_pma_0.tcl # Configuration -# CONFIG_TCL_FILES = ./config.tcl +CONFIG_TCL_FILES = ./config.tcl include ../common/vivado.mk diff --git a/example/KR260/fpga/fpga_1g/config.tcl b/example/KR260/fpga/fpga_1g/config.tcl new file mode 100644 index 0000000..168762b --- /dev/null +++ b/example/KR260/fpga/fpga_1g/config.tcl @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: MIT +# +# Copyright (c) 2025 FPGA Ninja, LLC +# +# Authors: +# - Alex Forencich +# + +set params [dict create] + +# SFP+ rate +# 0 for 1G, 1 for 10G +dict set params SFP_RATE "0" + +# apply parameters to top-level +set param_list {} +dict for {name value} $params { + lappend param_list $name=$value +} + +set_property generic $param_list [get_filesets sources_1] diff --git a/example/KR260/fpga/rtl/fpga.sv b/example/KR260/fpga/rtl/fpga.sv index a70ebaa..1ffc82c 100644 --- a/example/KR260/fpga/rtl/fpga.sv +++ b/example/KR260/fpga/rtl/fpga.sv @@ -24,7 +24,9 @@ module fpga # // device family parameter string FAMILY = "zynquplus", // Use 90 degree clock for RGMII transmit - parameter logic USE_CLK90 = 1'b1 + parameter logic USE_CLK90 = 1'b1, + // SFP rate selection (0 for 1G, 1 for 10G) + parameter logic SFP_RATE = 1'b1 ) ( /* @@ -386,10 +388,13 @@ phy3_rx_ctl_idelay ( .CNTVALUEOUT() ); -// 1000BASE-X SFP +// SFP +wire sfp_tx_p_int; +wire sfp_tx_n_int; + wire sfp_gmii_clk_int; wire sfp_gmii_rst_int; -wire sfp_gmii_clk_en_int; +wire sfp_gmii_clk_en_int = 1'b1; wire [7:0] sfp_gmii_txd_int; wire sfp_gmii_tx_en_int; wire sfp_gmii_tx_er_int; @@ -397,82 +402,92 @@ wire [7:0] sfp_gmii_rxd_int; wire sfp_gmii_rx_dv_int; wire sfp_gmii_rx_er_int; -wire sfp_gmii_txuserclk2; -wire sfp_gmii_resetdone; +if (SFP_RATE == 0) begin : sfp_phy + // 1000BASE-X -assign sfp_gmii_clk_int = sfp_gmii_txuserclk2; + wire sfp_gmii_txuserclk2; + wire sfp_gmii_resetdone; -taxi_sync_reset #( - .N(4) -) -sync_reset_sfp_inst ( - .clk(sfp_gmii_clk_int), - .rst(rst_125mhz_int || !sfp_gmii_resetdone), - .out(sfp_gmii_rst_int) -); + assign sfp_gmii_clk_int = sfp_gmii_txuserclk2; -wire [15:0] sfp_status_vect; + taxi_sync_reset #( + .N(4) + ) + sync_reset_sfp_inst ( + .clk(sfp_gmii_clk_int), + .rst(rst_125mhz_int || !sfp_gmii_resetdone), + .out(sfp_gmii_rst_int) + ); -wire sfp_status_link_status = sfp_status_vect[0]; -wire sfp_status_link_synchronization = sfp_status_vect[1]; -wire sfp_status_rudi_c = sfp_status_vect[2]; -wire sfp_status_rudi_i = sfp_status_vect[3]; -wire sfp_status_rudi_invalid = sfp_status_vect[4]; -wire sfp_status_rxdisperr = sfp_status_vect[5]; -wire sfp_status_rxnotintable = sfp_status_vect[6]; -wire sfp_status_phy_link_status = sfp_status_vect[7]; -wire [1:0] sfp_status_remote_fault_encdg = sfp_status_vect[9:8]; -wire [1:0] sfp_status_speed = sfp_status_vect[11:10]; -wire sfp_status_duplex = sfp_status_vect[12]; -wire sfp_status_remote_fault = sfp_status_vect[13]; -wire [1:0] sfp_status_pause = sfp_status_vect[15:14]; + wire [15:0] sfp_status_vect; -wire [4:0] sfp_config_vect; + wire sfp_status_link_status = sfp_status_vect[0]; + wire sfp_status_link_synchronization = sfp_status_vect[1]; + wire sfp_status_rudi_c = sfp_status_vect[2]; + wire sfp_status_rudi_i = sfp_status_vect[3]; + wire sfp_status_rudi_invalid = sfp_status_vect[4]; + wire sfp_status_rxdisperr = sfp_status_vect[5]; + wire sfp_status_rxnotintable = sfp_status_vect[6]; + wire sfp_status_phy_link_status = sfp_status_vect[7]; + wire [1:0] sfp_status_remote_fault_encdg = sfp_status_vect[9:8]; + wire [1:0] sfp_status_speed = sfp_status_vect[11:10]; + wire sfp_status_duplex = sfp_status_vect[12]; + wire sfp_status_remote_fault = sfp_status_vect[13]; + wire [1:0] sfp_status_pause = sfp_status_vect[15:14]; -assign sfp_config_vect[4] = 1'b0; // autonegotiation enable -assign sfp_config_vect[3] = 1'b0; // isolate -assign sfp_config_vect[2] = 1'b0; // power down -assign sfp_config_vect[1] = 1'b0; // loopback enable -assign sfp_config_vect[0] = 1'b0; // unidirectional enable + wire [4:0] sfp_config_vect; -basex_pcs_pma_0 -sfp_pcspma ( - .gtrefclk_p(sfp_mgt_refclk_p), - .gtrefclk_n(sfp_mgt_refclk_n), - .gtrefclk_out(), - .txn(sfp_tx_n), - .txp(sfp_tx_p), - .rxn(sfp_rx_n), - .rxp(sfp_rx_p), - .independent_clock_bufg(clk_62mhz_int), - .userclk_out(), - .userclk2_out(sfp_gmii_txuserclk2), - .rxuserclk_out(), - .rxuserclk2_out(), - .gtpowergood(), - .resetdone(sfp_gmii_resetdone), - .pma_reset_out(), - .mmcm_locked_out(), - .gmii_txd(sfp_gmii_txd_int), - .gmii_tx_en(sfp_gmii_tx_en_int), - .gmii_tx_er(sfp_gmii_tx_er_int), - .gmii_rxd(sfp_gmii_rxd_int), - .gmii_rx_dv(sfp_gmii_rx_dv_int), - .gmii_rx_er(sfp_gmii_rx_er_int), - .gmii_isolate(), - .configuration_vector(sfp_config_vect), - .status_vector(sfp_status_vect), - .reset(rst_125mhz_int), - .signal_detect(1'b1) -); + assign sfp_config_vect[4] = 1'b0; // autonegotiation enable + assign sfp_config_vect[3] = 1'b0; // isolate + assign sfp_config_vect[2] = 1'b0; // power down + assign sfp_config_vect[1] = 1'b0; // loopback enable + assign sfp_config_vect[0] = 1'b0; // unidirectional enable -assign sfp_gmii_clk_en_int = 1'b1; + basex_pcs_pma_0 + sfp_pcspma ( + .gtrefclk_p(sfp_mgt_refclk_p), + .gtrefclk_n(sfp_mgt_refclk_n), + .gtrefclk_out(), + .txn(sfp_tx_n), + .txp(sfp_tx_p), + .rxn(sfp_rx_n), + .rxp(sfp_rx_p), + .independent_clock_bufg(clk_62mhz_int), + .userclk_out(), + .userclk2_out(sfp_gmii_txuserclk2), + .rxuserclk_out(), + .rxuserclk2_out(), + .gtpowergood(), + .resetdone(sfp_gmii_resetdone), + .pma_reset_out(), + .mmcm_locked_out(), + .gmii_txd(sfp_gmii_txd_int), + .gmii_tx_en(sfp_gmii_tx_en_int), + .gmii_tx_er(sfp_gmii_tx_er_int), + .gmii_rxd(sfp_gmii_rxd_int), + .gmii_rx_dv(sfp_gmii_rx_dv_int), + .gmii_rx_er(sfp_gmii_rx_er_int), + .gmii_isolate(), + .configuration_vector(sfp_config_vect), + .status_vector(sfp_status_vect), + .reset(rst_125mhz_int), + .signal_detect(1'b1) + ); + +end else begin + // 10GBASE-R + + assign sfp_tx_p = sfp_tx_p_int; + assign sfp_tx_n = sfp_tx_n_int; + +end fpga_core #( .SIM(SIM), .VENDOR(VENDOR), .FAMILY(FAMILY), - .USE_CLK90(USE_CLK90) + .USE_CLK90(USE_CLK90), + .SFP_RATE(SFP_RATE) ) core_inst ( /* @@ -509,8 +524,15 @@ core_inst ( .phy3_reset_n(phy3_reset_n), /* - * Ethernet: 1000BASE-X SFP + * Ethernet: SFP+ */ + .sfp_rx_p(sfp_rx_p), + .sfp_rx_n(sfp_rx_n), + .sfp_tx_p(sfp_tx_p_int), + .sfp_tx_n(sfp_tx_n_int), + .sfp_mgt_refclk_p(sfp_mgt_refclk_p), + .sfp_mgt_refclk_n(sfp_mgt_refclk_n), + .sfp_gmii_clk(sfp_gmii_clk_int), .sfp_gmii_rst(sfp_gmii_rst_int), .sfp_gmii_clk_en(sfp_gmii_clk_en_int), @@ -520,10 +542,12 @@ core_inst ( .sfp_gmii_txd(sfp_gmii_txd_int), .sfp_gmii_tx_en(sfp_gmii_tx_en_int), .sfp_gmii_tx_er(sfp_gmii_tx_er_int), + .sfp_tx_disable(sfp_tx_disable), .sfp_tx_fault(sfp_tx_fault_int), .sfp_rx_los(sfp_rx_los_int), .sfp_mod_abs(sfp_mod_abs_int), + .sfp_i2c_scl_i(sfp_i2c_scl_i), .sfp_i2c_scl_o(sfp_i2c_scl_o), .sfp_i2c_scl_t(sfp_i2c_scl_t), diff --git a/example/KR260/fpga/rtl/fpga_core.sv b/example/KR260/fpga/rtl/fpga_core.sv index 6ecc47a..fbe44c4 100644 --- a/example/KR260/fpga/rtl/fpga_core.sv +++ b/example/KR260/fpga/rtl/fpga_core.sv @@ -24,7 +24,9 @@ module fpga_core # // device family parameter string FAMILY = "zynquplus", // Use 90 degree clock for RGMII transmit - parameter logic USE_CLK90 = 1'b1 + parameter logic USE_CLK90 = 1'b1, + // SFP rate selection (0 for 1G, 1 for 10G) + parameter logic SFP_RATE = 1'b1 ) ( /* @@ -61,8 +63,15 @@ module fpga_core # output wire logic phy3_reset_n, /* - * Ethernet: 1000BASE-X SFP + * Ethernet: SFP+ */ + input wire logic sfp_rx_p, + input wire logic sfp_rx_n, + output wire logic sfp_tx_p, + output wire logic sfp_tx_n, + input wire logic sfp_mgt_refclk_p, + input wire logic sfp_mgt_refclk_n, + input wire logic sfp_gmii_clk, input wire logic sfp_gmii_rst, input wire logic sfp_gmii_clk_en, @@ -77,6 +86,7 @@ module fpga_core # input wire logic sfp_tx_fault, input wire logic sfp_rx_los, input wire logic sfp_mod_abs, + input wire logic sfp_i2c_scl_i, output wire logic sfp_i2c_scl_o, output wire logic sfp_i2c_scl_t, @@ -222,74 +232,359 @@ phy3_eth_mac_inst ( // SFP+ assign sfp_tx_disable = 1'b0; -taxi_axis_if #(.DATA_W(8), .ID_W(8)) axis_sfp_eth(); -taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_sfp_tx_cpl(); +if (SFP_RATE == 0) begin : sfp_mac -taxi_eth_mac_1g_fifo #( - .PADDING_EN(1), - .MIN_FRAME_LEN(64), - .TX_FIFO_DEPTH(16384), - .TX_FRAME_FIFO(1), - .RX_FIFO_DEPTH(16384), - .RX_FRAME_FIFO(1) -) -sfp_eth_mac_inst ( - .rx_clk(sfp_gmii_clk), - .rx_rst(sfp_gmii_rst), - .tx_clk(sfp_gmii_clk), - .tx_rst(sfp_gmii_rst), - .logic_clk(clk), - .logic_rst(rst), + taxi_axis_if #(.DATA_W(8), .ID_W(8)) axis_sfp_eth(); + taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_sfp_tx_cpl(); - /* - * Transmit interface (AXI stream) - */ - .s_axis_tx(axis_sfp_eth), - .m_axis_tx_cpl(axis_sfp_tx_cpl), + taxi_eth_mac_1g_fifo #( + .PADDING_EN(1), + .MIN_FRAME_LEN(64), + .TX_FIFO_DEPTH(16384), + .TX_FRAME_FIFO(1), + .RX_FIFO_DEPTH(16384), + .RX_FRAME_FIFO(1) + ) + sfp_eth_mac_inst ( + .rx_clk(sfp_gmii_clk), + .rx_rst(sfp_gmii_rst), + .tx_clk(sfp_gmii_clk), + .tx_rst(sfp_gmii_rst), + .logic_clk(clk), + .logic_rst(rst), - /* - * Receive interface (AXI stream) - */ - .m_axis_rx(axis_sfp_eth), + /* + * Transmit interface (AXI stream) + */ + .s_axis_tx(axis_sfp_eth), + .m_axis_tx_cpl(axis_sfp_tx_cpl), - /* - * GMII interface - */ - .gmii_rxd(sfp_gmii_rxd), - .gmii_rx_dv(sfp_gmii_rx_dv), - .gmii_rx_er(sfp_gmii_rx_er), - .gmii_txd(sfp_gmii_txd), - .gmii_tx_en(sfp_gmii_tx_en), - .gmii_tx_er(sfp_gmii_tx_er), + /* + * Receive interface (AXI stream) + */ + .m_axis_rx(axis_sfp_eth), - /* - * Control - */ - .rx_clk_enable(sfp_gmii_clk_en), - .tx_clk_enable(sfp_gmii_clk_en), - .rx_mii_select(1'b0), - .tx_mii_select(1'b0), + /* + * GMII interface + */ + .gmii_rxd(sfp_gmii_rxd), + .gmii_rx_dv(sfp_gmii_rx_dv), + .gmii_rx_er(sfp_gmii_rx_er), + .gmii_txd(sfp_gmii_txd), + .gmii_tx_en(sfp_gmii_tx_en), + .gmii_tx_er(sfp_gmii_tx_er), - /* - * Status - */ - .tx_error_underflow(), - .tx_fifo_overflow(), - .tx_fifo_bad_frame(), - .tx_fifo_good_frame(), - .rx_error_bad_frame(), - .rx_error_bad_fcs(), - .rx_fifo_overflow(), - .rx_fifo_bad_frame(), - .rx_fifo_good_frame(), + /* + * Control + */ + .rx_clk_enable(sfp_gmii_clk_en), + .tx_clk_enable(sfp_gmii_clk_en), + .rx_mii_select(1'b0), + .tx_mii_select(1'b0), - /* - * Configuration - */ - .cfg_ifg(8'd12), - .cfg_tx_enable(1'b1), - .cfg_rx_enable(1'b1) -); + /* + * Status + */ + .tx_error_underflow(), + .tx_fifo_overflow(), + .tx_fifo_bad_frame(), + .tx_fifo_good_frame(), + .rx_error_bad_frame(), + .rx_error_bad_fcs(), + .rx_fifo_overflow(), + .rx_fifo_bad_frame(), + .rx_fifo_good_frame(), + + /* + * Configuration + */ + .cfg_ifg(8'd12), + .cfg_tx_enable(1'b1), + .cfg_rx_enable(1'b1) + ); + +end else begin : sfp_mac + + wire sfp_tx_clk; + wire sfp_tx_rst; + wire sfp_rx_clk; + wire sfp_rx_rst; + + wire sfp_rx_status; + + wire sfp_gtpowergood; + + wire sfp_mgt_refclk; + wire sfp_mgt_refclk_int; + wire sfp_mgt_refclk_bufg; + + wire sfp_rst; + + taxi_axis_if #(.DATA_W(64), .ID_W(8)) axis_sfp_tx[0:0](); + taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_sfp_tx_cpl[0:0](); + taxi_axis_if #(.DATA_W(64), .ID_W(8)) axis_sfp_rx[0:0](); + + if (SIM) begin + + assign sfp_gtpowergood = 1'b1; + + assign sfp_mgt_refclk = sfp_mgt_refclk_p; + assign sfp_mgt_refclk_int = sfp_mgt_refclk_p; + assign sfp_mgt_refclk_bufg = sfp_mgt_refclk_int; + + end else begin + + IBUFDS_GTE4 ibufds_gte3_sfp_mgt_refclk_inst ( + .I (sfp_mgt_refclk_p), + .IB (sfp_mgt_refclk_n), + .CEB (1'b0), + .O (sfp_mgt_refclk), + .ODIV2 (sfp_mgt_refclk_int) + ); + + BUFG_GT bufg_gt_sfp_mgt_refclk_inst ( + .CE (sfp_gtpowergood), + .CEMASK (1'b1), + .CLR (1'b0), + .CLRMASK (1'b1), + .DIV (3'd0), + .I (sfp_mgt_refclk_int), + .O (sfp_mgt_refclk_bufg) + ); + + end + + taxi_sync_reset #( + .N(4) + ) + sfp_sync_reset_inst ( + .clk(sfp_mgt_refclk_bufg), + .rst(rst), + .out(sfp_rst) + ); + + taxi_eth_mac_25g_us #( + .SIM(SIM), + .VENDOR(VENDOR), + .FAMILY(FAMILY), + + .CNT(1), + + // GT type + .GT_TYPE("GTH"), + + // PHY parameters + .PADDING_EN(1'b1), + .DIC_EN(1'b1), + .MIN_FRAME_LEN(64), + .PTP_TS_EN(1'b0), + .PTP_TS_FMT_TOD(1'b1), + .PTP_TS_W(96), + .PRBS31_EN(1'b0), + .TX_SERDES_PIPELINE(1), + .RX_SERDES_PIPELINE(1), + .COUNT_125US(125000/6.4) + ) + sfp_mac_inst ( + .xcvr_ctrl_clk(clk), + .xcvr_ctrl_rst(sfp_rst), + + /* + * Common + */ + .xcvr_gtpowergood_out(sfp_gtpowergood), + .xcvr_gtrefclk00_in(sfp_mgt_refclk), + .xcvr_qpll0lock_out(), + .xcvr_qpll0clk_out(), + .xcvr_qpll0refclk_out(), + + /* + * Serial data + */ + .xcvr_txp(sfp_tx_p), + .xcvr_txn(sfp_tx_n), + .xcvr_rxp(sfp_rx_p), + .xcvr_rxn(sfp_rx_n), + + /* + * MAC clocks + */ + .rx_clk(sfp_rx_clk), + .rx_rst_in('0), + .rx_rst_out(sfp_rx_rst), + .tx_clk(sfp_tx_clk), + .tx_rst_in('0), + .tx_rst_out(sfp_tx_rst), + .ptp_sample_clk('0), + + /* + * Transmit interface (AXI stream) + */ + .s_axis_tx(axis_sfp_tx), + .m_axis_tx_cpl(axis_sfp_tx_cpl), + + /* + * Receive interface (AXI stream) + */ + .m_axis_rx(axis_sfp_rx), + + /* + * PTP clock + */ + .tx_ptp_ts('0), + .tx_ptp_ts_step('0), + .rx_ptp_ts('0), + .rx_ptp_ts_step('0), + + + /* + * Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE) + */ + .tx_lfc_req('0), + .tx_lfc_resend('0), + .rx_lfc_en('0), + .rx_lfc_req(), + .rx_lfc_ack('0), + + /* + * Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC) + */ + .tx_pfc_req('0), + .tx_pfc_resend('0), + .rx_pfc_en('0), + .rx_pfc_req(), + .rx_pfc_ack('0), + + /* + * Pause interface + */ + .tx_lfc_pause_en('0), + .tx_pause_req('0), + .tx_pause_ack(), + + /* + * Status + */ + .tx_start_packet(), + .tx_error_underflow(), + .rx_start_packet(), + .rx_error_count(), + .rx_error_bad_frame(), + .rx_error_bad_fcs(), + .rx_bad_block(), + .rx_sequence_error(), + .rx_block_lock(), + .rx_high_ber(), + .rx_status(sfp_rx_status), + .stat_tx_mcf(), + .stat_rx_mcf(), + .stat_tx_lfc_pkt(), + .stat_tx_lfc_xon(), + .stat_tx_lfc_xoff(), + .stat_tx_lfc_paused(), + .stat_tx_pfc_pkt(), + .stat_tx_pfc_xon(), + .stat_tx_pfc_xoff(), + .stat_tx_pfc_paused(), + .stat_rx_lfc_pkt(), + .stat_rx_lfc_xon(), + .stat_rx_lfc_xoff(), + .stat_rx_lfc_paused(), + .stat_rx_pfc_pkt(), + .stat_rx_pfc_xon(), + .stat_rx_pfc_xoff(), + .stat_rx_pfc_paused(), + + /* + * Configuration + */ + .cfg_ifg('{1{8'd12}}), + .cfg_tx_enable('1), + .cfg_rx_enable('1), + .cfg_tx_prbs31_enable('0), + .cfg_rx_prbs31_enable('0), + .cfg_mcf_rx_eth_dst_mcast('{1{48'h01_80_C2_00_00_01}}), + .cfg_mcf_rx_check_eth_dst_mcast('1), + .cfg_mcf_rx_eth_dst_ucast('{1{48'd0}}), + .cfg_mcf_rx_check_eth_dst_ucast('0), + .cfg_mcf_rx_eth_src('{1{48'd0}}), + .cfg_mcf_rx_check_eth_src('0), + .cfg_mcf_rx_eth_type('{1{16'h8808}}), + .cfg_mcf_rx_opcode_lfc('{1{16'h0001}}), + .cfg_mcf_rx_check_opcode_lfc('1), + .cfg_mcf_rx_opcode_pfc('{1{16'h0101}}), + .cfg_mcf_rx_check_opcode_pfc('1), + .cfg_mcf_rx_forward('0), + .cfg_mcf_rx_enable('0), + .cfg_tx_lfc_eth_dst('{1{48'h01_80_C2_00_00_01}}), + .cfg_tx_lfc_eth_src('{1{48'h80_23_31_43_54_4C}}), + .cfg_tx_lfc_eth_type('{1{16'h8808}}), + .cfg_tx_lfc_opcode('{1{16'h0001}}), + .cfg_tx_lfc_en('0), + .cfg_tx_lfc_quanta('{1{16'hffff}}), + .cfg_tx_lfc_refresh('{1{16'h7fff}}), + .cfg_tx_pfc_eth_dst('{1{48'h01_80_C2_00_00_01}}), + .cfg_tx_pfc_eth_src('{1{48'h80_23_31_43_54_4C}}), + .cfg_tx_pfc_eth_type('{1{16'h8808}}), + .cfg_tx_pfc_opcode('{1{16'h0101}}), + .cfg_tx_pfc_en('0), + .cfg_tx_pfc_quanta('{1{'{8{16'hffff}}}}), + .cfg_tx_pfc_refresh('{1{'{8{16'h7fff}}}}), + .cfg_rx_lfc_opcode('{1{16'h0001}}), + .cfg_rx_lfc_en('0), + .cfg_rx_pfc_opcode('{1{16'h0101}}), + .cfg_rx_pfc_en('0) + ); + + taxi_axis_async_fifo #( + .DEPTH(16384), + .RAM_PIPELINE(2), + .FRAME_FIFO(1), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_OVERSIZE_FRAME(1), + .DROP_BAD_FRAME(1), + .DROP_WHEN_FULL(1) + ) + sfp_mac_fifo ( + /* + * AXI4-Stream input (sink) + */ + .s_clk(sfp_rx_clk), + .s_rst(sfp_rx_rst), + .s_axis(axis_sfp_rx[0]), + + /* + * AXI4-Stream output (source) + */ + .m_clk(sfp_tx_clk), + .m_rst(sfp_tx_rst), + .m_axis(axis_sfp_tx[0]), + + /* + * Pause + */ + .s_pause_req(1'b0), + .s_pause_ack(), + .m_pause_req(1'b0), + .m_pause_ack(), + + /* + * Status + */ + .s_status_depth(), + .s_status_depth_commit(), + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_depth(), + .m_status_depth_commit(), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() + ); + +end endmodule diff --git a/example/KR260/fpga/tb/fpga_core/Makefile b/example/KR260/fpga/tb/fpga_core/Makefile index 64acd4b..aa22e54 100644 --- a/example/KR260/fpga/tb/fpga_core/Makefile +++ b/example/KR260/fpga/tb/fpga_core/Makefile @@ -20,6 +20,7 @@ MODULE = $(COCOTB_TEST_MODULES) TOPLEVEL = $(COCOTB_TOPLEVEL) VERILOG_SOURCES += ../../rtl/$(DUT).sv VERILOG_SOURCES += ../../lib/taxi/rtl/eth/taxi_eth_mac_1g_fifo.f +VERILOG_SOURCES += ../../lib/taxi/rtl/eth/us/taxi_eth_mac_25g_us.f VERILOG_SOURCES += ../../lib/taxi/rtl/eth/taxi_eth_mac_1g_rgmii_fifo.f VERILOG_SOURCES += ../../lib/taxi/rtl/sync/taxi_sync_reset.sv VERILOG_SOURCES += ../../lib/taxi/rtl/sync/taxi_sync_signal.sv @@ -35,6 +36,7 @@ export PARAM_SIM := "1'b1" export PARAM_VENDOR := "\"XILINX\"" export PARAM_FAMILY := "\"zynquplus\"" export PARAM_USE_CLK90 := "1'b1" +export PARAM_SFP_RATE := "1'b1" ifeq ($(SIM), icarus) PLUSARGS += -fst diff --git a/example/KR260/fpga/tb/fpga_core/baser.py b/example/KR260/fpga/tb/fpga_core/baser.py new file mode 120000 index 0000000..ac1737a --- /dev/null +++ b/example/KR260/fpga/tb/fpga_core/baser.py @@ -0,0 +1 @@ +../../lib/taxi/tb/eth/baser.py \ No newline at end of file diff --git a/example/KR260/fpga/tb/fpga_core/test_fpga_core.py b/example/KR260/fpga/tb/fpga_core/test_fpga_core.py index 93802ea..c798ec9 100644 --- a/example/KR260/fpga/tb/fpga_core/test_fpga_core.py +++ b/example/KR260/fpga/tb/fpga_core/test_fpga_core.py @@ -11,7 +11,9 @@ Authors: import logging import os +import sys +import pytest import cocotb_test.simulator import cocotb @@ -20,6 +22,17 @@ from cocotb.clock import Clock from cocotb.triggers import RisingEdge, Timer, Combine from cocotbext.eth import GmiiFrame, GmiiSource, GmiiSink, RgmiiPhy +from cocotbext.eth import XgmiiFrame + +try: + from baser import BaseRSerdesSource, BaseRSerdesSink +except ImportError: + # attempt import from current directory + sys.path.insert(0, os.path.join(os.path.dirname(__file__))) + try: + from baser import BaseRSerdesSource, BaseRSerdesSink + finally: + del sys.path[0] class TB: @@ -29,18 +42,29 @@ class TB: self.log = SimLog("cocotb.tb") self.log.setLevel(logging.DEBUG) - cocotb.start_soon(Clock(dut.sfp_gmii_clk, 8, units="ns").start()) - self.baset_phy2 = RgmiiPhy(dut.phy2_rgmii_txd, dut.phy2_rgmii_tx_ctl, dut.phy2_rgmii_tx_clk, dut.phy2_rgmii_rxd, dut.phy2_rgmii_rx_ctl, dut.phy2_rgmii_rx_clk, speed=speed) self.baset_phy3 = RgmiiPhy(dut.phy3_rgmii_txd, dut.phy3_rgmii_tx_ctl, dut.phy3_rgmii_tx_clk, dut.phy3_rgmii_rxd, dut.phy3_rgmii_rx_ctl, dut.phy3_rgmii_rx_clk, speed=speed) - self.sfp_source = GmiiSource(dut.sfp_gmii_rxd, dut.sfp_gmii_rx_er, dut.sfp_gmii_rx_dv, - dut.sfp_gmii_clk, dut.sfp_gmii_rst, dut.sfp_gmii_clk_en) - self.sfp_sink = GmiiSink(dut.sfp_gmii_txd, dut.sfp_gmii_tx_er, dut.sfp_gmii_tx_en, - dut.sfp_gmii_clk, dut.sfp_gmii_rst, dut.sfp_gmii_clk_en) + if dut.SFP_RATE.value == 0: + cocotb.start_soon(Clock(dut.sfp_gmii_clk, 8, units="ns").start()) + + self.sfp_source = GmiiSource(dut.sfp_gmii_rxd, dut.sfp_gmii_rx_er, dut.sfp_gmii_rx_dv, + dut.sfp_gmii_clk, dut.sfp_gmii_rst, dut.sfp_gmii_clk_en) + self.sfp_sink = GmiiSink(dut.sfp_gmii_txd, dut.sfp_gmii_tx_er, dut.sfp_gmii_tx_en, + dut.sfp_gmii_clk, dut.sfp_gmii_rst, dut.sfp_gmii_clk_en) + else: + cocotb.start_soon(Clock(dut.sfp_mgt_refclk_p, 6.4, units="ns").start()) + + ch = dut.sfp_mac.sfp_mac_inst.ch[0] + + cocotb.start_soon(Clock(ch.ch_inst.tx_clk, 6.4, units="ns").start()) + cocotb.start_soon(Clock(ch.ch_inst.rx_clk, 6.4, units="ns").start()) + + self.sfp_source = BaseRSerdesSource(ch.ch_inst.serdes_rx_data, ch.ch_inst.serdes_rx_hdr, ch.ch_inst.rx_clk, slip=ch.ch_inst.serdes_rx_bitslip, reverse=True) + self.sfp_sink = BaseRSerdesSink(ch.ch_inst.serdes_tx_data, ch.ch_inst.serdes_tx_hdr, ch.ch_inst.tx_clk, reverse=True) cocotb.start_soon(self._run_clk()) @@ -61,6 +85,9 @@ class TB: self.dut.rst.value = 0 self.dut.sfp_gmii_rst.value = 0 + for k in range(10): + await RisingEdge(self.dut.clk) + async def _run_clk(self): t = Timer(2, 'ns') while True: @@ -116,6 +143,46 @@ async def mac_test(tb, source, sink): tb.log.info("MAC test done") +async def mac_test_25g(tb, source, sink): + tb.log.info("Test MAC") + + tb.log.info("Multiple small packets") + + count = 64 + + pkts = [bytearray([(x+k) % 256 for x in range(60)]) for k in range(count)] + + for p in pkts: + await source.send(XgmiiFrame.from_payload(p)) + + for k in range(count): + rx_frame = await sink.recv() + + tb.log.info("RX frame: %s", rx_frame) + + assert rx_frame.get_payload() == pkts[k] + assert rx_frame.check_fcs() + + tb.log.info("Multiple large packets") + + count = 32 + + pkts = [bytearray([(x+k) % 256 for x in range(1514)]) for k in range(count)] + + for p in pkts: + await source.send(XgmiiFrame.from_payload(p)) + + for k in range(count): + rx_frame = await sink.recv() + + tb.log.info("RX frame: %s", rx_frame) + + assert rx_frame.get_payload() == pkts[k] + assert rx_frame.check_fcs() + + tb.log.info("MAC test done") + + @cocotb.test() async def run_test(dut): @@ -123,19 +190,24 @@ async def run_test(dut): await tb.init() + tests = [] + tb.log.info("Start BASE-T MAC loopback test on PHY2") - phy2_test_cr = cocotb.start_soon(mac_test(tb, tb.baset_phy2.rx, tb.baset_phy2.tx)) + tests.append(cocotb.start_soon(mac_test(tb, tb.baset_phy2.rx, tb.baset_phy2.tx))) tb.log.info("Start BASE-T MAC loopback test on PHY3") - phy3_test_cr = cocotb.start_soon(mac_test(tb, tb.baset_phy3.rx, tb.baset_phy3.tx)) + tests.append(cocotb.start_soon(mac_test(tb, tb.baset_phy3.rx, tb.baset_phy3.tx))) - tb.log.info("Start SFP MAC loopback test") + if dut.SFP_RATE.value == 0: + tb.log.info("Start 1G SFP MAC loopback test") + tests.append(cocotb.start_soon(mac_test(tb, tb.sfp_source, tb.sfp_sink))) + else: + tb.log.info("Start 10G SFP MAC loopback test") + tests.append(cocotb.start_soon(mac_test_25g(tb, tb.sfp_source, tb.sfp_sink))) - sfp_test_cr = cocotb.start_soon(mac_test(tb, tb.sfp_source, tb.sfp_sink)) - - await Combine(phy2_test_cr, phy3_test_cr, sfp_test_cr) + await Combine(*tests) await RisingEdge(dut.clk) await RisingEdge(dut.clk) @@ -161,7 +233,8 @@ def process_f_files(files): return list(lst.values()) -def test_fpga_core(request): +@pytest.mark.parametrize("sfp_rate", [0, 1]) +def test_fpga_core(request, sfp_rate): dut = "fpga_core" module = os.path.splitext(os.path.basename(__file__))[0] toplevel = dut @@ -169,6 +242,7 @@ def test_fpga_core(request): verilog_sources = [ os.path.join(rtl_dir, f"{dut}.sv"), os.path.join(lib_dir, "taxi", "rtl", "eth", "taxi_eth_mac_1g_fifo.f"), + os.path.join(lib_dir, "taxi", "rtl", "eth", "us", "taxi_eth_mac_25g_us.f"), os.path.join(lib_dir, "taxi", "rtl", "eth", "taxi_eth_mac_1g_rgmii_fifo.f"), os.path.join(lib_dir, "taxi", "rtl", "sync", "taxi_sync_reset.sv"), os.path.join(lib_dir, "taxi", "rtl", "sync", "taxi_sync_signal.sv"), @@ -182,6 +256,7 @@ def test_fpga_core(request): parameters['VENDOR'] = "\"XILINX\"" parameters['FAMILY'] = "\"zynquplus\"" parameters['USE_CLK90'] = "1'b1" + parameters['SFP_RATE'] = f"1'b{sfp_rate}" extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}