Files
b210-k7-fpga/top/x400/ip/xge_pcs_pma/ten_gige_phy.v
T
Wade Fife 31c638f98c 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
2022-08-25 13:55:30 -05:00

324 lines
11 KiB
Verilog

//
// Copyright 2021 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: ten_gige_phy
//
// Description:
//
// Wrapper for the Xilinx xxv_ethernet IP (10G/25G Ethernet Subsystem).
//
module ten_gige_phy (
input wire areset,
input wire dclk,
output wire xgmii_clk,
// Transceiver IO
output wire txp,
output wire txn,
input wire rxp,
input wire rxn,
// XGMII Interface
input wire [63:0] xgmii_txd,
input wire [ 7:0] xgmii_txc,
output wire [63:0] xgmii_rxd,
output wire [ 7:0] xgmii_rxc,
// GTYE4_COMMON
input wire qpll0_refclk,
input wire qpll0_clk,
input wire qpll0_lock,
output wire qpll0_reset,
input wire qpll1_refclk,
input wire qpll1_clk,
input wire qpll1_lock,
output wire qpll1_reset,
output wire rxrecclkout,
output wire [7:0] core_status,
output reg reset_done
);
// Create a constant that tells us if we're in simulation or synthesis.
localparam SIMULATION = 0
//synthesis translate_off
+ 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 tx_reset;
wire rx_reset;
wire a_gt_reset_tx_done, gt_reset_tx_done;
wire a_gt_reset_rx_done, gt_reset_rx_done;
wire stat_rx_status_tmp;
reg stat_rx_status = 0;
reg [WAIT_W-1:0] wait_count = 0;
reg gt_rx_reset_in = 0;
//---------------------------------------------------------------------------
// Xilinx 10G/25G IP High Speed Ethernet Subsystem Instance
//---------------------------------------------------------------------------
// All connections below follow the Xilinx IP example design, except that
// rx_core_clk is driven by tx_mii_clk instead of rx_clk_out. This puts the
// RX and TX interfaces on the same clock domain.
// gtwiz_reset_qpll1reset_out is not connected to qpll1reset in the example
// design. Instead, qpll1reset is connected to 0.
assign qpll1_reset = 1'b0;
xge_pcs_pma xge_pcs_pma_i (
.gt_rxp_in_0 (rxp),
.gt_rxn_in_0 (rxn),
.gt_txp_out_0 (txp),
.gt_txn_out_0 (txn),
.tx_mii_clk_0 (xgmii_clk),
.rx_core_clk_0 (xgmii_clk),
.rx_clk_out_0 (),
.gt_loopback_in_0 (3'b0),
.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_eyescandataerror_0 (),
.gt_eyescanreset_0 (1'b0),
.gt_eyescantrigger_0 (1'b0),
.gt_pcsrsvdin_0 (16'b0),
.gt_rxbufreset_0 (1'b0),
.gt_rxbufstatus_0 (),
.gt_rxcdrhold_0 (1'b0),
.gt_rxcommadeten_0 (1'b0),
.gt_rxdfeagchold_0 (1'b0),
.gt_rxdfelpmreset_0 (1'b0),
.gt_rxlatclk_0 (1'b0),
.gt_rxlpmen_0 (1'b0),
.gt_rxpcsreset_0 (1'b0),
.gt_rxpmareset_0 (1'b0),
.gt_rxpolarity_0 (1'b0),
.gt_rxprbscntreset_0 (1'b0),
.gt_rxprbserr_0 (),
.gt_rxprbssel_0 (4'b0),
.gt_rxrate_0 (3'b0),
.gt_rxslide_in_0 (1'b0),
.gt_rxstartofseq_0 (),
.gt_txbufstatus_0 (),
.gt_txinhibit_0 (1'b0),
.gt_txlatclk_0 (1'b0),
.gt_txmaincursor_0 (7'h50),
.gt_txpcsreset_0 (1'b0),
.gt_txpmareset_0 (1'b0),
.gt_txpolarity_0 (1'b0),
.gt_txpostcursor_0 (5'b0),
.gt_txprbsforceerr_0 (1'b0),
.gt_txelecidle_0 (1'b0),
.gt_txprbssel_0 (4'b0),
.gt_txprecursor_0 (5'b0),
.gt_txdiffctrl_0 (5'h18),
.gt_drpdo_0 (),
.gt_drprdy_0 (),
.gt_drpen_0 (1'b0),
.gt_drpwe_0 (1'b0),
.gt_drpaddr_0 (10'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),
.dclk (dclk),
.qpll0clk_in (qpll0_clk),
.qpll0refclk_in (qpll0_refclk),
.qpll1clk_in (qpll1_clk),
.qpll1refclk_in (qpll1_refclk),
.gtwiz_reset_qpll0lock_in (qpll0_lock),
.gtwiz_reset_qpll1lock_in (qpll1_lock),
.gtwiz_reset_qpll0reset_out (qpll0_reset),
.gtwiz_reset_qpll1reset_out ()
);
//---------------------------------------------------------------------------
// Status
//---------------------------------------------------------------------------
assign core_status[7:1] = 0; // Unused
assign core_status[0] = stat_rx_status; // Link status
// Safely combine the RX and TX reset done signals into a single glitch-free
// signal.
synchronizer sync_reset_tx_done (
.clk (xgmii_clk),
.rst (1'b0),
.in (a_gt_reset_tx_done),
.out (gt_reset_tx_done)
);
synchronizer sync_reset_rx_done (
.clk (xgmii_clk),
.rst (1'b0),
.in (a_gt_reset_rx_done),
.out (gt_reset_rx_done)
);
always @(posedge xgmii_clk) begin : ResetDoneProc
reset_done <= gt_reset_tx_done & gt_reset_rx_done;
end
//---------------------------------------------------------------------------
// Reset Logic
//---------------------------------------------------------------------------
// The reset synchronization below is taken from the example design, except
// that rx_clk_out was replaced by tx_mii_clk (xgmii_clk).
synchronizer sync_rx_serdes_reset (
.clk (xgmii_clk),
.rst (1'b0),
.in (~a_gt_reset_rx_done),
.out (rx_serdes_reset)
);
synchronizer sync_tx_reset (
.clk (xgmii_clk),
.rst (1'b0),
.in (~a_gt_reset_tx_done),
.out (tx_reset)
);
synchronizer sync_rx_reset (
.clk (xgmii_clk),
.rst (1'b0),
.in (~a_gt_reset_rx_done),
.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