module fpga (
    // CPU reset button
    input  wire        CPU_RESET_n,
    // buttons
    input  wire [3:0]  BUTTON,
    input  wire [3:0]  SW,
    // LEDs
    output wire [6:0]  HEX0_D,
    output wire        HEX0_DP,
    output wire [6:0]  HEX1_D,
    output wire        HEX1_DP,
    output wire [3:0]  LED,
    output wire [3:0]  LED_BRACKET,
    output wire        LED_RJ45_L,
    output wire        LED_RJ45_R,
    // Temperature control
    //inout wire        TEMP_CLK,
    //inout wire        TEMP_DATA,
    //input wire        TEMP_INT_n,
    //input wire        TEMP_OVERT_n,
    output wire        FAN_CTRL,
    // 50 MHz clock inputs
    input  wire        OSC_50_B3B,
    input  wire        OSC_50_B3D,
    input  wire        OSC_50_B4A,
    input  wire        OSC_50_B4D,
    input  wire        OSC_50_B7A,
    input  wire        OSC_50_B7D,
    input  wire        OSC_50_B8A,
    input  wire        OSC_50_B8D,
    // PCIe interface
    //input  wire        PCIE_PERST_n,
    //input  wire        PCIE_REFCLK_p,
    //input  wire [7:0]  PCIE_RX_p,
    //output wire [7:0]  PCIE_TX_p,
    //input  wire        PCIE_WAKE_n,
    //inout  wire        PCIE_SMBCLK,
    //inout  wire        PCIE_SMBDAT,
    // Si570
    inout  wire        CLOCK_SCL,
    inout  wire        CLOCK_SDA,
    // 10G Ethernet
    input  wire        SFPA_LOS,
    input  wire        SFPA_TXFAULT,
    input  wire        SFPA_MOD0_PRESNT_n,
    inout  wire        SFPA_MOD1_SCL,
    inout  wire        SFPA_MOD2_SDA,
    output wire        SFPA_TXDISABLE,
    output wire [1:0]  SPFA_RATESEL,
    input  wire        SFPA_RX_p,
    output wire        SFPA_TX_p,
    input  wire        SFPB_LOS,
    input  wire        SFPB_TXFAULT,
    input  wire        SFPB_MOD0_PRESNT_n,
    inout  wire        SFPB_MOD1_SCL,
    inout  wire        SFPB_MOD2_SDA,
    output wire        SFPB_TXDISABLE,
    output wire [1:0]  SPFB_RATESEL,
    input  wire        SFPB_RX_p,
    output wire        SFPB_TX_p,
    input  wire        SFPC_LOS,
    input  wire        SFPC_TXFAULT,
    input  wire        SFPC_MOD0_PRESNT_n,
    inout  wire        SFPC_MOD1_SCL,
    inout  wire        SFPC_MOD2_SDA,
    output wire        SFPC_TXDISABLE,
    output wire [1:0]  SPFC_RATESEL,
    input  wire        SFPC_RX_p,
    output wire        SFPC_TX_p,
    input  wire        SFPD_LOS,
    input  wire        SFPD_TXFAULT,
    input  wire        SFPD_MOD0_PRESNT_n,
    inout  wire        SFPD_MOD1_SCL,
    inout  wire        SFPD_MOD2_SDA,
    output wire        SFPD_TXDISABLE,
    output wire [1:0]  SPFD_RATESEL,
    input  wire        SFPD_RX_p,
    output wire        SFPD_TX_p,
    input  wire        SFP_REFCLK_P
);

// Clock and reset

wire clk_50mhz = OSC_50_B3B;
wire rst_50mhz;

sync_reset #(
    .N(4)
)
sync_reset_50mhz_inst (
    .clk(clk_50mhz),
    .rst(~CPU_RESET_n),
    .out(rst_50mhz)
);

wire clk_156mhz;
wire rst_156mhz;

wire phy_pll_locked;

sync_reset #(
    .N(4)
)
sync_reset_156mhz_inst (
    .clk(clk_156mhz),
    .rst(rst_50mhz | ~phy_pll_locked),
    .out(rst_156mhz)
);

// GPIO

