axis_xgmii_tx_64_code

/*

  

Copyright (c) 2015-2017 Alex Forencich

  

Permission is hereby granted, free of charge, to any person obtaining a copy

of this software and associated documentation files (the "Software"), to deal

in the Software without restriction, including without limitation the rights

to use, copy, modify, merge, publish, distribute, sublicense, and/or sell

copies of the Software, and to permit persons to whom the Software is

furnished to do so, subject to the following conditions:

  

The above copyright notice and this permission notice shall be included in

all copies or substantial portions of the Software.

  

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY

FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER

LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,

OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN

THE SOFTWARE.

  

*/

  

// Language: Verilog 2001

  

`resetall

`timescale 1ns / 1ps

`default_nettype none

  

/*

 * AXI4-Stream XGMII frame transmitter (AXI in, XGMII out)

 */

  //! Modulo que convierte interfaz AXI a XGMII

 module axis_xgmii_tx_64 #

 (

     parameter DATA_WIDTH = 64,                                        //! Parametro que define el tamaño de los datos

     parameter KEEP_WIDTH = (DATA_WIDTH/8),                            //! Cantidad de bytes de datos que deben ser válidos

     parameter CTRL_WIDTH = (DATA_WIDTH/8),                            //! Cantidad de bits de control

     parameter ENABLE_PADDING = 1,                                     //! Flag para habilitar el relleno de tramas

     parameter ENABLE_DIC = 1,                                         //! Flag para habilitar DIC (Data Integrity Check)

     parameter MIN_FRAME_LENGTH = 64,                                  //! Longitud mínima de la trama

     parameter PTP_TS_ENABLE = 0,                                      //! Flag para habilitar protocolo PTP

     parameter PTP_TS_FMT_TOD = 1,                                     //! Flag para cambiar el formato de la marca de tiempo según "Time of day"

     parameter PTP_TS_WIDTH = PTP_TS_FMT_TOD ? 96 : 64,                //! Parametro del ancho de la marca del tiempo

     parameter PTP_TS_CTRL_IN_TUSER = 0,                               //! Flag para habilitar control PTP en tuser

     parameter PTP_TAG_ENABLE = PTP_TS_ENABLE,                         //! Flag para habilitar etiqueta PTP

     parameter PTP_TAG_WIDTH = 16,                                     //! Ancho de la etiqueta PTP

     parameter USER_WIDTH = (PTP_TS_ENABLE ?                           //! Parametro que define el ancho de los datos de usuario

                            (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) +

                            (PTP_TS_CTRL_IN_TUSER ? 1 : 0) : 0) + 1

 )

 (

     input  wire                      clk,                             //! Señal de clock

     input  wire                      rst,                             //! Señal de reset

     /*

      * AXI input

      */

     input  wire [DATA_WIDTH-1:0]     s_axis_tdata,                    //! Entrada de datos de la interfaz AXI

     input  wire [KEEP_WIDTH-1:0]     s_axis_tkeep,                    //! Entrada de bits válidos de la interfaz AXI

     input  wire                      s_axis_tvalid,                   //! Señal de datos válidos de la interfaz AXI

     output wire                      s_axis_tready,                   //! Señal de que la interfaz AXI está lista para recibir datos

     input  wire                      s_axis_tlast,                    //! Señal de fin de trama de la interfaz AXI

     input  wire [USER_WIDTH-1:0]     s_axis_tuser,                    //! Entrada de datos de usuario de la interfaz AXI

     /*

      * XGMII output

      */

     output wire [DATA_WIDTH-1:0]     xgmii_txd,                       //! Salida de datos de la interfaz XGMII

     output wire [CTRL_WIDTH-1:0]     xgmii_txc,                       //! Salida de control de la interfaz XGMII

     /*

      * PTP

      */

     input  wire [PTP_TS_WIDTH-1:0]   ptp_ts,                          //! Entrada de marca de tiempo PTP

     output wire [PTP_TS_WIDTH-1:0]   m_axis_ptp_ts,                   //! Salida de marca de tiempo PTP

     output wire [PTP_TAG_WIDTH-1:0]  m_axis_ptp_ts_tag,               //! Salida de etiqueta de marca de tiempo PTP

     output wire                      m_axis_ptp_ts_valid,             //! Señal de validez de marca de tiempo PTP

     /*

      * Configuration

      */

     input  wire [7:0]                cfg_ifg,                         //! Señal de configuración de espacio interframe

     input  wire                      cfg_tx_enable,                   //! Señal de configuración para habilitar transmisión

     /*

      * Status

      */

     output wire [1:0]                start_packet,                    //! Estado indicando inicio de paquete

     output wire                      error_underflow                  //! Señal de error por desbordamiento

 );

 parameter EMPTY_WIDTH = $clog2(KEEP_WIDTH);                           //! Parametro del ancho de vacío

 parameter MIN_LEN_WIDTH = $clog2(MIN_FRAME_LENGTH-4-CTRL_WIDTH+1);    //! Parametro del ancho mínimo de la longitud

  

// bus width assertions

initial begin

    if (DATA_WIDTH != 64) begin

        $error("Error: Interface width must be 64");

        $finish;

    end

  

    if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin

        $error("Error: Interface requires byte (8-bit) granularity");

        $finish;

    end

end

  

localparam [7:0]

    ETH_PRE = 8'h55,                  //! Preambulo de Ethernet

    ETH_SFD = 8'hD5;                  //! Delimitador de inicio de trama de Ethernet

  

localparam [7:0]

    XGMII_IDLE = 8'h07,               //! Código de control de XGMII para estado inactivo

    XGMII_START = 8'hfb,              //! Código de control de XGMII para inicio de paquete

    XGMII_TERM = 8'hfd,               //! Código de control de XGMII para terminación de paquete

    XGMII_ERROR = 8'hfe;              //! Código de control de XGMII para error

  

localparam [2:0]

    STATE_IDLE = 3'd0,                //! Estado IDLE

    STATE_PAYLOAD = 3'd1,             //! Estado PAYLOAD

    STATE_PAD = 3'd2,                 //! Estado PAD

    STATE_FCS_1 = 3'd3,               //! Estado FCS_1

    STATE_FCS_2 = 3'd4,               //! Estado FCS_2

    STATE_ERR = 3'd5,                 //! Estado ERR

    STATE_IFG = 3'd6;                 //! Estado IFG

  

reg [2:0] state_reg = STATE_IDLE, state_next;                       //! Registro y próximo estado del FSM

  

// Señales de control del datapath

reg reset_crc;                                                  //! Señal para resetear el CRC

reg update_crc;                                                 //! Señal para actualizar el CRC

  

reg swap_lanes_reg = 1'b0, swap_lanes_next;                     //! Registro y próximo valor de swap de lanes

reg [31:0] swap_txd = 32'd0;                                    //! Datos para swap de lanes

reg [3:0] swap_txc = 4'd0;                                      //! Datos de control para swap de lanes

  

reg [DATA_WIDTH-1:0] s_axis_tdata_masked;                       //! Datos de la entrada AXI con máscara aplicada

  

reg [DATA_WIDTH-1:0] s_tdata_reg = 0, s_tdata_next;             //! Registro y próximo valor de datos de entrada AXI

reg [EMPTY_WIDTH-1:0] s_empty_reg = 0, s_empty_next;            //! Registro y próximo valor de datos vacíos

  

reg [DATA_WIDTH-1:0] fcs_output_txd_0;                          //! Datos de salida del primer FCS

reg [DATA_WIDTH-1:0] fcs_output_txd_1;                          //! Datos de salida del segundo FCS

reg [CTRL_WIDTH-1:0] fcs_output_txc_0;                          //! Control de salida del primer FCS

reg [CTRL_WIDTH-1:0] fcs_output_txc_1;                          //! Control de salida del segundo FCS

  

reg [7:0] ifg_offset;                                           //! Desplazamiento para el intervalo entre tramas

  

reg frame_start_reg = 1'b0, frame_start_next;                               //! Registro y próximo valor de inicio de trama

reg frame_reg = 1'b0, frame_next;                                           //! Registro y próximo valor de trama

reg frame_error_reg = 1'b0, frame_error_next;                               //! Registro y próximo valor de error de trama

reg [MIN_LEN_WIDTH-1:0] frame_min_count_reg = 0, frame_min_count_next;      //! Registro y próximo valor de contador de longitud mínima de trama

  

reg [7:0] ifg_count_reg = 8'd0, ifg_count_next;                             //! Registro y próximo valor de contador de intervalo entre tramas

reg [1:0] deficit_idle_count_reg = 2'd0, deficit_idle_count_next;           //! Registro y próximo valor de contador de déficit de inactividad

  

reg s_axis_tready_reg = 1'b0, s_axis_tready_next;                           //! Registro y próximo valor de disponibilidad de AXI

  

reg [PTP_TS_WIDTH-1:0] m_axis_ptp_ts_reg = 0;                               //! Registro de marca de tiempo PTP

reg [PTP_TS_WIDTH-1:0] m_axis_ptp_ts_adj_reg = 0;                           //! Registro de ajuste de marca de tiempo PTP

reg [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag_reg = 0;                          //! Registro de etiqueta de marca de tiempo PTP

reg m_axis_ptp_ts_valid_reg = 1'b0;                                         //! Registro de validez de marca de tiempo PTP

reg m_axis_ptp_ts_valid_int_reg = 1'b0;                                     //! Registro interno de validez de marca de tiempo PTP

reg m_axis_ptp_ts_borrow_reg = 1'b0;                                        //! Registro de ajuste de marca de tiempo PTP

  

reg [31:0] crc_state_reg[7:0];                                              //! Registro de estado de CRC

wire [31:0] crc_state_next[7:0];                                            //! Próximo valor del estado de CRC

  

reg [4+16-1:0] last_ts_reg = 0;                                             //! Registro del último timestamp

reg [4+16-1:0] ts_inc_reg = 0;                                              //! Registro de incremento del timestamp

  

reg [DATA_WIDTH-1:0] xgmii_txd_reg = {CTRL_WIDTH{XGMII_IDLE}}, xgmii_txd_next;      //! Registro y próximo valor de datos XGMII

reg [CTRL_WIDTH-1:0] xgmii_txc_reg = {CTRL_WIDTH{1'b1}}, xgmii_txc_next;            //! Registro y próximo valor de control XGMII

  

reg start_packet_reg = 2'b00;                                               //! Registro de inicio de paquete

reg error_underflow_reg = 1'b0, error_underflow_next;                       //! Registro y próximo valor de error por desbordamiento

  
  

assign s_axis_tready = s_axis_tready_reg;

  

assign xgmii_txd = xgmii_txd_reg;

assign xgmii_txc = xgmii_txc_reg;

  

assign m_axis_ptp_ts = PTP_TS_ENABLE ? ((!PTP_TS_FMT_TOD || m_axis_ptp_ts_borrow_reg) ? m_axis_ptp_ts_reg : m_axis_ptp_ts_adj_reg) : 0;

assign m_axis_ptp_ts_tag = PTP_TAG_ENABLE ? m_axis_ptp_ts_tag_reg : 0;

assign m_axis_ptp_ts_valid = PTP_TS_ENABLE || PTP_TAG_ENABLE ? m_axis_ptp_ts_valid_reg : 1'b0;

  

assign start_packet = start_packet_reg;

assign error_underflow = error_underflow_reg;

  

generate

    genvar n;

  

    for (n = 0; n < 8; n = n + 1) begin : crc

        //! Instancia lfsr para generar CRC

        lfsr #(

            .LFSR_WIDTH(32),

            .LFSR_POLY(32'h4c11db7),

            .LFSR_CONFIG("GALOIS"),

            .LFSR_FEED_FORWARD(0),

            .REVERSE(1),

            .DATA_WIDTH(8*(n+1)),

            .STYLE("AUTO")

        )

        eth_crc (

            .data_in(s_tdata_reg[0 +: 8*(n+1)]),

            .state_in(crc_state_reg[7]),

            .data_out(),

            .state_out(crc_state_next[n])

        );

    end

  

endgenerate

  

//! Convierte un byte de control k de 8 bits a un valor de 3 bits que representa el número de bits vacíos (no válidos) en el byte

function [2:0] keep2empty;

    input [7:0] k;

    casez (k)

        8'bzzzzzzz0: keep2empty = 3'd7;

        8'bzzzzzz01: keep2empty = 3'd7;

        8'bzzzzz011: keep2empty = 3'd6;

        8'bzzzz0111: keep2empty = 3'd5;

        8'bzzz01111: keep2empty = 3'd4;

        8'bzz011111: keep2empty = 3'd3;

        8'bz0111111: keep2empty = 3'd2;

        8'b01111111: keep2empty = 3'd1;

        8'b11111111: keep2empty = 3'd0;

    endcase

endfunction

  

integer j;

  

//! Aplica mascara a los datos de entrada

always @* begin

    for (j = 0; j < 8; j = j + 1) begin

        s_axis_tdata_masked[j*8 +: 8] = s_axis_tkeep[j] ? s_axis_tdata[j*8 +: 8] : 8'd0;

    end

end

  

// FCS cycle calculation

//! Calculo del ciclo FCS

always @* begin

    casez (s_empty_reg)

        3'd7: begin

            fcs_output_txd_0 = {{2{XGMII_IDLE}}, XGMII_TERM, ~crc_state_next[0][31:0], s_tdata_reg[7:0]}; // configura los datos de salida con el símbolo de finalizacion y el CRC invertido

            fcs_output_txd_1 = {8{XGMII_IDLE}}; // rellena el resto con símbolos IDLE

            fcs_output_txc_0 = 8'b11100000; // configura los caracteres de control para indicar terminacion e IDLE

            fcs_output_txc_1 = 8'b11111111; // configura los caracteres de control para indicar terminacion e IDLE

            ifg_offset = 8'd3; // establece el offset de IFG

        end

        3'd6: begin

            fcs_output_txd_0 = {XGMII_IDLE, XGMII_TERM, ~crc_state_next[1][31:0], s_tdata_reg[15:0]}; // configura los datos de salida con el símbolo de terminacion y el CRC invertido

            fcs_output_txd_1 = {8{XGMII_IDLE}}; // rellena el resto con símbolos IDLE

            fcs_output_txc_0 = 8'b11000000; // configura los caracteres de control para indicar terminacion e IDLE

            fcs_output_txc_1 = 8'b11111111; // configura los caracteres de control para indicar terminacion e IDLE

            ifg_offset = 8'd2; // establece el offset de IFG

        end

        3'd5: begin

            fcs_output_txd_0 = {XGMII_TERM, ~crc_state_next[2][31:0], s_tdata_reg[23:0]}; // configura los datos de salida con el símbolo de terminacion y el CRC invertido

            fcs_output_txd_1 = {8{XGMII_IDLE}}; // rellena el resto con símbolos IDLE

            fcs_output_txc_0 = 8'b10000000; // configura los caracteres de control para indicar terminacion e IDLE

            fcs_output_txc_1 = 8'b11111111; // configura los caracteres de control para indicar terminacion e IDLE

            ifg_offset = 8'd1; // establece el offset de IFG

        end

        3'd4: begin

            fcs_output_txd_0 = {~crc_state_next[3][31:0], s_tdata_reg[31:0]}; // configura los datos de salida con el CRC invertido

            fcs_output_txd_1 = {{7{XGMII_IDLE}}, XGMII_TERM}; // rellena el resto con símbolos IDLE y termina con el símbolo de finalizacion

            fcs_output_txc_0 = 8'b00000000; // configura los caracteres de control para indicar terminacion e IDLE

            fcs_output_txc_1 = 8'b11111111; // configura los caracteres de control para indicar terminacion e IDLE

            ifg_offset = 8'd8; // establece el offset de IFG

        end

        3'd3: begin

            fcs_output_txd_0 = {~crc_state_next[4][23:0], s_tdata_reg[39:0]}; // configura los datos de salida con el CRC invertido

            fcs_output_txd_1 = {{6{XGMII_IDLE}}, XGMII_TERM, ~crc_state_reg[4][31:24]}; // rellena el resto con símbolos IDLE y termina con el símbolo de finalizacion

            fcs_output_txc_0 = 8'b00000000; // configura los caracteres de control para indicar terminacion e IDLE

            fcs_output_txc_1 = 8'b11111110; // configura los caracteres de control para indicar terminacion e IDLE

            ifg_offset = 8'd7; // establece el offset de IFG

        end

        3'd2: begin

            fcs_output_txd_0 = {~crc_state_next[5][15:0], s_tdata_reg[47:0]}; // configura los datos de salida con el CRC invertido

            fcs_output_txd_1 = {{5{XGMII_IDLE}}, XGMII_TERM, ~crc_state_reg[5][31:16]}; // rellena el resto con símbolos IDLE y termina con el símbolo de finalizacion

            fcs_output_txc_0 = 8'b00000000; // configura los caracteres de control para indicar terminacion e IDLE

            fcs_output_txc_1 = 8'b11111100; // configura los caracteres de control para indicar terminacion e IDLE

            ifg_offset = 8'd6; // establece el offset de IFG

        end

        3'd1: begin

            fcs_output_txd_0 = {~crc_state_next[6][7:0], s_tdata_reg[55:0]}; // configura los datos de salida con el CRC invertido

            fcs_output_txd_1 = {{4{XGMII_IDLE}}, XGMII_TERM, ~crc_state_reg[6][31:8]}; // rellena el resto con símbolos IDLE y termina con el símbolo de finalizacion

            fcs_output_txc_0 = 8'b00000000; // configura los caracteres de control para indicar terminacion e IDLE

            fcs_output_txc_1 = 8'b11111000; // configura los caracteres de control para indicar terminacion e IDLE

            ifg_offset = 8'd5; // establece el offset de IFG

        end

        3'd0: begin

            fcs_output_txd_0 = s_tdata_reg; // copia los datos recibidos directamente

            fcs_output_txd_1 = {{3{XGMII_IDLE}}, XGMII_TERM, ~crc_state_reg[7][31:0]}; // rellena el resto con símbolos IDLE y termina con el símbolo de finalizacion y el CRC invertido

            fcs_output_txc_0 = 8'b00000000; // configura los caracteres de control para indicar terminacion e IDLE

            fcs_output_txc_1 = 8'b11110000; // configura los caracteres de control para indicar terminacion e IDLE

            ifg_offset = 8'd4; // establece el offset de IFG

        end

    endcase

end

  

//! Define una combinación de asignaciones y lógica de control que determina el comportamiento de un sistema de transmisión de datos

always @* begin

    state_next = STATE_IDLE;

  

    reset_crc = 1'b0;

    update_crc = 1'b0;

  

    swap_lanes_next = swap_lanes_reg;

  

    frame_start_next = 1'b0;

    frame_next = frame_reg;

    frame_error_next = frame_error_reg;

    frame_min_count_next = frame_min_count_reg;

  

    ifg_count_next = ifg_count_reg;

    deficit_idle_count_next = deficit_idle_count_reg;

  

    s_axis_tready_next = 1'b0;

  

    s_tdata_next = s_tdata_reg;

    s_empty_next = s_empty_reg;

  

    // XGMII idle

    xgmii_txd_next = {CTRL_WIDTH{XGMII_IDLE}};

    xgmii_txc_next = {CTRL_WIDTH{1'b1}};

  

    error_underflow_next = 1'b0;

  

    // si los datos son validos y está listo

    if (s_axis_tvalid && s_axis_tready) begin

        frame_next = !s_axis_tlast; // establece el frame siguiente como el opuesto de tlast

    end

  

    case (state_reg)

        // Caso estado idle

        STATE_IDLE: begin

            frame_error_next = 1'b0; // reinicia la señal de error de trama

            frame_min_count_next = MIN_FRAME_LENGTH-4-CTRL_WIDTH; // establece el contador de longitud mínima de la trama

            reset_crc = 1'b1; // resetea el CRC

            s_axis_tready_next = cfg_tx_enable; // prepara la señal de "ready" si la transmisión está habilitada

            // configura los datos y control de XGMII como IDLE

            xgmii_txd_next = {CTRL_WIDTH{XGMII_IDLE}};

            xgmii_txc_next = {CTRL_WIDTH{1'b1}};

            s_tdata_next = s_axis_tdata_masked; // copia los datos de entrada para el next

            s_empty_next = keep2empty(s_axis_tkeep); // calcula cuántos bytes son vacíos

            if (s_axis_tvalid && s_axis_tready) begin

                // si los datos de entrada son válidos y están listos

                xgmii_txd_next = {ETH_SFD, {6{ETH_PRE}}, XGMII_START}; // configura los datos de salida con el preámbulo y el SFD

                xgmii_txc_next = 8'b00000001; // configura el control de XGMII para indicar el inicio

                frame_start_next = 1'b1; // marca el inicio de la trama

                s_axis_tready_next = 1'b1; // indica que está listo para recibir más datos

                state_next = STATE_PAYLOAD; // pasa al estado PAYLOAD

            end else begin

                swap_lanes_next = 1'b0; // reinicia la señal de intercambio de carriles

                ifg_count_next = 8'd0; // reinicia el contador de IFG

                deficit_idle_count_next = 2'd0; // reinicia el contador de IDLE deficitario

                state_next = STATE_IDLE; // permanece en el estado IDLE

            end

        end

        // Caso estado PAYLOAD

        STATE_PAYLOAD: begin

            // transfer payload

            update_crc = 1'b1; // actualiza el CRC

            s_axis_tready_next = 1'b1; // indica que está listo para recibir más datos

            if (frame_min_count_reg > CTRL_WIDTH) begin

                frame_min_count_next = frame_min_count_reg - CTRL_WIDTH; // disminuye el contador de longitud mínima de la trama

            end else begin

                frame_min_count_next = 0; // resetea el contador si es menor o igual al ancho de control

            end

            xgmii_txd_next = s_tdata_reg; // pasa los datos del PAYLOAD a la salida

            xgmii_txc_next = {CTRL_WIDTH{1'b0}}; // establece el control a 0 (válido)

            s_tdata_next = s_axis_tdata_masked; // copia los datos de entrada a la siguiente etapa

            s_empty_next = keep2empty(s_axis_tkeep); // calcula cuántos bytes son vacíos

            if (!s_axis_tvalid || s_axis_tlast) begin

                // si los datos de entrada no son válidos o es el último dato

                s_axis_tready_next = frame_next; // descarta la trama

                frame_error_next = !s_axis_tvalid || s_axis_tuser[0]; // marca error de trama si los datos no son válidos o hay error en el usuario

                error_underflow_next = !s_axis_tvalid; // marca error si los datos no son válidos

                if (ENABLE_PADDING && frame_min_count_reg) begin

                    if (frame_min_count_reg > CTRL_WIDTH) begin

                        s_empty_next = 0; // no hay bytes vacíos

                        state_next = STATE_PAD; // pasa al estado de relleno

                    end else begin

                        if (keep2empty(s_axis_tkeep) > CTRL_WIDTH-frame_min_count_reg) begin

                            s_empty_next = CTRL_WIDTH-frame_min_count_reg; // ajusta los bytes vacíos

                        end

                        if (frame_error_next) begin

                            state_next = STATE_ERR; // pasa al estado de error

                        end else begin

                            state_next = STATE_FCS_1; // pasa al estado de cálculo de FCS

                        end

                    end

                end else begin

                    if (frame_error_next) begin

                        state_next = STATE_ERR; // pasa al estado de error

                    end else begin

                        state_next = STATE_FCS_1; // pasa al estado de cálculo de FCS

                    end

                end

            end else begin

                state_next = STATE_PAYLOAD; // permanece en el estado de PAYLOAD

            end

        end

        // Caso estado de relleno

        STATE_PAD: begin

            // pad frame to MIN_FRAME_LENGTH

            s_axis_tready_next = frame_next; // descarta la trama

            xgmii_txd_next = s_tdata_reg; // pasa los datos de entrada a la salida XGMII

            xgmii_txc_next = {CTRL_WIDTH{1'b0}}; // establece el control a 0 (válido)

            s_tdata_next = 64'd0; // siguiente dato es 0

            s_empty_next = 0; // no hay bytes vacíos

            update_crc = 1'b1; // actualiza el CRC

            if (frame_min_count_reg > CTRL_WIDTH) begin

                frame_min_count_next = frame_min_count_reg - CTRL_WIDTH; // disminuye el contador de longitud mínima de la trama

                state_next = STATE_PAD; // permanece en el estado de relleno

            end else begin

                frame_min_count_next = 0; // resetea el contador

                s_empty_next = CTRL_WIDTH-frame_min_count_reg; // ajusta los bytes vacíos

                if (frame_error_reg) begin

                    state_next = STATE_ERR; // pasa al estado de error

                end else begin

                    state_next = STATE_FCS_1; // pasa al estado de cálculo de FCS

                end

            end

        end  

        // Caso estado de calculo de FCS

        STATE_FCS_1: begin

            // last cycle

            s_axis_tready_next = frame_next; // descarta la trama

            xgmii_txd_next = fcs_output_txd_0; // envía la primera parte del FCS

            xgmii_txc_next = fcs_output_txc_0; // establece el control para la primera parte del FCS

            update_crc = 1'b1; // actualiza el CRC

            // calcula el intervalo de fragmento de relleno (IFG)

            ifg_count_next = (cfg_ifg > 8'd12 ? cfg_ifg : 8'd12) - ifg_offset + (swap_lanes_reg ? 8'd4 : 8'd0) + deficit_idle_count_reg;

            if (s_empty_reg <= 4) begin

                state_next = STATE_FCS_2; // si hay 4 bytes o menos vacíos, pasa al estado FCS_2

            end else begin

                state_next = STATE_IFG; // si hay más de 4 bytes vacíos, pasa al estado IFG

            end

        end

        // Caso calculo de FCS_2        

        STATE_FCS_2: begin

            // last cycle

            s_axis_tready_next = frame_next; // descarta la trama

            xgmii_txd_next = fcs_output_txd_1; // envía la segunda parte del FCS

            xgmii_txc_next = fcs_output_txc_1; // establece el control para la segunda parte del FCS

            if (ENABLE_DIC) begin

                if (ifg_count_next > 8'd7) begin

                    state_next = STATE_IFG; // si el IFG es mayor a 7, pasa al estado IFG

                end else begin

                    if (ifg_count_next >= 8'd4) begin

                        deficit_idle_count_next = ifg_count_next - 8'd4; // ajusta el conteo de deficit idle

                        swap_lanes_next = 1'b1; // intercambia los carriles

                    end else begin

                        deficit_idle_count_next = ifg_count_next; // ajusta el conteo de deficit idle

                        ifg_count_next = 8'd0; // resetea el conteo de IFG

                        swap_lanes_next = 1'b0; // no intercambia los carriles

                    end

                    s_axis_tready_next = cfg_tx_enable; // habilita la transmisión si está configurado

                    state_next = STATE_IDLE; // vuelve al estado IDLE

                end

            end else begin

                if (ifg_count_next > 8'd4) begin

                    state_next = STATE_IFG; // si el IFG es mayor a 4, pasa al estado IFG

                end else begin

                    s_axis_tready_next = cfg_tx_enable; // habilita la transmisión si está configurado

                    swap_lanes_next = ifg_count_next != 0; // intercambia los carriles si el IFG no es 0

                    state_next = STATE_IDLE; // vuelve al estado IDLE

                end

            end

        end

        // Caso estado de error        

        STATE_ERR: begin

            // terminate packet with error

            s_axis_tready_next = frame_next; // descarta la trama

            // XGMII error

            xgmii_txd_next = {XGMII_TERM, {7{XGMII_ERROR}}}; // envía terminación y error en XGMII

            xgmii_txc_next = {CTRL_WIDTH{1'b1}}; // establece el control para el error

            ifg_count_next = 8'd12; // establece el intervalo de IFG a 12

            stat_next = STATE_IFG; // pasa al estado IFG

        end

        // Caso estado IFG

        STATE_IFG: begin

            // send IFG

            s_axis_tready_next = frame_next; // descarta la trama

            // XGMII idle

            xgmii_txd_next = {CTRL_WIDTH{XGMII_IDLE}}; // envía XGMII idle

            xgmii_txc_next = {CTRL_WIDTH{1'b1}}; // establece el control para idle

            if (ifg_count_reg > 8'd8) begin

                ifg_count_next = ifg_count_reg - 8'd8; // resta 8 del contador de IFG

            end else begin

                ifg_count_next = 8'd0; // establece el contador de IFG a 0

            end

            if (ENABLE_DIC) begin

                if (ifg_count_next > 8'd7 || frame_reg) begin

                    state_next = STATE_IFG; // permanece en el estado IFG si el contador es mayor a 7 o hay trama

                end else begin

                    if (ifg_count_next >= 8'd4) begin

                        deficit_idle_count_next = ifg_count_next - 8'd4; // ajusta el contador de deficit idle

                        swap_lanes_next = 1'b1; // habilita el intercambio de lanes

                    end else begin

                        deficit_idle_count_next = ifg_count_next; // actualiza el contador de deficit idle

                        ifg_count_next = 8'd0; // establece el contador de IFG a 0

                        swap_lanes_next = 1'b0; // deshabilita el intercambio de lanes

                    end

                    s_axis_tready_next = cfg_tx_enable; // habilita la transmisión

                    state_next = STATE_IDLE; // pasa al estado IDLE

                end

            end else begin

                if (ifg_count_next > 8'd4 || frame_reg) begin

                    state_next = STATE_IFG; // permanece en el estado IFG si el contador es mayor a 4 o hay trama

                end else begin

                    s_axis_tready_next = cfg_tx_enable; // habilita la transmisión

                    swap_lanes_next = ifg_count_next != 0; // habilita el intercambio de lanes si el contador no es 0

                    state_next = STATE_IDLE; // pasa al estado IDLE

                end

            end

        end        

    endcase

end

  

//! Actualiza registros de control y datos, gestiona la marca de tiempo PTP y el estado del CRC, y controla la transmisión de datos a través del medio físico

always @(posedge clk) begin

    state_reg <= state_next;

  

    swap_lanes_reg <= swap_lanes_next;

  

    frame_start_reg <= frame_start_next;

    frame_reg <= frame_next;

    frame_error_reg <= frame_error_next;

    frame_min_count_reg <= frame_min_count_next;

  

    ifg_count_reg <= ifg_count_next;

    deficit_idle_count_reg <= deficit_idle_count_next;

  

    s_tdata_reg <= s_tdata_next;

    s_empty_reg <= s_empty_next;

  

    s_axis_tready_reg <= s_axis_tready_next;

  

    m_axis_ptp_ts_valid_reg <= 1'b0;

    m_axis_ptp_ts_valid_int_reg <= 1'b0;

  

    start_packet_reg <= 2'b00;

    error_underflow_reg <= error_underflow_next;

  

    if (PTP_TS_ENABLE && PTP_TS_FMT_TOD) begin

        m_axis_ptp_ts_valid_reg <= m_axis_ptp_ts_valid_int_reg; // actualiza la validez de la marca de tiempo PTP

        m_axis_ptp_ts_adj_reg[15:0] <= m_axis_ptp_ts_reg[15:0]; // copia los 16 bits menos significativos de la marca de tiempo PTP

        {m_axis_ptp_ts_borrow_reg, m_axis_ptp_ts_adj_reg[45:16]} <= $signed({1'b0, m_axis_ptp_ts_reg[45:16]}) - $signed(31'd1000000000); // ajusta la marca de tiempo restando 1,000,000,000

        m_axis_ptp_ts_adj_reg[47:46] <= 0; // establece los bits 47 y 46 a 0

        m_axis_ptp_ts_adj_reg[95:48] <= m_axis_ptp_ts_reg[95:48] + 1; // incrementa los bits 95 a 48 en 1

    end    

  

    if (frame_start_reg) begin

        if (swap_lanes_reg) begin

            if (PTP_TS_ENABLE) begin

                if (PTP_TS_FMT_TOD) begin

                    m_axis_ptp_ts_reg[45:0] <= ptp_ts[45:0] + (ts_inc_reg >> 1); // ajusta la marca de tiempo sumando la mitad del incremento

                    m_axis_ptp_ts_reg[95:48] <= ptp_ts[95:48]; // copia los bits 95 a 48 de la marca de tiempo

                end else begin

                    m_axis_ptp_ts_reg <= ptp_ts + (ts_inc_reg >> 1); // ajusta la marca de tiempo completa sumando la mitad del incremento

                end

            end

            start_packet_reg <= 2'b10; // actualiza el registro de inicio de paquete

        end else begin

            if (PTP_TS_ENABLE) begin

                m_axis_ptp_ts_reg <= ptp_ts; // copia la marca de tiempo

            end

            start_packet_reg <= 2'b01; // actualiza el registro de inicio de paquete

        end

        if (PTP_TS_ENABLE) begin

            if (PTP_TS_CTRL_IN_TUSER) begin

                m_axis_ptp_ts_tag_reg <= s_axis_tuser >> 2; // extrae la etiqueta de marca de tiempo del bus de usuario

                if (PTP_TS_FMT_TOD) begin

                    m_axis_ptp_ts_valid_int_reg <= s_axis_tuser[1]; // actualiza la validez de la marca de tiempo interna

                end else begin

                    m_axis_ptp_ts_valid_reg <= s_axis_tuser[1]; // actualiza la validez de la marca de tiempo

                end

            end else begin

                m_axis_ptp_ts_tag_reg <= s_axis_tuser >> 1; // extrae la etiqueta de marca de tiempo del bus de usuario

                if (PTP_TS_FMT_TOD) begin

                    m_axis_ptp_ts_valid_int_reg <= 1'b1; // marca la validez de la marca de tiempo interna como válida

                end else begin

                    m_axis_ptp_ts_valid_reg <= 1'b1; // marca la validez de la marca de tiempo como válida

                end

            end

        end

    end    

  

    crc_state_reg[0] <= crc_state_next[0];

    crc_state_reg[1] <= crc_state_next[1];

    crc_state_reg[2] <= crc_state_next[2];

    crc_state_reg[3] <= crc_state_next[3];

    crc_state_reg[4] <= crc_state_next[4];

    crc_state_reg[5] <= crc_state_next[5];

    crc_state_reg[6] <= crc_state_next[6];

  

    if (update_crc) begin

        crc_state_reg[7] <= crc_state_next[7];

    end

  

    if (reset_crc) begin

        crc_state_reg[7] <= 32'hFFFFFFFF;

    end

  

    swap_txd <= xgmii_txd_next[63:32];

    swap_txc <= xgmii_txc_next[7:4];

  

    if (swap_lanes_reg) begin

        xgmii_txd_reg <= {xgmii_txd_next[31:0], swap_txd}; // si se deben intercambiar los lanes, combina las 4 primeras líneas de datos con los datos intercambiados

        xgmii_txc_reg <= {xgmii_txc_next[3:0], swap_txc}; // combina las 4 primeras líneas de control con el control intercambiado

    end else begin

        xgmii_txd_reg <= xgmii_txd_next; // si no se intercambian las líneas, simplemente pasa los datos siguientes a los registros

        xgmii_txc_reg <= xgmii_txc_next;

    end

  

    last_ts_reg <= ptp_ts;

    ts_inc_reg <= ptp_ts - last_ts_reg;

  

    if (rst) begin

        state_reg <= STATE_IDLE;

  

        frame_start_reg <= 1'b0;

        frame_reg <= 1'b0;

  

        swap_lanes_reg <= 1'b0;

  

        ifg_count_reg <= 8'd0;

        deficit_idle_count_reg <= 2'd0;

  

        s_axis_tready_reg <= 1'b0;

  

        m_axis_ptp_ts_valid_reg <= 1'b0;

        m_axis_ptp_ts_valid_int_reg <= 1'b0;

  

        xgmii_txd_reg <= {CTRL_WIDTH{XGMII_IDLE}};

        xgmii_txc_reg <= {CTRL_WIDTH{1'b1}};

  

        start_packet_reg <= 2'b00;

        error_underflow_reg <= 1'b0;

    end

end

  

endmodule

  

`resetall