aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/x400/ip/eth_100g_bd
diff options
context:
space:
mode:
authorWade Fife <wade.fife@ettus.com>2021-06-08 19:40:46 -0500
committerAaron Rossetto <aaron.rossetto@ni.com>2021-06-10 11:56:58 -0500
commit6d3765605262016a80f71e36357f749ea35cbe5a (patch)
tree7d62d6622befd4132ac1ee085effa1426f7f53e5 /fpga/usrp3/top/x400/ip/eth_100g_bd
parentf706b89e6974e28ce76aadeeb06169becc86acba (diff)
downloaduhd-6d3765605262016a80f71e36357f749ea35cbe5a.tar.gz
uhd-6d3765605262016a80f71e36357f749ea35cbe5a.tar.bz2
uhd-6d3765605262016a80f71e36357f749ea35cbe5a.zip
fpga: x400: Add support for X410 motherboard FPGA
Co-authored-by: Andrew Moch <Andrew.Moch@ni.com> Co-authored-by: Daniel Jepson <daniel.jepson@ni.com> Co-authored-by: Javier Valenzuela <javier.valenzuela@ni.com> Co-authored-by: Joerg Hofrichter <joerg.hofrichter@ni.com> Co-authored-by: Kumaran Subramoniam <kumaran.subramoniam@ni.com> Co-authored-by: Max Köhler <max.koehler@ni.com> Co-authored-by: Michael Auchter <michael.auchter@ni.com> Co-authored-by: Paul Butler <paul.butler@ni.com> Co-authored-by: Wade Fife <wade.fife@ettus.com> Co-authored-by: Hector Rubio <hrubio@ni.com>
Diffstat (limited to 'fpga/usrp3/top/x400/ip/eth_100g_bd')
-rw-r--r--fpga/usrp3/top/x400/ip/eth_100g_bd/Makefile.inc53
-rw-r--r--fpga/usrp3/top/x400/ip/eth_100g_bd/PkgEth100gLbus.sv36
-rw-r--r--fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g.sv1220
-rw-r--r--fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g_axis2lbus.sv120
-rw-r--r--fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g_bd.tcl361
-rw-r--r--fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g_lbus2axis.sv555
-rw-r--r--fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/Makefile62
-rw-r--r--fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/axi_lbus_tb.sv285
-rw-r--r--fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/lbus_all_tb.sv22
-rw-r--r--fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/lbus_axi_tb.sv343
-rw-r--r--fpga/usrp3/top/x400/ip/eth_100g_bd/model_100gbe.sv234
11 files changed, 3291 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x400/ip/eth_100g_bd/Makefile.inc b/fpga/usrp3/top/x400/ip/eth_100g_bd/Makefile.inc
new file mode 100644
index 000000000..88c9c1184
--- /dev/null
+++ b/fpga/usrp3/top/x400/ip/eth_100g_bd/Makefile.inc
@@ -0,0 +1,53 @@
+#
+# Copyright 2021 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+include $(TOOLS_DIR)/make/viv_ip_builder.mak
+
+IP_100G_HDL_SRCS = $(addprefix $(IP_DIR)/eth_100g_bd/, \
+PkgEth100gLbus.sv \
+eth_100g.sv \
+eth_100g_axis2lbus.sv \
+eth_100g_lbus2axis.sv \
+)
+
+IP_100G_HDL_SIM_SRCS = $(addprefix $(IP_DIR)/eth_100g_bd/, \
+model_100gbe.sv \
+) \
+$(wildcard $(addprefix $(IP_BUILD_DIR)/eth_100g_bd/eth_100g_bd/, \
+sim/eth_100g_bd.v\
+ip/*/ip_0/sim/*.v\
+ip/*/sim/*.h\
+ip/*/sim/*.v\
+ip/eth_100g_bd_cmac_usplus_0_0/cmac_usplus_v2_6_1/*.v\
+ip/eth_100g_bd_cmac_usplus_0_0/eth_100g_bd_cmac_usplus_0_0/example_design/*.v\
+ip/eth_100g_bd_cmac_usplus_0_0/eth_100g_bd_cmac_usplus_0_0/header_files/*.h\
+ip/eth_100g_bd_cmac_usplus_0_0/eth_100g_bd_cmac_usplus_0_0.v\
+ipshared/*/hdl/*.v\
+ipshared/*/hdl/*.sv\
+))
+
+IP_100G_ORIG_SRCS = $(addprefix $(IP_DIR)/eth_100g_bd/, \
+eth_100g_bd.tcl \
+)
+
+IP_100G_BDTCL_SRCS = $(addprefix $(IP_BUILD_DIR)/eth_100g_bd/, \
+eth_100g_bd.tcl \
+)
+
+IP_100G_BD_SRCS = $(addprefix $(IP_BUILD_DIR)/eth_100g_bd/, \
+eth_100g_bd/eth_100g_bd.bd \
+)
+
+BD_100G_BD_OUTS = $(addprefix $(IP_BUILD_DIR)/eth_100g_bd/, \
+eth_100g_bd.bd.out \
+eth_100g_bd/eth_100g_bd_ooc.xdc \
+eth_100g_bd/synth/eth_100g_bd.v \
+)
+
+EMPTY_IP_SRCS =
+
+$(IP_100G_BD_SRCS) $(BD_100G_BD_OUTS) $(IP_100G_BDTCL_SRCS): $(IP_100G_ORIG_SRCS)
+ $(call BUILD_VIVADO_BDTCL,eth_100g_bd,$(ARCH),$(PART_ID),$(IP_DIR),$(IP_BUILD_DIR),$(EMPTY_IP_SRCS))
diff --git a/fpga/usrp3/top/x400/ip/eth_100g_bd/PkgEth100gLbus.sv b/fpga/usrp3/top/x400/ip/eth_100g_bd/PkgEth100gLbus.sv
new file mode 100644
index 000000000..588313e55
--- /dev/null
+++ b/fpga/usrp3/top/x400/ip/eth_100g_bd/PkgEth100gLbus.sv
@@ -0,0 +1,36 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: PkgEth100gLbus
+//
+// Description:
+//
+// Package to define an Lbus record
+//
+
+//-----------------------------------------------------------------------------
+// Lbus interface
+//
+// This is the segmented local bus interface on the Xilinx CMAC IP
+// see Xilinx CMAC documentation for detail
+// https://www.xilinx.com/support/documentation/ip_documentation/cmac_usplus/v2_4/pg203-cmac-usplus.pdf
+//-----------------------------------------------------------------------------
+
+package PkgEth100gLbus;
+
+ localparam DATA_WIDTH = 512;
+ localparam NUM_SEG = 4;
+ localparam SEG_DATA_WIDTH = DATA_WIDTH/NUM_SEG;
+
+ typedef struct packed {
+ logic [SEG_DATA_WIDTH-1:0] data;
+ logic [$clog2(SEG_DATA_WIDTH/8)-1:0] mty;
+ logic sop;
+ logic eop;
+ logic err;
+ logic ena;
+ } lbus_t;
+
+endpackage : PkgEth100gLbus
diff --git a/fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g.sv b/fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g.sv
new file mode 100644
index 000000000..456db06d9
--- /dev/null
+++ b/fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g.sv
@@ -0,0 +1,1220 @@
+//
+// Copyright 2021 Ettus Research, A National Instruments brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: eth_100g
+//
+// Description: Wrapper for the Xilinx 100G mac
+
+
+module eth_100g #(
+ logic PAUSE_EN = 1,
+ logic [15:0] PAUSE_QUANTA = 16'hFFFF,
+ logic [15:0] PAUSE_REFRESH = 16'hFFFF
+ )(
+
+ // Resets
+ input logic areset,
+ // Clock for misc stuff
+ input logic clk100,
+ // Low jitter refclk
+ input logic refclk_p,
+ input logic refclk_n,
+ // RX Clk for output
+ output logic rx_rec_clk_out,
+ // MGT high-speed IO
+ output logic[3:0] tx_p,
+ output logic[3:0] tx_n,
+ input logic[3:0] rx_p,
+ input logic[3:0] rx_n,
+
+ // Data port
+ output logic mgt_clk,
+ output logic mgt_rst,
+ input logic mgt_pause_req,
+ // Interface clocks for mgt_tx and mgt_rx are NOT used (logic uses mgt_clk)
+ AxiStreamIf.slave mgt_tx,
+ AxiStreamIf.master mgt_rx,
+ // Axi port
+ AxiLiteIf.slave mgt_axil,
+ // Misc
+ output logic [31:0] phy_status,
+ input logic [31:0] mac_ctrl,
+ output logic [31:0] mac_status,
+ output logic phy_reset,
+ output logic link_up
+);
+
+ logic tx_ovfout;
+ logic tx_unfout;
+ logic stat_rx_aligned;
+ logic stat_auto_config_done;
+ logic stat_auto_config_done_bclk;
+ logic usr_tx_reset;
+ logic usr_rx_reset;
+
+ // Heirarchical refference (xilinx says it will synthesize)
+ // eth_100g_bd_i/cmac_usplus_0/gt_rxrecclkout}
+ assign rx_rec_clk_out = eth_100g_bd_i.cmac_usplus_0.gt_rxrecclkout[0];
+ //status registers
+ always_comb begin
+ phy_status = 0;
+ phy_status[0] = usr_tx_reset;
+ phy_status[1] = usr_rx_reset;
+
+ end
+
+ logic [8:0] pause_mask; // from mac ctl register bits 24:16
+
+ always_ff @(posedge mgt_clk) begin : mac_status_reg
+ if (mgt_rst) begin
+ mac_status <= 0;
+ end else begin
+ mac_status[0] <= mac_status[0] || tx_ovfout;
+ mac_status[1] <= mac_status[1] || tx_unfout;
+ mac_status[2] <= stat_rx_aligned;
+ mac_status[3] <= mac_status[3] || (!mgt_rx.tready && mgt_rx.tvalid);
+ mac_status[4] <= stat_auto_config_done;
+ mac_status[24:16] <= pause_mask;
+ end
+ end
+
+ //extra simulation checks
+ localparam USE_MAC_CHECKS = 1;
+ if (USE_MAC_CHECKS) begin
+ always_ff @(posedge mgt_rx.clk) begin : check_no_holdoff
+ if (!mgt_rx.rst) begin
+ if (!mgt_rx.tready && mgt_rx.tvalid) begin
+ $fatal(1,"MAC RX can't hold off the MAC");
+ end
+ assert(tx_ovfout==0) else
+ $fatal(1,"MAC TX had an overflow!");
+ assert(tx_unfout==0) else
+ $fatal(1,"MAC TX had an underflow!");
+ end
+ end
+ end
+
+ initial begin
+ assert (mgt_tx.DATA_WIDTH == 512) else
+ $fatal("mgt_rx.DATA_WIDTH must be 512");
+ // $clog2(512/8)+1
+ assert (mgt_rx.USER_WIDTH == 7) else
+ $fatal("mgt_rx.USER_WIDTH must be 7");
+ assert (mgt_tx.TDATA == 1) else
+ $fatal("mgt_tx.TDATA must be enabled");
+ assert (mgt_tx.TUSER == 1) else
+ $fatal("mgt_tx.TUSER must be enabled");
+ assert (mgt_tx.TKEEP == 1) else
+ $fatal("mgt_tx.TKEEP must be enabled");
+ assert (mgt_tx.TLAST == 1) else
+ $fatal("mgt_tx.TLAST must be enabled");
+ assert (mgt_rx.DATA_WIDTH == 512) else
+ $fatal("mgt_rx.DATA_WIDTH must be 512");
+ // $clog2(512/8)+1
+ assert (mgt_rx.USER_WIDTH == 7) else
+ $fatal("mgt_rx.DATA_WIDTH must be 7");
+ assert (mgt_rx.TDATA == 1) else
+ $fatal("mgt_rx.TDATA must be enabled");
+ assert (mgt_rx.TUSER == 1) else
+ $fatal("mgt_rx.TUSER must be enabled");
+ assert (mgt_rx.TKEEP == 0) else
+ $fatal("mgt_rx.TKEEP must not be enabled");
+ assert (mgt_rx.TLAST == 1) else
+ $fatal("mgt_rx.TLAST must be enabled");
+ end
+
+ AxiStreamIf #(.DATA_WIDTH(512),.TUSER(0),.TKEEP(0))
+ eth100g_tx(mgt_clk,mgt_rst);
+ AxiStreamIf #(.DATA_WIDTH(512),.USER_WIDTH(7),.TKEEP(0))
+ eth100g_rx(mgt_clk,mgt_rst);
+
+ logic mgt_tx_idle;
+ logic mgt_tx_pause;
+
+ always_comb begin
+ eth100g_tx.tdata = mgt_tx.tdata;
+ eth100g_tx.tuser = 0;
+ eth100g_tx.tkeep = mgt_tx.tkeep;
+ eth100g_tx.tvalid = mgt_tx.tvalid && !mgt_tx_pause;
+ eth100g_tx.tlast = mgt_tx.tlast;
+ mgt_tx.tready = eth100g_tx.tready && !mgt_tx_pause;
+ end
+
+ always_comb begin
+ mgt_rx.tdata = eth100g_rx.tdata;
+ mgt_rx.tuser = eth100g_rx.tuser;
+ mgt_rx.tuser[mgt_rx.USER_WIDTH-1] = // assign error bit [MSB]
+ // CRC failure
+ eth100g_rx.tuser[mgt_rx.USER_WIDTH-1] ||
+ // Missed a DATA word.
+ mgt_rx.tvalid && !mgt_rx.tready;
+ mgt_rx.tkeep = eth100g_rx.tkeep;
+ mgt_rx.tvalid = eth100g_rx.tvalid;
+ mgt_rx.tlast = eth100g_rx.tlast;
+ // The MAC ignores hold off. Data must be consumed every clock it is valid.
+ // eth100g_rx.tready = mgt_rx.tready;
+ end
+
+ // This is a heavily replicated signal, add some pipeline
+ // to it to make it easier to spread out
+ logic mgt_rst_0;
+
+
+ // Flow control signals
+ // 0-7 map to PCP codes 0-7. 8 is a global pause request
+ logic [8:0] stat_rx_pause_req ;
+ logic [8:0] ctl_tx_pause_req ; // drive for at least 16 clocks
+ logic ctl_tx_resend_pause; // resend the pause request (tieing this high forces a spam of resend requests)
+
+ // QuantaPeriod is 512 bit times or 5.12 ns
+ // resend pause requests so (quanta*QuantaPeriod)/(refresh*QuantaPeriod) is the percentage of BW that gets through.
+ // pause_mask is part of the mac_ctrl register
+ always_comb begin
+ ctl_tx_resend_pause = 0;
+ ctl_tx_pause_req = '0;
+ if (mgt_pause_req) begin
+ ctl_tx_pause_req = pause_mask;
+ end
+ end
+ logic mgt_tx_pause_req;
+ assign mgt_tx_pause_req = (pause_mask & stat_rx_pause_req) != 0;
+ always_ff @(posedge mgt_clk,posedge areset) begin : reset_timing_dff
+ if (areset) begin
+ mgt_rst_0 <= 1'b1;
+ mgt_rst <= 1'b1;
+ mgt_tx_pause <= 1'b0;
+ mgt_tx_idle <= 1'b1;
+ end else begin
+ mgt_rst_0 <= !link_up;
+ mgt_rst <= mgt_rst_0;
+ //idle until a valid sets
+ if (mgt_tx_idle) begin
+ if (!eth100g_tx.tvalid) begin
+ mgt_tx_pause <= mgt_tx_pause_req;
+ end else begin
+ // one clock packet
+ if (eth100g_tx.tvalid && eth100g_tx.tlast && eth100g_tx.tready) begin
+ mgt_tx_idle <= 1;
+ mgt_tx_pause <= mgt_tx_pause_req;
+ end else begin
+ mgt_tx_idle <= 0;
+ end
+ end
+ //set idle if end of packet is accepted
+ end else if (eth100g_tx.tvalid && eth100g_tx.tlast && eth100g_tx.tready) begin
+ mgt_tx_idle <= 1;
+ mgt_tx_pause <= mgt_tx_pause_req;
+ end
+ end
+ end
+
+ always_comb phy_reset = usr_tx_reset || usr_rx_reset;
+ always_comb link_up = stat_rx_aligned && !phy_reset;
+
+ // resets stat counts and moves the total to the readable version.
+ localparam PM_COUNT = 40000;
+ logic pm_tick = 0;
+ logic [15:0] pm_tick_count;
+ always_ff @(posedge mgt_axil.clk) begin : pm_tick_counter
+ if (mgt_axil.rst) begin
+ pm_tick_count = 0;
+ pm_tick = 0;
+ end else begin
+ if (pm_tick_count == PM_COUNT-1) begin
+ pm_tick_count = 0;
+ pm_tick = 1;
+ end else begin
+ pm_tick_count = pm_tick_count+1;
+ pm_tick = 0;
+ end
+ end
+ end
+
+ `include "../../../../lib/axi4lite_sv/axi_lite.vh"
+ AxiLiteIf_v #(.DATA_WIDTH(mgt_axil.DATA_WIDTH),.ADDR_WIDTH(32))
+ mgt_axil_v(.clk(mgt_axil.clk),.rst(mgt_axil.rst));
+
+ localparam AUTO_CONNECT=1;
+ // When enabled the port will automatically attempt to connect to an Ethernet partner
+ // without requiring any action from SW. If it is not defined, SW will have to perform
+ // a similar set of writes. Xilinx publishes a driver for the MAC, that we could associate.
+ // The sequence of writes was taken from the CMAC example, without any deep knowledge
+ // of what the standard Ethernet connection protocol is.
+
+ // Inject writes to perform connection inbetween other SW writes to read the mac.
+ if (AUTO_CONNECT) begin : yes_auto_connect
+ // defined in https://www.xilinx.com/support/documentation/ip_documentation/cmac_usplus/v2_4/pg203-cmac-usplus.pdf
+ // pg 187
+ localparam CONFIGURATION_TX_REG1 = 32'h000C;
+ localparam ctl_tx_ctl_enable = 0;
+ localparam ctl_tx_ctl_tx_send_lfi = 3;
+ localparam ctl_tx_ctl_tx_send_rfi = 4;
+ localparam ctl_tx_ctl_tx_send_idle = 5;
+ localparam ctl_tx_ctl_test_pattern = 16;
+
+ localparam CONFIGURATION_RX_REG1 = 32'h0014;
+ localparam ctl_rx_ctl_enable = 0;
+ localparam ctl_rx_ctl_rx_force_resync = 7;
+ localparam ctl_rx_ctl_test_pattern = 8;
+
+ localparam RSFEC_CONFIG_INDICATION_CORRECTION = 32'h1000;
+ localparam rs_fec_in_ctl_rx_rsfec_enable_correction = 0;
+ localparam rs_fec_in_ctl_rx_rsfec_enable_indication = 1;
+ localparam rs_fec_in_ctl_rsfec_ieee_error_indication_mode = 2;
+
+ localparam RSFEC_CONFIG_ENABLE = 32'h107C;
+ localparam rs_fec_in_ctl_rx_rsfec_enable = 0;
+ localparam rs_fec_in_ctl_tx_rsfec_enable = 1;
+
+ // Extra configuration for Pause Frames (CMAC guide Pg 210)
+ //0x0084 : 32'h00003DFF [CONFIGURATION_RX_FLOW_CONTROL_CONTROL_REG1]
+ //0x0088 : 32'h0001C631 [CONFIGURATION_RX_FLOW_CONTROL_CONTROL_REG2]
+ //0x0048 : 32'hFFFFFFFF [CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG1]
+ //0x004C : 32'hFFFFFFFF [CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG2]
+ //0x0050 : 32'hFFFFFFFF [CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG3]
+ //0x0054 : 32'hFFFFFFFF [CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG4]
+ //0x0058 : 32'h0000FFFF [CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG5]
+ //0x0034 : 32'hFFFFFFFF [CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG1]
+ //0x0038 : 32'hFFFFFFFF [CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG2]
+ //0x003C : 32'hFFFFFFFF [CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG3]
+ //0x0040 : 32'hFFFFFFFF [CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG4]
+ //0x0044 : 32'h0000FFFF [CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG5]
+ //0x0030 : 32'h000001FF [CONFIGURATION_TX_FLOW_CONTROL_CONTROL_REG1]
+
+ localparam CONFIGURATION_RX_FLOW_CONTROL_CONTROL_REG1 = 32'h0084;
+ //3DFF - 0011 1101 1111 1111
+ localparam ctl_rx_pause_en = 0; // 9 bits
+ localparam ctl_rx_enable_gcp = 10;
+ localparam ctl_rx_enable_pcp = 11;
+ localparam ctl_rx_enable_gpp = 12;
+ localparam ctl_rx_enable_ppp = 13;
+ localparam ctl_rx_pause_ack = 23; // 8 bits
+ localparam CONFIGURATION_RX_FLOW_CONTROL_CONTROL_REG2 = 32'h0088;
+ //1C631 - 0001 1100 0110 0011 0001
+ localparam ctl_rx_check_mcast_gcp = 0; //1
+ localparam ctl_rx_check_ucast_gcp = 1;
+ localparam ctl_rx_check_sa_gcp = 2;
+ localparam ctl_rx_check_etype_gcp = 3;
+ localparam ctl_rx_check_opcode_gcp = 4; //1
+ localparam ctl_rx_check_mcast_pcp = 5; //1
+ localparam ctl_rx_check_ucast_pcp = 6;
+ localparam ctl_rx_check_sa_pcp = 7;
+ localparam ctl_rx_check_etype_pcp = 8;
+ localparam ctl_rx_check_opcode_pcp = 9; //1
+ localparam ctl_rx_check_mcast_gpp = 10; //1
+ localparam ctl_rx_check_ucast_gpp = 11;
+ localparam ctl_rx_check_sa_gpp = 12;
+ localparam ctl_rx_check_etype_gpp = 13;
+ localparam ctl_rx_check_opcode_gpp = 14; //1
+ localparam ctl_rx_check_opcode_ppp = 15; //1
+ localparam ctl_rx_check_mcast_ppp = 16; //1
+ localparam ctl_rx_check_ucast_ppp = 17;
+ localparam ctl_rx_check_sa_ppp = 18;
+ localparam ctl_rx_check_etype_ppp = 19;
+
+ localparam CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG1 = 32'h0048;
+ localparam ctl_tx_pause_quanta0 = 0;
+ localparam ctl_tx_pause_quanta1 = 16;
+ localparam CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG2 = 32'h004C;
+ localparam ctl_tx_pause_quanta2 = 0;
+ localparam ctl_tx_pause_quanta3 = 16;
+ localparam CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG3 = 32'h0050;
+ localparam ctl_tx_pause_quanta4 = 0;
+ localparam ctl_tx_pause_quanta5 = 16;
+ localparam CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG4 = 32'h0054;
+ localparam ctl_tx_pause_quanta6 = 0;
+ localparam ctl_tx_pause_quanta7 = 16;
+ localparam CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG5 = 32'h0058;
+ localparam ctl_tx_pause_quanta8 = 0;
+ localparam CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG1 = 32'h0034;
+ localparam ctl_tx_pause_refresh_timer0 = 0;
+ localparam ctl_tx_pause_refresh_timer1 = 16;
+ localparam CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG2 = 32'h0038;
+ localparam ctl_tx_pause_refresh_timer2 = 0;
+ localparam ctl_tx_pause_refresh_timer3 = 16;
+ localparam CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG3 = 32'h003C;
+ localparam ctl_tx_pause_refresh_timer4 = 0;
+ localparam ctl_tx_pause_refresh_timer5 = 16;
+ localparam CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG4 = 32'h0040;
+ localparam ctl_tx_pause_refresh_timer6 = 0;
+ localparam ctl_tx_pause_refresh_timer7 = 16;
+ localparam CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG5 = 32'h0044;
+ localparam ctl_tx_pause_refresh_timer8 = 0;
+ localparam CONFIGURATION_TX_FLOW_CONTROL_CONTROL_REG1 = 32'h0030;
+ // 1FF
+ localparam ctl_tx_pause_enable = 0; // 9 bits
+
+
+ AxiLiteIf #(.DATA_WIDTH(mgt_axil.DATA_WIDTH),.ADDR_WIDTH(32))
+ auto_axil(.clk(mgt_axil.clk),.rst(mgt_axil.rst));
+
+ typedef enum logic [4:0] {
+ ST_RESET = 5'd0,
+ ST_WR_CONFIGURATION_TX_REG1_IDLE = 5'd1,
+ ST_WR_RSFEC_CONFIG_INDICATION_CORRECTION = 5'd2,
+ ST_WR_RSFEC_CONFIG_ENABLE = 5'd3,
+ ST_WR_CONFIGURATION_RX_REG1 = 5'd4,
+ ST_WAIT = 5'd5,
+ ST_WR_CONFIGURATION_TX_REG1_TX_ENABLE = 5'd6,
+ ST_READY = 5'd7,
+ // EXTRA PAUSE WRITES
+ ST_WR_CONFIGURATION_RX_FLOW_CONTROL_CONTROL_REG1 = 5'd8,
+ ST_WR_CONFIGURATION_RX_FLOW_CONTROL_CONTROL_REG2 = 5'd9,
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG1 = 5'd10,
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG2 = 5'd11,
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG3 = 5'd12,
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG4 = 5'd13,
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG5 = 5'd14,
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG1 = 5'd15,
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG2 = 5'd16,
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG3 = 5'd17,
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG4 = 5'd18,
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG5 = 5'd19,
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_CONTROL_REG1 = 5'd20
+ } auto_connect_state_t;
+
+ auto_connect_state_t auto_connect_state = ST_RESET;
+ logic auto_rst0, auto_rst1, auto_rst2, auto_rst3;
+ logic mgt_axil_in_progress;
+ logic w_req, aw_req;
+ logic phy_reset_bclk,stat_rx_aligned_bclk;
+ logic auto_enable;
+
+ synchronizer #( .STAGES(2), .WIDTH(1), .INITIAL_VAL(0) ) phy_reset_sync_i (
+ .clk(mgt_axil.clk), .rst(1'b0), .in(phy_reset), .out(phy_reset_bclk)
+ );
+
+ synchronizer #( .STAGES(2), .WIDTH(1), .INITIAL_VAL(0) ) rx_aligned_sync_i (
+ .clk(mgt_axil.clk), .rst(1'b0), .in(stat_rx_aligned), .out(stat_rx_aligned_bclk)
+ );
+
+ synchronizer #( .STAGES(2), .WIDTH(1), .INITIAL_VAL(0) ) auto_config_done_sync_i (
+ .clk(mgt_clk), .rst(1'b0), .in(stat_auto_config_done_bclk), .out(stat_auto_config_done)
+ );
+
+
+ synchronizer #( .STAGES(2), .WIDTH(1), .INITIAL_VAL(0) ) auto_enable_sync_i (
+ .clk(mgt_axil.clk), .rst(1'b0), .in(mac_ctrl[0]), .out(auto_enable)
+ );
+
+ synchronizer #( .STAGES(2), .WIDTH(9), .INITIAL_VAL(9'h100) ) pause_mask_sync_i (
+ .clk(mgt_clk), .rst(1'b0), .in(mac_ctrl[24:16]), .out(pause_mask)
+ );
+
+
+ always_ff @(posedge mgt_axil.clk) begin : auto_enable_logic
+ if (mgt_axil.rst) begin
+ auto_rst0 <= 1'b1;
+ auto_rst1 <= 1'b1;
+ auto_rst2 <= 1'b1;
+ auto_rst3 <= 1'b1;
+ auto_connect_state <= ST_RESET;
+ stat_auto_config_done_bclk <= 1'b0;
+ mgt_axil_in_progress <= 1'b0;
+ w_req <= 1'b0;
+ aw_req <= 1'b0;
+
+ // default is to drive mgt_axi_through
+ /* write address channel */
+ auto_axil.awaddr <= 'bX;
+ auto_axil.awvalid <= 1'b0;
+ mgt_axil.awready <= 1'b0;
+ /* write data channel */
+ auto_axil.wdata <= 'bX;
+ auto_axil.wstrb <= 'b0;
+ auto_axil.wvalid <= 1'b0;
+ mgt_axil.wready <= 1'b0;
+ /* write resp channel */
+ mgt_axil.bresp[1:0] <= 'b0;
+ mgt_axil.bvalid <= 1'b0;
+ auto_axil.bready <= 1'b0;
+ /* read address channel */
+ auto_axil.araddr <= 'b0;
+ auto_axil.arvalid <= 1'b0;
+ mgt_axil.arready <= 1'b0;
+ /* read resp channel */
+ mgt_axil.rdata <= 'bX;
+ mgt_axil.rresp[1:0] <= 'b0;
+ mgt_axil.rvalid <= 1'b0;
+ auto_axil.rready <= 1'b0;
+
+ end else begin
+ // 4 clocks to mimic Xilinx Example behavior
+ auto_rst0 <= phy_reset_bclk;
+ auto_rst1 <= auto_rst0;
+ auto_rst2 <= auto_rst1;
+ auto_rst3 <= auto_rst2;
+ // assumes one access in flight at time (valid for standard Xilinx AXIL driver)
+ // set if anyone starts driving a W Address / W Data / R Address channel
+ if (auto_axil.awvalid || auto_axil.wvalid || auto_axil.arvalid) begin
+ mgt_axil_in_progress <= 1'b1;
+ // clear on an acknowledged response
+ end else if ((auto_axil.bvalid && auto_axil.bready) ||
+ (auto_axil.rvalid && auto_axil.rready)) begin
+ mgt_axil_in_progress <= 1'b0;
+ end
+
+ // default is to drive mgt_axi_through
+ /* write address channel */
+ auto_axil.awaddr <= mgt_axil.awaddr;
+ auto_axil.awvalid <= mgt_axil.awvalid;
+ mgt_axil.awready <= auto_axil.awready;
+ /* write data channel */
+ auto_axil.wdata <= mgt_axil.wdata;
+ auto_axil.wstrb <= mgt_axil.wstrb;
+ auto_axil.wvalid <= mgt_axil.wvalid;
+ mgt_axil.wready <= auto_axil.wready;
+ /* write resp channel */
+ mgt_axil.bresp <= auto_axil.bresp;
+ mgt_axil.bvalid <= auto_axil.bvalid;
+ auto_axil.bready <= mgt_axil.bready;
+ /* read address channel */
+ auto_axil.araddr <= mgt_axil.araddr;
+ auto_axil.arvalid <= mgt_axil.arvalid;
+ mgt_axil.arready <= auto_axil.arready;
+ /* read resp channel */
+ mgt_axil.rdata <= auto_axil.rdata;
+ mgt_axil.rresp <= auto_axil.rresp;
+ mgt_axil.rvalid <= auto_axil.rvalid;
+ auto_axil.rready <= mgt_axil.rready;
+
+ if (auto_rst3) begin
+ auto_connect_state = ST_RESET;
+ stat_auto_config_done_bclk <= 1'b0;
+ end else begin
+ case (auto_connect_state)
+
+ ST_RESET: begin
+ stat_auto_config_done_bclk <= 1'b0;
+ if (!mgt_axil_in_progress && auto_enable) begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_connect_state <= ST_WR_CONFIGURATION_TX_REG1_IDLE;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_TX_REG1_IDLE: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // start transmitting alignment pattern
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[ctl_tx_ctl_enable] <= 0;
+ auto_axil.wdata[ctl_tx_ctl_tx_send_idle] <= 0;
+ auto_axil.wdata[ctl_tx_ctl_tx_send_lfi] <= 0;
+ auto_axil.wdata[ctl_tx_ctl_tx_send_rfi] <= 1;
+ auto_axil.wdata[ctl_tx_ctl_test_pattern] <= 0;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_TX_REG1;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_RSFEC_CONFIG_INDICATION_CORRECTION;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_RSFEC_CONFIG_INDICATION_CORRECTION: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // configure fec
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[rs_fec_in_ctl_rx_rsfec_enable_correction] <= 1;
+ auto_axil.wdata[rs_fec_in_ctl_rx_rsfec_enable_indication] <= 1;
+ auto_axil.wdata[rs_fec_in_ctl_rsfec_ieee_error_indication_mode] <= 1;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= RSFEC_CONFIG_INDICATION_CORRECTION;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_RSFEC_CONFIG_ENABLE;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_RSFEC_CONFIG_ENABLE: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // enable fec
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[rs_fec_in_ctl_rx_rsfec_enable] <= 1;
+ auto_axil.wdata[rs_fec_in_ctl_tx_rsfec_enable] <= 1;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= RSFEC_CONFIG_ENABLE;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_RX_REG1;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_RX_REG1: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[ctl_rx_ctl_enable] <= 1;
+ auto_axil.wdata[ctl_rx_ctl_rx_force_resync] <= 0;
+ auto_axil.wdata[ctl_rx_ctl_test_pattern] <= 0;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_RX_REG1;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WAIT;
+ end
+ end
+
+ ST_WAIT: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // don't drive any writes, but hold off bus
+ auto_axil.wdata <= 0;
+ auto_axil.wstrb <= 0;
+ auto_axil.wvalid <= 0;
+ auto_axil.awaddr <= 0;
+ auto_axil.awvalid <= 0;
+ auto_axil.bready <= 0;
+ if (stat_rx_aligned_bclk) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_TX_REG1_TX_ENABLE;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_TX_REG1_TX_ENABLE: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // stop transmitting alignment pattern
+ // and start transmitting data
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[ctl_tx_ctl_enable] <= 1;
+ auto_axil.wdata[ctl_tx_ctl_tx_send_idle] <= 0;
+ auto_axil.wdata[ctl_tx_ctl_tx_send_lfi] <= 0;
+ auto_axil.wdata[ctl_tx_ctl_tx_send_rfi] <= 0;
+ auto_axil.wdata[ctl_tx_ctl_test_pattern] <= 0;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_TX_REG1;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ if (PAUSE_EN) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_RX_FLOW_CONTROL_CONTROL_REG1;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end else begin
+ auto_connect_state <= ST_READY;
+ end
+ end
+ end
+
+ ST_WR_CONFIGURATION_RX_FLOW_CONTROL_CONTROL_REG1: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ //3DFF - 0011 1101 1111 1111
+ auto_axil.wdata[ctl_rx_pause_en+:9] <= '1;
+ auto_axil.wdata[ctl_rx_enable_gcp] <= 1;
+ auto_axil.wdata[ctl_rx_enable_pcp] <= 1;
+ auto_axil.wdata[ctl_rx_enable_gpp] <= 1;
+ auto_axil.wdata[ctl_rx_enable_ppp] <= 1;
+ //ctl_rx_pause_ack = 23; // 8 bits NOT SET
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_RX_FLOW_CONTROL_CONTROL_REG1;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_RX_FLOW_CONTROL_CONTROL_REG2;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_RX_FLOW_CONTROL_CONTROL_REG2: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ //1C631 - 0001 1100 0110 0011 0001
+ auto_axil.wdata[ctl_rx_check_mcast_gcp ] <= 1; //1
+ auto_axil.wdata[ctl_rx_check_ucast_gcp ] <= 0;
+ auto_axil.wdata[ctl_rx_check_sa_gcp ] <= 0;
+ auto_axil.wdata[ctl_rx_check_etype_gcp ] <= 0;
+ auto_axil.wdata[ctl_rx_check_opcode_gcp] <= 1; //1
+ auto_axil.wdata[ctl_rx_check_mcast_pcp ] <= 1; //1
+ auto_axil.wdata[ctl_rx_check_ucast_pcp ] <= 0;
+ auto_axil.wdata[ctl_rx_check_sa_pcp ] <= 0;
+ auto_axil.wdata[ctl_rx_check_etype_pcp ] <= 0;
+ auto_axil.wdata[ctl_rx_check_opcode_pcp] <= 1; //1
+ auto_axil.wdata[ctl_rx_check_mcast_gpp ] <= 1; //1
+ auto_axil.wdata[ctl_rx_check_ucast_gpp ] <= 0;
+ auto_axil.wdata[ctl_rx_check_sa_gpp ] <= 0;
+ auto_axil.wdata[ctl_rx_check_etype_gpp ] <= 0;
+ auto_axil.wdata[ctl_rx_check_opcode_gpp] <= 1; //1
+ auto_axil.wdata[ctl_rx_check_opcode_ppp] <= 1; //1
+ auto_axil.wdata[ctl_rx_check_mcast_ppp ] <= 1; //1
+ auto_axil.wdata[ctl_rx_check_ucast_ppp ] <= 0;
+ auto_axil.wdata[ctl_rx_check_sa_ppp ] <= 0;
+ auto_axil.wdata[ctl_rx_check_etype_ppp ] <= 0;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_RX_FLOW_CONTROL_CONTROL_REG2;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG1;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG1: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[ctl_tx_pause_quanta0+:16] <= PAUSE_QUANTA;
+ auto_axil.wdata[ctl_tx_pause_quanta1+:16] <= PAUSE_QUANTA;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG1;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG2;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG2: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[ctl_tx_pause_quanta2+:16] <= PAUSE_QUANTA;
+ auto_axil.wdata[ctl_tx_pause_quanta3+:16] <= PAUSE_QUANTA;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG2;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG3;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG3: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[ctl_tx_pause_quanta4+:16] <= PAUSE_QUANTA;
+ auto_axil.wdata[ctl_tx_pause_quanta5+:16] <= PAUSE_QUANTA;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG3;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG4;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG4: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[ctl_tx_pause_quanta6+:16] <= PAUSE_QUANTA;
+ auto_axil.wdata[ctl_tx_pause_quanta7+:16] <= PAUSE_QUANTA;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG4;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG5;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG5: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[ctl_tx_pause_quanta8+:16] <= PAUSE_QUANTA;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_TX_FLOW_CONTROL_QUANTA_REG5;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG1;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG1: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[ctl_tx_pause_refresh_timer0+:16] <= PAUSE_REFRESH;
+ auto_axil.wdata[ctl_tx_pause_refresh_timer1+:16] <= PAUSE_REFRESH;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG1;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG2;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG2: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[ctl_tx_pause_refresh_timer2+:16] <= PAUSE_REFRESH;
+ auto_axil.wdata[ctl_tx_pause_refresh_timer3+:16] <= PAUSE_REFRESH;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG2;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG3;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG3: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[ctl_tx_pause_refresh_timer4+:16] <= PAUSE_REFRESH;
+ auto_axil.wdata[ctl_tx_pause_refresh_timer5+:16] <= PAUSE_REFRESH;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG3;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG4;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG4: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[ctl_tx_pause_refresh_timer6+:16] <= PAUSE_REFRESH;
+ auto_axil.wdata[ctl_tx_pause_refresh_timer7+:16] <= PAUSE_REFRESH;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG4;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG5;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG5: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ auto_axil.wdata[ctl_tx_pause_refresh_timer8+:16] <= PAUSE_REFRESH;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_TX_FLOW_CONTROL_REFRESH_REG5;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_WR_CONFIGURATION_TX_FLOW_CONTROL_CONTROL_REG1;
+ w_req <= 1'b1;
+ aw_req <= 1'b1;
+ end
+ end
+
+ ST_WR_CONFIGURATION_TX_FLOW_CONTROL_CONTROL_REG1: begin
+ mgt_axil.awready <= 0;
+ mgt_axil.wready <= 0;
+ mgt_axil.arready <= 0;
+ auto_axil.arvalid <= 0;
+ // turn on RX interface
+ auto_axil.wdata <= 0;
+ // 1FF
+ auto_axil.wdata[ctl_tx_pause_enable+:9] <= '1;
+ auto_axil.wstrb <= '1;
+ auto_axil.wvalid <= w_req;
+ auto_axil.awaddr <= CONFIGURATION_TX_FLOW_CONTROL_CONTROL_REG1;
+ auto_axil.awvalid <= aw_req;
+ auto_axil.bready <= 1'b1;
+ if (auto_axil.wready) begin
+ auto_axil.wvalid <= 1'b0;
+ w_req <= 1'b0;
+ end
+ if (auto_axil.awready) begin
+ auto_axil.awvalid <= 1'b0;
+ aw_req <= 1'b0;
+ end
+ if (auto_axil.bvalid) begin
+ auto_connect_state <= ST_READY;
+ end
+ end
+
+ ST_READY: begin
+ stat_auto_config_done_bclk <= 1'b1;
+ if (!stat_rx_aligned_bclk) begin
+ auto_connect_state <= ST_RESET;
+ end
+ end
+
+ endcase
+ end
+ end
+ end
+
+ always_comb begin
+ `AXI4LITE_ASSIGN(mgt_axil_v,auto_axil)
+
+ // window address to 0x0000-x1FFF
+ mgt_axil_v.araddr = 0;
+ mgt_axil_v.araddr[12:0] = auto_axil.araddr[12:0];
+ // window address to 0x0000-x1FFF
+ mgt_axil_v.awaddr = 0;
+ mgt_axil_v.awaddr[12:0] = auto_axil.awaddr[12:0];
+ end
+
+ end else begin : no_auto_connect
+
+ always_comb begin
+ `AXI4LITE_ASSIGN(mgt_axil_v,mgt_axil)
+
+ // window address to 0x0000-x1FFF
+ mgt_axil_v.araddr = 0;
+ mgt_axil_v.araddr[12:0] = mgt_axil.araddr[12:0];
+ // window address to 0x0000-x1FFF
+ mgt_axil_v.awaddr = 0;
+ mgt_axil_v.awaddr[12:0] = mgt_axil.awaddr[12:0];
+
+ stat_auto_config_done_bclk = 1'b1;
+ end
+ end
+
+ import PkgEth100gLbus::*;
+
+
+ lbus_t lbus_rx [3:0];
+ lbus_t lbus_tx [3:0];
+ logic lbus_tx_rdyout;
+
+ eth_100g_lbus2axi #(.NUM_SEG(4)) lbus2axi (
+ .axis(eth100g_rx),
+ .lbus_in(lbus_rx)
+ );
+
+ eth_100g_axi2lbus #(.NUM_SEG(4)) axi2lbus (
+ .axis(eth100g_tx),
+ .lbus_rdy(lbus_tx_rdyout),
+ .lbus_out(lbus_tx)
+ );
+
+ eth_100g_bd eth_100g_bd_i (
+ .refclk_clk_n(refclk_n),
+ .refclk_clk_p(refclk_p),
+ .gt_rx_gt_port_0_n(rx_n[0]),
+ .gt_rx_gt_port_0_p(rx_p[0]),
+ .gt_rx_gt_port_1_n(rx_n[1]),
+ .gt_rx_gt_port_1_p(rx_p[1]),
+ .gt_rx_gt_port_2_n(rx_n[2]),
+ .gt_rx_gt_port_2_p(rx_p[2]),
+ .gt_rx_gt_port_3_n(rx_n[3]),
+ .gt_rx_gt_port_3_p(rx_p[3]),
+ .init_clk(clk100),
+ .gt_tx_gt_port_0_n(tx_n[0]),
+ .gt_tx_gt_port_0_p(tx_p[0]),
+ .gt_tx_gt_port_1_n(tx_n[1]),
+ .gt_tx_gt_port_1_p(tx_p[1]),
+ .gt_tx_gt_port_2_n(tx_n[2]),
+ .gt_tx_gt_port_2_p(tx_p[2]),
+ .gt_tx_gt_port_3_n(tx_n[3]),
+ .gt_tx_gt_port_3_p(tx_p[3]),
+ .sys_reset(areset),
+ .usr_rx_reset(usr_rx_reset),
+ .usr_tx_reset(usr_tx_reset),
+ .gt_txusrclk2(mgt_clk),
+ .rx_clk(mgt_clk), //feedback in
+ .tx_ovfout(tx_ovfout),
+ .tx_unfout(tx_unfout),
+ .stat_rx_aligned(stat_rx_aligned),
+ .drp_clk(clk100),
+ .core_drp_daddr(10'b0),
+ .core_drp_den(1'b0),
+ .core_drp_di(16'b0),
+ .core_drp_do(),
+ .core_drp_drdy(),
+ .core_drp_dwe(1'b0),
+ `AXI4LITE_PORT_ASSIGN(s_axi,mgt_axil_v)
+ .pm_tick(pm_tick),
+ .ctl_tx_pause_req(ctl_tx_pause_req),
+ .ctl_tx_resend_pause(ctl_tx_resend_pause),
+ .stat_rx_pause_req(stat_rx_pause_req),
+ .eth100g_rx_lbus_seg0_data(lbus_rx[0].data),
+ .eth100g_rx_lbus_seg0_ena(lbus_rx[0].ena),
+ .eth100g_rx_lbus_seg0_eop(lbus_rx[0].eop),
+ .eth100g_rx_lbus_seg0_err(lbus_rx[0].err),
+ .eth100g_rx_lbus_seg0_mty(lbus_rx[0].mty),
+ .eth100g_rx_lbus_seg0_sop(lbus_rx[0].sop),
+ .eth100g_rx_lbus_seg1_data(lbus_rx[1].data),
+ .eth100g_rx_lbus_seg1_ena(lbus_rx[1].ena),
+ .eth100g_rx_lbus_seg1_eop(lbus_rx[1].eop),
+ .eth100g_rx_lbus_seg1_err(lbus_rx[1].err),
+ .eth100g_rx_lbus_seg1_mty(lbus_rx[1].mty),
+ .eth100g_rx_lbus_seg1_sop(lbus_rx[1].sop),
+ .eth100g_rx_lbus_seg2_data(lbus_rx[2].data),
+ .eth100g_rx_lbus_seg2_ena(lbus_rx[2].ena),
+ .eth100g_rx_lbus_seg2_eop(lbus_rx[2].eop),
+ .eth100g_rx_lbus_seg2_err(lbus_rx[2].err),
+ .eth100g_rx_lbus_seg2_mty(lbus_rx[2].mty),
+ .eth100g_rx_lbus_seg2_sop(lbus_rx[2].sop),
+ .eth100g_rx_lbus_seg3_data(lbus_rx[3].data),
+ .eth100g_rx_lbus_seg3_ena(lbus_rx[3].ena),
+ .eth100g_rx_lbus_seg3_eop(lbus_rx[3].eop),
+ .eth100g_rx_lbus_seg3_err(lbus_rx[3].err),
+ .eth100g_rx_lbus_seg3_mty(lbus_rx[3].mty),
+ .eth100g_rx_lbus_seg3_sop(lbus_rx[3].sop),
+ .eth100g_tx_lbus_seg0_data(lbus_tx[0].data),
+ .eth100g_tx_lbus_seg0_ena(lbus_tx[0].ena),
+ .eth100g_tx_lbus_seg0_eop(lbus_tx[0].eop),
+ .eth100g_tx_lbus_seg0_err(lbus_tx[0].err),
+ .eth100g_tx_lbus_seg0_mty(lbus_tx[0].mty),
+ .eth100g_tx_lbus_seg0_sop(lbus_tx[0].sop),
+ .eth100g_tx_lbus_seg1_data(lbus_tx[1].data),
+ .eth100g_tx_lbus_seg1_ena(lbus_tx[1].ena),
+ .eth100g_tx_lbus_seg1_eop(lbus_tx[1].eop),
+ .eth100g_tx_lbus_seg1_err(lbus_tx[1].err),
+ .eth100g_tx_lbus_seg1_mty(lbus_tx[1].mty),
+ .eth100g_tx_lbus_seg1_sop(lbus_tx[1].sop),
+ .eth100g_tx_lbus_seg2_data(lbus_tx[2].data),
+ .eth100g_tx_lbus_seg2_ena(lbus_tx[2].ena),
+ .eth100g_tx_lbus_seg2_eop(lbus_tx[2].eop),
+ .eth100g_tx_lbus_seg2_err(lbus_tx[2].err),
+ .eth100g_tx_lbus_seg2_mty(lbus_tx[2].mty),
+ .eth100g_tx_lbus_seg2_sop(lbus_tx[2].sop),
+ .eth100g_tx_lbus_seg3_data(lbus_tx[3].data),
+ .eth100g_tx_lbus_seg3_ena(lbus_tx[3].ena),
+ .eth100g_tx_lbus_seg3_eop(lbus_tx[3].eop),
+ .eth100g_tx_lbus_seg3_err(lbus_tx[3].err),
+ .eth100g_tx_lbus_seg3_mty(lbus_tx[3].mty),
+ .eth100g_tx_lbus_seg3_sop(lbus_tx[3].sop),
+ .eth100g_tx_tx_rdyout(lbus_tx_rdyout));
+
+endmodule
diff --git a/fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g_axis2lbus.sv b/fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g_axis2lbus.sv
new file mode 100644
index 000000000..a3c319043
--- /dev/null
+++ b/fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g_axis2lbus.sv
@@ -0,0 +1,120 @@
+//
+// Copyright 2021 Ettus Research, A National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: eth_100g_axi2lbus
+//
+// Description:
+// Translate from AXI4S (Xilinx segmented ifc) to lbus.
+//
+// Built using example provided from Xilinx
+//
+// Parameters:
+// - FIFO_DEPTH - FIFO will be 2** deep
+// - NUM_SEG - Number of lbus segments coming in
+
+import PkgEth100gLbus::*;
+
+module eth_100g_axi2lbus #(
+ parameter FIFO_DEPTH = 5,
+ parameter NUM_SEG = 4
+)
+(
+
+ // AXIS IF
+ AxiStreamIf.slave axis,
+
+ // Lbus Segments
+
+ input logic lbus_rdy,
+ output lbus_t lbus_out [NUM_SEG-1:0]
+
+);
+
+ localparam SEG_DATA_WIDTH = DATA_WIDTH/NUM_SEG;
+ localparam SEG_BYTES = SEG_DATA_WIDTH/8;
+ localparam SEG_MTY_WIDTH = $clog2(SEG_BYTES);
+
+ // post rotation lbus signals
+ lbus_t lbus_d [NUM_SEG-1:0];
+
+ // Find last so we can find SOP
+ logic found_last;
+
+ // Propagate ready when asserting , propagate delayed ready while deasserting
+ logic axis_tready_i;
+ assign axis.tready = axis_tready_i | lbus_rdy;
+
+ always @(posedge axis.clk) begin
+ axis_tready_i <= lbus_rdy;
+ end
+
+ //declare a segment width axis bus so I can use it's methods
+ AxiStreamIf #(.DATA_WIDTH(SEG_DATA_WIDTH),.USER_WIDTH($clog2(SEG_BYTES)))
+ seg_axi (axis.clk, axis.rst);
+ assign seg_axi.tlast = 1'b1;
+
+ logic [NUM_SEG:0] valid;
+ assign valid[NUM_SEG] = 1'b0;
+
+ genvar b;
+ genvar s;
+ generate begin : lbus_gen
+ for (s=0; s < NUM_SEG; s=s+1) begin : segment_loop
+ // Reverse data byte ordering on each segment
+ for (b = 0; b < DATA_WIDTH/32; b=b+1) begin : byte_loop
+ assign lbus_d[s].data[b*8 +: 8] = axis.tdata[((s+1)*(DATA_WIDTH/NUM_SEG)-8-(b*8)) +: 8];
+ end : byte_loop
+ // valid if tkeep is set for any bytes in the segment
+ assign valid[s] = (| axis.tkeep[s*SEG_BYTES +: SEG_BYTES]) & axis.tvalid;
+ // enable when valid and transfering
+ assign lbus_d[s].ena = valid[s] & axis.tready;
+ // eop on last valid byte if last is set
+ // we init an extra valid bit to 0 so if all valid bits for all segments are set we trigger an eop on the final segment
+ assign lbus_d[s].eop = (valid[s] ^ valid[s+1]) & axis.tlast;
+ // set error on all segmetns if tuser is set
+ assign lbus_d[s].err = valid[s] & axis.tuser;
+ // translate keep to trailing bytes and invert sign
+ always_comb begin
+ if (lbus_d[s].eop) begin
+ lbus_d[s].mty = SEG_BYTES - seg_axi.keep2trailing(axis.tkeep[s*SEG_BYTES+: SEG_BYTES]);
+ end else begin
+ lbus_d[s].mty = 'b0;
+ end
+ end
+ //SOP can only occur on segment 0, so init all the bits to zero, then assign segment 0
+ if (s==0) begin
+ assign lbus_d[s].sop = found_last & axis.tvalid & axis.tready;
+ end else begin
+ assign lbus_d[s].sop = 1'b0;
+ end
+ // assign the output DFF's
+ always_ff @(posedge axis.clk) begin
+ lbus_out[s].data <= lbus_d[s].data;
+ lbus_out[s].ena <= lbus_d[s].ena;
+ lbus_out[s].sop <= lbus_d[s].sop;
+ lbus_out[s].eop <= lbus_d[s].eop;
+ lbus_out[s].err <= lbus_d[s].err;
+ lbus_out[s].mty <= lbus_d[s].mty;
+ end
+
+ end : segment_loop
+ end : lbus_gen
+ endgenerate
+
+
+
+ // SOP Statemachine
+ always_ff @(posedge axis.clk) begin : sop_sm
+ if(axis.rst) begin
+ found_last <= 1'b1;
+ end else begin
+ if(axis.tvalid & axis.tlast & axis.tready) found_last <= 1'b1;
+ else if(axis.tvalid & axis.tready) found_last <= 1'b0;
+ end
+ end : sop_sm
+
+endmodule
+
+
diff --git a/fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g_bd.tcl b/fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g_bd.tcl
new file mode 100644
index 000000000..624e0902e
--- /dev/null
+++ b/fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g_bd.tcl
@@ -0,0 +1,361 @@
+
+################################################################
+# This is a generated script based on design: eth_100g_bd
+#
+# Though there are limitations about the generated script,
+# the main purpose of this utility is to make learning
+# IP Integrator Tcl commands easier.
+################################################################
+
+namespace eval _tcl {
+proc get_script_folder {} {
+ set script_path [file normalize [info script]]
+ set script_folder [file dirname $script_path]
+ return $script_folder
+}
+}
+variable script_folder
+set script_folder [_tcl::get_script_folder]
+
+################################################################
+# Check if script is running in correct Vivado version.
+################################################################
+set scripts_vivado_version 2019.1
+set current_vivado_version [version -short]
+
+if { [string first $scripts_vivado_version $current_vivado_version] == -1 } {
+ puts ""
+ catch {common::send_msg_id "BD_TCL-109" "ERROR" "This script was generated using Vivado <$scripts_vivado_version> and is being run in <$current_vivado_version> of Vivado. Please run the script in Vivado <$scripts_vivado_version> then open the design in Vivado <$current_vivado_version>. Upgrade the design by running \"Tools => Report => Report IP Status...\", then run write_bd_tcl to create an updated script."}
+
+ return 1
+}
+
+################################################################
+# START
+################################################################
+
+# To test this script, run the following commands from Vivado Tcl console:
+# source eth_100g_bd_script.tcl
+
+# If there is no project opened, this script will create a
+# project, but make sure you do not have an existing project
+# <./myproj/project_1.xpr> in the current working folder.
+
+set list_projs [get_projects -quiet]
+if { $list_projs eq "" } {
+ create_project project_1 myproj -part xczu28dr-ffvg1517-1-e
+}
+
+
+# CHANGE DESIGN NAME HERE
+variable design_name
+set design_name eth_100g_bd
+
+# If you do not already have an existing IP Integrator design open,
+# you can create a design using the following command:
+# create_bd_design $design_name
+
+# Creating design if needed
+set errMsg ""
+set nRet 0
+
+set cur_design [current_bd_design -quiet]
+set list_cells [get_bd_cells -quiet]
+
+if { ${design_name} eq "" } {
+ # USE CASES:
+ # 1) Design_name not set
+
+ set errMsg "Please set the variable <design_name> to a non-empty value."
+ set nRet 1
+
+} elseif { ${cur_design} ne "" && ${list_cells} eq "" } {
+ # USE CASES:
+ # 2): Current design opened AND is empty AND names same.
+ # 3): Current design opened AND is empty AND names diff; design_name NOT in project.
+ # 4): Current design opened AND is empty AND names diff; design_name exists in project.
+
+ if { $cur_design ne $design_name } {
+ common::send_msg_id "BD_TCL-001" "INFO" "Changing value of <design_name> from <$design_name> to <$cur_design> since current design is empty."
+ set design_name [get_property NAME $cur_design]
+ }
+ common::send_msg_id "BD_TCL-002" "INFO" "Constructing design in IPI design <$cur_design>..."
+
+} elseif { ${cur_design} ne "" && $list_cells ne "" && $cur_design eq $design_name } {
+ # USE CASES:
+ # 5) Current design opened AND has components AND same names.
+
+ set errMsg "Design <$design_name> already exists in your project, please set the variable <design_name> to another value."
+ set nRet 1
+} elseif { [get_files -quiet ${design_name}.bd] ne "" } {
+ # USE CASES:
+ # 6) Current opened design, has components, but diff names, design_name exists in project.
+ # 7) No opened design, design_name exists in project.
+
+ set errMsg "Design <$design_name> already exists in your project, please set the variable <design_name> to another value."
+ set nRet 2
+
+} else {
+ # USE CASES:
+ # 8) No opened design, design_name not in project.
+ # 9) Current opened design, has components, but diff names, design_name not in project.
+
+ common::send_msg_id "BD_TCL-003" "INFO" "Currently there is no design <$design_name> in project, so creating one..."
+
+ create_bd_design $design_name
+
+ common::send_msg_id "BD_TCL-004" "INFO" "Making design <$design_name> as current_bd_design."
+ current_bd_design $design_name
+
+}
+
+common::send_msg_id "BD_TCL-005" "INFO" "Currently the variable <design_name> is equal to \"$design_name\"."
+
+if { $nRet != 0 } {
+ catch {common::send_msg_id "BD_TCL-114" "ERROR" $errMsg}
+ return $nRet
+}
+
+set bCheckIPsPassed 1
+##################################################################
+# CHECK IPs
+##################################################################
+set bCheckIPs 1
+if { $bCheckIPs == 1 } {
+ set list_check_ips "\
+xilinx.com:ip:cmac_usplus:2.6\
+xilinx.com:ip:xlconstant:1.1\
+"
+
+ set list_ips_missing ""
+ common::send_msg_id "BD_TCL-006" "INFO" "Checking if the following IPs exist in the project's IP catalog: $list_check_ips ."
+
+ foreach ip_vlnv $list_check_ips {
+ set ip_obj [get_ipdefs -all $ip_vlnv]
+ if { $ip_obj eq "" } {
+ lappend list_ips_missing $ip_vlnv
+ }
+ }
+
+ if { $list_ips_missing ne "" } {
+ catch {common::send_msg_id "BD_TCL-115" "ERROR" "The following IPs are not found in the IP Catalog:\n $list_ips_missing\n\nResolution: Please add the repository containing the IP(s) to the project." }
+ set bCheckIPsPassed 0
+ }
+
+}
+
+if { $bCheckIPsPassed != 1 } {
+ common::send_msg_id "BD_TCL-1003" "WARNING" "Will not continue with creation of design due to the error(s) above."
+ return 3
+}
+
+##################################################################
+# DESIGN PROCs
+##################################################################
+
+
+
+# Procedure to create entire design; Provide argument to make
+# procedure reusable. If parentCell is "", will use root.
+proc create_root_design { parentCell } {
+
+ variable script_folder
+ variable design_name
+
+ if { $parentCell eq "" } {
+ set parentCell [get_bd_cells /]
+ }
+
+ # Get object for parentCell
+ set parentObj [get_bd_cells $parentCell]
+ if { $parentObj == "" } {
+ catch {common::send_msg_id "BD_TCL-100" "ERROR" "Unable to find parent cell <$parentCell>!"}
+ return
+ }
+
+ # Make sure parentObj is hier blk
+ set parentType [get_property TYPE $parentObj]
+ if { $parentType ne "hier" } {
+ catch {common::send_msg_id "BD_TCL-101" "ERROR" "Parent <$parentObj> has TYPE = <$parentType>. Expected to be <hier>."}
+ return
+ }
+
+ # Save current instance; Restore later
+ set oldCurInst [current_bd_instance .]
+
+ # Set parent object as current
+ current_bd_instance $parentObj
+
+
+ # Create interface ports
+ set core_drp [ create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:drp_rtl:1.0 core_drp ]
+
+ set eth100g_rx [ create_bd_intf_port -mode Master -vlnv xilinx.com:display_cmac_usplus:lbus_ports:2.0 eth100g_rx ]
+
+ set eth100g_tx [ create_bd_intf_port -mode Slave -vlnv xilinx.com:display_cmac_usplus:lbus_ports:2.0 eth100g_tx ]
+
+ set gt_rx [ create_bd_intf_port -mode Slave -vlnv xilinx.com:display_cmac_usplus:gt_ports:2.0 gt_rx ]
+
+ set gt_tx [ create_bd_intf_port -mode Master -vlnv xilinx.com:display_cmac_usplus:gt_ports:2.0 gt_tx ]
+
+ set refclk [ create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:diff_clock_rtl:1.0 refclk ]
+ set_property -dict [ list \
+ CONFIG.FREQ_HZ {156250000} \
+ ] $refclk
+
+ set s_axi [ create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:aximm_rtl:1.0 s_axi ]
+ set_property -dict [ list \
+ CONFIG.ADDR_WIDTH {32} \
+ CONFIG.ARUSER_WIDTH {0} \
+ CONFIG.AWUSER_WIDTH {0} \
+ CONFIG.BUSER_WIDTH {0} \
+ CONFIG.DATA_WIDTH {32} \
+ CONFIG.HAS_BRESP {1} \
+ CONFIG.HAS_BURST {0} \
+ CONFIG.HAS_CACHE {0} \
+ CONFIG.HAS_LOCK {0} \
+ CONFIG.HAS_PROT {0} \
+ CONFIG.HAS_QOS {0} \
+ CONFIG.HAS_REGION {0} \
+ CONFIG.HAS_RRESP {1} \
+ CONFIG.HAS_WSTRB {1} \
+ CONFIG.ID_WIDTH {0} \
+ CONFIG.MAX_BURST_LENGTH {1} \
+ CONFIG.NUM_READ_OUTSTANDING {1} \
+ CONFIG.NUM_READ_THREADS {1} \
+ CONFIG.NUM_WRITE_OUTSTANDING {1} \
+ CONFIG.NUM_WRITE_THREADS {1} \
+ CONFIG.PROTOCOL {AXI4LITE} \
+ CONFIG.READ_WRITE_MODE {READ_WRITE} \
+ CONFIG.RUSER_BITS_PER_BYTE {0} \
+ CONFIG.RUSER_WIDTH {0} \
+ CONFIG.SUPPORTS_NARROW_BURST {0} \
+ CONFIG.WUSER_BITS_PER_BYTE {0} \
+ CONFIG.WUSER_WIDTH {0} \
+ ] $s_axi
+
+
+ # Create ports
+ set ctl_tx_pause_req [ create_bd_port -dir I -from 8 -to 0 ctl_tx_pause_req ]
+ set ctl_tx_resend_pause [ create_bd_port -dir I ctl_tx_resend_pause ]
+ set drp_clk [ create_bd_port -dir I -type clk drp_clk ]
+ set gt_txusrclk2 [ create_bd_port -dir O -type clk gt_txusrclk2 ]
+ set_property -dict [ list \
+ CONFIG.ASSOCIATED_BUSIF {eth100g_rx:eth100g_tx} \
+ CONFIG.FREQ_HZ {322265625} \
+ ] $gt_txusrclk2
+ set init_clk [ create_bd_port -dir I -type clk init_clk ]
+ set pm_tick [ create_bd_port -dir I pm_tick ]
+ set rx_clk [ create_bd_port -dir I -type clk rx_clk ]
+ set_property -dict [ list \
+ CONFIG.FREQ_HZ {322265625} \
+ ] $rx_clk
+ set s_axi_aclk [ create_bd_port -dir I -type clk s_axi_aclk ]
+ set s_axi_sreset [ create_bd_port -dir I -type rst s_axi_sreset ]
+ set_property -dict [ list \
+ CONFIG.POLARITY {ACTIVE_HIGH} \
+ ] $s_axi_sreset
+ set stat_rx_aligned [ create_bd_port -dir O stat_rx_aligned ]
+ set stat_rx_pause_req [ create_bd_port -dir O -from 8 -to 0 stat_rx_pause_req ]
+ set sys_reset [ create_bd_port -dir I -type rst sys_reset ]
+ set_property -dict [ list \
+ CONFIG.POLARITY {ACTIVE_HIGH} \
+ ] $sys_reset
+ set tx_ovfout [ create_bd_port -dir O tx_ovfout ]
+ set tx_unfout [ create_bd_port -dir O tx_unfout ]
+ set usr_rx_reset [ create_bd_port -dir O -type rst usr_rx_reset ]
+ set usr_tx_reset [ create_bd_port -dir O -type rst usr_tx_reset ]
+
+ # Create instance: cmac_usplus_0, and set properties
+ set cmac_usplus_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:cmac_usplus:2.6 cmac_usplus_0 ]
+ set_property -dict [ list \
+ CONFIG.CMAC_CAUI4_MODE {1} \
+ CONFIG.CMAC_CORE_SELECT {CMACE4_X0Y0} \
+ CONFIG.ENABLE_AXI_INTERFACE {1} \
+ CONFIG.GT_DRP_CLK {100} \
+ CONFIG.GT_GROUP_SELECT {X0Y4~X0Y7} \
+ CONFIG.GT_REF_CLK_FREQ {156.25} \
+ CONFIG.INCLUDE_AUTO_NEG_LT_LOGIC {0} \
+ CONFIG.INCLUDE_RS_FEC {1} \
+ CONFIG.INCLUDE_SHARED_LOGIC {2} \
+ CONFIG.INCLUDE_STATISTICS_COUNTERS {1} \
+ CONFIG.LANE10_GT_LOC {NA} \
+ CONFIG.LANE1_GT_LOC {X0Y4} \
+ CONFIG.LANE2_GT_LOC {X0Y5} \
+ CONFIG.LANE3_GT_LOC {X0Y6} \
+ CONFIG.LANE4_GT_LOC {X0Y7} \
+ CONFIG.LANE5_GT_LOC {NA} \
+ CONFIG.LANE6_GT_LOC {NA} \
+ CONFIG.LANE7_GT_LOC {NA} \
+ CONFIG.LANE8_GT_LOC {NA} \
+ CONFIG.LANE9_GT_LOC {NA} \
+ CONFIG.NUM_LANES {4} \
+ CONFIG.RX_CHECK_ACK {0} \
+ CONFIG.RX_EQ_MODE {AUTO} \
+ CONFIG.RX_FLOW_CONTROL {1} \
+ CONFIG.TX_FLOW_CONTROL {1} \
+ CONFIG.USER_INTERFACE {LBUS} \
+ ] $cmac_usplus_0
+
+ # Create instance: tie_loopback, and set properties
+ set tie_loopback [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 tie_loopback ]
+ set_property -dict [ list \
+ CONFIG.CONST_VAL {0} \
+ CONFIG.CONST_WIDTH {12} \
+ ] $tie_loopback
+
+ # Create instance: tie_zero, and set properties
+ set tie_zero [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 tie_zero ]
+ set_property -dict [ list \
+ CONFIG.CONST_VAL {0} \
+ ] $tie_zero
+
+ # Create interface connections
+ connect_bd_intf_net -intf_net RefClk_1 [get_bd_intf_ports refclk] [get_bd_intf_pins cmac_usplus_0/gt_ref_clk]
+ connect_bd_intf_net -intf_net Rx_1 [get_bd_intf_ports gt_rx] [get_bd_intf_pins cmac_usplus_0/gt_rx]
+ connect_bd_intf_net -intf_net cmac_usplus_0_gt_tx [get_bd_intf_ports gt_tx] [get_bd_intf_pins cmac_usplus_0/gt_tx]
+ connect_bd_intf_net -intf_net cmac_usplus_0_lbus_rx [get_bd_intf_ports eth100g_rx] [get_bd_intf_pins cmac_usplus_0/lbus_rx]
+ connect_bd_intf_net -intf_net eth_100g_tx_1 [get_bd_intf_ports eth100g_tx] [get_bd_intf_pins cmac_usplus_0/lbus_tx]
+ connect_bd_intf_net -intf_net sDrp_1 [get_bd_intf_ports core_drp] [get_bd_intf_pins cmac_usplus_0/core_drp]
+ connect_bd_intf_net -intf_net s_axi_1 [get_bd_intf_ports s_axi] [get_bd_intf_pins cmac_usplus_0/s_axi]
+
+ # Create port connections
+ connect_bd_net -net SysClk_1 [get_bd_ports init_clk] [get_bd_pins cmac_usplus_0/init_clk]
+ connect_bd_net -net aResetIn_1 [get_bd_ports sys_reset] [get_bd_pins cmac_usplus_0/sys_reset]
+ connect_bd_net -net cmac_usplus_0_gt_txusrclk2 [get_bd_ports gt_txusrclk2] [get_bd_pins cmac_usplus_0/gt_txusrclk2]
+ connect_bd_net -net cmac_usplus_0_stat_rx_aligned [get_bd_ports stat_rx_aligned] [get_bd_pins cmac_usplus_0/stat_rx_aligned]
+ connect_bd_net -net cmac_usplus_0_stat_rx_pause_req [get_bd_ports stat_rx_pause_req] [get_bd_pins cmac_usplus_0/stat_rx_pause_req]
+ connect_bd_net -net cmac_usplus_0_tx_ovfout [get_bd_ports tx_ovfout] [get_bd_pins cmac_usplus_0/tx_ovfout]
+ connect_bd_net -net cmac_usplus_0_tx_unfout [get_bd_ports tx_unfout] [get_bd_pins cmac_usplus_0/tx_unfout]
+ connect_bd_net -net cmac_usplus_0_usr_rx_reset [get_bd_ports usr_rx_reset] [get_bd_pins cmac_usplus_0/usr_rx_reset]
+ connect_bd_net -net cmac_usplus_0_usr_tx_reset [get_bd_ports usr_tx_reset] [get_bd_pins cmac_usplus_0/usr_tx_reset]
+ connect_bd_net -net ctl_tx_pause_req_1 [get_bd_ports ctl_tx_pause_req] [get_bd_pins cmac_usplus_0/ctl_tx_pause_req]
+ connect_bd_net -net ctl_tx_resend_pause_1 [get_bd_ports ctl_tx_resend_pause] [get_bd_pins cmac_usplus_0/ctl_tx_resend_pause]
+ connect_bd_net -net drp_clk_1 [get_bd_ports drp_clk] [get_bd_pins cmac_usplus_0/drp_clk]
+ connect_bd_net -net pm_tick_1 [get_bd_ports pm_tick] [get_bd_pins cmac_usplus_0/pm_tick]
+ connect_bd_net -net rx_clk_1 [get_bd_ports rx_clk] [get_bd_pins cmac_usplus_0/rx_clk]
+ connect_bd_net -net s_axi_aclk_1 [get_bd_ports s_axi_aclk] [get_bd_pins cmac_usplus_0/s_axi_aclk]
+ connect_bd_net -net s_axi_sreset_1 [get_bd_ports s_axi_sreset] [get_bd_pins cmac_usplus_0/s_axi_sreset]
+ connect_bd_net -net tie_loopback_dout [get_bd_pins cmac_usplus_0/gt_loopback_in] [get_bd_pins tie_loopback/dout]
+ connect_bd_net -net tie_zero_dout [get_bd_pins cmac_usplus_0/gtwiz_reset_rx_datapath] [get_bd_pins cmac_usplus_0/gtwiz_reset_tx_datapath] [get_bd_pins tie_zero/dout]
+
+ # Create address segments
+ create_bd_addr_seg -range 0x00002000 -offset 0x00000000 [get_bd_addr_spaces s_axi] [get_bd_addr_segs cmac_usplus_0/s_axi/Reg] SEG_cmac_usplus_0_Reg
+
+
+ # Restore current instance
+ current_bd_instance $oldCurInst
+
+ validate_bd_design
+ save_bd_design
+}
+# End of create_root_design()
+
+
+##################################################################
+# MAIN FLOW
+##################################################################
+
+create_root_design ""
+
+
diff --git a/fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g_lbus2axis.sv b/fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g_lbus2axis.sv
new file mode 100644
index 000000000..0fd1ab42f
--- /dev/null
+++ b/fpga/usrp3/top/x400/ip/eth_100g_bd/eth_100g_lbus2axis.sv
@@ -0,0 +1,555 @@
+//
+// Copyright 2021 Ettus Research, A National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: eth_100g_lbus2axi
+//
+// Description:
+// Translate from lbus (xilinx segmented ifc) to
+// AXI4S.
+//
+// Built using example provided from Xilinx
+//
+// Parameters:
+// - FIFO_DEPTH - FIFO will be 2** deep
+// - NUM_SEG - Number of lbus segments coming in
+//
+// Notes on timing difficulty
+// The path back to pop is challenged
+// -LBUS is popped out of the FIFO (SRL read can be slow)
+// -LBUS is rotated N to 1 Mux (N= number of segments) For 100g N=4
+// -Find where EOP is (search for the first 1)
+// -Unrotate the number of words and use that to calculate pop
+//
+// Fifo Output
+// Data starts from the SRL and is indexed by the read pointer
+// Data_Valid comes from a comparison on fullness
+// Invalid control is forced to zero (necessary for algorithm)
+// It's not necessary to force all the data to zero just the control plane.
+//
+// Fifo output data is rotated (4 to 1) mux then reinterpreted as lbus data
+//
+// The rotated control signals are analyzed to determine
+// no_eop, no_sop, some_empty, no_ena
+//
+// eop is specifically inspected in a 4in,4out function to find a pseudo
+// one hot. this is unrotated along with enable, and combined with
+// datavalid to determine the next pop, which controls incrementing of the
+// rd_pointer.
+//
+
+import PkgEth100gLbus::*;
+
+module eth_100g_lbus2axi #(
+ parameter FIFO_DEPTH = 5,
+ parameter NUM_SEG = 4
+)
+(
+
+ // AXIS IF
+ AxiStreamIf.master axis,
+
+ // Lbus Segments
+ input lbus_t lbus_in [NUM_SEG-1:0]
+
+);
+
+ localparam SEG_BYTES = SEG_DATA_WIDTH/8;
+ localparam SEG_MTY_WIDTH = $clog2(SEG_BYTES);
+ localparam SEG_SHMEAR_WIDTH = SEG_DATA_WIDTH + SEG_MTY_WIDTH + 4;
+
+ //////////////////////////////////////////////////////////////////////////////////
+ ////////////////// Data Input to FIFO ///////////
+ //////////////////////////////////////////////////////////////////////////////////
+
+ lbus_t lbus_fout_p[NUM_SEG-1:0]; //{ena,err,eop,sop,mty,data}
+ lbus_t lbus_fout[NUM_SEG-1:0]; //{ena,err,eop,sop,mty,data}
+ //FIFO Logic
+ logic push;
+ logic [NUM_SEG-1:0] pop;
+
+ logic [NUM_SEG-1:0] full;
+ logic [NUM_SEG-1:0] empty;
+
+ // always push the fifo on all lanes
+ assign push = lbus_in[0].ena;
+
+ // For each lane of incoming data place it into a separate FIFO
+ generate
+ genvar b1,gseg1;
+ begin : gen_seg_fifo
+ for(gseg1 = 0; gseg1 < NUM_SEG; gseg1=gseg1+1) begin
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // INLINE FIFO
+ //////////////////////////////////////////////////////////////////////////////////
+
+ // simulation error if we push a full fifo
+ always_comb begin
+ if (push) begin
+ assert (!full[gseg1]) else $error("Pushing full fifo!");
+ end
+ end
+
+ // limit fanout to improve timing
+ (* max_fanout = 75 *) logic [4:0] a;
+
+ for (b1=0;b1<SEG_DATA_WIDTH;b1=b1+1) begin : gen_srl_data
+ SRLC32E srl_data(
+ .Q(lbus_fout_p[gseg1].data[b1]), .Q31(),
+ .A(a),
+ .CE(push),.CLK(axis.clk),.D(lbus_in[gseg1].data[b1])
+ );
+ end
+ for (b1=0;b1<SEG_MTY_WIDTH;b1=b1+1) begin : gen_srl_mty
+ SRLC32E srl_mty(
+ .Q(lbus_fout_p[gseg1].mty[b1]), .Q31(),
+ .A(a),
+ .CE(push),.CLK(axis.clk),.D(lbus_in[gseg1].mty[b1])
+ );
+ end
+ SRLC32E srl_err(
+ .Q(lbus_fout_p[gseg1].err), .Q31(),
+ .A(a),
+ .CE(push),.CLK(axis.clk),.D(lbus_in[gseg1].err)
+ );
+
+ // empty on prebuffer and SRL
+ logic my_empty;
+ always @(posedge axis.clk)
+ begin
+ if(axis.rst) begin
+ a <= 0;
+ my_empty <= 1;
+ full[gseg1] <= 0;
+ end else if(pop[gseg1] & ~push) begin
+ full[gseg1] <= 0;
+ if(a==0) begin
+ my_empty <= 1;
+ end else begin
+ a <= a - 1;
+ end
+ end else if(push & ~pop[gseg1]) begin
+ my_empty <= 0;
+ if(~my_empty) begin
+ a <= a + 1;
+ end
+ if(a == 30) begin
+ full[gseg1] <= 1;
+ end
+ end
+ end
+
+ // FIFO for time sensitive control signals. This creates a separate 31 deep fifo from
+ // DFF's on just 3 signals. The data signals continue to use an SRL to save space.
+ // The design bellow is a FIFO followed by a single DFF regsiter that is automatically
+ // prefilled when the FIFO has data.
+ logic [4:0] w_ptr,r_ptr,r_ptr_d,fullness;
+ logic [31:0] ena_mem, sop_mem, eop_mem;
+
+ // Final fifo stage after memory to remove address muxing from timing path
+ // this adds one clock of latency to empty flag as it will take 2 clocks to propagate
+ // into fifo.
+
+ //push critical timing signals to final flop. This adds 1 clock of latency on
+ // the final empty flag, but removes muxing of the memory elements
+ logic push_dff;
+
+ //using r_ptr_d to avoid extra latency in fullness change
+ always_comb begin
+ if (pop[gseg1]) begin
+ r_ptr_d = r_ptr+1;
+ end else begin
+ r_ptr_d = r_ptr;
+ end
+ fullness = w_ptr-r_ptr_d;
+ push_dff = (fullness != 0) & (pop[gseg1] | empty[gseg1]);
+ end
+
+ // speedier fifo implementation on these three control signals
+ // THE goal of this complexity is to have the outputs be a direct FF output
+ // instead of a muxed memory output.
+ always @(posedge axis.clk)
+ begin
+ if(axis.rst) begin
+ ena_mem <= '0;
+ sop_mem <= '0;
+ eop_mem <= '0;
+ lbus_fout_p[gseg1].ena <= 1'b0;
+ lbus_fout_p[gseg1].sop <= 1'b0;
+ lbus_fout_p[gseg1].eop <= 1'b0;
+ w_ptr <= 0;
+ r_ptr <= 0;
+ empty[gseg1] <= 1'b1;
+ end else begin
+ if(push) begin
+ ena_mem[w_ptr] <= lbus_in[gseg1].ena;
+ sop_mem[w_ptr] <= lbus_in[gseg1].sop;
+ eop_mem[w_ptr] <= lbus_in[gseg1].eop;
+ w_ptr <= w_ptr+1;
+ end
+
+ r_ptr <= r_ptr_d;
+
+ if (push_dff) begin
+ empty[gseg1] <= 1'b0;
+ lbus_fout_p[gseg1].ena <= ena_mem[r_ptr_d];
+ lbus_fout_p[gseg1].sop <= sop_mem[r_ptr_d];
+ lbus_fout_p[gseg1].eop <= eop_mem[r_ptr_d];
+ end else if (pop[gseg1]) begin
+ empty[gseg1] <= 1'b1;
+ end
+ end
+ end
+
+ // clear the enables if this fifo segment is not valid
+ always_comb begin
+ //default assignment
+ lbus_fout[gseg1] = lbus_fout_p[gseg1];
+ if (empty[gseg1]) begin
+ // clear ena,err,eop,sop,mty (But not data - saves fanout!)
+ lbus_fout[gseg1].ena = 0;
+ lbus_fout[gseg1].err = 0;
+ lbus_fout[gseg1].eop = 0;
+ lbus_fout[gseg1].sop = 0;
+ lbus_fout[gseg1].mty = '0;
+ end else begin
+ // clear bits if the segment isn't enabled
+ lbus_fout[gseg1].eop = lbus_fout_p[gseg1].eop && lbus_fout_p[gseg1].ena;
+ lbus_fout[gseg1].sop = lbus_fout_p[gseg1].sop && lbus_fout_p[gseg1].ena;
+ lbus_fout[gseg1].err = lbus_fout_p[gseg1].err && lbus_fout_p[gseg1].ena;
+ end
+ end
+
+ end
+ end : gen_seg_fifo
+ endgenerate
+
+ // post rotation lbus signals
+ lbus_t lbus_rot [NUM_SEG-1:0];
+
+ // rotated signals as vectors for decision making
+ logic [NUM_SEG-1:0] ena;
+ logic [NUM_SEG-1:0] sop;
+ logic [NUM_SEG-1:0] eop;
+ logic [NUM_SEG-1:0] rot_ena;
+ logic [NUM_SEG-1:0] rot_sop;
+ logic [NUM_SEG-1:0] rot_eop;
+ logic [NUM_SEG-1:0] rot_empty;
+
+ always_comb begin
+ foreach (rot_ena[s]) begin
+ ena[s] = lbus_fout[s].ena;
+ sop[s] = lbus_fout[s].sop;
+ eop[s] = lbus_fout[s].eop;
+ rot_ena[s] = lbus_rot[s].ena;
+ rot_sop[s] = lbus_rot[s].sop;
+ rot_eop[s] = lbus_rot[s].eop;
+ end
+ end
+
+
+ logic [$clog2(NUM_SEG)-1:0] rot;
+
+ //////////////////////////////////////////////////////////////////////////////////
+ ////////////////// Generate Decision Information ///////////
+ //////////////////////////////////////////////////////////////////////////////////
+ logic no_sop;
+ logic no_eop;
+ logic no_ena;
+ logic some_empty;
+ logic send_idle;
+
+ always_comb begin
+ no_sop = sop == 0;
+ no_eop = eop == 0;
+ no_ena = ena == 0;
+ // check for an empy byte
+ some_empty = 1'b0;
+ foreach (ena[seg]) begin : segment_loop
+ if (ena[seg] == 0) begin
+ some_empty = 1'b1;
+ end
+ end : segment_loop;
+ end
+
+ always_comb begin
+ if (no_ena) begin
+ send_idle = 1'b0;
+ end else begin
+ // generally either there is an EOP with some empty segments
+ // or all empty segments on an unrotated bus. I'm not sure
+ // what this implies on an rotated bus
+ send_idle = no_eop & some_empty;
+ end
+ end
+
+ //////////////////////////////////////////////////////////////////////////////////
+ ////////////////// Calculate Pop ///////////
+ //////////////////////////////////////////////////////////////////////////////////
+ // After rotation figure out how far till eop
+
+ //==========================================================================
+ // one-hot to thermometer code
+ // The goal is to find how far down till we reach the first eop
+ // This represents the bytes we will trasnfer this clock
+ //==========================================================================
+ // Xilinx example
+ // case (in_reqs)
+ // 4'b1000: onehot2thermo = 4'b1111;
+ //
+ // 4'b1100: onehot2thermo = 4'b0111;
+ // 4'b0100: onehot2thermo = 4'b0111;
+ //
+ // 4'b1110: onehot2thermo = 4'b0011;
+ // 4'b0110: onehot2thermo = 4'b0011;
+ // 4'b0010: onehot2thermo = 4'b0011;
+ //
+ // 4'b1111: onehot2thermo = 4'b0001;
+ // 4'b0111: onehot2thermo = 4'b0001;
+ // 4'b0011: onehot2thermo = 4'b0001;
+ // 4'b0001: onehot2thermo = 4'b0001;
+ //
+ // default: onehot2thermo = 4'b0000;
+ // endcase
+ logic [NUM_SEG-1:0] rot_xfer_now;
+ logic [NUM_SEG-1:0] mask [NUM_SEG-1:0];
+ logic [NUM_SEG-1:0] m1hot [NUM_SEG-1:0];
+ logic [NUM_SEG-1:0] meop [NUM_SEG-1:0];
+ logic [NUM_SEG-1:0] match;
+
+ always_comb begin
+ rot_xfer_now = '0;
+ foreach (rot_eop[s]) begin
+ // The function
+ // XXX1=>0001
+ // XX10=>0011
+ // X100=>0111
+ // 1000=>1111
+ // MASK
+ // 2**(0+1)-1 = 0001
+ // 2**(1+1)-1 = 0011
+ // 2**(2+1)-1 = 0111
+ // 2**(3+1)-1 = 1111
+ mask[s] = 2**(s+1)-1; // Constant
+ // MASK
+ // 2**0 = 0001
+ // 2**1 = 0010
+ // 2**2 = 0100
+ // 2**3 = 1000
+ m1hot[s] = 2**s; // Constant
+ // Mask valid_eop
+ meop[s] = rot_eop & mask[s];
+ // compare against 1hot
+ match[s] = meop[s] == m1hot[s];
+ if (match[s]) begin
+ rot_xfer_now = mask[s];
+ end
+ end
+ end
+
+ // unrotate the values and calculate pop
+ logic [NUM_SEG-1:0] xfer_now;
+ logic [NUM_SEG-1:0] filler_seg;
+
+ always_comb begin
+ if (send_idle)
+ xfer_now = '0;
+ else if (no_eop | no_sop)
+ xfer_now = '1;
+ else
+ // rotate left
+ xfer_now = {rot_xfer_now,rot_xfer_now} >> (NUM_SEG - rot);
+ end
+
+ // Flush out valid segments with no enable
+ assign filler_seg = ~ena & ~empty;
+
+ assign pop = (xfer_now | filler_seg) & ~empty;
+
+ //////////////////////////////////////////////////////////////////////////////////
+ ////////////////// Calculate Rotate for the next clock ///////////
+ //////////////////////////////////////////////////////////////////////////////////
+ logic [$clog2(NUM_SEG)-1:0] next_rot;
+ always_comb begin
+ next_rot = 0;
+ foreach (rot_empty[s]) begin
+ if (~rot_empty[s] & lbus_rot[s].sop) begin
+ next_rot = s;
+ end
+ end
+ end
+
+ always @(posedge axis.clk)
+ begin
+ if(axis.rst) begin
+ rot <= '0;
+ //no valid data on any segment
+ end else if( no_ena ) begin
+ rot <= '0;
+ // If EOP, but no SoP
+ end else if( no_sop & ~no_eop & some_empty) begin
+ rot <= '0;
+ // If SOP, accumulate rotation to push to seg 0
+ end else if( ~no_sop ) begin
+ rot <= rot+next_rot;
+ end
+ end
+
+ //////////////////////////////////////////////////////////////////////////////////
+ ////////////////// Rotation of segments from fifo output ///////////
+ //////////////////////////////////////////////////////////////////////////////////
+ generate
+ genvar b2,gseg2;
+ begin : rotate_lbus
+ //perform a bitwise rotation.
+ for(b2 = 0; b2 < SEG_SHMEAR_WIDTH; b2=b2+1) begin
+ logic [NUM_SEG-1:0] slice, slice_rotated;
+
+ //copy a horizontal slice across the segments
+ for(gseg2 = 0; gseg2 < NUM_SEG; gseg2=gseg2+1) begin
+ assign slice[gseg2] = lbus_fout[gseg2][b2];
+ end
+
+ // rotate the slice (should make SEG_SHMEAR_WIDTH copies of NUM_SEG to 1 mux)
+ assign slice_rotated = {slice,slice} >> rot; //rotate_right
+
+ // Copy slice back to the struct
+ for(gseg2 = 0; gseg2 < NUM_SEG; gseg2=gseg2+1) begin
+ assign lbus_rot[gseg2][b2] = slice_rotated[gseg2];
+ end
+
+ end
+ end : rotate_lbus
+ endgenerate
+
+ always_comb begin : rotate_data_valid
+ rot_empty = {empty,empty} >> rot; //rotate_right
+ end : rotate_data_valid
+
+ //////////////////////////////////////////////////////////////////////////////////
+ ////////////////// LBUS out DFF /////////////////////
+ //////////////////////////////////////////////////////////////////////////////////
+ // This pipe stage is mainly to allow suming MTY bits and to add space for
+ // Vivado to try to pipeline the output
+ // post rotation lbus signals
+ lbus_t lbus_out [NUM_SEG-1:0];
+ logic [NUM_SEG-1:0] axi_seg_valid;
+
+ always_ff @(posedge axis.clk)
+ begin
+ if (axis.rst) begin
+ foreach (lbus_out[seg]) begin : segment_loop
+ lbus_out[seg] <= '0;
+ end
+ axi_seg_valid <= '0;
+ end else begin
+ lbus_out <= lbus_rot;
+ if (send_idle)
+ axi_seg_valid <= '0;
+ else if (no_eop)
+ axi_seg_valid <= '1;
+ else
+ axi_seg_valid <= rot_xfer_now;
+ end
+ end
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Generate AXI
+ ////////////////////////////////////////////////////////////////////////////
+ logic [axis.DATA_WIDTH - 1:0] axis_tdata_w;
+ logic [$clog2(axis.DATA_WIDTH/8) - 1:0] axis_tuser_bytes_w;
+ logic [axis.DATA_WIDTH/8 - 1:0] axis_tkeep_w;
+ logic [NUM_SEG-1:0] axis_tlast_w;
+ logic [NUM_SEG-1:0] axis_tvalid_w;
+ logic [NUM_SEG-1:0] axis_tuser_err_w;
+
+ always_comb begin : axis_translate
+ axis_tuser_bytes_w = 'd0; // init to zero before summing
+ foreach (axis_tvalid_w[seg]) begin : segment_loop
+ axis_tvalid_w[seg] = lbus_out[seg].ena & axi_seg_valid[seg];
+ axis_tlast_w[seg] = lbus_out[seg].eop;
+ axis_tuser_err_w[seg] = lbus_out[seg].err;
+
+ // sum all the segment mty vectors
+ if (lbus_out[seg].ena && axi_seg_valid[seg]) begin
+ axis_tuser_bytes_w += SEG_DATA_WIDTH/8 - lbus_out[seg].mty;
+ end
+
+ // 512 bit word = 64 bytes = 4 X 128 bit(16 byte) segments
+ // assign bytes : LbusOrder
+ // S0 : S0B0..S0B15
+ // S1 : S1B0..S1B15
+ // S2 : S2B0..S2B15
+ // S3 : S3B0..S3B15
+ // AXI (swap Endianess on each segment)
+ // AXI = S3B15..S3B0, S2B15..S2B0, S1B15..S1B0, S0B15..S0B0
+ for(int b = 0; b < SEG_BYTES; b=b+1) begin : tdata_loop
+ // ( 1 * 128 )-8- 0*8) 120+:8 = S0B0
+ // ( 1 * 128 )-8- 1*8) 112+:8 = S0B1
+ // ...
+ // ( 1 * 128 )-8-14*8) 8+:8 = S0B14
+ // ( 1 * 128 )-8-15*8) 0+:8 = S0B15
+ ////////////////////////////////////
+ // ( 2 * 128 )-8- 0*8) 248+:8 = S1B0
+ // ( 2 * 128 )-8- 1*8) 240+:8 = S1B1
+ // ...
+ // ( 2 * 128 )-8-14*8) 136+:8 = S1B14
+ // ( 2 * 128 )-8-15*8) 128+:8 = S1B15
+ ////////////////////////////////////
+ // ...
+ ////////////////////////////////////
+ // ( 4 * 128 )-8- 0*8) 504+:8 = S3B0
+ // ( 4 * 128 )-8- 1*8) 496+:8 = S3B1
+ // ...
+ // ( 4 * 128 )-8-14*8) 136+:8 = S3B14
+ // ( 4 * 128 )-8-15*8) 384+:8 = S3B15
+ axis_tdata_w[((seg+1)*axis.DATA_WIDTH/NUM_SEG-8-b*8) +: 8] = lbus_out[seg].data[b*8 +: 8];
+ end : tdata_loop
+ end : segment_loop
+ end : axis_translate
+
+ // convert bytes to keep
+ always_comb begin
+ axis_tkeep_w = '1;
+ if (axis_tlast_w != 0 && axis_tuser_bytes_w != 0) begin
+ foreach(axis_tkeep_w[b]) begin
+ axis_tkeep_w[b] = axis_tuser_bytes_w > b;
+ end
+ end
+ end
+
+ //////////////////////////////////////////////////////////////////////////////////
+ ////////////////// AXIS output flop /////////////////////
+ //////////////////////////////////////////////////////////////////////////////////
+
+ localparam AXIS_MTY_WIDTH = $clog2(axis.BYTES_PER_WORD);
+
+ always_ff @(posedge axis.clk)
+ begin
+ if (axis.rst) begin
+ axis.tdata <= '0;
+ axis.tvalid <= 1'b0;
+ axis.tlast <= 1'b0;
+ axis.tuser <= '0;
+ axis.tkeep <= '0;
+ end else begin
+ axis.tdata <= axis_tdata_w;
+ axis.tvalid <= |axis_tvalid_w;
+ axis.tlast <= |axis_tlast_w;
+
+ if (axis.TKEEP == 1) begin
+ axis.tkeep <= axis_tkeep_w;
+ end else begin
+ axis.tkeep <= 'X;
+ end
+
+ // trailing bytes in last word
+ axis.tuser[AXIS_MTY_WIDTH-1:0] <= axis_tuser_bytes_w;
+ // MSB is error
+ axis.tuser[AXIS_MTY_WIDTH] <= |axis_tuser_err_w;
+ end
+ end
+
+endmodule
diff --git a/fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/Makefile b/fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/Makefile
new file mode 100644
index 000000000..5ec8b6868
--- /dev/null
+++ b/fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/Makefile
@@ -0,0 +1,62 @@
+#
+# 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)
+# Include viv_sim_preamble after defining BASE_DIR
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+#-------------------------------------------------
+# Design Specific
+#-------------------------------------------------
+# Include makefiles and sources for the DUT and its dependencies
+include $(BASE_DIR)/../lib/axi4s_sv/Makefile.srcs
+include $(BASE_DIR)/../lib/fifo/Makefile.srcs
+
+
+# If you generate the Xilinx CORE with an AXI interface you can find Xilinx's LBUS translators here
+#$(abspath ../../../build-ip/xczu28drffvg1517-1e/eth_100g_bd/eth_100g_bd/ip/eth_100g_bd_cmac_usplus_0_0/eth_100g_bd_cmac_usplus_0_0/example_design/eth_100g_bd_cmac_usplus_0_0_lbus2axis_segmented_top.v) \
+#$(abspath ../../../build-ip/xczu28drffvg1517-1e/eth_100g_bd/eth_100g_bd/ip/eth_100g_bd_cmac_usplus_0_0/eth_100g_bd_cmac_usplus_0_0/example_design/eth_100g_bd_cmac_usplus_0_0_axis2lbus_segmented_top.v) \
+
+DESIGN_SRCS = $(abspath \
+$(abspath ../eth_100g_axis2lbus.sv) \
+$(abspath ../eth_100g_lbus2axis.sv) \
+$(FIFO_SRCS) \
+$(AXI4S_SV_SRCS) \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+MODELSIM_LIBS += secureip unimacro_ver unisims_ver xilinx_vip xpm fifo_generator_v13_2_4
+MODELSIM_ARGS += glbl -t 1fs
+# Define toplevel module
+TB_TOP_MODULE ?= lbus_all_tb
+SIM_TOP = $(TB_TOP_MODULE)
+
+SIM_SRCS = \
+$(abspath ../PkgEth100gLbus.sv) \
+$(abspath axi_lbus_tb.sv) \
+$(abspath lbus_axi_tb.sv) \
+$(abspath $(TB_TOP_MODULE).sv) \
+$(VIVADO_PATH)/data/verilog/src/glbl.v \
+
+# 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 -keep_delta
+VLOG_ARGS = -keep_delta
+
+#-------------------------------------------------
+# 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/ip/eth_100g_bd/lbus_tb/axi_lbus_tb.sv b/fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/axi_lbus_tb.sv
new file mode 100644
index 000000000..cfde2d5aa
--- /dev/null
+++ b/fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/axi_lbus_tb.sv
@@ -0,0 +1,285 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axi_lbus_tb
+//
+// Description:
+//
+// Testbench for eth_interface
+//
+
+module axi_lbus_tb #(
+ parameter TEST_NAME = ""
+)(
+ /* no IO */
+);
+ // Include macros and time declarations for use with PkgTestExec
+ `define TEST_EXEC_OBJ test
+ `include "test_exec.svh"
+ import PkgAxiStreamBfm::*;
+ import PkgEthernet::*;
+ import PkgTestExec::*;
+ import PkgEth100gLbus::*;
+
+ localparam DATA_WIDTH = 512;
+ localparam USER_WIDTH = $clog2(DATA_WIDTH/8)+1;
+ localparam NUM_SEG = 4;
+ localparam SEG_DATA_WIDTH = DATA_WIDTH/NUM_SEG;
+ localparam SEG_BYTES = SEG_DATA_WIDTH/8;
+
+ //----------------------------------------------------
+ // clocks
+ //----------------------------------------------------
+ logic clk;
+ logic rst;
+
+ sim_clock_gen #(.PERIOD(5), .AUTOSTART(1))
+ clk_gen (.clk(clk), .rst(rst));
+
+ //----------------------------------------------------
+ //interfaces
+ //----------------------------------------------------
+ AxiStreamIf #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH))
+ axis (clk, rst);
+
+ //----------------------------------------------------
+ // DUT
+ //----------------------------------------------------
+ lbus_t lbus_out [NUM_SEG-1:0];
+ lbus_t lbus_g [NUM_SEG-1:0];
+ logic lbus_rdy = 1;
+
+ eth_100g_axi2lbus #(.FIFO_DEPTH(5),.NUM_SEG(NUM_SEG)) DUT (
+ .axis(axis),
+ .lbus_rdy(lbus_rdy),
+ .lbus_out(lbus_out)
+ );
+
+ //----------------------------------------------------
+ // Xilinx golden model
+ // When Xilinx is generated with an AXI interface xilinx prints
+ // an axi2lbus converter that has trouble meeting timing
+ // this is preserved to allow comparison to that
+ // TODO: remove when timing is passing and refactor is done
+ //----------------------------------------------------
+/*
+ eth_100g_bd_cmac_usplus_0_0_axis2lbus_segmented_top GOLD (
+ .core_clk(clk),
+ .core_rst(rst),
+
+ // AXIS IF
+ .axis_tvalid(axis.tvalid),
+ .axis_tready(),
+ .axis_tdata(axis.tdata),
+ .axis_tlast(axis.tlast),
+ .axis_tkeep(axis.tkeep),
+ .axis_tuser(1'b0),
+ // LBUS IF
+ .lbus_rdyout(lbus_rdy),
+ .lbus_ovfout(1'b0),
+ .lbus_unfout(1'b0),
+ // Segment 0
+ .lbus_ena0(lbus_g[0].ena),
+ .lbus_data0(lbus_g[0].data),
+ .lbus_sop0(lbus_g[0].sop),
+ .lbus_eop0(lbus_g[0].eop),
+ .lbus_mty0(lbus_g[0].mty),
+ .lbus_err0(lbus_g[0].err),
+ // Segment 1
+ .lbus_ena1(lbus_g[1].ena),
+ .lbus_data1(lbus_g[1].data),
+ .lbus_sop1(lbus_g[1].sop),
+ .lbus_eop1(lbus_g[1].eop),
+ .lbus_mty1(lbus_g[1].mty),
+ .lbus_err1(lbus_g[1].err),
+ // Segment 2
+ .lbus_ena2(lbus_g[2].ena),
+ .lbus_data2(lbus_g[2].data),
+ .lbus_sop2(lbus_g[2].sop),
+ .lbus_eop2(lbus_g[2].eop),
+ .lbus_mty2(lbus_g[2].mty),
+ .lbus_err2(lbus_g[2].err),
+ // Segment 3
+ .lbus_ena3(lbus_g[3].ena),
+ .lbus_data3(lbus_g[3].data),
+ .lbus_sop3(lbus_g[3].sop),
+ .lbus_eop3(lbus_g[3].eop),
+ .lbus_mty3(lbus_g[3].mty),
+ .lbus_err3(lbus_g[3].err)
+ );
+*/
+ //----------------------------------------------------
+ //BFMS
+ //----------------------------------------------------
+
+ TestExec test = new();
+ AxiStreamBfm #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH)) axi =
+ new(.slave(null),.master(axis));
+
+ //---------------------------------------------------------------------------
+ // Tests
+ //---------------------------------------------------------------------------
+
+ // use Test timeout to check reset goes away
+ task test_reset();
+ test.start_test({TEST_NAME,"Wait for Reset"}, 10us);
+ wait(!rst);
+ repeat (10) @(posedge clk);
+ test.end_test();
+ endtask : test_reset
+
+ typedef AxiStreamPacket #(DATA_WIDTH,USER_WIDTH) AxisPacket_t;
+ typedef XportStreamPacket #(DATA_WIDTH) XportPacket_t;
+
+
+ task automatic check_lbus(AxisPacket_t packets[$]);
+
+ int idle_insert = 0;
+ int byte_count = 0;
+ int first_clk = 1;
+
+ // loop over packets
+ foreach(packets[i]) begin
+ automatic raw_pkt_t pay;
+ pay = packets[i].dump_bytes;
+
+ first_clk=1;
+ while (pay.size() > 0) begin
+ @(posedge clk);
+ if (lbus_rdy) begin
+ if (lbus_out[0].ena) begin
+ for (int s=0; s < NUM_SEG; s++) begin
+ byte_count = 0;
+ for (int b = SEG_DATA_WIDTH/8-1; b >= 0; b--) begin
+ if (pay.size() > 0) begin
+ assert (lbus_out[s].data[b*8 +: 8] == pay.pop_front()) else $error("Data Mismatch");
+ byte_count++;
+ end
+ end
+ assert (lbus_out[s].ena == (byte_count > 0) ) else $error("Ena Mismatch");
+ if (lbus_out[s].ena) begin
+ if (first_clk && s==0) begin
+ assert (lbus_out[s].sop == 1) else $error("Sop not set");
+ end else begin
+ assert (lbus_out[s].sop == 0) else $error("Sop Set");
+ end
+ assert (lbus_out[s].mty == (SEG_BYTES-byte_count) % SEG_BYTES) else $error("Mty Mismatch");
+ assert (lbus_out[s].eop != (pay.size() > 0) ) else $error("Eop Mismatch");
+ assert (lbus_out[s].err == 0) else $error("Err Mismatch"); // not checking error yet
+ end else begin
+ assert (lbus_out[s].sop == 0) else $error("Sop Set while disabled");
+ assert (lbus_out[s].mty == 0) else $error("Mty set while disabled");
+ assert (lbus_out[s].eop == 0) else $error("Eop set while disabled");
+ assert (lbus_out[s].err == 0) else $error("Err set while disabled");
+ end
+ end // segment loop
+ first_clk=0;
+ end // first segment is enabled
+
+ end else begin //not lbus_rdy
+ for (int s=0; s < NUM_SEG; s++) begin
+ assert (lbus_out[s].ena == 0) else $error("Idle Ena set");
+ assert (lbus_out[s].err == 0) else $error("Idle Err set");
+ assert (lbus_out[s].sop == 0) else $error("Idle Sop set");
+ assert (lbus_out[s].eop == 0) else $error("Idle Eop set");
+ assert (lbus_out[s].mty == 0) else $error("Idle Mty non zero");
+ end
+ end
+ end // while pay.size > 0
+ end // foreach packet
+
+ endtask : check_lbus;
+
+ task automatic test_transfers(int num_samples[$]);
+ automatic AxisPacket_t send[$];
+ automatic AxisPacket_t expected[$];
+ automatic int sample_sum = 0;
+
+ test.start_test({TEST_NAME,"Test Transfer"}, 200us);
+
+ foreach (num_samples[i]) begin
+ automatic raw_pkt_t pay;
+
+ 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];
+ send[i].push_bytes(pay);
+
+ // rebuild the expected packet for comparison without the preamble
+ expected[i].push_bytes(pay);
+ end
+
+ fork
+ begin // tx_thread
+ foreach (send[i]) begin
+ axi.put(send[i]);
+ end
+ end
+ begin //rx_thread
+ check_lbus(expected);
+ end
+ join
+
+ test.end_test();
+ endtask : test_transfers
+
+ //----------------------------------------------------
+ // Main test loop
+ //----------------------------------------------------
+ initial begin : tb_main
+ automatic int num_samples[$];
+ automatic int random_value;
+ clk_gen.reset();
+
+ // stalling is not allowed by whoever is reading the MAC
+ // i.e. ready is ignored
+ axi.set_master_stall_prob(0);
+ axi.run();
+ test_reset();
+
+ $display("Fixed Sequences");
+ num_samples = {64,65,66,67,68,69,70,71};
+ test_transfers(num_samples);
+ num_samples = {64,128,256,512,256,64,64,64};
+ test_transfers(num_samples);
+ num_samples = {65,64,64,64,64,64,64,64};
+ test_transfers(num_samples);
+ num_samples = {65,64,80,80,80,80,80,80};
+ test_transfers(num_samples);
+ num_samples = {64,64,96,80,96,80,96,80};
+ test_transfers(num_samples);
+ $display("Tons of 64");
+ num_samples.delete();
+ repeat(1000) begin
+ num_samples.push_back(64);
+ end
+ test_transfers(num_samples);
+ $display("Tons of 65");
+ num_samples.delete();
+ repeat(1000) begin
+ num_samples.push_back(65);
+ end
+ test_transfers(num_samples);
+ $display("Tons of Random");
+ num_samples.delete();
+ repeat(1000) begin
+ random_value = $urandom_range(64,512);
+ num_samples.push_back(random_value);
+ end
+ test_transfers(num_samples);
+
+
+ // 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
+ clk_gen.kill();
+ end // initial begin
+
+endmodule
diff --git a/fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/lbus_all_tb.sv b/fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/lbus_all_tb.sv
new file mode 100644
index 000000000..98caa0a86
--- /dev/null
+++ b/fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/lbus_all_tb.sv
@@ -0,0 +1,22 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: lbus_all_tb
+//
+// Description:
+//
+// Testbench for LBU<->AXI
+//
+
+module lbus_all_tb #(
+ /* no PARAM */
+)(
+ /* no IO */
+);
+
+ lbus_axi_tb #(.TEST_NAME("L2A")) L2A ();
+ axi_lbus_tb #(.TEST_NAME("A2L")) A2L ();
+
+endmodule
diff --git a/fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/lbus_axi_tb.sv b/fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/lbus_axi_tb.sv
new file mode 100644
index 000000000..fa5812abe
--- /dev/null
+++ b/fpga/usrp3/top/x400/ip/eth_100g_bd/lbus_tb/lbus_axi_tb.sv
@@ -0,0 +1,343 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: lbus_axi_tb
+//
+// Description:
+//
+// Testbench for eth_interface
+//
+
+module lbus_axi_tb #(
+ parameter TEST_NAME = ""
+)(
+ /* no IO */
+);
+ // Include macros and time declarations for use with PkgTestExec
+ `define TEST_EXEC_OBJ test
+ `include "test_exec.svh"
+ import PkgAxiStreamBfm::*;
+ import PkgEthernet::*;
+ import PkgTestExec::*;
+ import PkgEth100gLbus::*;
+
+ localparam DATA_WIDTH = 512;
+ localparam USER_WIDTH = $clog2(DATA_WIDTH/8)+1;
+ localparam NUM_SEG = 4;
+ localparam SEG_DATA_WIDTH = DATA_WIDTH/NUM_SEG;
+ localparam SEG_BYTES = SEG_DATA_WIDTH/8;
+
+ //----------------------------------------------------
+ // clocks
+ //----------------------------------------------------
+ logic clk;
+ logic rst;
+
+ sim_clock_gen #(.PERIOD(5), .AUTOSTART(1))
+ clk_gen (.clk(clk), .rst(rst));
+
+ //----------------------------------------------------
+ //interfaces
+ //----------------------------------------------------
+ AxiStreamIf #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH))
+ axis (clk, rst);
+
+ AxiStreamIf #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH))
+ axig (clk, rst);
+
+ //----------------------------------------------------
+ // DUT
+ //----------------------------------------------------
+ lbus_t lbus_in [NUM_SEG-1:0];
+
+ eth_100g_lbus2axi #(.FIFO_DEPTH(5),.NUM_SEG(NUM_SEG)) DUT (
+ .axis(axis),
+ .lbus_in(lbus_in)
+ );
+
+ //----------------------------------------------------
+ // Xilinx golden model
+ // When Xilinx is generated with an AXI interface xilinx prints
+ // a lbus2axis converter that has trouble meeting timing
+ // this is preserved to allow comparison to that
+ // TODO: remove when timing is passing and refactor is done
+ //----------------------------------------------------
+/*
+ eth_100g_bd_cmac_usplus_0_0_lbus2axis_segmented_top GOLD (
+ .core_clk(clk),
+ .core_rst(rst),
+
+ // AXIS IF
+ .axis_tvalid(axig.tvalid),
+ .axis_tdata(axig.tdata),
+ .axis_tlast(axig.tlast),
+ .axis_tkeep(axig.tkeep),
+ .axis_tuser(),
+ // Segment 0
+ .lbus_ena0(lbus_in[0].ena),
+ .lbus_data0(lbus_in[0].data),
+ .lbus_sop0(lbus_in[0].sop),
+ .lbus_eop0(lbus_in[0].eop),
+ .lbus_mty0(lbus_in[0].mty),
+ .lbus_err0(lbus_in[0].err),
+ // Segment 1
+ .lbus_ena1(lbus_in[1].ena),
+ .lbus_data1(lbus_in[1].data),
+ .lbus_sop1(lbus_in[1].sop),
+ .lbus_eop1(lbus_in[1].eop),
+ .lbus_mty1(lbus_in[1].mty),
+ .lbus_err1(lbus_in[1].err),
+ // Segment 2
+ .lbus_ena2(lbus_in[2].ena),
+ .lbus_data2(lbus_in[2].data),
+ .lbus_sop2(lbus_in[2].sop),
+ .lbus_eop2(lbus_in[2].eop),
+ .lbus_mty2(lbus_in[2].mty),
+ .lbus_err2(lbus_in[2].err),
+ // Segment 3
+ .lbus_ena3(lbus_in[3].ena),
+ .lbus_data3(lbus_in[3].data),
+ .lbus_sop3(lbus_in[3].sop),
+ .lbus_eop3(lbus_in[3].eop),
+ .lbus_mty3(lbus_in[3].mty),
+ .lbus_err3(lbus_in[3].err)
+ );
+*/
+ //----------------------------------------------------
+ //BFMS
+ //----------------------------------------------------
+
+ TestExec test = new();
+ AxiStreamBfm #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH)) axi =
+ new(.slave(axis),.master(null));
+
+ //---------------------------------------------------------------------------
+ // Tests
+ //---------------------------------------------------------------------------
+
+ // use Test timeout to check reset goes away
+ task test_reset();
+ test.start_test({TEST_NAME,"Wait for Reset"}, 10us);
+ wait(!rst);
+ repeat (10) @(posedge clk);
+ test.end_test();
+ endtask : test_reset
+
+ typedef AxiStreamPacket #(DATA_WIDTH,USER_WIDTH) AxisPacket_t;
+ typedef XportStreamPacket #(DATA_WIDTH) XportPacket_t;
+
+ task automatic clear_lbus_in();
+ for (int i=0 ; i < NUM_SEG ; ++i) begin
+ lbus_in[i].data = 'b0;
+ lbus_in[i].mty = 'b0;
+ lbus_in[i].sop = 1'b0;
+ lbus_in[i].eop = 1'b0;
+ lbus_in[i].err = 1'b0;
+ lbus_in[i].ena = 1'b0;
+ end
+ endtask : clear_lbus_in;
+
+ task automatic send_lbus(AxisPacket_t packets[$]);
+
+ int seg = 0;
+ int b = 0;
+ int idle_insert = 0;
+
+ // loop over packets
+ foreach(packets[i]) begin
+ automatic raw_pkt_t pay;
+ pay = packets[i].dump_bytes;
+ // set for the first segment
+ lbus_in[seg].sop = 1;
+ // empty this packets payload
+ while (pay.size() > 0) begin
+ lbus_in[seg].ena = 1;
+ lbus_in[seg].data |= pay.pop_front()<<(SEG_DATA_WIDTH-b*8-8);
+ if (pay.size() == 0) begin
+ lbus_in[seg].eop = 1;
+ lbus_in[seg].mty = SEG_DATA_WIDTH-1-b;
+ end
+ if (seg == NUM_SEG-1 && b == SEG_DATA_WIDTH/8-1) begin
+ @(posedge clk);
+ clear_lbus_in();
+ idle_insert++;
+ // insert period idle period
+ if (idle_insert > 2) begin
+ @(posedge clk);
+ idle_insert =0;
+ end
+ end
+ b = (b+1) % (SEG_DATA_WIDTH/8);
+ if (b==0) begin
+ seg = (seg+1) % NUM_SEG;
+ end
+ end // pay.size > 0
+ if (b != 0) begin
+ seg = (seg+1) % NUM_SEG;
+ if (seg == 0) begin
+ @(posedge clk);
+ clear_lbus_in();
+ idle_insert++;
+ // insert period idle period
+ if (idle_insert > 2) begin
+ @(posedge clk);
+ idle_insert =0;
+ end
+ end
+ b = 0;
+ end
+
+ // 25% of the time we start a new packet
+ if ($urandom_range(99) < 25) begin
+ b = 0;
+ seg = 0;
+ // 25% of the time add an idle cycle
+ if ($urandom_range(99) < 25) begin
+ @(posedge clk);
+ clear_lbus_in();
+ idle_insert++;
+ // insert period idle period
+ if (idle_insert > 2) begin
+ idle_insert =0;
+ @(posedge clk);
+ end
+ @(posedge clk);
+ end else begin
+ @(posedge clk);
+ clear_lbus_in();
+ idle_insert++;
+ // insert period idle period
+ if (idle_insert > 2) begin
+ idle_insert =0;
+ @(posedge clk);
+ end
+ end
+ end
+
+ end // foreach packet
+
+ @(posedge clk);
+ clear_lbus_in();
+
+ endtask : send_lbus;
+
+ task automatic compare_packet(AxisPacket_t actual, expected);
+
+ automatic XportPacket_t actual_copy = new();
+ automatic XportPacket_t expected_copy = new();
+ actual_copy.import_axis(actual);
+ expected_copy.import_axis(expected);
+
+ expected_copy.tkeep_to_tuser();
+ actual_copy.clear_unused_bytes();
+
+ if (!expected_copy.equal(actual_copy)) begin
+ $display("Expected");
+ expected_copy.print();
+ $display("Actual");
+ actual_copy.print();
+ if (!expected_copy.equal(actual_copy))
+ $error("ERROR :: packet mismatch");
+
+ end
+
+ endtask : compare_packet;
+
+ task automatic test_transfers(int num_samples[$]);
+ automatic AxisPacket_t send[$];
+ automatic AxisPacket_t expected[$];
+ automatic int sample_sum = 0;
+
+ test.start_test({TEST_NAME,"Test Transfer"}, 200us);
+
+ foreach (num_samples[i]) begin
+ automatic raw_pkt_t pay;
+
+ 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];
+ send[i].push_bytes(pay);
+
+ // rebuild the expected packet for comparison without the preamble
+ expected[i].push_bytes(pay);
+ end
+
+ fork
+ begin // tx_thread
+ send_lbus(send);
+ end
+ begin //rx_thread
+ foreach(expected[i]) begin
+ automatic AxisPacket_t actual;
+ axi.get(actual);
+ compare_packet(actual,expected[i]);
+ end
+ end
+ join
+
+ test.end_test();
+ endtask : test_transfers
+
+ //----------------------------------------------------
+ // Main test loop
+ //----------------------------------------------------
+ initial begin : tb_main
+ automatic int num_samples[$];
+ automatic int random_value;
+ clk_gen.reset();
+
+ // stalling is not allowed by whoever is reading the MAC
+ // i.e. ready is ignored
+ axi.slave_tready_init = 1'b1;
+ axi.set_master_stall_prob(0);
+ axi.set_slave_stall_prob(0);
+ axi.run();
+ clear_lbus_in();
+ test_reset();
+
+ $display("Fixed Sequences");
+ num_samples = {64,65,66,67,68,69,70,71};
+ test_transfers(num_samples);
+ num_samples = {64,128,256,512,256,64,64,64};
+ test_transfers(num_samples);
+ num_samples = {65,64,64,64,64,64,64,64};
+ test_transfers(num_samples);
+ num_samples = {65,64,80,80,80,80,80,80};
+ test_transfers(num_samples);
+ num_samples = {64,64,96,80,96,80,96,80};
+ test_transfers(num_samples);
+ $display("Tons of 64");
+ num_samples.delete();
+ repeat(1000) begin
+ num_samples.push_back(64);
+ end
+ test_transfers(num_samples);
+ $display("Tons of 65");
+ num_samples.delete();
+ repeat(1000) begin
+ num_samples.push_back(65);
+ end
+ test_transfers(num_samples);
+ $display("Tons of Random");
+ num_samples.delete();
+ repeat(1000) begin
+ random_value = $urandom_range(64,512);
+ num_samples.push_back(random_value);
+ end
+ test_transfers(num_samples);
+
+
+ // 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
+ clk_gen.kill();
+ end // initial begin
+
+endmodule
diff --git a/fpga/usrp3/top/x400/ip/eth_100g_bd/model_100gbe.sv b/fpga/usrp3/top/x400/ip/eth_100g_bd/model_100gbe.sv
new file mode 100644
index 000000000..e7995dd44
--- /dev/null
+++ b/fpga/usrp3/top/x400/ip/eth_100g_bd/model_100gbe.sv
@@ -0,0 +1,234 @@
+//
+// Copyright 2021 Ettus Research, A National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: model_100gbe
+//
+// Description:
+//
+// A wrapper of the 100gbe core to axistream interface. this model can be
+// used drive packets into the X400 translated to serial Ethernet. This is
+// generally pretty slower than just driving things in at the output of the
+// mac.
+//
+
+package Pkg100gbMac;
+ import PkgAxiLite::*;
+ import PkgAxiLiteBfm::*;
+ import PkgTestExec::*;
+
+ localparam DATA_WIDTH =32;
+ localparam ADDR_WIDTH =15;
+
+ typedef AxiLiteBfm #(DATA_WIDTH, ADDR_WIDTH) MacAxiLiteBfm_t;
+
+ // defined in https://www.xilinx.com/support/documentation/ip_documentation/cmac_usplus/v2_4/pg203-cmac-usplus.pdf
+ // pg 187
+ localparam CONFIGURATION_TX_REG1 = 32'h000C;
+ localparam ctl_tx_ctl_enable = 0;
+ localparam ctl_tx_ctl_tx_send_lfi = 3;
+ localparam ctl_tx_ctl_tx_send_rfi = 4;
+ localparam ctl_tx_ctl_tx_send_idle = 5;
+ localparam ctl_tx_ctl_test_pattern = 16;
+
+ localparam CONFIGURATION_RX_REG1 = 32'h0014;
+ localparam ctl_rx_ctl_enable = 0;
+ localparam ctl_rx_ctl_rx_force_resync = 7;
+ localparam ctl_rx_ctl_test_pattern = 8;
+
+ localparam RSFEC_CONFIG_INDICATION_CORRECTION = 32'h1000;
+ localparam rs_fec_in_ctl_rx_rsfec_enable_correction = 0;
+ localparam rs_fec_in_ctl_rx_rsfec_enable_indication = 1;
+ localparam rs_fec_in_ctl_rsfec_ieee_error_indication_mode = 2;
+
+ localparam RSFEC_CONFIG_ENABLE = 32'h107C;
+ localparam rs_fec_in_ctl_rx_rsfec_enable = 0;
+ localparam rs_fec_in_ctl_tx_rsfec_enable = 1;
+
+ localparam STAT_RX_STATUS_REG = 32'h0204;
+ localparam stat_rx_status = 0;
+ localparam stat_rx_aligned = 1;
+ localparam stat_rx_misaligned = 2;
+ localparam stat_rx_aligned_err = 3;
+
+ task automatic init_mac (int offset, MacAxiLiteBfm_t axi);
+ automatic logic [31:0] data;
+ automatic resp_t resp;
+
+ // start transmitting alignment pattern
+ data = 0;
+ data[ctl_tx_ctl_enable] = 0;
+ data[ctl_tx_ctl_tx_send_idle] = 0;
+ data[ctl_tx_ctl_tx_send_lfi] = 0;
+ data[ctl_tx_ctl_tx_send_rfi] = 1;
+ data[ctl_tx_ctl_test_pattern] = 0;
+ axi.wr(CONFIGURATION_TX_REG1+offset,data);
+
+ // configure fec
+ data = 0;
+ data[rs_fec_in_ctl_rx_rsfec_enable_correction] = 1;
+ data[rs_fec_in_ctl_rx_rsfec_enable_indication] = 1;
+ data[rs_fec_in_ctl_rsfec_ieee_error_indication_mode] = 1;
+ axi.wr(RSFEC_CONFIG_INDICATION_CORRECTION+offset,data);
+
+ data = 0;
+ data[rs_fec_in_ctl_rx_rsfec_enable] = 1;
+ data[rs_fec_in_ctl_tx_rsfec_enable] = 1;
+ axi.wr(RSFEC_CONFIG_ENABLE+offset,data);
+
+ // turn on RX interface
+ data = 0;
+ data[ctl_rx_ctl_enable] = 1;
+ data[ctl_rx_ctl_rx_force_resync] = 0;
+ data[ctl_rx_ctl_test_pattern] = 0;
+ axi.wr(CONFIGURATION_RX_REG1+offset,data);
+
+ do begin
+ axi.rd_block(STAT_RX_STATUS_REG+offset,data,resp);
+ assert (resp==OKAY);
+ end while (data[stat_rx_aligned] !== 1);
+
+ // stop transmitting alignment pattern
+ // and start transmitting data
+ data = 0;
+ data[ctl_tx_ctl_enable] = 1;
+ data[ctl_tx_ctl_tx_send_idle] = 0;
+ data[ctl_tx_ctl_tx_send_lfi] = 0;
+ data[ctl_tx_ctl_tx_send_rfi] = 0;
+ data[ctl_tx_ctl_test_pattern] = 0;
+ axi.wr(CONFIGURATION_TX_REG1+offset,data);
+
+ endtask : init_mac
+
+endpackage : Pkg100gbMac
+
+module model_100gbe (
+ input logic areset,
+ // 156.25 Mhz refclk
+ input logic ref_clk,
+
+ // QSFP high-speed IO
+ output logic [3:0] tx_p,
+ output logic [3:0] tx_n,
+ input logic [3:0] rx_p,
+ input logic [3:0] rx_n,
+
+ // CLK and RESET out
+ output logic mgt_clk,
+ output logic mgt_rst,
+ output logic link_up,
+
+ // Data port
+ AxiStreamIf.slave mgt_tx,
+ AxiStreamIf.master mgt_rx
+
+);
+
+ // Include macros and time declarations for use with PkgTestExec
+ `define TEST_EXEC_OBJ test
+ `include "test_exec.svh"
+ import PkgAxiLiteBfm::*;
+ import PkgTestExec::*;
+
+ logic refclk_p;
+ logic refclk_n;
+
+ logic clk40,clk40_rst;
+ logic clk100,clk100_rst;
+ logic phy_reset;
+
+ assign refclk_p = ref_clk;
+ assign refclk_n = ~ref_clk;
+
+ //interface
+ AxiLiteIf #(Pkg100gbMac::DATA_WIDTH,Pkg100gbMac::ADDR_WIDTH)
+ mgt_axil (clk40, clk40_rst);
+ //bfm
+ Pkg100gbMac::MacAxiLiteBfm_t axi = new(.master(mgt_axil));
+ TestExec mac_test = new();
+
+ sim_clock_gen #(.PERIOD(25.0), .AUTOSTART(1))
+ clk40_gen (.clk(clk40), .rst(clk40_rst));
+ sim_clock_gen #(.PERIOD(100.0), .AUTOSTART(1))
+ clk100_gen (.clk(clk100), .rst(clk100_rst));
+
+ initial begin : init_model
+ clk40_gen.reset();
+ axi.run();
+ wait(!clk40_rst);
+ repeat (10) @(posedge clk40);
+
+ wait(!phy_reset); // both usr_clk's are ok
+
+ mac_test.start_test("model_100gbe::Wait for MAC link_up", 150us);
+ //Added autoconnect - uncomment to test connecting over AXI
+ //Pkg100gbMac::init_mac(0,axi);
+ mac_test.end_test();
+ end
+
+
+ AxiStreamIf #(.DATA_WIDTH(512),.USER_WIDTH(7),.TKEEP(0))
+ eth100g_rx(mgt_clk,mgt_rst);
+
+ always_comb begin
+ mgt_rx.tdata = eth100g_rx.tdata;
+ mgt_rx.tuser = eth100g_rx.tuser;
+ mgt_rx.tkeep = eth100g_rx.trailing2keep(eth100g_rx.tuser);
+ mgt_rx.tvalid = eth100g_rx.tvalid;
+ mgt_rx.tlast = eth100g_rx.tlast;
+ eth100g_rx.tready = mgt_rx.tready;
+ // The MAC ignores hold off. Data must be consumed every clock it is valid.
+ if (!mgt_rst) begin
+ if (!mgt_rx.tready && mgt_rx.tvalid) begin
+ $error("Model 100Gbe : can't hold off the MAC");
+ end
+ end
+ end
+
+ // model does not pause. Users could access this heirarchically to test it
+ logic mgt_pause_req;
+ assign mgt_pause_req = 1'b0;
+
+ // hold off link up untill the stat_auto_config writes are complete
+ logic link_up_model;
+ logic [31:0] mac_status;
+ always_comb begin
+ link_up = link_up_model && mac_status[4];
+ end
+
+ eth_100g #(.PAUSE_QUANTA(10),.PAUSE_REFRESH(100)) eth_100gx (
+ .areset(areset),
+ //-- Free running 100 MHz clock used for InitClk and AxiLite to mac
+ //-- 3.125 - 161.132812 MHz.
+ .clk100(clk100),
+ // MGT Reference Clock 100/125/156.25/161.1328125 MHz
+ .refclk_p(refclk_p),
+ .refclk_n(refclk_n),
+ // MGT TX/RX differential signals
+ .tx_p(tx_p),
+ .tx_n(tx_n),
+ .rx_p(rx_p),
+ .rx_n(rx_n),
+ // 322.26666 Mhz clock generated by 100G Phy from RefClock
+ .mgt_clk(mgt_clk),
+ .mgt_rst(mgt_rst),
+ // pause
+ .mgt_pause_req(mgt_pause_req),
+ //------------------------ AXI Stream TX Interface ------------------------
+ .mgt_tx(mgt_tx),
+ //---------------------- AXI Stream RX Interface ------------------------
+ // There is no RxTReady signal support by the Ethernet100G IP. Received data has to
+ // be read immediately or it is lost.
+ // tUser indicates an error on rcvd packet
+ .mgt_rx(eth100g_rx),
+ .mgt_axil(mgt_axil),
+ // LEDs of QSFP28 port
+ .phy_status(),
+ .mac_ctrl(32'h01000001), // autoconfig / pause mask set to global
+ .mac_status(mac_status),
+ .phy_reset(phy_reset),
+ .link_up(link_up_model)
+ );
+
+endmodule