wire [3:0] btn_int;
wire [3:0] sw_int;
wire [3:0] led_int;
wire [3:0] led_bkt_int;
wire [6:0] led_hex0_d_int;
wire led_hex0_dp_int;
wire [6:0] led_hex1_d_int;
wire led_hex1_dp_int;

debounce_switch #(
    .WIDTH(8),
    .N(4),
    .RATE(156250)
)
debounce_switch_inst (
    .clk(clk_156mhz),
    .rst(rst_156mhz),
    .in({BUTTON,
        SW}),
    .out({btn_int,
        sw_int})
);

assign LED = ~led_int;
assign LED_BRACKET = ~led_bkt_int;
assign HEX0_D = ~led_hex0_d_int;
assign HEX0_DP = ~led_hex0_dp_int;
assign HEX1_D = ~led_hex1_d_int;
assign HEX1_DP = ~led_hex1_dp_int;

assign FAN_CTRL = 1;

// Si570 oscillator I2C init

wire si570_scl_i;
wire si570_scl_o;
wire si570_scl_t;
wire si570_sda_i;
wire si570_sda_o;
wire si570_sda_t;

assign si570_sda_i = CLOCK_SDA;
assign CLOCK_SDA = si570_sda_t ? 1'bz : si570_sda_o;
assign si570_scl_i = CLOCK_SCL;
assign CLOCK_SCL = si570_scl_t ? 1'bz : si570_scl_o;

wire [6:0] si570_i2c_cmd_address;
wire si570_i2c_cmd_start;
wire si570_i2c_cmd_read;
wire si570_i2c_cmd_write;
wire si570_i2c_cmd_write_multiple;
wire si570_i2c_cmd_stop;
wire si570_i2c_cmd_valid;
wire si570_i2c_cmd_ready;

wire [7:0] si570_i2c_data;
wire si570_i2c_data_valid;
wire si570_i2c_data_ready;
wire si570_i2c_data_last;

si570_i2c_init
si570_i2c_init_inst (
    .clk(clk_50mhz),
    .rst(rst_50mhz),
    .cmd_address(si570_i2c_cmd_address),
    .cmd_start(si570_i2c_cmd_start),
    .cmd_read(si570_i2c_cmd_read),
    .cmd_write(si570_i2c_cmd_write),
    .cmd_write_multiple(si570_i2c_cmd_write_multiple),
    .cmd_stop(si570_i2c_cmd_stop),
    .cmd_valid(si570_i2c_cmd_valid),
    .cmd_ready(si570_i2c_cmd_ready),
    .data_out(si570_i2c_data),
    .data_out_valid(si570_i2c_data_valid),
    .data_out_ready(si570_i2c_data_ready),
    .data_out_last(si570_i2c_data_last),
    .busy(),
    .start(1)
);

i2c_master
si570_i2c_master_inst (
    .clk(clk_50mhz),
    .rst(rst_50mhz),
    .cmd_address(si570_i2c_cmd_address),
    .cmd_start(si570_i2c_cmd_start),
    .cmd_read(si570_i2c_cmd_read),
    .cmd_write(si570_i2c_cmd_write),
    .cmd_write_multiple(si570_i2c_cmd_write_multiple),
    .cmd_stop(si570_i2c_cmd_stop),
    .cmd_valid(si570_i2c_cmd_valid),
    .cmd_ready(si570_i2c_cmd_ready),
    .data_in(si570_i2c_data),
    .data_in_valid(si570_i2c_data_valid),
    .data_in_ready(si570_i2c_data_ready),
    .data_in_last(si570_i2c_data_last),
    .data_out(),
    .data_out_valid(),
    .data_out_ready(1),
    .data_out_last(),
    .scl_i(si570_scl_i),
    .scl_o(si570_scl_o),
    .scl_t(si570_scl_t),
    .sda_i(si570_sda_i),
    .sda_o(si570_sda_o),
    .sda_t(si570_sda_t),
    .busy(),
    .bus_control(),
    .bus_active(),
    .missed_ack(),
    .prescale(312),
    .stop_on_idle(1)
);

// 10G Ethernet PHY

