// SPDX-License-Identifier: CERN-OHL-S-2.0
/*

Copyright (c) 2015-2025 FPGA Ninja, LLC

Authors:
- Alex Forencich

*/

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

/*
 * GMII PHY interface
 */
module taxi_gmii_phy_if #
(
    // simulation (set to avoid vendor primitives)
    parameter logic SIM = 1'b0,
    // vendor ("GENERIC", "XILINX", "ALTERA")
    parameter VENDOR = "XILINX",
    // device family
    parameter FAMILY = "virtex7"
)
(
    input  wire logic        gtx_clk,
    input  wire logic        gtx_rst,

    /*
     * GMII interface to MAC
     */
    output wire logic        mac_gmii_rx_clk,
    output wire logic        mac_gmii_rx_rst,
    output wire logic [7:0]  mac_gmii_rxd,
    output wire logic        mac_gmii_rx_dv,
    output wire logic        mac_gmii_rx_er,
    output wire logic        mac_gmii_tx_clk,
    output wire logic        mac_gmii_tx_rst,
    input  wire logic [7:0]  mac_gmii_txd,
    input  wire logic        mac_gmii_tx_en,
    input  wire logic        mac_gmii_tx_er,

    /*
     * GMII interface to PHY
     */
    input  wire logic        phy_gmii_rx_clk,
    input  wire logic [7:0]  phy_gmii_rxd,
    input  wire logic        phy_gmii_rx_dv,
    input  wire logic        phy_gmii_rx_er,
    input  wire logic        phy_mii_tx_clk,
    output wire logic        phy_gmii_tx_clk,
    output wire logic [7:0]  phy_gmii_txd,
    output wire logic        phy_gmii_tx_en,
    output wire logic        phy_gmii_tx_er,

    /*
     * Status
     */
    output wire logic [1:0]  link_speed
);

// PHY speed detection
logic [2:0] rx_prescale = 3'd0;

always_ff @(posedge mac_gmii_rx_clk) begin
    rx_prescale <= rx_prescale + 3'd1;
end

wire rx_prescale_sync;

taxi_sync_signal #(
    .WIDTH(1),
    .N(2)
)
rx_prescale_sync_inst (
    .clk(gtx_clk),
    .in(rx_prescale[2]),
    .out(rx_prescale_sync)
);

logic [6:0] rx_speed_count_1 = '0;
logic [1:0] rx_speed_count_2 = '0;
logic rx_prescale_sync_last_reg = 1'b0;

logic [1:0] link_speed_reg = '0;

assign link_speed = link_speed_reg;

always_ff @(posedge gtx_clk) begin
    rx_prescale_sync_last_reg <= rx_prescale_sync;
    rx_speed_count_1 <= rx_speed_count_1 + 1;

    if (rx_prescale_sync ^ rx_prescale_sync_last_reg) begin
        rx_speed_count_2 <= rx_speed_count_2 + 1;
    end

    if (&rx_speed_count_1) begin
        // reference count overflow - 10M
        rx_speed_count_1 <= '0;
        rx_speed_count_2 <= '0;
        link_speed_reg <= 2'b00;
    end

    if (&rx_speed_count_2) begin
        // prescaled count overflow - 100M or 1000M
        rx_speed_count_1 <= '0;
        rx_speed_count_2 <= '0;
        if (rx_speed_count_1[6:5] != 0) begin
            // large reference count - 100M
            link_speed_reg <= 2'b01;
        end else begin
            // small reference count - 1000M
            link_speed_reg <= 2'b10;
        end
    end

    if (gtx_rst) begin
        rx_speed_count_1 <= '0;
        rx_speed_count_2 <= '0;
        link_speed_reg <= 2'b10;
    end
end

taxi_ssio_sdr_in #(
    .SIM(SIM),
    .VENDOR(VENDOR),
    .FAMILY(FAMILY),
    .WIDTH(10)
)
rx_ssio_sdr_inst (
    .input_clk(phy_gmii_rx_clk),
    .input_d({phy_gmii_rxd, phy_gmii_rx_dv, phy_gmii_rx_er}),
    .output_clk(mac_gmii_rx_clk),
    .output_q({mac_gmii_rxd, mac_gmii_rx_dv, mac_gmii_rx_er})
);

taxi_ssio_sdr_out #(
    .SIM(SIM),
    .VENDOR(VENDOR),
    .FAMILY(FAMILY),
    .WIDTH(10)
)
tx_ssio_sdr_inst (
    .clk(mac_gmii_tx_clk),
    .input_d({mac_gmii_txd, mac_gmii_tx_en, mac_gmii_tx_er}),
    .output_clk(phy_gmii_tx_clk),
    .output_q({phy_gmii_txd, phy_gmii_tx_en, phy_gmii_tx_er})
);

if (!SIM && VENDOR == "XILINX") begin
    // Xilinx/AMD device support

    BUFGMUX
    gmii_bufgmux_inst (
        .I0(phy_mii_tx_clk),
        .I1(gtx_clk),
        .S(link_speed_reg[1]),
        .O(mac_gmii_tx_clk)
    );

end else begin
    // generic/simulation implementation (no vendor primitives)

    assign mac_gmii_tx_clk = link_speed_reg[1] ? gtx_clk : phy_mii_tx_clk;

end

// reset sync
taxi_sync_reset #(
    .N(4)
)
tx_reset_sync_inst (
    .clk(mac_gmii_tx_clk),
    .rst(gtx_rst),
    .out(mac_gmii_tx_rst)
);

taxi_sync_reset #(
    .N(4)
)
rx_reset_sync_inst (
    .clk(mac_gmii_rx_clk),
    .rst(gtx_rst),
    .out(mac_gmii_rx_rst)
);

endmodule

`resetall
