`resetall
`timescale 1ns / 1ps
`default_nettype none

module ethernet_top #
(
    parameter integer AXI_ADDR_WIDTH	= 32,
    parameter integer AXI_DATA_WIDTH	= 32    
)
(
    /*
     * Clock: 125MHz LVDS
     * Reset:  active low
     */
    input  wire       clk_freerun,
    input  wire       reset,
    output wire [3:0] eth_clk,
    output wire [3:0] eth_resetn,

    input  wire [15:0] packet_size,

    axi4s_intf.master rx_udp_0,
    axi4s_intf.slave  tx_udp_0,

    axi4s_intf.master rx_udp_1,
    axi4s_intf.slave  tx_udp_1,    

    /*
     * Ethernet: SFP+
     */
    input  wire       sfp0_rx_p,
    input  wire       sfp0_rx_n,
    output wire       sfp0_tx_p,
    output wire       sfp0_tx_n,
    input  wire       sfp1_rx_p,
    input  wire       sfp1_rx_n,
    output wire       sfp1_tx_p,
    output wire       sfp1_tx_n,
    input  wire       sfp2_rx_p,
    input  wire       sfp2_rx_n,
    output wire       sfp2_tx_p,
    output wire       sfp2_tx_n,
    input  wire       sfp3_rx_p,
    input  wire       sfp3_rx_n,
    output wire       sfp3_tx_p,
    output wire       sfp3_tx_n,
    input  wire       sfp_mgt_refclk_0_p,
    input  wire       sfp_mgt_refclk_0_n,
    output wire       sfp0_tx_disable_b,
    output wire       sfp1_tx_disable_b,
    output wire       sfp2_tx_disable_b,
    output wire       sfp3_tx_disable_b
);

// Internal 156.25 MHz clock
wire clk_156mhz_int;
wire rst_156mhz_int;

// XGMII 10G PHY
assign sfp0_tx_disable_b = 1'b0;
assign sfp1_tx_disable_b = 1'b0;
assign sfp2_tx_disable_b = 1'b0;
assign sfp3_tx_disable_b = 1'b0;

wire        sfp0_tx_clk_int;
wire        sfp0_tx_rst_int;
wire [63:0] sfp0_txd_int;
wire [7:0]  sfp0_txc_int;
wire        sfp0_rx_clk_int;
wire        sfp0_rx_rst_int;
wire [63:0] sfp0_rxd_int;
wire [7:0]  sfp0_rxc_int;

wire        sfp1_tx_clk_int;
wire        sfp1_tx_rst_int;
wire [63:0] sfp1_txd_int;
wire [7:0]  sfp1_txc_int;
wire        sfp1_rx_clk_int;
wire        sfp1_rx_rst_int;
wire [63:0] sfp1_rxd_int;
wire [7:0]  sfp1_rxc_int;

wire        sfp2_tx_clk_int;
wire        sfp2_tx_rst_int;
wire [63:0] sfp2_txd_int;
wire [7:0]  sfp2_txc_int;
wire        sfp2_rx_clk_int;
wire        sfp2_rx_rst_int;
wire [63:0] sfp2_rxd_int;
wire [7:0]  sfp2_rxc_int;

wire        sfp3_tx_clk_int;
wire        sfp3_tx_rst_int;
wire [63:0] sfp3_txd_int;
wire [7:0]  sfp3_txc_int;
wire        sfp3_rx_clk_int;
wire        sfp3_rx_rst_int;
wire [63:0] sfp3_rxd_int;
wire [7:0]  sfp3_rxc_int;

assign clk_156mhz_int = sfp0_tx_clk_int;
assign rst_156mhz_int = sfp0_tx_rst_int;

// assign eth_resetn = !sfp0_tx_rst_int;
// assign eth_clk = sfp0_tx_clk_int;
assign eth_resetn[0] = !sfp0_tx_rst_int;
assign eth_clk[0]    = sfp0_tx_clk_int;
assign eth_resetn[1] = !sfp1_tx_rst_int;
assign eth_clk[1]    = sfp1_tx_clk_int;
assign eth_resetn[2] = !sfp2_tx_rst_int;
assign eth_clk[2]    = sfp2_tx_clk_int;
assign eth_resetn[3] = !sfp3_tx_rst_int;
assign eth_clk[3]    = sfp3_tx_clk_int;

wire sfp0_rx_block_lock;
wire sfp1_rx_block_lock;
wire sfp2_rx_block_lock;
wire sfp3_rx_block_lock;

wire sfp_mgt_refclk_0;

wire reset_freerun;

sync_reset #(
    .N(4)
)
sync_reset_freerun_inst (
    .clk(clk_freerun),
    .rst(reset),
    .out(reset_freerun)
);

IBUFDS_GTE3 ibufds_gte4_sfp_mgt_refclk_0_inst (
    .I     (sfp_mgt_refclk_0_p),
    .IB    (sfp_mgt_refclk_0_n),
    .CEB   (1'b0),
    .O     (sfp_mgt_refclk_0),
    .ODIV2 ()
);


eth_xcvr_phy_quad_wrapper
sfp_phy_inst (
    .xcvr_ctrl_clk(clk_freerun),
    .xcvr_ctrl_rst(reset_freerun),

    /*
     * Common
     */
    .xcvr_gtpowergood_out(),

    /*
     * PLL
     */
    .xcvr_gtrefclk00_in(sfp_mgt_refclk_0),

    /*
     * Serial data
     */
    .xcvr_txp({sfp3_tx_p, sfp2_tx_p, sfp1_tx_p, sfp0_tx_p}),
    .xcvr_txn({sfp3_tx_n, sfp2_tx_n, sfp1_tx_n, sfp0_tx_n}),
    .xcvr_rxp({sfp3_rx_p, sfp2_rx_p, sfp1_rx_p, sfp0_rx_p}),
    .xcvr_rxn({sfp3_rx_n, sfp2_rx_n, sfp1_rx_n, sfp0_rx_n}),

    /*
     * PHY connections
     */
    .phy_1_tx_clk(sfp0_tx_clk_int),
    .phy_1_tx_rst(sfp0_tx_rst_int),
    .phy_1_xgmii_txd(sfp0_txd_int),
    .phy_1_xgmii_txc(sfp0_txc_int),
    .phy_1_rx_clk(sfp0_rx_clk_int),
    .phy_1_rx_rst(sfp0_rx_rst_int),
    .phy_1_xgmii_rxd(sfp0_rxd_int),
    .phy_1_xgmii_rxc(sfp0_rxc_int),
    .phy_1_tx_bad_block(),
    .phy_1_rx_error_count(),
    .phy_1_rx_bad_block(),
    .phy_1_rx_sequence_error(),
    .phy_1_rx_block_lock(sfp0_rx_block_lock),
    .phy_1_rx_status(),
    .phy_1_cfg_tx_prbs31_enable(1'b0),
    .phy_1_cfg_rx_prbs31_enable(1'b0),

    .phy_2_tx_clk(sfp1_tx_clk_int),
    .phy_2_tx_rst(sfp1_tx_rst_int),
    .phy_2_xgmii_txd(sfp1_txd_int),
    .phy_2_xgmii_txc(sfp1_txc_int),
    .phy_2_rx_clk(sfp1_rx_clk_int),
    .phy_2_rx_rst(sfp1_rx_rst_int),
    .phy_2_xgmii_rxd(sfp1_rxd_int),
    .phy_2_xgmii_rxc(sfp1_rxc_int),
    .phy_2_tx_bad_block(),
    .phy_2_rx_error_count(),
    .phy_2_rx_bad_block(),
    .phy_2_rx_sequence_error(),
    .phy_2_rx_block_lock(sfp1_rx_block_lock),
    .phy_2_rx_status(),
    .phy_2_cfg_tx_prbs31_enable(1'b0),
    .phy_2_cfg_rx_prbs31_enable(1'b0),

    .phy_3_tx_clk(sfp2_tx_clk_int),
    .phy_3_tx_rst(sfp2_tx_rst_int),
    .phy_3_xgmii_txd(sfp2_txd_int),
    .phy_3_xgmii_txc(sfp2_txc_int),
    .phy_3_rx_clk(sfp2_rx_clk_int),
    .phy_3_rx_rst(sfp2_rx_rst_int),
    .phy_3_xgmii_rxd(sfp2_rxd_int),
    .phy_3_xgmii_rxc(sfp2_rxc_int),
    .phy_3_tx_bad_block(),
    .phy_3_rx_error_count(),
    .phy_3_rx_bad_block(),
    .phy_3_rx_sequence_error(),
    .phy_3_rx_block_lock(sfp2_rx_block_lock),
    .phy_3_rx_status(),
    .phy_3_cfg_tx_prbs31_enable(1'b0),
    .phy_3_cfg_rx_prbs31_enable(1'b0),

    .phy_4_tx_clk(sfp3_tx_clk_int),
    .phy_4_tx_rst(sfp3_tx_rst_int),
    .phy_4_xgmii_txd(sfp3_txd_int),
    .phy_4_xgmii_txc(sfp3_txc_int),
    .phy_4_rx_clk(sfp3_rx_clk_int),
    .phy_4_rx_rst(sfp3_rx_rst_int),
    .phy_4_xgmii_rxd(sfp3_rxd_int),
    .phy_4_xgmii_rxc(sfp3_rxc_int),
    .phy_4_tx_bad_block(),
    .phy_4_rx_error_count(),
    .phy_4_rx_bad_block(),
    .phy_4_rx_sequence_error(),
    .phy_4_rx_block_lock(sfp3_rx_block_lock),
    .phy_4_rx_status(),
    .phy_4_cfg_tx_prbs31_enable(1'b0),
    .phy_4_cfg_rx_prbs31_enable(1'b0)
);

assign sfp2_txd_int = 64'h0707070707070707;
assign sfp2_txc_int = 8'hff;
assign sfp3_txd_int = 64'h0707070707070707;
assign sfp3_txc_int = 8'hff;

fpga_core # (
    .IP_ADDR(2),
    .PORT_IN(1234),
    .MAC_ADDR(48'h02_00_00_00_00_00)
) 
core_inst_0 (
    /*
     * Clock: 156.25 MHz
     * Synchronous reset
     */
    // .clk(clk_156mhz_int),
    // .rst(rst_156mhz_int),
    .clk(sfp0_tx_clk_int),
    .rst(sfp0_tx_rst_int),    
    .packet_size(packet_size),

    .rx_fifo_udp_payload_axis_tdata(rx_udp_0.tdata),
    .rx_fifo_udp_payload_axis_tkeep(rx_udp_0.tkeep),
    .rx_fifo_udp_payload_axis_tvalid(rx_udp_0.tvalid),
    .rx_fifo_udp_payload_axis_tready(rx_udp_0.tready),
    .rx_fifo_udp_payload_axis_tlast(rx_udp_0.tlast),
    .rx_fifo_udp_payload_axis_tuser(rx_udp_0.tuser),    

    .tx_fifo_udp_payload_axis_tdata(tx_udp_0.tdata),
    .tx_fifo_udp_payload_axis_tkeep(tx_udp_0.tkeep),
    .tx_fifo_udp_payload_axis_tvalid(tx_udp_0.tvalid),
    .tx_fifo_udp_payload_axis_tready(tx_udp_0.tready),
    .tx_fifo_udp_payload_axis_tlast(tx_udp_0.tlast),
    .tx_fifo_udp_payload_axis_tuser(tx_udp_0.tuser),
    .tx_fifo_udp_payload_axis_tdest(tx_udp_0.tdest),    

    /*
     * Ethernet: SFP+
     */
    .sfp_tx_clk(sfp0_tx_clk_int),
    .sfp_tx_rst(sfp0_tx_rst_int),
    .sfp_txd(sfp0_txd_int),
    .sfp_txc(sfp0_txc_int),
    .sfp_rx_clk(sfp0_rx_clk_int),
    .sfp_rx_rst(sfp0_rx_rst_int),
    .sfp_rxd(sfp0_rxd_int),
    .sfp_rxc(sfp0_rxc_int)
);

fpga_core # (
    .IP_ADDR(3),
    .PORT_IN(1235),
    .MAC_ADDR(48'h03_00_00_00_00_00)
) 
core_inst_1 (
    /*
     * Clock: 156.25 MHz
     * Synchronous reset
     */
    // .clk(clk_156mhz_int),
    // .rst(rst_156mhz_int),
    .clk(sfp1_tx_clk_int),
    .rst(sfp1_tx_rst_int),    
    .packet_size(packet_size),

    .rx_fifo_udp_payload_axis_tdata(rx_udp_1.tdata),
    .rx_fifo_udp_payload_axis_tkeep(rx_udp_1.tkeep),
    .rx_fifo_udp_payload_axis_tvalid(rx_udp_1.tvalid),
    .rx_fifo_udp_payload_axis_tready(rx_udp_1.tready),
    .rx_fifo_udp_payload_axis_tlast(rx_udp_1.tlast),
    .rx_fifo_udp_payload_axis_tuser(rx_udp_1.tuser),    

    .tx_fifo_udp_payload_axis_tdata(tx_udp_1.tdata),
    .tx_fifo_udp_payload_axis_tkeep(tx_udp_1.tkeep),
    .tx_fifo_udp_payload_axis_tvalid(tx_udp_1.tvalid),
    .tx_fifo_udp_payload_axis_tready(tx_udp_1.tready),
    .tx_fifo_udp_payload_axis_tlast(tx_udp_1.tlast),
    .tx_fifo_udp_payload_axis_tuser(tx_udp_1.tuser),
    .tx_fifo_udp_payload_axis_tdest(tx_udp_1.tdest),    

    /*
     * Ethernet: SFP+
     */
    .sfp_tx_clk(sfp1_tx_clk_int),
    .sfp_tx_rst(sfp1_tx_rst_int),
    .sfp_txd(sfp1_txd_int),
    .sfp_txc(sfp1_txc_int),
    .sfp_rx_clk(sfp1_rx_clk_int),
    .sfp_rx_rst(sfp1_rx_rst_int),
    .sfp_rxd(sfp1_rxd_int),
    .sfp_rxc(sfp1_rxc_int)
);


endmodule


`resetall