wire [71:0] sfp_a_tx_dc;
wire [71:0] sfp_a_rx_dc;
wire [71:0] sfp_b_tx_dc;
wire [71:0] sfp_b_rx_dc;
wire [71:0] sfp_c_tx_dc;
wire [71:0] sfp_c_rx_dc;
wire [71:0] sfp_d_tx_dc;
wire [71:0] sfp_d_rx_dc;

wire [367:0] phy_reconfig_from_xcvr;
wire [559:0] phy_reconfig_to_xcvr;

assign SFPA_MOD1_SCL = 1'bz;
assign SFPA_MOD2_SDA = 1'bz;
assign SFPA_TXDISABLE = 1'b0;
assign SPFA_RATESEL = 2'b00;

assign SFPB_MOD1_SCL = 1'bz;
assign SFPB_MOD2_SDA = 1'bz;
assign SFPB_TXDISABLE = 1'b0;
assign SPFB_RATESEL = 2'b00;

assign SFPC_MOD1_SCL = 1'bz;
assign SFPC_MOD2_SDA = 1'bz;
assign SFPC_TXDISABLE = 1'b0;
assign SPFC_RATESEL = 2'b00;

assign SFPD_MOD1_SCL = 1'bz;
assign SFPD_MOD2_SDA = 1'bz;
assign SFPD_TXDISABLE = 1'b0;
assign SPFD_RATESEL = 2'b00;

