Files
b210-k7-fpga/lib/xge_interface/xge_mac_wrapper.v
T
Andrew Moch 56d114b508 fpga: lib: Fix 10 GbE cut-through mode
When operating in cut-through mode the hold off from the MAC was not
applying back pressure, so when the TX interface filled up, data was
dropped. This bug was introduced when cut-through was added, and does
not impact the original implementation.


Original-commit: 6cd9662e2b8f2e159805015f3dd9087089a454af
2020-09-16 17:23:23 -05:00

498 lines
16 KiB
Verilog

/////////////////////////////////////////////////////////////////////
//
// Copyright 2013-2020 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: xge_mac_wrapper
// Description:
// Wrap XGE MAC + optional wishbone interface
//
// *) Signals are crossed between the MAC's own 156.25MHz clock domain and the
// main FPGA clock domain.
// *) 6 byte Padding is added at RX, including metadata so that IP headers become aligned.
// *) 6 Byte padding is stripped at TX, so that Eth header data starts immediately.
// *) TX & RX can buffer at least an MTU sized packet
// *) On TX, to not start an Ethernet Tx until a complete packet is present in the
// last Tx FIFO so that the MAC doesn't underrun.
//
// Parameters:
// - PORTNUM: Refers to which ethernet port is being built. Added to padding but not used
// - WISHBONE: If set use wishbone implementation
// - ADD_PREAMBLE: If set add/remove 6 byte padding used in old ethernet_interface
// - CROSS_TO_SYSCLK: If set cross AXI streams to the sys_clk domain.
// - CUT_THROUGH: If > 0, how many words to wait before starting to transmit.
/////////////////////////////////////////////////////////////////////
module xge_mac_wrapper #(
parameter PORTNUM = 8'd0,
parameter WISHBONE = 1,
parameter ADD_PREAMBLE = 1,
parameter CROSS_TO_SYSCLK = 1,
parameter CUT_THROUGH = 0
)(
// XGMII
input xgmii_clk,
output [63:0] xgmii_txd,
output [7:0] xgmii_txc,
input [63:0] xgmii_rxd,
input [7:0] xgmii_rxc,
// Client FIFO Interfaces
input sys_clk,
input sys_rst, // From sys_clk domain.
output [63:0] rx_tdata,
output [3:0] rx_tuser,
output rx_tlast,
output rx_tvalid,
input rx_tready,
input [63:0] tx_tdata,
input [3:0] tx_tuser, // Bit[3] (error) is ignored for now.
input tx_tlast,
input tx_tvalid,
output tx_tready,
// Control and Status
input phy_ready,
input ctrl_tx_enable,
output status_crc_error,
output status_fragment_error,
output status_txdfifo_ovflow,
output status_txdfifo_udflow,
output status_rxdfifo_ovflow,
output status_rxdfifo_udflow,
output status_pause_frame_rx,
output status_local_fault,
output status_remote_fault,
// MDIO
output mdc,
output mdio_in,
input mdio_out,
// Wishbone interface
input [7:0] wb_adr_i, // To wishbone_if0 of wishbone_if.v
input wb_clk_i, // To sync_clk_wb0 of sync_clk_wb.v, ...
input wb_cyc_i, // To wishbone_if0 of wishbone_if.v
input [31:0] wb_dat_i, // To wishbone_if0 of wishbone_if.v
input wb_rst_i, // To sync_clk_wb0 of sync_clk_wb.v, ...
input wb_stb_i, // To wishbone_if0 of wishbone_if.v
input wb_we_i, // To wishbone_if0 of wishbone_if.v
output wb_ack_o, // From wishbone_if0 of wishbone_if.v
output [31:0] wb_dat_o, // From wishbone_if0 of wishbone_if.v
output wb_int_o // From wishbone_if0 of wishbone_if.v
);
//
// Generate 156MHz synchronized sys_rst locally
//
wire xgmii_reset, ctrl_tx_enable_xclk;
wire phy_ready_xgmiiclk, sys_rst_xgmiiclk;
synchronizer #(
.INITIAL_VAL(1'b0), .STAGES(3)
) phy_ready_sync_i (
.clk(xgmii_clk), .rst(1'b0 /* no reset */), .in(phy_ready), .out(phy_ready_xgmiiclk)
);
if (CROSS_TO_SYSCLK) begin : reset_cross
synchronizer #(
.INITIAL_VAL(1'b1), .STAGES(3)
) sys_rst_sync_i (
.clk(xgmii_clk), .rst(1'b0 /* no reset */), .in(sys_rst), .out(sys_rst_xgmiiclk)
);
assign xgmii_reset = !phy_ready_xgmiiclk || sys_rst_xgmiiclk;
end else begin : reset_no_cross
assign xgmii_reset = !phy_ready_xgmiiclk;
end
synchronizer #(
.INITIAL_VAL(1'b1), .STAGES(3)
) tx_enabled_sync_i (
.clk(xgmii_clk), .rst(1'b0 /* no reset */), .in(ctrl_tx_enable), .out(ctrl_tx_enable_xclk)
);
//
// 10G MAC
//
wire [63:0] eth_rx_data;
wire eth_rx_avail;
wire eth_rx_eof;
wire eth_rx_err;
wire [2:0] eth_rx_occ;
wire eth_rx_sof;
wire eth_rx_valid;
wire eth_rx_ren;
wire eth_tx_full;
wire [63:0] eth_tx_data;
wire eth_tx_eof;
wire [2:0] eth_tx_occ;
wire eth_tx_sof;
wire eth_tx_valid;
generate if (WISHBONE == 1) begin : wishbone_mac
xge_mac_wb xge_mac_wb (
// Clocks and Resets
.clk_156m25 (xgmii_clk),
.clk_xgmii_rx (xgmii_clk),
.clk_xgmii_tx (xgmii_clk),
.reset_156m25_n (~xgmii_reset),
.reset_xgmii_rx_n (~xgmii_reset),
.reset_xgmii_tx_n (~xgmii_reset),
// XGMII
.xgmii_txc (xgmii_txc[7:0]),
.xgmii_txd (xgmii_txd[63:0]),
.xgmii_rxc (xgmii_rxc[7:0]),
.xgmii_rxd (xgmii_rxd[63:0]),
// MDIO
.mdc (mdc),
.mdio_out (mdio_in),// Switch sense of in and out here for master and slave.
.mdio_tri (mdio_tri),
.xge_gpo (),
.mdio_in (mdio_out), // Switch sense of in and out here for master and slave.
.xge_gpi (/*{2'b00,align_status,mgt_tx_ready,sync_status[3:0]}*/0),
// Packet interface
.pkt_rx_avail (eth_rx_avail),
.pkt_rx_data (eth_rx_data),
.pkt_rx_eop (eth_rx_eof),
.pkt_rx_err (eth_rx_err),
.pkt_rx_mod (eth_rx_occ),
.pkt_rx_sop (eth_rx_sof),
.pkt_rx_val (eth_rx_valid),
.pkt_tx_full (eth_tx_full),
// Inputs
.pkt_rx_ren (eth_rx_ren),
.pkt_tx_data (eth_tx_data),
.pkt_tx_eop (eth_tx_eof),
.pkt_tx_mod (eth_tx_occ),
.pkt_tx_sop (eth_tx_sof),
.pkt_tx_val (eth_tx_valid),
.wb_ack_o (wb_ack_o),
.wb_dat_o (wb_dat_o),
.wb_adr_i (wb_adr_i[7:0]),
.wb_clk_i (wb_clk_i),
.wb_cyc_i (wb_cyc_i),
.wb_dat_i (wb_dat_i),
.wb_rst_i (wb_rst_i),
.wb_stb_i (wb_stb_i),
.wb_we_i (wb_we_i),
.wb_int_o (xge_int)
);
assign status_crc_error = 1'b0;
assign status_fragment_error = 1'b0;
assign status_txdfifo_ovflow = 1'b0;
assign status_txdfifo_udflow = 1'b0;
assign status_rxdfifo_ovflow = 1'b0;
assign status_rxdfifo_udflow = 1'b0;
assign status_pause_frame_rx = 1'b0;
assign status_local_fault = 1'b0;
assign status_remote_fault = 1'b0;
end else begin : xge_mac
xge_mac xge_mac (
// Clocks and Resets
.clk_156m25 (xgmii_clk),
.clk_xgmii_rx (xgmii_clk),
.clk_xgmii_tx (xgmii_clk),
.reset_156m25_n (~xgmii_reset),
.reset_xgmii_rx_n (~xgmii_reset),
.reset_xgmii_tx_n (~xgmii_reset),
// XGMII
.xgmii_txc (xgmii_txc[7:0]),
.xgmii_txd (xgmii_txd[63:0]),
.xgmii_rxc (xgmii_rxc[7:0]),
.xgmii_rxd (xgmii_rxd[63:0]),
// Packet interface
.pkt_rx_avail (eth_rx_avail),
.pkt_rx_data (eth_rx_data),
.pkt_rx_eop (eth_rx_eof),
.pkt_rx_err (eth_rx_err),
.pkt_rx_mod (eth_rx_occ),
.pkt_rx_sop (eth_rx_sof),
.pkt_rx_val (eth_rx_valid),
.pkt_tx_full (eth_tx_full),
// Inputs
.pkt_rx_ren (eth_rx_ren),
.pkt_tx_data (eth_tx_data),
.pkt_tx_eop (eth_tx_eof),
.pkt_tx_mod (eth_tx_occ),
.pkt_tx_sop (eth_tx_sof),
.pkt_tx_val (eth_tx_valid),
// Control and Status
.ctrl_tx_enable (ctrl_tx_enable_xclk),
.status_crc_error (status_crc_error),
.status_fragment_error (status_fragment_error),
.status_txdfifo_ovflow (status_txdfifo_ovflow),
.status_txdfifo_udflow (status_txdfifo_udflow),
.status_rxdfifo_ovflow (status_rxdfifo_ovflow),
.status_rxdfifo_udflow (status_rxdfifo_udflow),
.status_pause_frame_rx (status_pause_frame_rx),
.status_local_fault (status_local_fault),
.status_remote_fault (status_remote_fault)
);
assign wb_ack_o = 1'b0;
assign wb_dat_o = 1'b0;
assign wb_int_o = 1'b0;
assign mdio_in = 1'b0;
assign mdc = 1'b0;
end
endgenerate
///////////////////////////////////////////////////////////////////////////////////////
// RX FIFO Chain
///////////////////////////////////////////////////////////////////////////////////////
wire [63:0] rx_tdata_int;
wire [3:0] rx_tuser_int;
wire rx_tlast_int;
wire rx_tvalid_int;
wire rx_tready_int;
//
// Logic to drive pkt_rx_ren on XGE MAC
//
xge_handshake xge_handshake (
.clk(xgmii_clk),
.reset(xgmii_reset),
.pkt_rx_ren(eth_rx_ren),
.pkt_rx_avail(eth_rx_avail),
.pkt_rx_eop(eth_rx_eof)
);
if (ADD_PREAMBLE) begin : rx_preamble
//
// Add pad of 6 empty bytes before MAC addresses of new Rxed packet so that IP
// headers are aligned. Also put metadata in first octet of pad that shows
// ingress port.
//
xge64_to_axi64 #(
.LABEL(PORTNUM)
) xge64_to_axi64 (
.clk(xgmii_clk),
.reset(xgmii_reset),
.clear(1'b0),
.datain(eth_rx_data),
.occ(eth_rx_occ),
.sof(eth_rx_sof),
.eof(eth_rx_eof),
.err(eth_rx_err),
.valid(eth_rx_valid),
.axis_tdata(rx_tdata_int),
.axis_tuser(rx_tuser_int),
.axis_tlast(rx_tlast_int),
.axis_tvalid(rx_tvalid_int),
.axis_tready(rx_tready_int)
);
end else begin : rx_no_preamble
assign rx_tdata_int = eth_rx_data;
assign rx_tuser_int[3] = eth_rx_err;
assign rx_tuser_int[2:0] = eth_rx_occ;
assign rx_tlast_int = eth_rx_eof;
assign rx_tvalid_int = eth_rx_valid;
// there is no holdoff so ignore rx_tready_int
end
if (CROSS_TO_SYSCLK) begin : rx_cross
//
// Large FIFO must be able to run input side at 64b@156MHz to sustain 10Gb Rx.
//
axi64_4k_2clk_fifo rxfifo_2clk (
.s_aresetn(~xgmii_reset),
.s_aclk(xgmii_clk),
.s_axis_tvalid(rx_tvalid_int),
.s_axis_tready(rx_tready_int),
.s_axis_tdata(rx_tdata_int),
.s_axis_tlast(rx_tlast_int),
.s_axis_tuser(rx_tuser_int),
.axis_wr_data_count(),
.m_aclk(sys_clk),
.m_axis_tvalid(rx_tvalid),
.m_axis_tready(rx_tready),
.m_axis_tdata(rx_tdata),
.m_axis_tlast(rx_tlast),
.m_axis_tuser(rx_tuser),
.axis_rd_data_count()
);
end else begin : rx_no_cross
assign rx_tdata = rx_tdata_int;
assign rx_tuser = rx_tuser_int;
assign rx_tlast = rx_tlast_int;
assign rx_tvalid = rx_tvalid_int;
assign rx_tready_int = rx_tready;
end
///////////////////////////////////////////////////////////////////////////////////////
// TX FIFO Chain
///////////////////////////////////////////////////////////////////////////////////////
wire [63:0] tx_tdata_int;
wire [3:0] tx_tuser_int;
wire tx_tlast_int;
wire tx_tvalid_int;
wire tx_tready_int;
wire [63:0] tx_tdata_int2;
wire [3:0] tx_tuser_int2;
wire tx_tlast_int2;
wire tx_tvalid_int2;
wire tx_tready_int2;
wire tx_tvalid_int3;
wire tx_tready_int3;
wire tx_sof_int3;
wire enable_tx;
if (CROSS_TO_SYSCLK) begin : tx_cross
axi64_4k_2clk_fifo txfifo_2clk_1x (
.s_aresetn(~xgmii_reset),
.s_aclk(sys_clk),
.s_axis_tvalid(tx_tvalid),
.s_axis_tready(tx_tready),
.s_axis_tdata(tx_tdata),
.s_axis_tlast(tx_tlast),
.s_axis_tuser(tx_tuser),
.axis_wr_data_count(),
.m_aclk(xgmii_clk),
.m_axis_tvalid(tx_tvalid_int),
.m_axis_tready(tx_tready_int),
.m_axis_tdata(tx_tdata_int),
.m_axis_tlast(tx_tlast_int),
.m_axis_tuser(tx_tuser_int),
.axis_rd_data_count()
);
end else begin : tx_no_cross
assign tx_tdata_int = tx_tdata;
assign tx_tuser_int = tx_tuser;
assign tx_tlast_int = tx_tlast;
assign tx_tvalid_int = tx_tvalid;
assign tx_tready = tx_tready_int;
end
if (ADD_PREAMBLE) begin : tx_preamble
//
// Strip the 6 octet ethernet padding we used internally.
// Put SOF into bit[3] of tuser.
//
axi64_to_xge64 axi64_to_xge64 (
.clk(xgmii_clk),
.reset(xgmii_reset),
.clear(1'b0),
.s_axis_tdata(tx_tdata_int),
.s_axis_tuser(tx_tuser_int),
.s_axis_tlast(tx_tlast_int),
.s_axis_tvalid(tx_tvalid_int),
.s_axis_tready(tx_tready_int),
.m_axis_tdata(tx_tdata_int2),
.m_axis_tuser(tx_tuser_int2),
.m_axis_tlast(tx_tlast_int2),
.m_axis_tvalid(tx_tvalid_int2),
.m_axis_tready(tx_tready_int2)
);
end else begin : tx_no_preamble
reg sof = 1'b1;
// Add SOF
always @(posedge xgmii_clk) begin : add_sof
if (xgmii_reset) begin
sof <= 1'b1;
end else if (tx_tvalid_int && tx_tready_int) begin
sof <= tx_tlast_int;
end
end
assign tx_tdata_int2 = tx_tdata_int;
assign tx_tuser_int2[3] = sof && tx_tvalid_int;
assign tx_tuser_int2[2:0] = tx_tuser_int[2:0];
assign tx_tlast_int2 = tx_tlast_int;
assign tx_tvalid_int2 = tx_tvalid_int;
assign tx_tready_int = tx_tready_int2;
end
//
// Large FIFO can hold a max sized ethernet packet.
//
wire [15:0] tx_occupied;
localparam TX_FIFO_SIZE = CUT_THROUGH > 0 ? $clog2(CUT_THROUGH)+1 : 10;
axi_fifo #(.WIDTH(64+4+1), .SIZE(TX_FIFO_SIZE)) txfifo (
.clk(xgmii_clk), .reset(xgmii_reset), .clear(1'b0),
.i_tdata({tx_tlast_int2, tx_tuser_int2, tx_tdata_int2}),
.i_tvalid(tx_tvalid_int2),
.i_tready(tx_tready_int2),
.o_tvalid(tx_tvalid_int3),
.o_tready(tx_tready_int3),
.o_tdata({eth_tx_eof,tx_sof_int3,eth_tx_occ,eth_tx_data}),
.space(), .occupied(tx_occupied)
);
//
// add cut through if we have "enough" data buffered up
//
reg cut_through;
reg cut_wait;
if (CUT_THROUGH > 0) begin : yes_cut_through
wire cut_start;
wire cut_end;
assign cut_start = tx_occupied > CUT_THROUGH;
assign cut_end = eth_tx_eof && eth_tx_valid;
// Add SOF
always @(posedge xgmii_clk) begin : cut_through_dff
if (xgmii_reset) begin
cut_through <= 1'b0;
cut_wait <= 1'b0;
end else begin
cut_wait <= eth_tx_full;
if (cut_start) begin
cut_through <= 1'b1;
end else if (cut_end) begin
cut_through <= 1'b0;
end
end
end
end else begin : no_cut_through
always @(*) begin
cut_through <= 0;
cut_wait <= 0;
end
end
//
// Monitor number of Ethernet packets in tx_fifo2
//
axi_count_packets_in_fifo axi_count_packets_in_fifo (
.clk(xgmii_clk),
.reset(xgmii_reset),
.in_axis_tvalid(tx_tvalid_int2),
.in_axis_tready(tx_tready_int2),
.in_axis_tlast(tx_tlast_int2),
.out_axis_tvalid(tx_tvalid_int3),
.out_axis_tready(tx_tready_int3),
.out_axis_tlast(eth_tx_eof),
.pkt_tx_full(eth_tx_full),
.enable_tx(enable_tx)
);
//
//
// Suppress FIFO flags to stop overflow of MAC in Tx direction
//
if (CUT_THROUGH > 0) begin : yes_cut_through_ready
assign tx_tready_int3 = (cut_through && !(cut_wait));
assign eth_tx_valid = (cut_through && !(cut_wait)) & tx_tvalid_int3;
assign eth_tx_sof = (cut_through && !(cut_wait)) & tx_sof_int3;
end else begin : no_cut_through_ready
assign tx_tready_int3 = enable_tx;
assign eth_tx_valid = enable_tx & tx_tvalid_int3;
assign eth_tx_sof = enable_tx & tx_sof_int3;
end
endmodule