diff options
Diffstat (limited to 'fpga/usrp3/top/x400/sim/x4xx_qsfp_wrapper')
3 files changed, 1852 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x400/sim/x4xx_qsfp_wrapper/Makefile b/fpga/usrp3/top/x400/sim/x4xx_qsfp_wrapper/Makefile new file mode 100644 index 000000000..0e669b6e5 --- /dev/null +++ b/fpga/usrp3/top/x400/sim/x4xx_qsfp_wrapper/Makefile @@ -0,0 +1,134 @@ +# +# Copyright 2021 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +#------------------------------------------------- +# Top-of-Makefile +#------------------------------------------------- +# Define BASE_DIR to point to the "top" dir +BASE_DIR = $(abspath ../../../../top) +IP_DIR = $(BASE_DIR)/x400/ip + + +# Include viv_sim_preamble after defining BASE_DIR +include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak +IP_BUILD_DIR = $(BASE_DIR)/x400/build-ip/xczu28drffvg1517-1e + +#------------------------------------------------- +# Design Specific +#------------------------------------------------- +# Define part using PART_ID (<device>/<package>/<speedgrade>) +ARCH = zynquplusRFSOC +PART_ID = xczu28dr/ffvg1517/-1/e + +# Include makefiles and sources for the DUT and its dependencies +include $(BASE_DIR)/../lib/control/Makefile.srcs +include $(BASE_DIR)/../lib/axi/Makefile.srcs +include $(BASE_DIR)/../lib/axi4_sv/Makefile.srcs +include $(BASE_DIR)/../lib/axi4s_sv/Makefile.srcs +include $(BASE_DIR)/../lib/axi4lite_sv/Makefile.srcs +include $(BASE_DIR)/../lib/packet_proc/Makefile.srcs +include $(BASE_DIR)/../lib/xge_interface/Makefile.srcs +include $(BASE_DIR)/../lib/xge/Makefile.srcs +include $(BASE_DIR)/../lib/wb_spi/Makefile.srcs +include $(BASE_DIR)/../lib/fifo/Makefile.srcs +include $(BASE_DIR)/../lib/rfnoc/utils/Makefile.srcs +include $(BASE_DIR)/../lib/rfnoc/xport/Makefile.srcs +include $(BASE_DIR)/../lib/rfnoc/xport_sv/Makefile.srcs +include $(BASE_DIR)/../lib/rfnoc/crossbar/Makefile.srcs +include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs +include $(BASE_DIR)/../lib/xge/Makefile.srcs +include $(IP_DIR)/Makefile.inc + + +IP_AXI_ETH_DMA_BD_HDL_SIM_SRCS = $(wildcard $(addprefix $(IP_BUILD_DIR)/axi_eth_dma_bd/axi_eth_dma_bd/, \ +sim/axi_eth_dma_bd.v\ +ip/*/sim/*.h\ +ip/*/sim/*.v\ +ip/*/sim/*.vhd\ +ip/*/bd_0/hdl/*.v\ +ip/*/bd_0/sim/*.v\ +ip/*/bd_0/ip/ip_*/sim/*.v\ +ip/*/bd_0/ip/ip_*/sim/*.sv\ +ip/*/bd_0/ip/ip_*/sim/*.vhd\ +ipshared/*/hdl/*.sv\ +ipshared/*/hdl/*.v\ +ipshared/*/simulation/*.v\ +ipshared/*/hdl/verilog/*.v\ +ipshared/*/hdl/verilog/*.svh\ +ipshared/*/hdl/verilog/*.vh\ +)) + +IP_AXI_INTERCONNECT_DMA_BD_HDL_SIM_SRCS = $(wildcard $(addprefix $(IP_BUILD_DIR)/axi_interconnect_dma_bd/axi_interconnect_dma_bd/, \ +ip/*/sim/*.v\ +)) + +TOP_SRC = \ +$(abspath $(BASE_DIR)/x400/x4xx_qsfp_wrapper.sv) \ +$(abspath $(BASE_DIR)/x400/x4xx_qsfp_wrapper_temp.sv) \ +$(abspath $(BASE_DIR)/x400/x4xx_mgt_io_core.sv) \ +$(abspath $(BASE_DIR)/x400/x4xx.v) + +# Xilinx IP wants lots of libraries +MODELSIM_LIBS += secureip unimacro_ver unisims_ver xilinx_vip xpm +# Needed for the HACK_SRC, speeds up the alignment phase (still long!) +VLOG_ARGS = +define+SIM_SPEED_UP +SVLOG_ARGS = -lint +define+BUILD_100G=1 +# Xilinx IP wants a second file loaded +MODELSIM_ARGS = glbl -t 1fs + +DESIGN_SRCS = $(abspath \ +$(AXI4_SV_SRCS) \ +$(AXI4S_SV_SRCS) \ +$(AXI4LITE_SV_SRCS) \ +$(FIFO_SRCS) \ +$(CONTROL_LIB_SRCS) \ +$(AXI_SRCS) \ +$(XGE_INTERFACE_SRCS) \ +$(PACKET_PROC_SRCS) \ +$(RFNOC_UTIL_SRCS) \ +$(RFNOC_XPORT_SRCS) \ +$(RFNOC_XPORT_SV_SRCS) \ +$(RFNOC_XBAR_SRCS) \ +$(RFNOC_CORE_SRCS) \ +$(WISHBONE_SRCS) \ +$(XGE_SRCS) \ +$(XGE_INTERFACE_SRCS) \ +$(XGE_PCS_PMA_SRCS) \ +$(IP_AXI_ETH_DMA_BD_HDL_SIM_SRCS) \ +$(IP_AXI_INTERCONNECT_DMA_BD_HDL_SIM_SRCS) \ +$(IP_AXI_INTERCONNECT_ETH_HDL_SRCS) \ +$(IP_AXI_INTERCONNECT_DMA_HDL_SRCS) \ +$(IP_AXI_ETH_DMA_BD_HDL_SRCS) \ +$(IP_100G_HDL_SRCS) \ +$(AURORA_PHY_SRCS) \ +$(IP_HDL_SIM_SRCS) \ +$(TOP_SRC) \ +) + +#------------------------------------------------- +# Testbench Specific +#------------------------------------------------- +# Define only one toplevel module +TB_TOP_MODULE ?= x4xx_qsfp_wrapper_all_tb + +SIM_TOP = $(TB_TOP_MODULE) + +SIM_SRCS = \ +$(abspath x4xx_qsfp_wrapper_tb.sv) \ +$(abspath $(TB_TOP_MODULE).sv) + +# Suppressing the following worthless reminder. +#* Warning: M:/usrp4-hw/oss-repo/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes.sv(228): (vlog-2583) [SVCHK] - +# Extra checking for conflicts with always_comb and always_latch variables is done at vopt time +SVLOG_ARGS = -suppress 2583 + +#------------------------------------------------- +# Bottom-of-Makefile +#------------------------------------------------- +# Include all simulator specific makefiles here +# Each should define a unique target to simulate +# e.g. xsim, vsim, etc and a common "clean" target +include $(BASE_DIR)/../tools/make/viv_simulator.mak diff --git a/fpga/usrp3/top/x400/sim/x4xx_qsfp_wrapper/x4xx_qsfp_wrapper_all_tb.sv b/fpga/usrp3/top/x400/sim/x4xx_qsfp_wrapper/x4xx_qsfp_wrapper_all_tb.sv new file mode 100644 index 000000000..2357d7c9c --- /dev/null +++ b/fpga/usrp3/top/x400/sim/x4xx_qsfp_wrapper/x4xx_qsfp_wrapper_all_tb.sv @@ -0,0 +1,77 @@ +// +// Copyright 2021 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: x4xx_qsfp_wrapper_all_tb +// +// Description: +// +// Testbench for the QSFP wrapper to allow testing all protocols. +// + +`include "./x4xx_mgt_types.vh" + +module x4xx_qsfp_wrapper_all_tb; + + x4xx_qsfp_wrapper_tb #( + .TEST_NAME ("100GbE_F"), + .PROTOCOL0 (`MGT_100GbE), + .CHDR_W (512), + .USE_MAC (0) + ) ETH_100Gb_fast (); + + x4xx_qsfp_wrapper_tb #( + .TEST_NAME ("10GbE_F"), + .PROTOCOL0 (`MGT_10GbE), + .CHDR_W (64), + .USE_MAC (0) + ) ETH_10Gb_fast (); + + x4xx_qsfp_wrapper_tb #( + .TEST_NAME ("10GbE_x4_F"), + .PROTOCOL0 (`MGT_10GbE), + .PROTOCOL1 (`MGT_10GbE), + .PROTOCOL2 (`MGT_10GbE), + .PROTOCOL3 (`MGT_10GbE), + .CHDR_W (64), + .USE_MAC (0) + ) ETH_10Gb_x4_fast (); + + x4xx_qsfp_wrapper_tb #( + .TEST_NAME ("100GbE_512S"), + .PROTOCOL0 (`MGT_100GbE), + .CHDR_W (512), + .USE_MAC (1) + ) ETH_100Gb_512serial (); + + x4xx_qsfp_wrapper_tb #( + .TEST_NAME ("100GbE_128S"), + .PROTOCOL0 (`MGT_100GbE), + .CHDR_W (128), + .USE_MAC (1) + ) ETH_100Gb_128serial (); + + x4xx_qsfp_wrapper_tb #( + .TEST_NAME ("10GbE_S"), + .PROTOCOL0 (`MGT_10GbE), + .CHDR_W (64), + .USE_MAC (1) + ) ETH_10Gb_serial (); + + bit clk,rst; + + sim_clock_gen #(100.0) clk_gen (clk, rst); + + // Wait for all done + always_ff@(posedge clk) begin + if (ETH_100Gb_fast.test.done && + ETH_10Gb_fast.test.done && + ETH_10Gb_x4_fast.test.done && + ETH_100Gb_512serial.test.done && + ETH_100Gb_128serial.test.done && + ETH_10Gb_serial.test.done + ) $finish(1); + end + +endmodule diff --git a/fpga/usrp3/top/x400/sim/x4xx_qsfp_wrapper/x4xx_qsfp_wrapper_tb.sv b/fpga/usrp3/top/x400/sim/x4xx_qsfp_wrapper/x4xx_qsfp_wrapper_tb.sv new file mode 100644 index 000000000..2d8172de9 --- /dev/null +++ b/fpga/usrp3/top/x400/sim/x4xx_qsfp_wrapper/x4xx_qsfp_wrapper_tb.sv @@ -0,0 +1,1641 @@ +// +// Copyright 2021 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: x4xx_qsfp_wrapper_tb +// +// Description: +// +// Testbench for x4xx_qsfp_wrapper. +// +// Parameters: +// +// TEST_NAME : String added to test output +// PROTOCOL : Must be {100Gbe, 10GbE, 1GbE, Aurora, Disabled} +// USE_MAC : When set simulate through MAC and PHY. When false, cut before +// the MAC. +// + +`include "./x4xx_mgt_types.vh" + + +module x4xx_qsfp_wrapper_tb #( + parameter TEST_NAME = "x4xx_qsfp_wrapper_tb", + parameter PROTOCOL0 = `MGT_10GbE, + parameter PROTOCOL1 = `MGT_Disabled, + parameter PROTOCOL2 = `MGT_Disabled, + parameter PROTOCOL3 = `MGT_Disabled, + parameter CHDR_W = 64, + parameter logic USE_MAC = 1 +) ( + /* no IO */ +); + // Include macros and time declarations for use with PkgTestExec + `define TEST_EXEC_OBJ test + `include "test_exec.svh" + import PkgAxiStreamBfm::*; + import PkgAxiLiteBfm::*; + import PkgTestExec::*; + import PkgChdrUtils::*; + import PkgChdrBfm::*; + import PkgEthernet::*; + + //--------------------------------------------------------------------------- + // Local Parameters + //--------------------------------------------------------------------------- + + localparam int CPU_W = 64; + + localparam int ENET_W = PROTOCOL0 == `MGT_100GbE?512:64; + // 10gbe Mac AUTOEXPANDS small packets > 64 bit + localparam AUTOEXPAND_TO_64 = PROTOCOL0 == `MGT_100GbE?0:USE_MAC; + + localparam [7:0] PORTNUM = 0; + localparam MDIO_EN = 0; + localparam [4:0] MDIO_PHYADDR = 0; + localparam [15:0] RFNOC_PROTOVER = {8'd1, 8'd0}; + + localparam ENET_USER_W = $clog2(ENET_W/8)+1; + localparam CPU_USER_W = $clog2(CPU_W/8)+1; + localparam CHDR_USER_W = $clog2(CHDR_W/8)+1; + // allows the DUT to push full words and tb does not check tuser/tkeep of packets it's transmitting + localparam IGNORE_EXTRA_DATA = 0; + + localparam PREAMBLE_BYTES = (PROTOCOL0 == `MGT_100GbE) ? 0 : 0; + localparam USER_CLK_PERIOD = (PROTOCOL0 == `MGT_100GbE) ? 3.1 : 6.4; + localparam SV_ETH_IFC = 1; + + localparam logic[3:0] DISABLED = {PROTOCOL3 == `MGT_Disabled, + PROTOCOL2 == `MGT_Disabled, + PROTOCOL1 == `MGT_Disabled, + PROTOCOL0 == `MGT_Disabled}; + localparam logic[3:0] IS10GBE = {PROTOCOL3 == `MGT_10GbE, + PROTOCOL2 == `MGT_10GbE, + PROTOCOL1 == `MGT_10GbE, + PROTOCOL0 == `MGT_10GbE}; + + //--------------------------------------------------------------------------- + // Clocks and resets + //--------------------------------------------------------------------------- + + bit clk200,clk100,clk40,clk156p25,userclk,sim_userclk; + bit clk200_rst,clk100_rst,clk40_rst,clk40_rstn,refclk_rst,userclk_rst,sim_userclk_rst; + logic refclk_p,refclk_n; + logic done = 0; + + + // 322.2666 MHz ref - clock generated by 100G core. + // 156.25 MHz ref - clock generated by 10G core. + // If we simulate the actual xilinx core, don't use this clock + sim_clock_gen #(.PERIOD(USER_CLK_PERIOD), .AUTOSTART(1)) + userclk_gen (.clk(sim_userclk), .rst(sim_userclk_rst)); + //156.25 MHz ref + sim_clock_gen #(.PERIOD(6.4), .AUTOSTART(1)) + refclk_gen (.clk(clk156p25), .rst(refclk_rst)); + always_comb begin + refclk_p = clk156p25; //156.25 + refclk_n = !clk156p25; + end + + sim_clock_gen #(.PERIOD(5.0), .AUTOSTART(1)) + clk200_gen (.clk(clk200), .rst(clk200_rst)); + sim_clock_gen #(.PERIOD(10.0), .AUTOSTART(1)) + clk100_gen (.clk(clk100), .rst(clk100_rst)); + sim_clock_gen #(.PERIOD(25.0), .AUTOSTART(1)) + clk40_gen (.clk(clk40), .rst(clk40_rst)); + always_comb clk40_rstn = !clk40_rst; + + //--------------------------------------------------------------------------- + // Bus Functional Models + //--------------------------------------------------------------------------- + TestExec test = new(); + + AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W)) + eth_tx [3:0] (userclk, userclk_rst); + AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W)) + eth_rx [3:0] (userclk, userclk_rst); + + AxiStreamIf #(.DATA_WIDTH(CHDR_W),.USER_WIDTH(CHDR_USER_W),.TKEEP(0),.TUSER(0)) + v2e [3:0] (clk200, clk200_rst); + AxiStreamIf #(.DATA_WIDTH(CHDR_W),.USER_WIDTH(CHDR_USER_W),.TKEEP(0),.TUSER(0)) + e2v [3:0] (clk200, clk200_rst); + + AxiStreamIf #(.DATA_WIDTH(CPU_W),.USER_WIDTH(CPU_USER_W),.TUSER(0)) + c2e [3:0] (clk40, clk40_rst); + AxiStreamIf #(.DATA_WIDTH(CPU_W),.USER_WIDTH(CPU_USER_W),.TUSER(0)) + e2c [3:0] (clk40, clk40_rst); + + AxiLiteIf #(.DATA_WIDTH(32),.ADDR_WIDTH(40)) + s_axi (clk40, clk40_rst); + + // Bus functional model for a axi_stream controller + AxiStreamBfm #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W)) eth []; + AxiStreamBfm #(.DATA_WIDTH(CHDR_W),.USER_WIDTH(CHDR_USER_W),.TKEEP(0),.TUSER(0)) v []; + AxiStreamBfm #(.DATA_WIDTH(CPU_W),.USER_WIDTH(CPU_USER_W),.TUSER(0)) cpu []; + AxiLiteBfm #(.DATA_WIDTH(32),.ADDR_WIDTH(40)) axi = new(.master(s_axi)); + + //--------------------------------------------------------------------------- + // Instantiate DUT + //--------------------------------------------------------------------------- + + logic [3:0] tx_p,tx_n,rx_p,rx_n; + logic [3:0] [31:0] port_info; + logic [15:0] device_id; + logic [3:0] link_up, activity; + logic QSFP_MODPRS_n =1'b0; + + logic [3:0] eth_rx_irq; + logic [3:0] eth_tx_irq; + + // ETH DMA AXI To CPU + AxiIf_v #(.DATA_WIDTH(128),.ADDR_WIDTH(49)) + axi_hp_v (clk40, clk40_rst); + + AxiLiteIf_v #(.DATA_WIDTH(32),.ADDR_WIDTH(40)) + s_axi_v (clk40, clk40_rst); + `include "../../../../../../lib/axi4lite_sv/axi_lite.vh" + `include "../../../../../../lib/axi4_sv/axi.vh" + always_comb begin + `AXI4LITE_ASSIGN(s_axi_v,s_axi) + axi_hp_v.arready = 1'b1; + axi_hp_v.awready = 1'b1; + axi_hp_v.wready = 1'b1; + axi_hp_v.rdata = '0; + axi_hp_v.rresp[1:0] = 2'b0; + axi_hp_v.rlast = 1'b0; + axi_hp_v.rvalid = 1'b0; + axi_hp_v.bresp[1:0] = 2'b0; + axi_hp_v.bvalid = 1'b0; + end + + `define MGT_IO0 dut.x4xx_qsfp_wrapper_i.mgt_lanes.lane_loop[0].x4xx_mgt_io_core_i + `define MGT_IO1 dut.x4xx_qsfp_wrapper_i.mgt_lanes.lane_loop[1].x4xx_mgt_io_core_i + `define MGT_IO2 dut.x4xx_qsfp_wrapper_i.mgt_lanes.lane_loop[2].x4xx_mgt_io_core_i + `define MGT_IO3 dut.x4xx_qsfp_wrapper_i.mgt_lanes.lane_loop[3].x4xx_mgt_io_core_i + `define QSFP_W dut.x4xx_qsfp_wrapper_i + + x4xx_qsfp_wrapper_temp #( + .PROTOCOL0 (PROTOCOL0), + .PROTOCOL1 (PROTOCOL1), + .PROTOCOL2 (PROTOCOL2), + .PROTOCOL3 (PROTOCOL3), + + .CPU_W (CPU_W), + .CHDR_W (CHDR_W), + .PORTNUM (0) + ) dut ( + .areset (refclk_rst), + .refclk_p (refclk_p), + .refclk_n (refclk_n), + .bus_rst (clk200_rst), + .clk40_rst (clk40_rst), + .clk100 (clk100), + .bus_clk (clk200), + .clk40 (clk40), + `AXI4_PORT_ASSIGN_NR(axi_hp,axi_hp_v) + `AXI4LITE_PORT_ASSIGN_NR(s_axi,s_axi_v) + .tx_p (tx_p), + .tx_n (tx_n), + .rx_p (rx_p), + .rx_n (rx_n), + + .e2v_tdata ({e2v[3].tdata, e2v[2].tdata, e2v[1].tdata, e2v[0].tdata}), + .e2v_tlast ({e2v[3].tlast, e2v[2].tlast, e2v[1].tlast, e2v[0].tlast}), + .e2v_tvalid ({e2v[3].tvalid, e2v[2].tvalid, e2v[1].tvalid, e2v[0].tvalid}), + .e2v_tready ({e2v[3].tready, e2v[2].tready, e2v[1].tready, e2v[0].tready}), + .v2e_tdata ({v2e[3].tdata, v2e[2].tdata, v2e[1].tdata, v2e[0].tdata}), + .v2e_tlast ({v2e[3].tlast, v2e[2].tlast, v2e[1].tlast, v2e[0].tlast}), + .v2e_tvalid ({v2e[3].tvalid, v2e[2].tvalid, v2e[1].tvalid, v2e[0].tvalid}), + .v2e_tready ({v2e[3].tready, v2e[2].tready, v2e[1].tready, v2e[0].tready}), + + .eth_tx_irq (eth_tx_irq), + .eth_rx_irq (eth_rx_irq), + + .rx_rec_clk_out (), + .device_id (device_id), + + .port_info_0 (port_info[0]), + .port_info_1 (port_info[1]), + .port_info_2 (port_info[2]), + .port_info_3 (port_info[3]), + + .link_up (link_up), + .activity (activity) + ); + + + //--------------------------------------------------------------------------- + // Connect to e2c c2e + //--------------------------------------------------------------------------- + // Ideally simulation would update to test Xilinx DMA block, but as a quick + // fix we are simulating just to the AXI stream bus. + if (PROTOCOL0 == `MGT_100GbE || PROTOCOL0 == `MGT_10GbE) begin + always_comb begin + e2c[0].tdata = `QSFP_W.mgt_lanes.lane_loop[0].eth_port.e2c.tdata; + e2c[0].tuser = `QSFP_W.mgt_lanes.lane_loop[0].eth_port.e2c.tuser; + e2c[0].tkeep = `QSFP_W.mgt_lanes.lane_loop[0].eth_port.e2c.tkeep; + e2c[0].tlast = `QSFP_W.mgt_lanes.lane_loop[0].eth_port.e2c.tlast; + e2c[0].tvalid = `QSFP_W.mgt_lanes.lane_loop[0].eth_port.e2c.tvalid; + force `QSFP_W.mgt_lanes.lane_loop[0].eth_port.e2c.tready = e2c[0].tready; + + force `QSFP_W.mgt_lanes.lane_loop[0].eth_port.c2e.tdata = c2e[0].tdata; + force `QSFP_W.mgt_lanes.lane_loop[0].eth_port.c2e.tuser = c2e[0].tuser; + force `QSFP_W.mgt_lanes.lane_loop[0].eth_port.c2e.tkeep = c2e[0].tkeep; + force `QSFP_W.mgt_lanes.lane_loop[0].eth_port.c2e.tlast = c2e[0].tlast; + force `QSFP_W.mgt_lanes.lane_loop[0].eth_port.c2e.tvalid = c2e[0].tvalid; + c2e[0].tready = `QSFP_W.mgt_lanes.lane_loop[0].eth_port.c2e.tready; + end + end + if (PROTOCOL1 == `MGT_10GbE) begin + always_comb begin + e2c[1].tdata = `QSFP_W.mgt_lanes.lane_loop[1].eth_port.e2c.tdata; + e2c[1].tuser = `QSFP_W.mgt_lanes.lane_loop[1].eth_port.e2c.tuser; + e2c[1].tkeep = `QSFP_W.mgt_lanes.lane_loop[1].eth_port.e2c.tkeep; + e2c[1].tlast = `QSFP_W.mgt_lanes.lane_loop[1].eth_port.e2c.tlast; + e2c[1].tvalid = `QSFP_W.mgt_lanes.lane_loop[1].eth_port.e2c.tvalid; + force `QSFP_W.mgt_lanes.lane_loop[1].eth_port.e2c.tready = e2c[1].tready; + + force `QSFP_W.mgt_lanes.lane_loop[1].eth_port.c2e.tdata = c2e[1].tdata; + force `QSFP_W.mgt_lanes.lane_loop[1].eth_port.c2e.tuser = c2e[1].tuser; + force `QSFP_W.mgt_lanes.lane_loop[1].eth_port.c2e.tkeep = c2e[1].tkeep; + force `QSFP_W.mgt_lanes.lane_loop[1].eth_port.c2e.tlast = c2e[1].tlast; + force `QSFP_W.mgt_lanes.lane_loop[1].eth_port.c2e.tvalid = c2e[1].tvalid; + c2e[1].tready = `QSFP_W.mgt_lanes.lane_loop[1].eth_port.c2e.tready; + end + end + if (PROTOCOL2 == `MGT_10GbE) begin + always_comb begin + e2c[2].tdata = `QSFP_W.mgt_lanes.lane_loop[2].eth_port.e2c.tdata; + e2c[2].tuser = `QSFP_W.mgt_lanes.lane_loop[2].eth_port.e2c.tuser; + e2c[2].tkeep = `QSFP_W.mgt_lanes.lane_loop[2].eth_port.e2c.tkeep; + e2c[2].tlast = `QSFP_W.mgt_lanes.lane_loop[2].eth_port.e2c.tlast; + e2c[2].tvalid = `QSFP_W.mgt_lanes.lane_loop[2].eth_port.e2c.tvalid; + force `QSFP_W.mgt_lanes.lane_loop[2].eth_port.e2c.tready = e2c[2].tready; + + force `QSFP_W.mgt_lanes.lane_loop[2].eth_port.c2e.tdata = c2e[2].tdata; + force `QSFP_W.mgt_lanes.lane_loop[2].eth_port.c2e.tuser = c2e[2].tuser; + force `QSFP_W.mgt_lanes.lane_loop[2].eth_port.c2e.tkeep = c2e[2].tkeep; + force `QSFP_W.mgt_lanes.lane_loop[2].eth_port.c2e.tlast = c2e[2].tlast; + force `QSFP_W.mgt_lanes.lane_loop[2].eth_port.c2e.tvalid = c2e[2].tvalid; + c2e[2].tready = `QSFP_W.mgt_lanes.lane_loop[2].eth_port.c2e.tready; + end + end + if (PROTOCOL3 == `MGT_10GbE) begin + always_comb begin + e2c[3].tdata = `QSFP_W.mgt_lanes.lane_loop[3].eth_port.e2c.tdata; + e2c[3].tuser = `QSFP_W.mgt_lanes.lane_loop[3].eth_port.e2c.tuser; + e2c[3].tkeep = `QSFP_W.mgt_lanes.lane_loop[3].eth_port.e2c.tkeep; + e2c[3].tlast = `QSFP_W.mgt_lanes.lane_loop[3].eth_port.e2c.tlast; + e2c[3].tvalid = `QSFP_W.mgt_lanes.lane_loop[3].eth_port.e2c.tvalid; + force `QSFP_W.mgt_lanes.lane_loop[3].eth_port.e2c.tready = e2c[3].tready; + + force `QSFP_W.mgt_lanes.lane_loop[3].eth_port.c2e.tdata = c2e[3].tdata; + force `QSFP_W.mgt_lanes.lane_loop[3].eth_port.c2e.tuser = c2e[3].tuser; + force `QSFP_W.mgt_lanes.lane_loop[3].eth_port.c2e.tkeep = c2e[3].tkeep; + force `QSFP_W.mgt_lanes.lane_loop[3].eth_port.c2e.tlast = c2e[3].tlast; + force `QSFP_W.mgt_lanes.lane_loop[3].eth_port.c2e.tvalid = c2e[3].tvalid; + c2e[3].tready = `QSFP_W.mgt_lanes.lane_loop[3].eth_port.c2e.tready; + end + end + + //--------------------------------------------------------------------------- + // Connect to Internal Bus + //--------------------------------------------------------------------------- + + logic [3:0] model_link_up; + if (USE_MAC) begin : use_mac + if (PROTOCOL0 == `MGT_100GbE) begin : use_mac_100 + model_100gbe model_100gbe_i ( + .areset (refclk_rst), + .ref_clk (refclk_p), + .tx_p (rx_p), + .tx_n (rx_n), + .rx_p (tx_p), + .rx_n (tx_n), + .mgt_clk (userclk), + .mgt_rst (userclk_rst), + .link_up (model_link_up[0]), + .mgt_tx (eth_rx[0]), + .mgt_rx (eth_tx[0]) + ); + end else begin : use_mac_single_lane + if (IS10GBE[0]) begin : use_mac0_10 + logic mgt_clk, mgt_rst; + model_10gbe #(.PORTNUM(0)) model_10gbe_0( + .areset (refclk_rst), + .ref_clk (refclk_p), + .tx_p (rx_p[0]), + .tx_n (rx_n[0]), + .rx_p (tx_p[0]), + .rx_n (tx_n[0]), + .mgt_clk (userclk), + .mgt_rst (userclk_rst), + .link_up (model_link_up[0]), + .mgt_tx (eth_rx[0]), + .mgt_rx (eth_tx[0]) + ); + end + if (IS10GBE[1]) begin : use_mac1_10 + logic mgt_clk, mgt_rst; + model_10gbe #(.PORTNUM(1)) model_10gbe_1( + .areset (refclk_rst), + .ref_clk (refclk_p), + .tx_p (rx_p[1]), + .tx_n (rx_n[1]), + .rx_p (tx_p[1]), + .rx_n (tx_n[1]), + .mgt_clk (userclk), + .mgt_rst (userclk_rst), + .link_up (model_link_up[1]), + .mgt_tx (eth_rx[1]), + .mgt_rx (eth_tx[1]) + ); + end + if (IS10GBE[2]) begin : use_mac2_10 + logic mgt_clk, mgt_rst; + model_10gbe #(.PORTNUM(2)) model_10gbe_2( + .areset (refclk_rst), + .ref_clk (refclk_p), + .tx_p (rx_p[2]), + .tx_n (rx_n[2]), + .rx_p (tx_p[2]), + .rx_n (tx_n[2]), + .mgt_clk (userclk), + .mgt_rst (userclk_rst), + .link_up (model_link_up[2]), + .mgt_tx (eth_rx[2]), + .mgt_rx (eth_tx[2]) + ); + end + if (IS10GBE[3]) begin : use_mac3_10 + logic mgt_clk, mgt_rst; + model_10gbe #(.PORTNUM(3)) model_10gbe_3( + .areset (refclk_rst), + .ref_clk (refclk_p), + .tx_p (rx_p[3]), + .tx_n (rx_n[3]), + .rx_p (tx_p[3]), + .rx_n (tx_n[3]), + .mgt_clk (userclk), + .mgt_rst (userclk_rst), + .link_up (model_link_up[3]), + .mgt_tx (eth_rx[3]), + .mgt_rx (eth_tx[3]) + ); + end + end : use_mac_single_lane + end else begin : skip_mac + assign model_link_up = 1; + always_comb begin + userclk = sim_userclk; + userclk_rst = sim_userclk_rst; + end + if (PROTOCOL0 == `MGT_100GbE) begin : skip_mac_100 + always_comb begin + force `MGT_IO0.core_100g.eth_100g_i.eth_100g_bd_i.gt_txusrclk2 = userclk; + force `MGT_IO0.core_100g.eth_100g_i.link_up = !userclk_rst; + + eth_tx[0].tdata = `MGT_IO0.core_100g.eth_100g_i.eth100g_tx.tdata; + eth_tx[0].tkeep = `MGT_IO0.core_100g.eth_100g_i.eth100g_tx.tkeep; + eth_tx[0].tuser = eth_tx[0].keep2trailing(eth_tx[0].tkeep); + eth_tx[0].tlast = `MGT_IO0.core_100g.eth_100g_i.eth100g_tx.tlast; + eth_tx[0].tvalid = `MGT_IO0.core_100g.eth_100g_i.eth100g_tx.tvalid; + force `MGT_IO0.core_100g.eth_100g_i.eth100g_tx.tready = eth_tx[0].tready; + force `MGT_IO0.core_100g.eth_100g_i.lbus_tx[0] = 0; + force `MGT_IO0.core_100g.eth_100g_i.lbus_tx[1] = 0; + force `MGT_IO0.core_100g.eth_100g_i.lbus_tx[2] = 0; + force `MGT_IO0.core_100g.eth_100g_i.lbus_tx[3] = 0; + + force `MGT_IO0.core_100g.eth_100g_i.eth100g_rx.tdata = eth_rx[0].tdata; + force `MGT_IO0.core_100g.eth_100g_i.eth100g_rx.tuser = eth_rx[0].tuser; + force `MGT_IO0.core_100g.eth_100g_i.eth100g_rx.tkeep = eth_rx[0].tkeep; + force `MGT_IO0.core_100g.eth_100g_i.eth100g_rx.tlast = eth_rx[0].tlast; + force `MGT_IO0.core_100g.eth_100g_i.eth100g_rx.tvalid = eth_rx[0].tvalid; + eth_rx[0].tready = 1; + end + end else begin : skip_mac_single_lane + if (IS10GBE[0]) begin : skip_mac0_10 + always_comb begin + force `MGT_IO0.mgt_clk = userclk; + force `MGT_IO0.mgt_rst = userclk_rst; + eth_tx[0].tdata = `MGT_IO0.core_10g.eth_10g_i.mgt_tx.tdata; + eth_tx[0].tuser = `MGT_IO0.core_10g.eth_10g_i.mgt_tx.tuser; + eth_tx[0].tkeep = `MGT_IO0.core_10g.eth_10g_i.mgt_tx.tkeep; + eth_tx[0].tlast = `MGT_IO0.core_10g.eth_10g_i.mgt_tx.tlast; + eth_tx[0].tvalid = `MGT_IO0.core_10g.eth_10g_i.mgt_tx.tvalid; + force `MGT_IO0.core_10g.eth_10g_i.mgt_tx.tready = eth_tx[0].tready; + + force `MGT_IO0.core_10g.eth_10g_i.mgt_rx.tdata = eth_rx[0].tdata; + force `MGT_IO0.core_10g.eth_10g_i.mgt_rx.tuser = eth_rx[0].tuser; + force `MGT_IO0.core_10g.eth_10g_i.mgt_rx.tlast = eth_rx[0].tlast; + force `MGT_IO0.core_10g.eth_10g_i.mgt_rx.tvalid = eth_rx[0].tvalid; + eth_rx[0].tready = 1; + end + end : skip_mac0_10 + if (IS10GBE[1]) begin : skip_mac1_10 + always_comb begin + force `MGT_IO1.mgt_clk = userclk; + force `MGT_IO1.mgt_rst = userclk_rst; + eth_tx[1].tdata = `MGT_IO1.core_10g.eth_10g_i.mgt_tx.tdata; + eth_tx[1].tuser = `MGT_IO1.core_10g.eth_10g_i.mgt_tx.tuser; + eth_tx[1].tkeep = `MGT_IO1.core_10g.eth_10g_i.mgt_tx.tkeep; + eth_tx[1].tlast = `MGT_IO1.core_10g.eth_10g_i.mgt_tx.tlast; + eth_tx[1].tvalid = `MGT_IO1.core_10g.eth_10g_i.mgt_tx.tvalid; + force `MGT_IO1.core_10g.eth_10g_i.mgt_tx.tready = eth_tx[1].tready; + + force `MGT_IO1.core_10g.eth_10g_i.mgt_rx.tdata = eth_rx[1].tdata; + force `MGT_IO1.core_10g.eth_10g_i.mgt_rx.tuser = eth_rx[1].tuser; + force `MGT_IO1.core_10g.eth_10g_i.mgt_rx.tlast = eth_rx[1].tlast; + force `MGT_IO1.core_10g.eth_10g_i.mgt_rx.tvalid = eth_rx[1].tvalid; + eth_rx[1].tready = 1; + end + end : skip_mac1_10 + if (IS10GBE[2]) begin : skip_mac2_10 + always_comb begin + force `MGT_IO2.mgt_clk = userclk; + force `MGT_IO2.mgt_rst = userclk_rst; + eth_tx[2].tdata = `MGT_IO2.core_10g.eth_10g_i.mgt_tx.tdata; + eth_tx[2].tuser = `MGT_IO2.core_10g.eth_10g_i.mgt_tx.tuser; + eth_tx[2].tkeep = `MGT_IO2.core_10g.eth_10g_i.mgt_tx.tkeep; + eth_tx[2].tlast = `MGT_IO2.core_10g.eth_10g_i.mgt_tx.tlast; + eth_tx[2].tvalid = `MGT_IO2.core_10g.eth_10g_i.mgt_tx.tvalid; + force `MGT_IO2.core_10g.eth_10g_i.mgt_tx.tready = eth_tx[2].tready; + + force `MGT_IO2.core_10g.eth_10g_i.mgt_rx.tdata = eth_rx[2].tdata; + force `MGT_IO2.core_10g.eth_10g_i.mgt_rx.tuser = eth_rx[2].tuser; + force `MGT_IO2.core_10g.eth_10g_i.mgt_rx.tlast = eth_rx[2].tlast; + force `MGT_IO2.core_10g.eth_10g_i.mgt_rx.tvalid = eth_rx[2].tvalid; + eth_rx[2].tready = 1; + end + end : skip_mac2_10 + if (IS10GBE[3]) begin : skip_mac3_10 + always_comb begin + force `MGT_IO3.mgt_clk = userclk; + force `MGT_IO3.mgt_rst = userclk_rst; + eth_tx[3].tdata = `MGT_IO3.core_10g.eth_10g_i.mgt_tx.tdata; + eth_tx[3].tuser = `MGT_IO3.core_10g.eth_10g_i.mgt_tx.tuser; + eth_tx[3].tkeep = `MGT_IO3.core_10g.eth_10g_i.mgt_tx.tkeep; + eth_tx[3].tlast = `MGT_IO3.core_10g.eth_10g_i.mgt_tx.tlast; + eth_tx[3].tvalid = `MGT_IO3.core_10g.eth_10g_i.mgt_tx.tvalid; + force `MGT_IO3.core_10g.eth_10g_i.mgt_tx.tready = eth_tx[3].tready; + + force `MGT_IO3.core_10g.eth_10g_i.mgt_rx.tdata = eth_rx[3].tdata; + force `MGT_IO3.core_10g.eth_10g_i.mgt_rx.tuser = eth_rx[3].tuser; + force `MGT_IO3.core_10g.eth_10g_i.mgt_rx.tlast = eth_rx[3].tlast; + force `MGT_IO3.core_10g.eth_10g_i.mgt_rx.tvalid = eth_rx[3].tvalid; + eth_rx[3].tready = 1; + end + end : skip_mac3_10 + end : skip_mac_single_lane + end + + //--------------------------------------------------------------------------- + // Reset + //--------------------------------------------------------------------------- + + task test_reset(); + wait(!clk200_rst && !clk100_rst && !clk40_rst); + repeat (10) @(posedge clk40); + endtask : test_reset + + //--------------------------------------------------------------------------- + // Test Registers + //--------------------------------------------------------------------------- + + // register offset for DMA controller + localparam REG_DMA = 'h0; + localparam MM2S_DMACR = REG_DMA + 'h0; + + // register offsets from x4xx_mgt_io_core + // MGT_IO Registers (NI_XGE registers) + localparam REG_BASE_SFP_IO = 32'h8000; + localparam REG_PORT_INFO = REG_BASE_SFP_IO + 'h0; + localparam REG_MAC_CTRL_STATUS = REG_BASE_SFP_IO + 'h4; + localparam REG_PHY_CTRL_STATUS = REG_BASE_SFP_IO + 'h8; + localparam REG_MAC_LED_CTL = REG_BASE_SFP_IO + 'hC; + + // Ethernet specific + localparam REG_ETH_MDIO_BASE = REG_BASE_SFP_IO + 'h10; + + // Aurora specific + localparam REG_AURORA_OVERRUNS = REG_BASE_SFP_IO + 'h20; + localparam REG_CHECKSUM_ERRORS = REG_BASE_SFP_IO + 'h24; + localparam REG_BIST_CHECKER_SAMPS = REG_BASE_SFP_IO + 'h28; + localparam REG_BIST_CHECKER_ERRORS = REG_BASE_SFP_IO + 'h2C; + + // At 0x9000 The OpenCores XGE MAC registers exist. + + // Set BASE for UIO - The package file defines the registers at +0x1000. + // NOTE that 0x9000/0x9004 has a local copy of the MAC REGISTER. + localparam BASE = 32'h9000; + localparam REG_AWIDTH = 16; + `include "../../../../lib/rfnoc/xport_sv/eth_regs.vh" + + // 100gbe Registers + localparam CMAC_BASE = 32'hC000; + localparam REG_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG1 = CMAC_BASE+ 32'h0048; + localparam REG_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG1 = CMAC_BASE+ 32'h0034; + + task test_registers(int lane); + localparam [7:0] COMPAT_NUM = 8'd2; + logic [7:0] MGT_PROTOCOL; + + automatic resp_t resp; + automatic logic activity = 0; + automatic logic reg_link_up = 0; + automatic logic [31:0] port_info; + automatic int data = 0; + automatic int LANE_BASE = 32'h10000 * lane; + string lane_str; + lane_str.itoa(lane); + + test.start_test({TEST_NAME," ",lane_str," Test/Setup Registers"}, 30us); + + case (lane) + 0 : MGT_PROTOCOL = PROTOCOL0; + 1 : MGT_PROTOCOL = PROTOCOL1; + 2 : MGT_PROTOCOL = PROTOCOL2; + 3 : MGT_PROTOCOL = PROTOCOL3; + endcase + + // testing AxiLite Model + repeat (4) begin + axi.wr(LANE_BASE+REG_MAC_LSB,++data); + axi.rd(LANE_BASE+REG_MAC_LSB,data); + axi.wr(LANE_BASE+REG_MAC_MSB,++data); + axi.rd(LANE_BASE+REG_MAC_MSB,data); + end + + // try with different idle states + axi.ready_idle_state = 1; + repeat (16) begin + axi.wr(LANE_BASE+REG_MAC_LSB,++data); + end + repeat (4) axi.rd(LANE_BASE+REG_MAC_LSB,data); + axi.ready_idle_state = 0; + repeat (16) begin + axi.wr(LANE_BASE+REG_MAC_LSB,++data); + end + repeat (4) axi.rd(LANE_BASE+REG_MAC_LSB,data); + + if (MGT_PROTOCOL == `MGT_100GbE && !USE_MAC) begin + reg_link_up = 1; + end + + port_info = {COMPAT_NUM, 6'h0, activity, reg_link_up, MGT_PROTOCOL, PORTNUM}; + axi.rd(LANE_BASE+REG_PORT_INFO,port_info); + // status/ctrl isn't used yet + if (MGT_PROTOCOL == `MGT_100GbE) axi.rd(LANE_BASE+REG_MAC_CTRL_STATUS,0); + else if (MGT_PROTOCOL == `MGT_10GbE) + if (USE_MAC) axi.rd(LANE_BASE+REG_MAC_CTRL_STATUS,32'h0000_0000); + else axi.rd(LANE_BASE+REG_MAC_CTRL_STATUS,32'h0000_0080); + else assert(1); + + axi.rd(LANE_BASE+REG_PHY_CTRL_STATUS,0); + // en 0 / value 1 + axi.rd(LANE_BASE+REG_MAC_LED_CTL,0); + // enabled with value 0 + axi.wr(LANE_BASE+REG_MAC_LED_CTL,1); + axi.rd(LANE_BASE+REG_MAC_LED_CTL,1); + activity = 0; + port_info = {COMPAT_NUM, 6'h0, activity, reg_link_up, MGT_PROTOCOL, PORTNUM}; + axi.rd(LANE_BASE+REG_PORT_INFO,port_info); + + // enabled with value 1 + axi.wr(LANE_BASE+REG_MAC_LED_CTL,3); + axi.rd(LANE_BASE+REG_MAC_LED_CTL,3); + activity = 1; + port_info = {COMPAT_NUM, 6'h0, activity, reg_link_up, MGT_PROTOCOL, PORTNUM}; + axi.rd(LANE_BASE+REG_PORT_INFO,port_info); + + // enet controls led + axi.wr(LANE_BASE+REG_MAC_LED_CTL,0); + // unused registers with 100g + axi.rd(LANE_BASE+REG_AURORA_OVERRUNS,32'h0); + axi.rd(LANE_BASE+REG_CHECKSUM_ERRORS,32'h0); + axi.rd(LANE_BASE+REG_BIST_CHECKER_SAMPS,32'h0); + axi.rd(LANE_BASE+REG_BIST_CHECKER_ERRORS,32'h0); + + // DEF_DEST_MAC/IP/UDP are defined in the + // sim_ethernet_lib.svh, as the destination + // addresses. Using the defaults means + // if I don't change the dest address on + // a packet it will go to the CHDR + axi.wr(LANE_BASE+REG_MAC_LSB,DEF_DEST_MAC_ADDR[31:0]); + axi.wr(LANE_BASE+REG_MAC_MSB,DEF_DEST_MAC_ADDR[47:32]); + axi.wr(LANE_BASE+REG_IP,DEF_DEST_IP_ADDR); + axi.wr(LANE_BASE+REG_UDP,DEF_DEST_UDP_PORT); + axi.wr(LANE_BASE+REG_BRIDGE_ENABLE,1); + axi.wr(LANE_BASE+REG_BRIDGE_MAC_LSB,DEF_BRIDGE_MAC_ADDR[31:0]); + axi.wr(LANE_BASE+REG_BRIDGE_MAC_MSB,DEF_BRIDGE_MAC_ADDR[47:32]); + axi.wr(LANE_BASE+REG_BRIDGE_IP,DEF_BRIDGE_IP_ADDR); + axi.wr(LANE_BASE+REG_BRIDGE_UDP,DEF_BRIDGE_UDP_PORT); + axi.wr(LANE_BASE+REG_BRIDGE_ENABLE,0); + + // Readback the values + axi.rd(LANE_BASE+REG_MAC_LSB,DEF_DEST_MAC_ADDR[31:0]); + axi.rd(LANE_BASE+REG_MAC_MSB,DEF_DEST_MAC_ADDR[47:32]); + axi.rd(LANE_BASE+REG_IP,DEF_DEST_IP_ADDR); + axi.rd(LANE_BASE+REG_UDP,DEF_DEST_UDP_PORT); + axi.rd(LANE_BASE+REG_BRIDGE_ENABLE,0); + axi.rd(LANE_BASE+REG_BRIDGE_MAC_LSB,DEF_BRIDGE_MAC_ADDR[31:0]); + axi.rd(LANE_BASE+REG_BRIDGE_MAC_MSB,DEF_BRIDGE_MAC_ADDR[47:32]); + axi.rd(LANE_BASE+REG_BRIDGE_IP,DEF_BRIDGE_IP_ADDR); + axi.rd(LANE_BASE+REG_BRIDGE_UDP,DEF_BRIDGE_UDP_PORT); + + // check the DMA controller + axi.rd(LANE_BASE+MM2S_DMACR, 32'h00010002); + // Hit reset bit, and poll for completion + axi.wr(LANE_BASE+MM2S_DMACR, 32'h4); + // Make sure reset asserts + axi.rd(LANE_BASE+MM2S_DMACR, 32'h00010006); + for (int i = 7; i >= 0; i--) begin + logic [31:0] readback; + logic [1:0] resp; + axi.rd_block(LANE_BASE+MM2S_DMACR, readback, resp); + if((readback & 32'h4) == 0) begin + // Reset bit cleared + break; + end + // Give up if it hasn't cleared after several tries + `ASSERT_ERROR(i != 0, "DMA controller reset failed to deassert."); + end + + axi.block(); + test.end_test(); + endtask : test_registers + + //--------------------------------------------------------------------------- + // Ethernet to CPU test + //--------------------------------------------------------------------------- + typedef ChdrData #(CHDR_W)::chdr_word_t chdr_word_t; + typedef chdr_word_t word_queue_t[$]; + + typedef XportStreamPacket #(ENET_W) EthXportPacket_t; + typedef AxiStreamPacket #(ENET_W,ENET_USER_W) EthAxisPacket_t; + + typedef XportStreamPacket #(CPU_W) CpuXportPacket_t; + typedef AxiStreamPacket #(CPU_W,CPU_USER_W) CpuAxisPacket_t; + + typedef XportStreamPacket #(CHDR_W) ChdrXportPacket_t; + typedef AxiStreamPacket #(CHDR_W,CHDR_USER_W) ChdrAxisPacket_t; + typedef ChdrPacket #(CHDR_W,CHDR_USER_W) ChdrPacket_t; + + task automatic test_ethcpu(int num_samples[$], int ERROR_PROB=2, int EXPECT_DROPS=0); + fork + if (!DISABLED[0]) test_ethcpu_lane(0,num_samples,ERROR_PROB,EXPECT_DROPS); + if (!DISABLED[1]) test_ethcpu_lane(1,num_samples,ERROR_PROB,EXPECT_DROPS); + if (!DISABLED[2]) test_ethcpu_lane(2,num_samples,ERROR_PROB,EXPECT_DROPS); + if (!DISABLED[3]) test_ethcpu_lane(3,num_samples,ERROR_PROB,EXPECT_DROPS); + join + endtask : test_ethcpu + + task automatic test_ethcpu_lane(int lane, int num_samples[$], int ERROR_PROB=2, int EXPECT_DROPS=0); + TestExec test_e2c = new(); + automatic EthXportPacket_t send[$]; + automatic CpuXportPacket_t expected[$]; + automatic int sample_sum = 0; + string lane_str; + lane_str.itoa(lane); + + test_e2c.start_test({TEST_NAME," ",lane_str," Ethernet to CPU"}, 60us*5); + // This path is + // eth_rx -> s_mac(eth_adapter) -> s_mac(eth_dispatch) -> + //// in_reg(AXI_FIFO)(SIZE=1) + // (eth_dispatch) in -> STATMACHINE (Dispatch) + cpu -> + //// out_reg_cpu(AXI_FIFO)(SIZE=1) + // (eth_dispatch) o_cpu -> + //// cpu_out_gate(AXI_GATE)(SIZE=11) + // (eth_dispatch) m_cpu -> (eth_adapter) e2c_chdr -> e2c_fifo + //// cpu_fifo(AXI_FIFO)(SIZE=CPU_FIFO_SIZE) + // (eth_adapater) m_cpu -> e2c + + foreach (num_samples[i]) begin + automatic eth_hdr_t eth_hdr; + automatic ipv4_hdr_t ipv4_hdr; + automatic udp_hdr_t udp_hdr; + automatic raw_pkt_t pay,udp_raw; + automatic int PREAMBLE; + + PREAMBLE = NO_PREAMBLE; + + expected[i] = new; + send[i] = new; + + udp_hdr.dest_port = 0; //change dest port from default so it goes to cpu + get_ramp_raw_pkt(.num_samps(num_samples[i]),.ramp_start((sample_sum)%256), + .ramp_inc(1),.pkt(pay),.SWIDTH(8)); + sample_sum += num_samples[i]; + udp_raw = build_udp_pkt(eth_hdr,ipv4_hdr,udp_hdr,pay, + .preamble(PREAMBLE)); + send[i].push_bytes(udp_raw); + send[i].tkeep_to_tuser(.ERROR_PROB(ERROR_PROB)); + + // rebuild the expected packet for comparison without the preamble + udp_raw = build_udp_pkt(eth_hdr,ipv4_hdr,udp_hdr,pay, + .preamble(NO_PREAMBLE)); + expected[i].push_bytes(udp_raw); + expected[i].tkeep_to_tuser(); + + end + + // iterate in descending order so deleting doesn't shift down + // the packets in future loop iterations. + for (int i= num_samples.size()-1 ; i >= 0; i--) begin + // original only checks for errors in last word. + if (!SV_ETH_IFC && send[i].has_error()) send[i].set_error(); + + // If a packet has an error it shouldn't make it through + if (send[i].has_error()) expected.delete(i); // MAC ERROR + + end + + fork + begin // tx_thread + foreach(send[i])begin + #1 eth[lane].put(send[i]); // delay causes CPU/CHDR traffic on input to be interleaved. + end + end + begin //rx_thread + if (EXPECT_DROPS > 0) begin + automatic int pkt_num = 0; + automatic logic [31:0] data; + automatic resp_t resp; + automatic int drop_count = 0; + automatic int rcvd_count = 0; + automatic int drop_count_reg = 0; + while (expected.size() > 0) begin + automatic CpuAxisPacket_t actual_a; + automatic CpuXportPacket_t actual = new(); + while (!cpu[lane].try_get(actual_a)) begin + axi.rd_block(REG_CPU_DROPPED,data,resp); + assert (resp==OKAY); + drop_count_reg += data; + // to account for case where we drop the last packet, + // stop looking if we have received or dropped all the packets + if (drop_count_reg+rcvd_count >= num_samples.size() && cpu[lane].slave_idle) begin + $display("Last packet dropped skipping to the end"); + expected.delete(); + drop_count = drop_count_reg; + // last packet may still be pending + #1us; // wait + void'(cpu[lane].try_get(actual_a)); + break; + end + end + if (expected.size() > 0) begin + actual.import_axis(actual_a); + actual.tkeep_to_tuser(); + + while (expected.size > 0 && actual.compare_no_user(expected[0],.PRINT_LVL(0))) begin + void'(expected.pop_front()); + ++drop_count; + ++pkt_num; + $display("Dropped packet %d",pkt_num); + `ASSERT_ERROR(drop_count < EXPECT_DROPS,"Exceeded anticipated number of dropped packets e2c"); + end + end + // expected size could of changed in while loop above + if (expected.size() > 0) begin + ++pkt_num; + ++rcvd_count; + $display("Rcvd packet %d",pkt_num); + void'(expected.pop_front()); + end + end + axi.rd_block(REG_CPU_DROPPED,data,resp); + assert (resp==OKAY); + drop_count_reg += data; + if (SV_ETH_IFC) begin + $display("Verify drop count is %d",drop_count_reg); + assert(drop_count_reg == drop_count) else $error("Drop count mismatch"); + end + end else begin + foreach(expected[i]) begin + automatic CpuAxisPacket_t actual_a; + automatic CpuXportPacket_t actual = new(); + cpu[lane].get(actual_a); + actual.import_axis(actual_a); + actual.tkeep_to_tuser(); + + `ASSERT_ERROR(!actual.compare_no_user(expected[i]),"failed to send packet to e2c"); + end + end + end + join + + test_e2c.end_test(); + endtask : test_ethcpu_lane + + task automatic wait_for_udp_packets(int lane, int udp_dest_port); + automatic EthAxisPacket_t actual_a; + automatic EthXportPacket_t actual = new(); + automatic raw_pkt_t rcv_raw,rcv_pay; + automatic udp_hdr_t rcv_udp; + automatic eth_hdr_t rcv_eth; + automatic ipv4_hdr_t rcv_ip; + automatic int try_count = 0; + automatic int PREAMBLE; + + PREAMBLE = NO_PREAMBLE; + + do begin + ++try_count; + // check if packet is for our port + #100; + eth[lane].peek(actual_a); + actual.import_axis(actual_a); + actual.tuser_to_tkeep(); + rcv_raw = actual.dump_bytes(); + if (PREAMBLE == ZERO_PREAMBLE) begin + repeat(6) rcv_raw.delete(0); // strip preamble + end + decode_udp_pkt(rcv_raw,rcv_eth,rcv_ip,rcv_udp,rcv_pay); + `ASSERT_ERROR(try_count != 100,"unclaimed packet on c2e"); + end while (rcv_udp.dest_port != udp_dest_port); + + endtask : wait_for_udp_packets + + task automatic test_cpueth(int num_samples[$]); + fork + if (!DISABLED[0]) test_cpueth_lane(0,num_samples); + if (!DISABLED[1]) test_cpueth_lane(1,num_samples); + if (!DISABLED[2]) test_cpueth_lane(2,num_samples); + if (!DISABLED[3]) test_cpueth_lane(3,num_samples); + join + endtask : test_cpueth + + task automatic test_cpueth_lane(int lane, int num_samples[$]); + TestExec test_c2e = new(); + automatic CpuXportPacket_t send[$]; + automatic EthXportPacket_t expected[$]; + automatic int sample_sum = 0; + string lane_str; + lane_str.itoa(lane); + + test_c2e.start_test({TEST_NAME," ",lane_str," CPU to Ethernet"}, 60us*5); + // This path is + // c2e -> (eth_adapter) s_cpu -> + //// (ARM_DEFRAMER)(IF ARM) + // (eth_adapater) c2e -> + //// (ETH_MUX)(SIZE=2) + // (eth_adapater) m_mac -> eth_tx + + foreach (num_samples[i]) begin + automatic eth_hdr_t eth_hdr; + automatic ipv4_hdr_t ipv4_hdr; + automatic udp_hdr_t udp_hdr; + automatic raw_pkt_t pay,udp_raw; + automatic int PREAMBLE; + PREAMBLE = NO_PREAMBLE; + + expected[i] = new; + send[i] = new; + + get_ramp_raw_pkt(.num_samps(num_samples[i]),.ramp_start((sample_sum)%256), + .ramp_inc(1),.pkt(pay),.SWIDTH(8)); + sample_sum += num_samples[i]; + udp_raw = build_udp_pkt(eth_hdr,ipv4_hdr,udp_hdr,pay, + .preamble(NO_PREAMBLE)); + send[i].push_bytes(udp_raw); + send[i].tkeep_to_tuser(); + + // rebuild the expected packet for comparison with a zero preamble + udp_raw = build_udp_pkt(eth_hdr,ipv4_hdr,udp_hdr,pay, + .preamble(PREAMBLE)); + //eth ifc expands pre CRC packets to 64 bytes on the C2E path + if (SV_ETH_IFC) begin + while (udp_raw.size < 64) begin + udp_raw.push_back(0); + end + end; + + expected[i].push_bytes(udp_raw); + expected[i].tkeep_to_tuser(); + end + + fork + begin // tx_thread + foreach(send[i])begin + cpu[lane].put(send[i]); + end + end + begin //rx_thread + foreach(expected[i]) begin + automatic EthAxisPacket_t actual_a; + automatic EthXportPacket_t actual = new(); + automatic raw_pkt_t rcv_raw,rcv_pay; + automatic udp_hdr_t rcv_udp; + automatic eth_hdr_t rcv_eth; + automatic ipv4_hdr_t rcv_ip; + automatic int try_count = 0; + + wait_for_udp_packets(lane,DEF_DEST_UDP_PORT); + eth[lane].get(actual_a); + actual.import_axis(actual_a); + if (!SV_ETH_IFC) begin + actual.tuser_to_tkeep(); + end + `ASSERT_ERROR(!actual.compare_w_pad(expected[i],!SV_ETH_IFC),"failed to send packet to c2e"); + end + end + join + test_c2e.end_test(); + + endtask : test_cpueth_lane + + //--------------------------------------------------------------------------- + // Ethernet to CHDR test + //--------------------------------------------------------------------------- + + function automatic word_queue_t bytes_to_words(raw_pkt_t pay); + automatic ChdrXportPacket_t axis_pkt = new(); + + axis_pkt.push_bytes(pay); + return axis_pkt.data; + + endfunction : bytes_to_words; + + function automatic raw_pkt_t flatten_chdr(ChdrPacket_t chdr_pkt); + automatic ChdrAxisPacket_t axis_chdr; + automatic ChdrXportPacket_t xport_chdr = new(); + axis_chdr = chdr_pkt.chdr_to_axis(); + foreach (axis_chdr.data[i]) begin + axis_chdr.keep[i] = '1; + axis_chdr.user[i] = '0; + end + xport_chdr.import_axis(axis_chdr); + return xport_chdr.dump_bytes(); + endfunction : flatten_chdr + + function automatic ChdrPacket_t unflatten_chdr(raw_pkt_t chdr_raw); + automatic ChdrXportPacket_t xport_chdr = new(); + automatic ChdrPacket_t chdr_pkt = new(); + xport_chdr.push_bytes(chdr_raw); + foreach (xport_chdr.data[i]) begin + xport_chdr.keep[i] = '1; + xport_chdr.user[i] = '0; + end + chdr_pkt.axis_to_chdr(xport_chdr); + return chdr_pkt; + endfunction : unflatten_chdr + + task automatic test_ethchdr(int num_samples[$], int ERROR_PROB=2, int EXPECT_DROPS=0); + fork + if (!DISABLED[0]) test_ethchdr_lane(0,num_samples,ERROR_PROB,EXPECT_DROPS); + if (!DISABLED[1]) test_ethchdr_lane(1,num_samples,ERROR_PROB,EXPECT_DROPS); + if (!DISABLED[2]) test_ethchdr_lane(2,num_samples,ERROR_PROB,EXPECT_DROPS); + if (!DISABLED[3]) test_ethchdr_lane(3,num_samples,ERROR_PROB,EXPECT_DROPS); + join + endtask : test_ethchdr; + + task automatic test_ethchdr_lane(int lane, int num_samples[$], int ERROR_PROB=2, int EXPECT_DROPS=0); + TestExec test_e2v = new(); + automatic EthXportPacket_t send[$]; + automatic ChdrXportPacket_t expected[$]; + automatic int sample_sum = 0; + string lane_str; + lane_str.itoa(lane); + + test_e2v.start_test({TEST_NAME," ",lane_str," Ethernet to CHDR"}, 60us); + // This path is + // eth_rx -> s_mac(eth_adapter) -> s_mac(eth_dispatch) -> + //// in_reg(AXI_FIFO)(SIZE=1) + // (eth_dispatch) in -> STATMACHINE (Dispatch) + chdr -> + //// chdr_user_fifo(AXI_FIFO)(SIZE=8) (capture eth header) + //// chdr_out_gate(AXI_GATE)(SIZE=11) + // (eth_dispatch) o_chdr -> + //// chdr_trim(CHDR_TRIM_PAYLOAD) + // (eth_dispatch) m_chdr -> (eth_adapater) e2x_chdr -> (xport_adapter_gen) s_axis_xport + //// xport_in_swap (AXIS_DATA_SWAP) + // (xport_adapter_gen) i_xport -> + //// mgmt_ep(CHDR_MGMT_PKT_HANDLER) + // (xport_adapter_gen) x2d -> + //// rtn_demux(AXI_SWITCH) x2x(loopback) or m_axis_rfnoc + // (xport_adapter_gen) m_axis_rfnoc -> (eth_adapter) e2x_fifo + //// chdr_fifo(AXI_FIFO)(SIZE=MTU) + // (eth_adapater) m_chdr -> e2v + + foreach (num_samples[i]) begin + automatic eth_hdr_t eth_hdr; + automatic ipv4_hdr_t ipv4_hdr; + automatic udp_hdr_t udp_hdr; + automatic raw_pkt_t pay,udp_raw,chdr_raw; + + automatic ChdrPacket_t chdr_pkt = new(); + automatic chdr_header_t chdr_hdr; + automatic chdr_word_t chdr_ts; + automatic chdr_word_t chdr_mdata[$]; + automatic chdr_word_t chdr_data[$]; + + automatic int PREAMBLE; + PREAMBLE = NO_PREAMBLE; + + expected[i] = new; + send[i] = new; + + // build a payload + get_ramp_raw_pkt(.num_samps(num_samples[i]),.ramp_start((sample_sum)%256), + .ramp_inc(1),.pkt(pay),.SWIDTH(8)); + sample_sum += num_samples[i]; + // Fill data in the chdr packet + chdr_hdr = '{ + vc : 0, + dst_epid : 0, + seq_num : 0, + pkt_type : CHDR_DATA_NO_TS, + num_mdata : 0, + default : 0 + }; + chdr_ts = 0; // no timestamp + chdr_mdata.delete(); // not adding meta data + chdr_data = bytes_to_words(pay); + + chdr_pkt.write_raw(chdr_hdr, chdr_data, chdr_mdata, chdr_ts); + chdr_raw = flatten_chdr(chdr_pkt); + + //build a udp packet + udp_raw = build_udp_pkt(eth_hdr,ipv4_hdr,udp_hdr,chdr_raw, + .preamble(PREAMBLE)); + send[i].push_bytes(udp_raw); + send[i].tkeep_to_tuser(.ERROR_PROB(ERROR_PROB)); + + // expect just the chdr packet (UDP stripped) + expected[i].push_bytes(chdr_raw); + expected[i].tkeep_to_tuser(); + + end + + // iterate in descending order so deleting doesn't shift down + // the packets in future loop iterations. + for (int i= num_samples.size()-1 ; i >= 0; i--) begin + // original only checks for errors in last word. + if (!SV_ETH_IFC && send[i].has_error()) send[i].set_error(); + // If a packet has an error it shouldn't make it through + if (send[i].has_error()) expected.delete(i);//MAC ERROR + end + + fork + begin // tx_thread + foreach(send[i])begin + #1 eth[lane].put(send[i]); // delay causes CPU/CHDR traffic on input to be interleaved. + end + end + begin //rx_thread + if (EXPECT_DROPS > 0) begin + automatic int pkt_num = 0; + automatic int drop_count = 0; + while (expected.size() > 0) begin + automatic ChdrAxisPacket_t actual_a; + automatic ChdrXportPacket_t actual = new(); + v[lane].get(actual_a); + actual.import_axis(actual_a); + actual.tuser_to_tkeep(); + while (expected.size > 0 && actual.compare_no_user(expected[0],.PRINT_LVL(0))) begin + void'(expected.pop_front()); + ++drop_count; + ++pkt_num; + $display("Dropped packet %d",pkt_num); + `ASSERT_ERROR(drop_count < EXPECT_DROPS,"Exceeded anticipated number of dropped packets e2v"); + end + if (expected.size() > 0) begin + ++pkt_num; + $display("Rcvd packet %d",pkt_num); + void'(expected.pop_front()); + end + end + if (SV_ETH_IFC) begin + $display("Verify drop count is %d",drop_count); + axi.rd(REG_CHDR_DROPPED,drop_count); + end + end else begin + foreach(expected[i]) begin + automatic ChdrAxisPacket_t actual_a; + automatic ChdrXportPacket_t actual = new(); + v[lane].get(actual_a); + actual.import_axis(actual_a); + actual.tuser_to_tkeep(); + `ASSERT_ERROR(!actual.compare_no_user(expected[i]),"failed to send packet e2v"); + end + end + end + join + test_e2v.end_test(); + + endtask : test_ethchdr_lane; + + + task automatic test_chdreth(int num_samples[$]); + fork + if (!DISABLED[0]) test_chdreth_lane(0,num_samples); + if (!DISABLED[1]) test_chdreth_lane(1,num_samples); + if (!DISABLED[2]) test_chdreth_lane(2,num_samples); + if (!DISABLED[3]) test_chdreth_lane(3,num_samples); + join + endtask : test_chdreth + + task automatic test_chdreth_lane(int lane, int num_samples[$]); + TestExec test_v2e = new(); + automatic ChdrXportPacket_t send[$]; + automatic EthXportPacket_t expected[$]; + automatic int sample_sum = 0; + string lane_str; + lane_str.itoa(lane); + + test_v2e.start_test({TEST_NAME," ",lane_str," CHDR to Ethernet"}, 60us); + // This path is + // v2e -> s_chdr(eth_adapter) -> s_axis_rfnoc (xport_adapter_gen) -> + //// axi_demux_mgmt_filter (AXI_DEMUX) (IF ALLOW_DISC) (discards discovery packets) + // (xport_adapter_gen) f2m -> + //// rtn_mux(AXI_MUX) between x2x and f2m + // (xport_adapter_gen) m2x -> + //// data_fifo/lookup_fifo (AXI_FIFO_SHORT) + //// LOOKUP LOGIC (lookup_fifo,data_fifo,results) + // (xport_adapter_gen) o_xport -> + //// xport_out_swap (AXIS_DATA_SWAP) + // (xport_adapter_gen) m_axis_xport -> (eth_adapater) x2e_chdr -> + //// ENET_HDR_LOGIC (frame_state) + // (eth_adapater) frame -> (eth_adapater) x2e_framed + //// (ETH_MUX)(SIZE=2) + // (eth_adapater) m_mac -> eth_tx + + foreach (num_samples[i]) begin + automatic eth_hdr_t eth_hdr; + automatic ipv4_hdr_t ipv4_hdr; + automatic udp_hdr_t udp_hdr; + automatic raw_pkt_t pay,udp_raw,chdr_raw; + + automatic ChdrPacket_t chdr_pkt = new(); + automatic chdr_header_t chdr_hdr; + automatic chdr_word_t chdr_ts; + automatic chdr_word_t chdr_mdata[$]; + automatic chdr_word_t chdr_data[$]; + + automatic int PREAMBLE; + if (PROTOCOL0 == `MGT_100GbE) PREAMBLE = NO_PREAMBLE; + else PREAMBLE = NO_PREAMBLE; + + // ModelSim should initialize the fields of ipv4_hdr to their default + // values, but for some reason it doesn't in this case. We add an + // initialization here to work around the bug for now. + ipv4_hdr = '{ + header_length : 4'd5, + version : 4'd4, + dscp : 6'b0000_00, + ecn : 2'b00, + length : 16'hXXXX, + identification: 16'h462E, + rsv_zero : 1'b0, + dont_frag : 1'b1, + more_frag : 1'b0, + frag_offset : 16'd0, + time_to_live : 16'd64, + protocol : UDP, + checksum : 16'hXXXX, + src_ip : DEF_SRC_IP_ADDR, + dest_ip : DEF_DEST_IP_ADDR + }; + + expected[i] = new; + send[i] = new; + + // build a payload + get_ramp_raw_pkt(.num_samps(num_samples[i]),.ramp_start((sample_sum)%256), + .ramp_inc(1),.pkt(pay),.SWIDTH(8)); + sample_sum += num_samples[i]; + + // Fill data in the chdr packet + chdr_hdr = '{ + vc : 0, + dst_epid : 0, + seq_num : 0, + pkt_type : CHDR_DATA_NO_TS, + num_mdata : 0, + default : 0 + }; + chdr_ts = 0; // no timestamp + chdr_mdata.delete(); // not adding meta data + chdr_data = bytes_to_words(pay); + + chdr_pkt.write_raw(chdr_hdr, chdr_data, chdr_mdata, chdr_ts); + chdr_raw = flatten_chdr(chdr_pkt); + + // send the raw chedar packet + send[i].push_bytes(chdr_raw); + send[i].tkeep_to_tuser(); + + //build a udp packet + // modify as the EthInterface does + udp_hdr.src_port = DEF_DEST_UDP_PORT; + udp_hdr.dest_port = 0; // Extract from router lookup results (Default) + udp_hdr.checksum = 0; // Checksum not calculated at this point + ipv4_hdr.src_ip = DEF_DEST_IP_ADDR; + ipv4_hdr.dest_ip = 0; // Extract from router lookup results (Default) + ipv4_hdr.dscp = 0; // hardcoded + ipv4_hdr.dont_frag = 1; // hardcoded + ipv4_hdr.identification = 0; // hardcoded + ipv4_hdr.time_to_live = 8'h10; //hardcoded + eth_hdr.src_mac = DEF_DEST_MAC_ADDR; + eth_hdr.dest_mac = 0; // Extract from router lookup results (Default) + + udp_raw = build_udp_pkt(eth_hdr,ipv4_hdr,udp_hdr,chdr_raw, + .preamble(PREAMBLE)); + + // XGE mac autoexpands all packet to size 60 (pre CRC) to meet 64 byte min packet + if (AUTOEXPAND_TO_64) begin + while (udp_raw.size < 60) begin + udp_raw.push_back(0); + end + end; + + // expect udp wrapped chdr + expected[i].push_bytes(udp_raw); + if (IGNORE_EXTRA_DATA) begin + expected[i].clear_user(); // expect all full words! + expected[i].tuser_to_tkeep(); + end else begin + expected[i].tkeep_to_tuser(); + end + end + + fork + begin // tx_thread + foreach(send[i])begin + v[lane].put(send[i]); + end + end + begin //rx_thread + foreach(expected[i]) begin + automatic EthAxisPacket_t actual_a; + automatic EthXportPacket_t actual = new(); + automatic eth_hdr_t eth_hdr; + automatic ipv4_hdr_t ipv4_hdr; + automatic udp_hdr_t udp_hdr; + automatic raw_pkt_t chdr_raw,actual_raw; + automatic ChdrPacket_t chdr_pkt; + automatic integer chdr_len; + automatic logic [7:0] trash; + localparam UDP_LEN = 8/*udp*/+20/*ipv4*/+14/*eth-no vlan*/; + + wait_for_udp_packets(.lane(lane),.udp_dest_port(0)); + eth[lane].get(actual_a); + actual.import_axis(actual_a); + // to get chdr_len + actual_raw = actual.dump_bytes(); + + repeat(PREAMBLE_BYTES) trash = actual_raw.pop_front(); + decode_udp_pkt(actual_raw,eth_hdr,ipv4_hdr,udp_hdr,chdr_raw); + //chdr_pkt = unflatten_chdr(chdr_raw); + if (IGNORE_EXTRA_DATA) begin + for (int w=chdr_pkt.header.length+UDP_LEN;w <actual_raw.size();w++) begin + actual_raw[w] = '0; + end + repeat(PREAMBLE_BYTES) actual_raw.push_front(0); + actual.empty(); + actual.push_bytes(actual_raw); + end + `ASSERT_ERROR(!actual.compare_w_error(expected[i]),"failed to send packet v2e"); + end + end + join + test_v2e.end_test(); + + endtask : test_chdreth_lane + + + //---------------------------------------------------- + //---------------------------------------------------- + //---------------------------------------------------- + // Main test loop + //---------------------------------------------------- + //---------------------------------------------------- + //---------------------------------------------------- + initial begin : tb_main + automatic int num_samples[$]; + automatic int cpu_num_samples[$]; + automatic int ERROR_PROB; + localparam QUICK = 1; + localparam CHECK_PAUSE = 0; + + // With QUANTA100/REFRESH200 I expected 50% slow down over + // 1024 ns 200*5.12 ns (a Quanta is 512 bit times, bit time=10 ps) + localparam PAUSE_QUANTA = 100; + localparam PAUSE_REFRESH = 200; + + //allocate BFM's + eth = new[4]; + v = new[4]; + cpu = new[4]; + + //associate with virtual interfaces + eth[0] = new(.master(eth_rx[0]), .slave(eth_tx[0])); + eth[1] = new(.master(eth_rx[1]), .slave(eth_tx[1])); + eth[2] = new(.master(eth_rx[2]), .slave(eth_tx[2])); + eth[3] = new(.master(eth_rx[3]), .slave(eth_tx[3])); + v[0] = new(.master(v2e[0]), .slave(e2v[0])); + v[1] = new(.master(v2e[1]), .slave(e2v[1])); + v[2] = new(.master(v2e[2]), .slave(e2v[2])); + v[3] = new(.master(v2e[3]), .slave(e2v[3])); + cpu[0] = new(.master(c2e[0]), .slave(e2c[0])); + cpu[1] = new(.master(c2e[1]), .slave(e2c[1])); + cpu[2] = new(.master(c2e[2]), .slave(e2c[2])); + cpu[3] = new(.master(c2e[3]), .slave(e2c[3])); + + test.start_test({TEST_NAME,"::Wait for Reset"}, 10us); + + clk40_gen.reset(); + clk100_gen.reset(); + clk200_gen.reset(); + if (!USE_MAC) userclk_gen.reset(); + refclk_gen.reset(); + // start tready high - MAC doesn't have a tready so we need model + // to always keep it's tready high + foreach(eth[lane])begin + if (!DISABLED[lane]) begin + eth[lane].slave_tready_init = 1; + eth[lane].run(); + cpu[lane].run(); + v[lane].run(); + end + end + axi.run(); + test_reset(); + + test.end_test(); + + foreach(eth[lane])begin + if (!DISABLED[lane]) begin + test_registers(lane); + end + end + + if (USE_MAC) begin + automatic logic [31:0] data; + automatic resp_t resp; + + // don't overflow/underflow the mac model + foreach(eth[lane])begin + if (!DISABLED[lane]) begin + eth[lane].set_master_stall_prob(0); + eth[lane].set_slave_stall_prob(0); + end + end + // bit errors need to be generated in a different + // way on the serial link. (Not Yet Implemented) + ERROR_PROB = 0; + + // 10GBE INIT + foreach(eth[lane])begin + if (IS10GBE[lane]) begin + automatic int LANE_BASE = 32'h10000 * lane; + + test.start_test({TEST_NAME,"::Wait for 10gbe MAC link_up"}, 150us); + axi.wr(LANE_BASE+REG_MAC_CTRL_STATUS,1); // turn on tx_enable + do begin + axi.rd_block(LANE_BASE+REG_PORT_INFO,data,resp); + assert (resp==OKAY); + end while (data[16] !== 1); //link_up + test.end_test(); + test.start_test({TEST_NAME,"::Wait for 10gbe MODEL link_up"}, 150us); + // check that model link is up + do begin + @(posedge clk40); + end while (model_link_up[lane] !== 1); + test.end_test(); + end + end + + // 100GBE_INIT + if (PROTOCOL0 == `MGT_100GbE) begin + test.start_test({TEST_NAME,"::Wait for 100gbe phy_reset"}, 20us); + // check that the DUT phy is out of reset. + do begin + axi.rd_block(REG_PHY_CTRL_STATUS,data,resp); + assert (resp==OKAY); + end while (data[1:0] !== 0); //usr_tx_reset and usr_rx_reset + + test.end_test(); + test.start_test({TEST_NAME,"::Wait for 100gbe MODEL link_up"}, 150us); + // check that model link is up + do begin + @(posedge clk40); + end while (model_link_up[0] !== 1); + test.end_test(); + test.start_test({TEST_NAME,"::Wait for 100gbe MAC link_up"}, 150us); + // Added Autoconnect which is on by default. uncomment to run manually + //Pkg100gbMac::init_mac(32'h4000,axi); + do begin + axi.rd_block(REG_PORT_INFO,data,resp); + assert (resp==OKAY); + end while (data[16] !== 1); //link_up + + test.end_test(); + test.start_test({TEST_NAME,"::Wait for auto config to complete"}, 10us); + + // check that the MAC auto config completed. + do begin + axi.rd_block(REG_MAC_CTRL_STATUS,data,resp); + assert (resp==OKAY); + end while (data[4] !== 1); //auto config complete + + data[0] = 1; // Autoconfig enable + data[24:16] = 9'h100; // pause mask (use global pause) + axi.wr(REG_MAC_CTRL_STATUS,data); // turn on tx_enable + + data[15:0] = PAUSE_QUANTA; + data[31:16] = PAUSE_QUANTA; + axi.wr(REG_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG1+0*4,data); + axi.wr(REG_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG1+1*4,data); + axi.wr(REG_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG1+2*4,data); + axi.wr(REG_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG1+3*4,data); + data[15:0] = PAUSE_QUANTA; + data[31:16] = 0; + axi.wr(REG_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG1+4*4,data); + + data[15:0] = PAUSE_REFRESH; + data[31:16] = PAUSE_REFRESH; + axi.wr(REG_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG1+0*4,data); + axi.wr(REG_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG1+1*4,data); + axi.wr(REG_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG1+2*4,data); + axi.wr(REG_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG1+3*4,data); + data[15:0] = PAUSE_REFRESH; + data[31:16] = 0; + axi.wr(REG_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG1+4*4,data); + data[15:0] = 64; // 64=4KBytes pause set + data[31:16] = 32; // 32=2KBytes pause clear + axi.wr(REG_PAUSE,data); + axi.block(); + + test.end_test(); + + end + end else begin + ERROR_PROB = 2; + end + + //Checks that pausing is working. + if (CHECK_PAUSE) begin + + // This test will take an excessive time when USE_MAC is true. + + foreach(eth[lane])begin + if (!DISABLED[lane]) begin + eth[lane].set_master_stall_prob(0); + eth[lane].set_slave_stall_prob(0); + cpu[lane].set_master_stall_prob(0); + cpu[lane].set_slave_stall_prob(0); + v[lane].set_master_stall_prob(0); + v[lane].set_slave_stall_prob(0); + end + end + num_samples = {7936,7936,7936,7936,7936,320, + 7936,7936,320,320,320,320, + // 280 512 byte packets (to help back things up) + 512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512, + 512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512, + 512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512, + 512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512, + 512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512, + 512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512, + 512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512, + 512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512, + 512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512, + 512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512, + 7936,7936 + }; + + + test_ethchdr(num_samples,.EXPECT_DROPS(0),.ERROR_PROB(0)); + // NOTE : The test ethcpu doesn't finish because the final packet doesn't + // make it through, and the algorithm doesn't time out. + num_samples = {7936,7936,7936,7936,7936,320, + 7936,7936,320,320,320,320}; + test_ethcpu(num_samples,.EXPECT_DROPS(9),.ERROR_PROB(0)); + + end + + foreach(eth[lane])begin + if (!DISABLED[lane]) begin + if (USE_MAC) begin + eth[lane].set_master_stall_prob(0); + eth[lane].set_slave_stall_prob(0); + cpu[lane].set_master_stall_prob(0); + cpu[lane].set_slave_stall_prob(0); + v[lane].set_master_stall_prob(0); + v[lane].set_slave_stall_prob(0); + end else begin + eth[lane].set_master_stall_prob(38); + eth[lane].set_slave_stall_prob(38); + cpu[lane].set_master_stall_prob(38); + cpu[lane].set_slave_stall_prob(38); + v[lane].set_master_stall_prob(38); + v[lane].set_slave_stall_prob(38); + end + end + end + + num_samples = {1,2,3,4,5,6,7,8, + ENET_W/8-1,ENET_W/8,ENET_W/8+1, + 2*ENET_W/8-1,2*ENET_W/8,2*ENET_W/8+1, + CPU_W/8-1,CPU_W/8,CPU_W/8+1, + 2*CPU_W/8-1,2*CPU_W/8,2*CPU_W/8+1, + CHDR_W/8-1,CHDR_W/8,CHDR_W/8+1, + 2*CHDR_W/8-1,2*CHDR_W/8,2*CHDR_W/8+1 + }; + //Option to add some extra samples for CPU packets to try to get + //above the min packet size of 64. (just the headers makes up 42 bytes) + //Packets less than 64 bytes should be padded to 64 + foreach (num_samples[i]) cpu_num_samples[i] = num_samples[i]+20; + test.start_test({TEST_NAME,"::PacketW Combos NO Errors"}, 10us*5); + fork // run in parallel + // ethrx + test_ethcpu(cpu_num_samples,.ERROR_PROB(0)); + test_ethchdr(num_samples,.ERROR_PROB(0)); + // ethtx + test_chdreth(num_samples); + test_cpueth(num_samples); + join + test.end_test(); + + if (!QUICK) begin + + test.start_test({TEST_NAME,"::PacketW Combos Errors"}, 10us*5); + fork // run in parallel + // ethrx + test_ethcpu(cpu_num_samples,.ERROR_PROB(ERROR_PROB)); + test_ethchdr(num_samples,.ERROR_PROB(ERROR_PROB)); + // ethtx + test_chdreth(num_samples); + test_cpueth(num_samples); + join + test.end_test(); + + if (USE_MAC) begin + // don't do huge packets with real MAC to save time + num_samples = {16,32,64,128,256,512,1024,1500}; + end else begin + num_samples = {16,32,64,128,256,512,1024,1500,1522,9000}; + end + test.start_test({TEST_NAME,"::Pwr2 NoErrors"}, 60us*5); + fork // run in parallel + // ethrx + test_ethcpu(cpu_num_samples,.ERROR_PROB(0)); + test_ethchdr(num_samples,.ERROR_PROB(0)); + // ethtx + test_chdreth(num_samples); + test_cpueth(num_samples); + join + test.end_test(); + end + + num_samples = {1,2,3,4,5,6,7,8, + ENET_W/8-1,ENET_W/8,ENET_W/8+1, + 2*ENET_W/8-1,2*ENET_W/8,2*ENET_W/8+1, + CPU_W/8-1,CPU_W/8,CPU_W/8+1, + 2*CPU_W/8-1,2*CPU_W/8,2*CPU_W/8+1, + CHDR_W/8-1,CHDR_W/8,CHDR_W/8+1, + 2*CHDR_W/8-1,2*CHDR_W/8,2*CHDR_W/8+1 + }; + test.start_test({TEST_NAME,"::Pktw NoStall+Error"}, 10us*5); + fork // run in parallel + // ethrx + test_ethcpu(cpu_num_samples,.ERROR_PROB(ERROR_PROB)); + test_ethchdr(num_samples,.ERROR_PROB(ERROR_PROB)); + // ethtx + test_chdreth(num_samples); + test_cpueth(num_samples); + join + test.end_test(); + + // repeat with back to back cpu/chdr packets + test.start_test({TEST_NAME,"::Serial Pktw NoStall+Error"}, 10us*5); + fork // run in parallel + // ethrx + begin + test_ethcpu(cpu_num_samples,.ERROR_PROB(ERROR_PROB)); + test_ethchdr(num_samples,.ERROR_PROB(ERROR_PROB)); + end + // ethtx + begin + test_chdreth(num_samples); + test_cpueth(num_samples); + end + join + test.end_test(); + + // End the TB, but don't $finish, since we don't want to kill other + // instances of this testbench that may be running. + test.end_tb(0); + + // Kill the clocks to end this instance of the testbench + clk40_gen.kill(); + clk100_gen.kill(); + clk200_gen.kill(); + if (!USE_MAC) userclk_gen.kill(); + refclk_gen.kill(); + done = 1; + + end // initial begin + +endmodule |