phy
phy_inst (
    .pll_ref_clk(SFP_REFCLK_P),
    .pll_locked(phy_pll_locked),

    .tx_serial_data_0(SFPA_TX_p),
    .rx_serial_data_0(SFPA_RX_p),
    .tx_serial_data_1(SFPB_TX_p),
    .rx_serial_data_1(SFPB_RX_p),
    .tx_serial_data_2(SFPC_TX_p),
    .rx_serial_data_2(SFPC_RX_p),
    .tx_serial_data_3(SFPD_TX_p),
    .rx_serial_data_3(SFPD_RX_p),

    .xgmii_tx_dc_0(sfp_a_tx_dc),
    .xgmii_rx_dc_0(sfp_a_rx_dc),
    .xgmii_tx_dc_1(sfp_b_tx_dc),
    .xgmii_rx_dc_1(sfp_b_rx_dc),
    .xgmii_tx_dc_2(sfp_c_tx_dc),
    .xgmii_rx_dc_2(sfp_c_rx_dc),
    .xgmii_tx_dc_3(sfp_d_tx_dc),
    .xgmii_rx_dc_3(sfp_d_rx_dc),

    .xgmii_rx_clk(clk_156mhz),
    .xgmii_tx_clk(clk_156mhz),

    .tx_ready(~rst_156mhz),
    .rx_ready(),

    .rx_data_ready(),

    .phy_mgmt_clk(clk_50mhz),
    .phy_mgmt_clk_reset(rst_50mhz),
    .phy_mgmt_address(9'd0),
    .phy_mgmt_read(1'b0),
    .phy_mgmt_readdata(),
    .phy_mgmt_waitrequest(),
    .phy_mgmt_write(1'b0),
    .phy_mgmt_writedata(32'd0),

    .reconfig_from_xcvr(phy_reconfig_from_xcvr),
    .reconfig_to_xcvr(phy_reconfig_to_xcvr)
);

phy_reconfig
phy_reconfig_inst (
    .reconfig_busy(),

    .mgmt_clk_clk(clk_50mhz),
    .mgmt_rst_reset(rst_50mhz),

    .reconfig_mgmt_address(7'd0),
    .reconfig_mgmt_read(1'b0),
    .reconfig_mgmt_readdata(),
    .reconfig_mgmt_waitrequest(),
    .reconfig_mgmt_write(1'b0),
    .reconfig_mgmt_writedata(32'd0),

    .reconfig_to_xcvr(phy_reconfig_to_xcvr),
    .reconfig_from_xcvr(phy_reconfig_from_xcvr)
);

// Convert XGMII interfaces

wire [63:0] sfp_a_txd_int;
wire [7:0]  sfp_a_txc_int;
wire [63:0] sfp_a_rxd_int;
wire [7:0]  sfp_a_rxc_int;
wire [63:0] sfp_b_txd_int;
wire [7:0]  sfp_b_txc_int;
wire [63:0] sfp_b_rxd_int;
wire [7:0]  sfp_b_rxc_int;
wire [63:0] sfp_c_txd_int;
wire [7:0]  sfp_c_txc_int;
wire [63:0] sfp_c_rxd_int;
wire [7:0]  sfp_c_rxc_int;
wire [63:0] sfp_d_txd_int;
wire [7:0]  sfp_d_txc_int;
wire [63:0] sfp_d_rxd_int;
wire [7:0]  sfp_d_rxc_int;

xgmii_interleave
xgmii_interleave_inst_a (
    .input_xgmii_d(sfp_a_txd_int),
    .input_xgmii_c(sfp_a_txc_int),
    .output_xgmii_dc(sfp_a_tx_dc)
);

xgmii_deinterleave
xgmii_deinterleave_inst_a (
    .input_xgmii_dc(sfp_a_rx_dc),
    .output_xgmii_d(sfp_a_rxd_int),
    .output_xgmii_c(sfp_a_rxc_int)
);

xgmii_interleave
xgmii_interleave_inst_b (
    .input_xgmii_d(sfp_b_txd_int),
    .input_xgmii_c(sfp_b_txc_int),
    .output_xgmii_dc(sfp_b_tx_dc)
);

xgmii_deinterleave
xgmii_deinterleave_inst_b (
    .input_xgmii_dc(sfp_b_rx_dc),
    .output_xgmii_d(sfp_b_rxd_int),
    .output_xgmii_c(sfp_b_rxc_int)
);

xgmii_interleave
xgmii_interleave_inst_c (
    .input_xgmii_d(sfp_c_txd_int),
    .input_xgmii_c(sfp_c_txc_int),
    .output_xgmii_dc(sfp_c_tx_dc)
);

xgmii_deinterleave
xgmii_deinterleave_inst_c (
    .input_xgmii_dc(sfp_c_rx_dc),
    .output_xgmii_d(sfp_c_rxd_int),
    .output_xgmii_c(sfp_c_rxc_int)
);

xgmii_interleave
xgmii_interleave_inst_d (
    .input_xgmii_d(sfp_d_txd_int),
    .input_xgmii_c(sfp_d_txc_int),
    .output_xgmii_dc(sfp_d_tx_dc)
);

xgmii_deinterleave
xgmii_deinterleave_inst_d (
    .input_xgmii_dc(sfp_d_rx_dc),
    .output_xgmii_d(sfp_d_rxd_int),
    .output_xgmii_c(sfp_d_rxc_int)
);

// Core logic

fpga_core
core_inst (
    /*
     * Clock: 156.25MHz
     * Synchronous reset
     */
    .clk(clk_156mhz),
    .rst(rst_156mhz),
    /*
     * GPIO
     */
    .btn(btn_int),
    .sw(sw_int),
    .led(led_int),
    .led_bkt(led_bkt_int),
    .led_hex0_d(led_hex0_d_int),
    .led_hex0_dp(led_hex0_dp_int),
    .led_hex1_d(led_hex1_d_int),
    .led_hex1_dp(led_hex1_dp_int),
    /*
     * 10G Ethernet
     */
    .sfp_a_txd(sfp_a_txd_int),
    .sfp_a_txc(sfp_a_txc_int),
    .sfp_a_rxd(sfp_a_rxd_int),
    .sfp_a_rxc(sfp_a_rxc_int),
    .sfp_b_txd(sfp_b_txd_int),
    .sfp_b_txc(sfp_b_txc_int),
    .sfp_b_rxd(sfp_b_rxd_int),
    .sfp_b_rxc(sfp_b_rxc_int),
    .sfp_c_txd(sfp_c_txd_int),
    .sfp_c_txc(sfp_c_txc_int),
    .sfp_c_rxd(sfp_c_rxd_int),
    .sfp_c_rxc(sfp_c_rxc_int),
    .sfp_d_txd(sfp_d_txd_int),
    .sfp_d_txc(sfp_d_txc_int),
    .sfp_d_rxd(sfp_d_rxd_int),
    .sfp_d_rxc(sfp_d_rxc_int)
);

endmodule