fpga: x400: Fix link status detection of 10 GbE IP

This fixes some issues with the 2021.1 version of the 10 GbE IP. In the
new version, link status is sporadically detected by the Xilinx IP when
nothing is connected to the port. Also, when the SFP is inserted, the
link status goes up and down multiple times. This change fixes this by
adding a counter and only considering it "linked" when the link has
been stable for some amount of time.


Original-commit: 846c7be2e42f1db6d7c7cc0b1128e014f83e3bae
This commit is contained in:
Wade Fife
2022-08-25 13:55:30 -05:00
committed by skooNI
parent 1b952d33df
commit 31c638f98c
+141 -55
View File
@@ -43,10 +43,24 @@ module ten_gige_phy (
output reg reset_done output reg reset_done
); );
localparam XGMII_FREQ = 125_000_000; // xgmii_clk frequency in Hz // Create a constant that tells us if we're in simulation or synthesis.
localparam RX_RST_WAIT = XGMII_FREQ/2; // Cycles to wait before resetting localparam SIMULATION = 0
localparam RX_RST_DURATION = 100; // Duration of reset in cycles //synthesis translate_off
localparam RX_RST_COUNT_W = $clog2(RX_RST_WAIT); + 1
//synthesis translate_on
;
// xgmii_clk frequency in Hz
localparam XGMII_FREQ = 125_000_000;
// Cycles to wait before period reset while link is down.
localparam RESET_WAIT = XGMII_FREQ/2;
// Cycles to wait before reporting the link is up. Use a much smaller number
// in simulation to save time.
localparam LINK_WAIT = SIMULATION ? (125) : RESET_WAIT;
// Duration of reset in cycles (must be less than LINK_WAIT)
localparam LINK_RST_DURATION = 100;
// Width of the wait counter. Must be wide enough for the WAIT values above.
localparam WAIT_W = $clog2(RESET_WAIT+1);
wire rx_serdes_reset; wire rx_serdes_reset;
wire tx_reset; wire tx_reset;
@@ -55,10 +69,11 @@ module ten_gige_phy (
wire a_gt_reset_tx_done, gt_reset_tx_done; wire a_gt_reset_tx_done, gt_reset_tx_done;
wire a_gt_reset_rx_done, gt_reset_rx_done; wire a_gt_reset_rx_done, gt_reset_rx_done;
wire stat_rx_status; wire stat_rx_status_tmp;
reg stat_rx_status = 0;
// reg [RX_RST_COUNT_W-1:0] rst_count; reg [WAIT_W-1:0] wait_count = 0;
// reg gt_rx_reset_in; reg gt_rx_reset_in = 0;
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Xilinx 10G/25G IP High Speed Ethernet Subsystem Instance // Xilinx 10G/25G IP High Speed Ethernet Subsystem Instance
@@ -77,10 +92,41 @@ module ten_gige_phy (
.gt_rxn_in_0 (rxn), .gt_rxn_in_0 (rxn),
.gt_txp_out_0 (txp), .gt_txp_out_0 (txp),
.gt_txn_out_0 (txn), .gt_txn_out_0 (txn),
.tx_mii_clk_0 (xgmii_clk),
.rx_core_clk_0 (xgmii_clk), .rx_core_clk_0 (xgmii_clk),
.rx_serdes_reset_0 (rx_serdes_reset), .rx_clk_out_0 (),
.txoutclksel_in_0 (3'b101), .gt_loopback_in_0 (3'b0),
.rxoutclksel_in_0 (3'b101), .rx_reset_0 (rx_reset),
.rxrecclkout_0 (rxrecclkout),
.rx_mii_d_0 (xgmii_rxd),
.rx_mii_c_0 (xgmii_rxc),
.ctl_rx_test_pattern_0 (1'b0),
.ctl_rx_test_pattern_enable_0 (1'b0),
.ctl_rx_data_pattern_select_0 (1'b0),
.ctl_rx_prbs31_test_pattern_enable_0 (1'b0),
.stat_rx_block_lock_0 (),
.stat_rx_framing_err_valid_0 (),
.stat_rx_framing_err_0 (),
.stat_rx_hi_ber_0 (),
.stat_rx_valid_ctrl_code_0 (),
.stat_rx_bad_code_0 (),
.stat_rx_bad_code_valid_0 (),
.stat_rx_error_valid_0 (),
.stat_rx_error_0 (),
.stat_rx_fifo_error_0 (),
.stat_rx_local_fault_0 (),
.stat_rx_status_0 (stat_rx_status_tmp), // rx_core_clk_0 domain
.tx_reset_0 (tx_reset),
.tx_mii_d_0 (xgmii_txd),
.tx_mii_c_0 (xgmii_txc),
.ctl_tx_test_pattern_0 (1'b0),
.ctl_tx_test_pattern_enable_0 (1'b0),
.ctl_tx_test_pattern_select_0 (1'b0),
.ctl_tx_data_pattern_select_0 (1'b0),
.ctl_tx_test_pattern_seed_a_0 (58'b0),
.ctl_tx_test_pattern_seed_b_0 (58'b0),
.ctl_tx_prbs31_test_pattern_enable_0 (1'b0),
.stat_tx_local_fault_0 (),
.gt_dmonitorout_0 (), .gt_dmonitorout_0 (),
.gt_eyescandataerror_0 (), .gt_eyescandataerror_0 (),
.gt_eyescanreset_0 (1'b0), .gt_eyescanreset_0 (1'b0),
@@ -104,7 +150,6 @@ module ten_gige_phy (
.gt_rxslide_in_0 (1'b0), .gt_rxslide_in_0 (1'b0),
.gt_rxstartofseq_0 (), .gt_rxstartofseq_0 (),
.gt_txbufstatus_0 (), .gt_txbufstatus_0 (),
.gt_txdiffctrl_0 (5'h18),
.gt_txinhibit_0 (1'b0), .gt_txinhibit_0 (1'b0),
.gt_txlatclk_0 (1'b0), .gt_txlatclk_0 (1'b0),
.gt_txmaincursor_0 (7'h50), .gt_txmaincursor_0 (7'h50),
@@ -113,21 +158,29 @@ module ten_gige_phy (
.gt_txpolarity_0 (1'b0), .gt_txpolarity_0 (1'b0),
.gt_txpostcursor_0 (5'b0), .gt_txpostcursor_0 (5'b0),
.gt_txprbsforceerr_0 (1'b0), .gt_txprbsforceerr_0 (1'b0),
.gt_txelecidle_0 (1'b0),
.gt_txprbssel_0 (4'b0), .gt_txprbssel_0 (4'b0),
.gt_txprecursor_0 (5'b0), .gt_txprecursor_0 (5'b0),
.rxrecclkout_0 (rxrecclkout), .gt_txdiffctrl_0 (5'h18),
.gt_drpclk_0 (dclk),
.gt_drpdo_0 (), .gt_drpdo_0 (),
.gt_drprdy_0 (), .gt_drprdy_0 (),
.gt_drpen_0 (1'b0), .gt_drpen_0 (1'b0),
.gt_drpwe_0 (1'b0), .gt_drpwe_0 (1'b0),
.gt_drpaddr_0 (10'b0), .gt_drpaddr_0 (10'b0),
.gt_drpdi_0 (16'b0), .gt_drpdi_0 (16'b0),
.gt_drpclk_0 (dclk),
.gt_drprst_0 (1'b0),
.gtpowergood_out_0 (),
.txoutclksel_in_0 (3'b101),
.rxoutclksel_in_0 (3'b101),
.rx_serdes_reset_0 (rx_serdes_reset),
.gt_reset_all_in_0 (areset),
.gt_tx_reset_in_0 (1'b0),
.gt_rx_reset_in_0 (gt_rx_reset_in),
.gt_reset_tx_done_out_0 (a_gt_reset_tx_done),
.gt_reset_rx_done_out_0 (a_gt_reset_rx_done),
.sys_reset (areset), .sys_reset (areset),
.dclk (dclk), .dclk (dclk),
.tx_mii_clk_0 (xgmii_clk),
.rx_clk_out_0 (),
.gtpowergood_out_0 (),
.qpll0clk_in (qpll0_clk), .qpll0clk_in (qpll0_clk),
.qpll0refclk_in (qpll0_refclk), .qpll0refclk_in (qpll0_refclk),
.qpll1clk_in (qpll1_clk), .qpll1clk_in (qpll1_clk),
@@ -135,45 +188,7 @@ module ten_gige_phy (
.gtwiz_reset_qpll0lock_in (qpll0_lock), .gtwiz_reset_qpll0lock_in (qpll0_lock),
.gtwiz_reset_qpll1lock_in (qpll1_lock), .gtwiz_reset_qpll1lock_in (qpll1_lock),
.gtwiz_reset_qpll0reset_out (qpll0_reset), .gtwiz_reset_qpll0reset_out (qpll0_reset),
.gtwiz_reset_qpll1reset_out (), .gtwiz_reset_qpll1reset_out ()
.gt_reset_tx_done_out_0 (a_gt_reset_tx_done),
.gt_reset_rx_done_out_0 (a_gt_reset_rx_done),
.gt_reset_all_in_0 (areset),
.gt_tx_reset_in_0 (1'b0),
.gt_rx_reset_in_0 (1'b0),
.rx_reset_0 (rx_reset),
.rx_mii_d_0 (xgmii_rxd),
.rx_mii_c_0 (xgmii_rxc),
.ctl_rx_test_pattern_0 (1'b0),
.ctl_rx_test_pattern_enable_0 (1'b0),
.ctl_rx_data_pattern_select_0 (1'b0),
.ctl_rx_prbs31_test_pattern_enable_0 (1'b0),
.stat_rx_framing_err_0 (),
.stat_rx_framing_err_valid_0 (),
.stat_rx_local_fault_0 (),
.stat_rx_block_lock_0 (),
.stat_rx_valid_ctrl_code_0 (),
.stat_rx_status_0 (stat_rx_status), // rx_core_clk_0 domain
.stat_rx_hi_ber_0 (),
.stat_rx_bad_code_0 (),
.stat_rx_bad_code_valid_0 (),
.stat_rx_error_0 (),
.stat_rx_error_valid_0 (),
.stat_rx_fifo_error_0 (),
.tx_reset_0 (tx_reset),
.tx_mii_d_0 (xgmii_txd),
.tx_mii_c_0 (xgmii_txc),
.stat_tx_local_fault_0 (),
.ctl_tx_test_pattern_0 (1'b0),
.ctl_tx_test_pattern_enable_0 (1'b0),
.ctl_tx_test_pattern_select_0 (1'b0),
.ctl_tx_data_pattern_select_0 (1'b0),
.ctl_tx_test_pattern_seed_a_0 (58'b0),
.ctl_tx_test_pattern_seed_b_0 (58'b0),
.ctl_tx_prbs31_test_pattern_enable_0 (1'b0),
.gt_loopback_in_0 (3'b0),
.gt_txelecidle_0 (1'b0),
.gt_drprst_0 (1'b0)
); );
@@ -234,4 +249,75 @@ module ten_gige_phy (
.out (rx_reset) .out (rx_reset)
); );
localparam [1:0] ST_IDLE = 0;
localparam [1:0] ST_RESET = 1;
localparam [1:0] ST_WAIT_FOR_LINK = 2;
localparam [1:0] ST_LINKED = 3;
reg [1:0] state = ST_IDLE;
// This state machine resets the RX GT part of the Xilinx IP periodically
// while the link is down. Once the Xilinx IP reports the link as up, it
// waits a bit to make sure the link is stable before reporting that it is
// up. This allows us to tolerate a some amount of flakiness while the port
// is being plugged in.
always @(posedge xgmii_clk, posedge areset) begin
if (areset) begin
gt_rx_reset_in <= 0;
wait_count <= 0;
stat_rx_status <= 0;
state <= ST_IDLE;
end else begin
case (state)
ST_IDLE : begin
// Wait for a delay then reset the RX GT core if the link is down.
wait_count <= wait_count + 1;
if (stat_rx_status_tmp) begin
// Link indication asserted, so let's see if it's stable.
wait_count <= 0;
state <= ST_WAIT_FOR_LINK;
end else if (wait_count >= RESET_WAIT) begin
// Link didn't assert during our wait, so reset the core, in case
// it's in a bad state.
wait_count <= 0;
state <= ST_RESET;
end
end
ST_RESET : begin
// Reset the RX GT logic
gt_rx_reset_in <= 1;
wait_count <= wait_count + 1;
if (wait_count >= LINK_RST_DURATION) begin
// Reset is done, so deassert and then try again
gt_rx_reset_in <= 0;
wait_count <= 0;
state <= ST_IDLE;
end
end
ST_WAIT_FOR_LINK : begin
// Wait for a while to make sure the link is stable
wait_count <= wait_count + 1;
if (!stat_rx_status_tmp) begin
// Link was not stable!
wait_count <= 0;
state <= ST_RESET;
end else if (wait_count >= LINK_WAIT) begin
// Link stayed stable during the wait
wait_count <= 0;
state <= ST_LINKED;
end
end
ST_LINKED : begin
// We've had a solid link for a while know, so let's call it good.
wait_count <= 0;
stat_rx_status <= 1;
if (!stat_rx_status_tmp) begin
stat_rx_status <= 0;
state <= ST_RESET;
end
end
endcase
end
end
endmodule endmodule