【【通信协议之ICMP协议的FPGA实现】】

news2024/9/27 23:20:29

通信协议之ICMP协议的FPGA实现

整体的实现框图如下所示
在这里插入图片描述

arp_rx.v

module arp_rx
  #(
    //开发板MAC地址 00-11-22-33-44-55
    parameter BOARD_MAC = 48'h00_11_22_33_44_55,  
    //开发板IP地址 192.168.1.10   
    parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10}      
    )
   (
    input                clk        , //时钟信号
    input                rst_n      , //复位信号,低电平有效
                                    
    input                gmii_rx_dv , //GMII输入数据有效信号
    input        [7:0]   gmii_rxd   , //GMII输入数据
    output  reg          arp_rx_done, //ARP接收完成信号
    output  reg          arp_rx_type, //ARP接收类型 0:请求  1:应答
    output  reg  [47:0]  src_mac    , //接收到的源MAC地址
    output  reg  [31:0]  src_ip       //接收到的源IP地址
    );

//parameter define
localparam  st_idle     = 5'b0_0001; //初始状态,等待接收前导码
localparam  st_preamble = 5'b0_0010; //接收前导码状态 
localparam  st_eth_head = 5'b0_0100; //接收以太网帧头
localparam  st_arp_data = 5'b0_1000; //接收ARP数据
localparam  st_rx_end   = 5'b1_0000; //接收结束
localparam  ETH_TPYE    = 16'h0806;  //以太网帧类型 ARP

//reg define
reg    [4:0]   cur_state ;
reg    [4:0]   next_state;                       
reg            skip_en   ; //控制状态跳转使能信号
reg            error_en  ; //解析错误使能信号
reg    [4:0]   cnt       ; //解析数据计数器
reg    [47:0]  des_mac_t ; //接收到的目的MAC地址
reg    [31:0]  des_ip_t  ; //接收到的目的IP地址
reg    [47:0]  src_mac_t ; //接收到的源MAC地址
reg    [31:0]  src_ip_t  ; //接收到的源IP地址
reg    [15:0]  eth_type  ; //以太网类型
reg    [15:0]  op_data   ; //操作码

//*****************************************************
//**                    main code
//*****************************************************

//(三段式状态机)同步时序描述状态转移
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cur_state <= st_idle;  
    else
        cur_state <= next_state;
end

//组合逻辑判断状态转移条件
always @(*) begin
    next_state = st_idle;
    case(cur_state)
        st_idle : begin                     //等待接收前导码
            if(skip_en) 
                next_state = st_preamble;
            else
                next_state = st_idle;    
        end
        st_preamble : begin                 //接收前导码
            if(skip_en) 
                next_state = st_eth_head;
            else if(error_en) 
                next_state = st_rx_end;    
            else
                next_state = st_preamble;   
        end
        st_eth_head : begin                 //接收以太网帧头
            if(skip_en) 
                next_state = st_arp_data;
            else if(error_en) 
                next_state = st_rx_end;
            else
                next_state = st_eth_head;   
        end  
        st_arp_data : begin                  //接收ARP数据
            if(skip_en)
                next_state = st_rx_end;
            else if(error_en)
                next_state = st_rx_end;
            else
                next_state = st_arp_data;   
        end                  
        st_rx_end : begin                   //接收结束
            if(skip_en)
                next_state = st_idle;
            else
                next_state = st_rx_end;          
        end
        default : next_state = st_idle;
    endcase                                          
end    

//时序电路描述状态输出,解析以太网数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        skip_en <= 1'b0;
        error_en <= 1'b0;
        cnt <= 5'd0;
        des_mac_t <= 48'd0;
        des_ip_t <= 32'd0;
        src_mac_t <= 48'd0;
        src_ip_t <= 32'd0;        
        eth_type <= 16'd0;
        op_data <= 16'd0;
        arp_rx_done <= 1'b0;
        arp_rx_type <= 1'b0;
        src_mac <= 48'd0;
        src_ip <= 32'd0;
    end
    else begin
        skip_en <= 1'b0;
        error_en <= 1'b0;  
        arp_rx_done <= 1'b0;
        case(next_state)
            st_idle : begin                                  //检测到第一个8'h55
                if((gmii_rx_dv == 1'b1) && (gmii_rxd == 8'h55)) 
                    skip_en <= 1'b1;
				else;
            end
            st_preamble : begin
                if(gmii_rx_dv) begin                         //解析前导码
                    cnt <= cnt + 5'd1;
                    if((cnt < 5'd6) && (gmii_rxd != 8'h55))  //7个8'h55  
                        error_en <= 1'b1;
                    else if(cnt==5'd6) begin
                        cnt <= 5'd0;
                        if(gmii_rxd==8'hd5)                  //1个8'hd5
                            skip_en <= 1'b1;
                        else
                            error_en <= 1'b1;    
                    end
					else;
                end 
				else;
            end
            st_eth_head : begin
                if(gmii_rx_dv) begin
                    cnt <= cnt + 5'b1;
                    if(cnt < 5'd6) 
                        des_mac_t <= {des_mac_t[39:0],gmii_rxd};
                    else if(cnt == 5'd6) begin
                        //判断MAC地址是否为开发板MAC地址或者公共地址
                        if((des_mac_t != BOARD_MAC)
                            && (des_mac_t != 48'hff_ff_ff_ff_ff_ff))           
                            error_en <= 1'b1;
                    end
                    else if(cnt == 5'd12) 
                        eth_type[15:8] <= gmii_rxd;          //以太网协议类型
                    else if(cnt == 5'd13) begin
                        eth_type[7:0] <= gmii_rxd;
                        cnt <= 5'd0;
                        if(eth_type[15:8] == ETH_TPYE[15:8]  //判断是否为ARP协议
                            && gmii_rxd == ETH_TPYE[7:0])
                            skip_en <= 1'b1; 
                        else
                            error_en <= 1'b1;                       
                    end 
					else;
                end  
				else;
            end
            st_arp_data : begin
                if(gmii_rx_dv) begin
                    cnt <= cnt + 5'd1;
                    if(cnt == 5'd6) 
                        op_data[15:8] <= gmii_rxd;           //操作码       
                    else if(cnt == 5'd7)
                        op_data[7:0] <= gmii_rxd;
                    else if(cnt >= 5'd8 && cnt < 5'd14)      //源MAC地址
                        src_mac_t <= {src_mac_t[39:0],gmii_rxd};
                    else if(cnt >= 5'd14 && cnt < 5'd18)     //源IP地址
                        src_ip_t<= {src_ip_t[23:0],gmii_rxd};
                    else if(cnt >= 5'd24 && cnt < 5'd28)     //目标IP地址
                        des_ip_t <= {des_ip_t[23:0],gmii_rxd};
                    else if(cnt == 5'd28) begin
                        cnt <= 5'd0;
                        if(des_ip_t == BOARD_IP) begin       //判断目的IP地址和操作码
                            if((op_data == 16'd1) || (op_data == 16'd2)) begin
                                skip_en <= 1'b1;
                                arp_rx_done <= 1'b1;
                                src_mac <= src_mac_t;
                                src_ip <= src_ip_t;
                                src_mac_t <= 48'd0;
                                src_ip_t <= 32'd0;
                                des_mac_t <= 48'd0;
                                des_ip_t <= 32'd0;
                                if(op_data == 16'd1)         
                                    arp_rx_type <= 1'b0;     //ARP请求
                                else
                                    arp_rx_type <= 1'b1;     //ARP应答
                            end
                            else
                                error_en <= 1'b1;
                        end 
                        else
                            error_en <= 1'b1;
                    end
					else;
                end
				else;
            end
            st_rx_end : begin     
                cnt <= 5'd0;
                //单包数据接收完成   
                if(gmii_rx_dv == 1'b0 && skip_en == 1'b0)
                    skip_en <= 1'b1; 
				else;
            end    
            default : ;
        endcase                                                        
    end
end

endmodule

arp_tx.v

module arp_tx( 
    input                clk        , //时钟信号
    input                rst_n      , //复位信号,低电平有效
    
    input                arp_tx_en  , //ARP发送使能信号
    input                arp_tx_type, //ARP发送类型 0:请求  1:应答
    input        [47:0]  des_mac    , //发送的目标MAC地址
    input        [31:0]  des_ip     , //发送的目标IP地址
    input        [31:0]  crc_data   , //CRC校验数据
    input         [7:0]  crc_next   , //CRC下次校验完成数据
    output  reg          tx_done    , //以太网发送完成信号
    output  reg          gmii_tx_en , //GMII输出数据有效信号
    output  reg  [7:0]   gmii_txd   , //GMII输出数据
    output  reg          crc_en     , //CRC开始校验使能
    output  reg          crc_clr      //CRC数据复位信号 
    );

//parameter define
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55;
//开发板IP地址 192.168.1.10
parameter BOARD_IP  = {8'd192,8'd168,8'd1,8'd10}; 
//目的MAC地址 ff_ff_ff_ff_ff_ff
parameter  DES_MAC   = 48'hff_ff_ff_ff_ff_ff;
//目的IP地址 192.168.1.102     
parameter  DES_IP    = {8'd192,8'd168,8'd1,8'd102};

localparam  st_idle      = 5'b0_0001; //初始状态,等待开始发送信号
localparam  st_preamble  = 5'b0_0010; //发送前导码+帧起始界定符
localparam  st_eth_head  = 5'b0_0100; //发送以太网帧头
localparam  st_arp_data  = 5'b0_1000; //
localparam  st_crc       = 5'b1_0000; //发送CRC校验值

localparam  ETH_TYPE     = 16'h0806 ; //以太网帧类型 ARP协议
localparam  HD_TYPE      = 16'h0001 ; //硬件类型 以太网
localparam  PROTOCOL_TYPE= 16'h0800 ; //上层协议为IP协议
//以太网数据最小为46个字节,不足部分填充数据
localparam  MIN_DATA_NUM = 16'd46   ;    

//reg define
reg  [4:0]  cur_state     ;
reg  [4:0]  next_state    ;
                          
reg  [7:0]  preamble[7:0] ; //前导码+SFD
reg  [7:0]  eth_head[13:0]; //以太网首部
reg  [7:0]  arp_data[27:0]; //ARP数据
                            
reg         tx_en_d0      ; //arp_tx_en信号延时
reg         tx_en_d1      ; 
reg			tx_en_d2	  ;
reg         skip_en       ; //控制状态跳转使能信号
reg  [5:0]  cnt           ; 
reg  [4:0]  data_cnt      ; //发送数据个数计数器
reg         tx_done_t     ; 
                                
//wire define                   
wire        pos_tx_en     ; //arp_tx_en信号上升沿

//*****************************************************
//**                    main code
//*****************************************************

assign  pos_tx_en = (~tx_en_d2) & tx_en_d1;
                           
//对arp_tx_en信号延时打拍两次,用于采arp_tx_en的上升沿
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        tx_en_d0 <= 1'b0;
        tx_en_d1 <= 1'b0;
		tx_en_d2 <= 1'b0;
    end    
    else begin
        tx_en_d0 <= arp_tx_en;
        tx_en_d1 <= tx_en_d0;
		tx_en_d2 <= tx_en_d1;
    end
end 

//(三段式状态机)同步时序描述状态转移
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cur_state <= st_idle;  
    else
        cur_state <= next_state;
end

//组合逻辑判断状态转移条件
always @(*) begin
    next_state = st_idle;
    case(cur_state)
        st_idle : begin                     //空闲状态
            if(skip_en)                
                next_state = st_preamble;
            else
                next_state = st_idle;
        end                          
        st_preamble : begin                 //发送前导码+帧起始界定符
            if(skip_en)
                next_state = st_eth_head;
            else
                next_state = st_preamble;      
        end
        st_eth_head : begin                 //发送以太网首部
            if(skip_en)
                next_state = st_arp_data;
            else
                next_state = st_eth_head;      
        end              
        st_arp_data : begin                 //发送ARP数据                      
            if(skip_en)
                next_state = st_crc;
            else
                next_state = st_arp_data;      
        end
        st_crc: begin                       //发送CRC校验值
            if(skip_en)
                next_state = st_idle;
            else
                next_state = st_crc;      
        end
        default : next_state = st_idle;   
    endcase
end                      

//时序电路描述状态输出,发送以太网数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        skip_en <= 1'b0; 
        cnt <= 6'd0;
        data_cnt <= 5'd0;
        crc_en <= 1'b0;
        gmii_tx_en <= 1'b0;
        gmii_txd <= 8'd0;
        tx_done_t <= 1'b0; 
        
        //初始化数组    
        //前导码 7个8'h55 + 1个8'hd5 
        preamble[0] <= 8'h55;                
        preamble[1] <= 8'h55;
        preamble[2] <= 8'h55;
        preamble[3] <= 8'h55;
        preamble[4] <= 8'h55;
        preamble[5] <= 8'h55;
        preamble[6] <= 8'h55;
        preamble[7] <= 8'hd5;
        //以太网帧头 
        eth_head[0] <= DES_MAC[47:40];      //目的MAC地址
        eth_head[1] <= DES_MAC[39:32];
        eth_head[2] <= DES_MAC[31:24];
        eth_head[3] <= DES_MAC[23:16];
        eth_head[4] <= DES_MAC[15:8];
        eth_head[5] <= DES_MAC[7:0];        
        eth_head[6] <= BOARD_MAC[47:40];    //源MAC地址
        eth_head[7] <= BOARD_MAC[39:32];    
        eth_head[8] <= BOARD_MAC[31:24];    
        eth_head[9] <= BOARD_MAC[23:16];    
        eth_head[10] <= BOARD_MAC[15:8];    
        eth_head[11] <= BOARD_MAC[7:0];     
        eth_head[12] <= ETH_TYPE[15:8];     //以太网帧类型
        eth_head[13] <= ETH_TYPE[7:0];      
        //ARP数据                           
        arp_data[0] <= HD_TYPE[15:8];       //硬件类型
        arp_data[1] <= HD_TYPE[7:0];
        arp_data[2] <= PROTOCOL_TYPE[15:8]; //上层协议类型
        arp_data[3] <= PROTOCOL_TYPE[7:0];
        arp_data[4] <= 8'h06;               //硬件地址长度,6
        arp_data[5] <= 8'h04;               //协议地址长度,4
        arp_data[6] <= 8'h00;               //OP,操作码 8'h01:ARP请求 8'h02:ARP应答
        arp_data[7] <= 8'h01;
        arp_data[8] <= BOARD_MAC[47:40];    //发送端(源)MAC地址
        arp_data[9] <= BOARD_MAC[39:32];
        arp_data[10] <= BOARD_MAC[31:24];
        arp_data[11] <= BOARD_MAC[23:16];
        arp_data[12] <= BOARD_MAC[15:8];
        arp_data[13] <= BOARD_MAC[7:0];
        arp_data[14] <= BOARD_IP[31:24];    //发送端(源)IP地址
        arp_data[15] <= BOARD_IP[23:16];
        arp_data[16] <= BOARD_IP[15:8];
        arp_data[17] <= BOARD_IP[7:0];
        arp_data[18] <= DES_MAC[47:40];     //接收端(目的)MAC地址
        arp_data[19] <= DES_MAC[39:32];
        arp_data[20] <= DES_MAC[31:24];
        arp_data[21] <= DES_MAC[23:16];
        arp_data[22] <= DES_MAC[15:8];
        arp_data[23] <= DES_MAC[7:0];  
        arp_data[24] <= DES_IP[31:24];      //接收端(目的)IP地址
        arp_data[25] <= DES_IP[23:16];
        arp_data[26] <= DES_IP[15:8];
        arp_data[27] <= DES_IP[7:0];
    end
    else begin
        skip_en <= 1'b0;
        crc_en <= 1'b0;
        gmii_tx_en <= 1'b0;
        tx_done_t <= 1'b0;
        case(next_state)
            st_idle : begin
                if(pos_tx_en) begin
                    skip_en <= 1'b1;  
                    //如果目标MAC地址和IP地址已经更新,则发送正确的地址
                    if((des_mac != 48'b0) || (des_ip != 32'd0)) begin
                        eth_head[0] <= des_mac[47:40];
                        eth_head[1] <= des_mac[39:32];
                        eth_head[2] <= des_mac[31:24];
                        eth_head[3] <= des_mac[23:16];
                        eth_head[4] <= des_mac[15:8];
                        eth_head[5] <= des_mac[7:0];  
                        arp_data[18] <= des_mac[47:40];
                        arp_data[19] <= des_mac[39:32];
                        arp_data[20] <= des_mac[31:24];
                        arp_data[21] <= des_mac[23:16];
                        arp_data[22] <= des_mac[15:8];
                        arp_data[23] <= des_mac[7:0];  
                        arp_data[24] <= des_ip[31:24];
                        arp_data[25] <= des_ip[23:16];
                        arp_data[26] <= des_ip[15:8];
                        arp_data[27] <= des_ip[7:0];
                    end
					else;
                    if(arp_tx_type == 1'b0)
                        arp_data[7] <= 8'h01;            //ARP请求 
                    else 
                        arp_data[7] <= 8'h02;            //ARP应答
                end 
				else;
            end                                                                   
            st_preamble : begin                          //发送前导码+帧起始界定符
                gmii_tx_en <= 1'b1;
                gmii_txd <= preamble[cnt];
                if(cnt == 6'd7) begin                        
                    skip_en <= 1'b1;
                    cnt <= 1'b0;    
                end
                else    
                    cnt <= cnt + 1'b1;                     
            end
            st_eth_head : begin                          //发送以太网首部
                gmii_tx_en <= 1'b1;
                crc_en <= 1'b1;
                gmii_txd <= eth_head[cnt];
                if (cnt == 6'd13) begin
                    skip_en <= 1'b1;
                    cnt <= 1'b0;
                end    
                else    
                    cnt <= cnt + 1'b1;    
            end                    
            st_arp_data : begin                          //发送ARP数据  
                crc_en <= 1'b1;
                gmii_tx_en <= 1'b1;
                //至少发送46个字节
                if (cnt == MIN_DATA_NUM - 1'b1) begin    
                    skip_en <= 1'b1;
                    cnt <= 1'b0;
                    data_cnt <= 1'b0;
                end    
                else    
                    cnt <= cnt + 1'b1;  
                if(data_cnt <= 6'd27) begin
                    data_cnt <= data_cnt + 1'b1;
                    gmii_txd <= arp_data[data_cnt];
                end    
                else
                    gmii_txd <= 8'd0;                    //Padding,填充0
            end
            st_crc      : begin                          //发送CRC校验值
                gmii_tx_en <= 1'b1;
                cnt <= cnt + 1'b1;
                if(cnt == 6'd0)
                    gmii_txd <= {~crc_next[0], ~crc_next[1], ~crc_next[2],~crc_next[3],
                                 ~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};
                else if(cnt == 6'd1)
                    gmii_txd <= {~crc_data[16], ~crc_data[17], ~crc_data[18],
                                 ~crc_data[19], ~crc_data[20], ~crc_data[21], 
                                 ~crc_data[22],~crc_data[23]};
                else if(cnt == 6'd2) begin
                    gmii_txd <= {~crc_data[8], ~crc_data[9], ~crc_data[10],
                                 ~crc_data[11],~crc_data[12], ~crc_data[13], 
                                 ~crc_data[14],~crc_data[15]};                              
                end
                else if(cnt == 6'd3) begin
                    gmii_txd <= {~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],
                                 ~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};  
                    tx_done_t <= 1'b1;
                    skip_en <= 1'b1;
                    cnt <= 1'b0;
                end   
				else;
            end                          
            default :;  
        endcase                                             
    end
end            

//发送完成信号及crc值复位信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        tx_done <= 1'b0;
        crc_clr <= 1'b0;
    end
    else begin
        tx_done <= tx_done_t;
        crc_clr <= tx_done_t;
    end
end

endmodule

arp.v

module arp(
    input                rst_n      , //复位信号,低电平有效
    //GMII接口
    input                gmii_rx_clk, //GMII接收数据时钟
    input                gmii_rx_dv , //GMII输入数据有效信号
    input        [7:0]   gmii_rxd   , //GMII输入数据
    input                gmii_tx_clk, //GMII发送数据时钟
    output               gmii_tx_en , //GMII输出数据有效信号
    output       [7:0]   gmii_txd   , //GMII输出数据          

    //用户接口
    output               arp_rx_done, //ARP接收完成信号
    output               arp_rx_type, //ARP接收类型 0:请求  1:应答
    output       [47:0]  src_mac    , //接收到目的MAC地址
    output       [31:0]  src_ip     , //接收到目的IP地址    
    input                arp_tx_en  , //ARP发送使能信号
    input                arp_tx_type, //ARP发送类型 0:请求  1:应答
    input        [47:0]  des_mac    , //发送的目标MAC地址
    input        [31:0]  des_ip     , //发送的目标IP地址
    output               tx_done      //以太网发送完成信号    
    );

//parameter define
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55;    
//开发板IP地址 192.168.1.10 
parameter BOARD_IP  = {8'd192,8'd168,8'd1,8'd10};   
//目的MAC地址 ff_ff_ff_ff_ff_ff
parameter  DES_MAC   = 48'hff_ff_ff_ff_ff_ff;
//目的IP地址 192.168.1.102     
parameter  DES_IP    = {8'd192,8'd168,8'd1,8'd102};

//wire define
wire           crc_en  ; //CRC开始校验使能
wire           crc_clr ; //CRC数据复位信号 
wire   [7:0]   crc_d8  ; //输入待校验8位数据
wire   [31:0]  crc_data; //CRC校验数据
wire   [31:0]  crc_next; //CRC下次校验完成数据

//*****************************************************
//**                    main code
//*****************************************************

assign  crc_d8 = gmii_txd;

//ARP接收模块    
arp_rx 
   #(
    .BOARD_MAC       (BOARD_MAC),         //参数例化
    .BOARD_IP        (BOARD_IP )
    )
   u_arp_rx(
    .clk             (gmii_rx_clk),
    .rst_n           (rst_n),

    .gmii_rx_dv      (gmii_rx_dv),
    .gmii_rxd        (gmii_rxd  ),
    .arp_rx_done     (arp_rx_done),
    .arp_rx_type     (arp_rx_type),
    .src_mac         (src_mac    ),
    .src_ip          (src_ip     )
    );                                           

//ARP发送模块
arp_tx
   #(
    .BOARD_MAC       (BOARD_MAC),         //参数例化
    .BOARD_IP        (BOARD_IP ),
    .DES_MAC         (DES_MAC  ),
    .DES_IP          (DES_IP   )
    )
   u_arp_tx(
    .clk             (gmii_tx_clk),
    .rst_n           (rst_n),

    .arp_tx_en       (arp_tx_en ),
    .arp_tx_type     (arp_tx_type),
    .des_mac         (des_mac   ),
    .des_ip          (des_ip    ),
    .crc_data        (crc_data  ),
    .crc_next        (crc_next[31:24]),
    .tx_done         (tx_done   ),
    .gmii_tx_en      (gmii_tx_en),
    .gmii_txd        (gmii_txd  ),
    .crc_en          (crc_en    ),
    .crc_clr         (crc_clr   )
    );     

//以太网发送CRC校验模块
crc32_d8   u_crc32_d8(
    .clk             (gmii_tx_clk),                      
    .rst_n           (rst_n      ),                          
    .data            (crc_d8     ),            
    .crc_en          (crc_en     ),                          
    .crc_clr         (crc_clr    ),                         
    .crc_data        (crc_data   ),                        
    .crc_next        (crc_next   )                         
    );

endmodule

crc_32_d8.v

module crc32_d8(
    input                 clk     ,  //时钟信号
    input                 rst_n   ,  //复位信号,低电平有效
    input         [7:0]   data    ,  //输入待校验8位数据
    input                 crc_en  ,  //crc使能,开始校验标志
    input                 crc_clr ,  //crc数据复位信号
    output   reg  [31:0]  crc_data,  //CRC校验数据
    output        [31:0]  crc_next   //CRC下次校验完成数据
  );

  //*****************************************************
  //**                    main code
  //*****************************************************

  //输入待校验8位数据,需要先将高低位互换
  wire    [7:0]  data_t;

  assign data_t = {data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7]};

  //CRC32的生成多项式为:G(x)= x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11
  //+ x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1

  assign crc_next[0] = crc_data[24] ^ crc_data[30] ^ data_t[0] ^ data_t[6];
  assign crc_next[1] = crc_data[24] ^ crc_data[25] ^ crc_data[30] ^ crc_data[31]
         ^ data_t[0] ^ data_t[1] ^ data_t[6] ^ data_t[7];
  assign crc_next[2] = crc_data[24] ^ crc_data[25] ^ crc_data[26] ^ crc_data[30]
         ^ crc_data[31] ^ data_t[0] ^ data_t[1] ^ data_t[2] ^ data_t[6]
         ^ data_t[7];
  assign crc_next[3] = crc_data[25] ^ crc_data[26] ^ crc_data[27] ^ crc_data[31]
         ^ data_t[1] ^ data_t[2] ^ data_t[3] ^ data_t[7];
  assign crc_next[4] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28]
         ^ crc_data[30] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[4]
         ^ data_t[6];
  assign crc_next[5] = crc_data[24] ^ crc_data[25] ^ crc_data[27] ^ crc_data[28]
         ^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[0]
         ^ data_t[1] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[6]
         ^ data_t[7];
  assign crc_next[6] = crc_data[25] ^ crc_data[26] ^ crc_data[28] ^ crc_data[29]
         ^ crc_data[30] ^ crc_data[31] ^ data_t[1] ^ data_t[2] ^ data_t[4]
         ^ data_t[5] ^ data_t[6] ^ data_t[7];
  assign crc_next[7] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[29]
         ^ crc_data[31] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5]
         ^ data_t[7];
  assign crc_next[8] = crc_data[0] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27]
         ^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4];
  assign crc_next[9] = crc_data[1] ^ crc_data[25] ^ crc_data[26] ^ crc_data[28]
         ^ crc_data[29] ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5];
  assign crc_next[10] = crc_data[2] ^ crc_data[24] ^ crc_data[26] ^ crc_data[27]
         ^ crc_data[29] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5];
  assign crc_next[11] = crc_data[3] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27]
         ^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4];
  assign crc_next[12] = crc_data[4] ^ crc_data[24] ^ crc_data[25] ^ crc_data[26]
         ^ crc_data[28] ^ crc_data[29] ^ crc_data[30] ^ data_t[0]
         ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5] ^ data_t[6];
  assign crc_next[13] = crc_data[5] ^ crc_data[25] ^ crc_data[26] ^ crc_data[27]
         ^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[1]
         ^ data_t[2] ^ data_t[3] ^ data_t[5] ^ data_t[6] ^ data_t[7];
  assign crc_next[14] = crc_data[6] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28]
         ^ crc_data[30] ^ crc_data[31] ^ data_t[2] ^ data_t[3] ^ data_t[4]
         ^ data_t[6] ^ data_t[7];
  assign crc_next[15] =  crc_data[7] ^ crc_data[27] ^ crc_data[28] ^ crc_data[29]
         ^ crc_data[31] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[7];
  assign crc_next[16] = crc_data[8] ^ crc_data[24] ^ crc_data[28] ^ crc_data[29]
         ^ data_t[0] ^ data_t[4] ^ data_t[5];
  assign crc_next[17] = crc_data[9] ^ crc_data[25] ^ crc_data[29] ^ crc_data[30]
         ^ data_t[1] ^ data_t[5] ^ data_t[6];
  assign crc_next[18] = crc_data[10] ^ crc_data[26] ^ crc_data[30] ^ crc_data[31]
         ^ data_t[2] ^ data_t[6] ^ data_t[7];
  assign crc_next[19] = crc_data[11] ^ crc_data[27] ^ crc_data[31] ^ data_t[3] ^ data_t[7];
  assign crc_next[20] = crc_data[12] ^ crc_data[28] ^ data_t[4];
  assign crc_next[21] = crc_data[13] ^ crc_data[29] ^ data_t[5];
  assign crc_next[22] = crc_data[14] ^ crc_data[24] ^ data_t[0];
  assign crc_next[23] = crc_data[15] ^ crc_data[24] ^ crc_data[25] ^ crc_data[30]
         ^ data_t[0] ^ data_t[1] ^ data_t[6];
  assign crc_next[24] = crc_data[16] ^ crc_data[25] ^ crc_data[26] ^ crc_data[31]
         ^ data_t[1] ^ data_t[2] ^ data_t[7];
  assign crc_next[25] = crc_data[17] ^ crc_data[26] ^ crc_data[27] ^ data_t[2] ^ data_t[3];
  assign crc_next[26] = crc_data[18] ^ crc_data[24] ^ crc_data[27] ^ crc_data[28]
         ^ crc_data[30] ^ data_t[0] ^ data_t[3] ^ data_t[4] ^ data_t[6];
  assign crc_next[27] = crc_data[19] ^ crc_data[25] ^ crc_data[28] ^ crc_data[29]
         ^ crc_data[31] ^ data_t[1] ^ data_t[4] ^ data_t[5] ^ data_t[7];
  assign crc_next[28] = crc_data[20] ^ crc_data[26] ^ crc_data[29] ^ crc_data[30]
         ^ data_t[2] ^ data_t[5] ^ data_t[6];
  assign crc_next[29] = crc_data[21] ^ crc_data[27] ^ crc_data[30] ^ crc_data[31]
         ^ data_t[3] ^ data_t[6] ^ data_t[7];
  assign crc_next[30] = crc_data[22] ^ crc_data[28] ^ crc_data[31] ^ data_t[4] ^ data_t[7];
  assign crc_next[31] = crc_data[23] ^ crc_data[29] ^ data_t[5];

  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      crc_data <= 32'hff_ff_ff_ff;
    else if(crc_clr)                             //CRC校验值复位
      crc_data <= 32'hff_ff_ff_ff;
    else if(crc_en)
      crc_data <= crc_next;
  end

endmodule

eth_ctrl.v

module eth_ctrl(
    input              clk              ,          //时钟
    input              rst_n            ,          //系统复位信号,低电平有效
    //ARP相关端口信号       
    input              arp_rx_done      ,         //ARP接收完成信号
    input              arp_rx_type      ,         //ARP接收类型 0:请求  1:应答
    output  reg        arp_tx_en        ,         //ARP发送使能信号
    output             arp_tx_type      ,         //ARP发送类型 0:请求  1:应答
    input              arp_tx_done      ,         //ARP发送完成信号
    input              arp_gmii_tx_en   ,         //ARP GMII输出数据有效信号
    input     [7:0]    arp_gmii_txd     ,         //ARP GMII输出数据
    //ICMP相关端口信号
    input              icmp_tx_start_en ,         //ICMP开始发送信号
    input              icmp_tx_done     ,         //ICMP发送完成信号
    input              icmp_gmii_tx_en  ,         //ICMP GMII输出数据有效信号
    input     [7:0]    icmp_gmii_txd    ,         //ICMP GMII输出数据
    //GMII发送引脚
    output             gmii_tx_en       ,         //GMII输出数据有效信号
    output    [7:0]    gmii_txd                   //GMII输出数据
  );

  //reg define
  reg        protocol_sw                ; //协议切换信号
  reg        icmp_tx_busy; //ICMP正在发送数据标志信号
  reg        arp_rx_flag; //接收到ARP请求信号的标志

  //*****************************************************
  //**                    main code
  //*****************************************************

  assign arp_tx_type = 1'b1;   //ARP发送类型固定为ARP应答
  assign gmii_tx_en = protocol_sw ? icmp_gmii_tx_en : arp_gmii_tx_en;
  assign gmii_txd = protocol_sw ? icmp_gmii_txd : arp_gmii_txd;

  //控制ICMP发送忙信号
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      icmp_tx_busy <= 1'b0;
    else if(icmp_tx_start_en)
      icmp_tx_busy <= 1'b1;
    else if(icmp_tx_done)
      icmp_tx_busy <= 1'b0;
  end

  //控制接收到ARP请求信号的标志
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      arp_rx_flag <= 1'b0;
    else if(arp_rx_done && (arp_rx_type == 1'b0))
      arp_rx_flag <= 1'b1;
    else
      arp_rx_flag <= 1'b0;
  end

  //控制protocol_sw和arp_tx_en信号
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
    begin
      protocol_sw <= 1'b0;
      arp_tx_en <= 1'b0;
    end
    else
    begin
      arp_tx_en <= 1'b0;
      if(icmp_tx_start_en)
        protocol_sw <= 1'b1;
      else if(arp_rx_flag && (icmp_tx_busy == 1'b0))
      begin
        protocol_sw <= 1'b0;
        arp_tx_en <= 1'b1;
      end
    end
  end

endmodule

eth_icmp_test.v – 这是整个的top函数

module    eth_icmp_test(
    input                   sys_clk     ,
    input                   sys_rst_n   ,
    // PL 浠ュお缃?
    input                   eth_rxc     ,   // 鎺ユ敹鏃堕挓
    input                   eth_rx_ctl  ,   // 杈撳叆鏈夋晥鏃堕挓
    input     [3 : 0]       eth_rxd     ,
    output                  eth_txc     ,
    output                  eth_tx_ctl  ,
    output    [3 : 0]       eth_txd     ,
    output                  eth_rst_n
  );




  // -------------------------------------------- //
  //           parameter  and  define             //
  // -------------------------------------------- //
  //parameter define
  //寮?鍙戞澘MAC鍦板潃 00-11-22-33-44-55
  parameter  BOARD_MAC =        48'h00_11_22_33_44_55       ;
  //寮?鍙戞澘IP鍦板潃 192.168.1.10
  parameter  BOARD_IP  =        {8'd192,8'd168,8'd1,8'd10}  ;
  //鐩殑MAC鍦板潃 ff_ff_ff_ff_ff_ff
  parameter  DES_MAC   =        48'hff_ff_ff_ff_ff_ff       ;
  //鐩殑IP鍦板潃 192.168.1.102
  parameter  DES_IP    =        {8'd192,8'd168,8'd1,8'd102} ;
  //杈撳叆鏁版嵁IO寤舵椂,姝ゅ涓?0,鍗充笉寤舵椂(濡傛灉涓簄,琛ㄧず寤舵椂n*78ps)
  parameter IDELAY_VALUE    = 0;



  // define

  //wire define
  wire          clk_200m        ; //鐢ㄤ簬IO寤舵椂鐨勬椂閽?

  wire          gmii_rx_clk     ; //GMII鎺ユ敹鏃堕挓
  wire          gmii_rx_dv      ; //GMII鎺ユ敹鏁版嵁鏈夋晥淇″彿
  wire  [7:0]   gmii_rxd        ; //GMII鎺ユ敹鏁版嵁
  wire          gmii_tx_clk     ; //GMII鍙戦?佹椂閽?
  wire          gmii_tx_en      ; //GMII鍙戦?佹暟鎹娇鑳戒俊鍙?
  wire  [7:0]   gmii_txd        ; //GMII鍙戦?佹暟鎹?

  wire          arp_gmii_tx_en  ; //ARP GMII杈撳嚭鏁版嵁鏈夋晥淇″彿
  wire  [7:0]   arp_gmii_txd    ; //ARP GMII杈撳嚭鏁版嵁
  wire          arp_rx_done     ; //ARP鎺ユ敹瀹屾垚淇″彿
  wire          arp_rx_type     ; //ARP鎺ユ敹绫诲瀷 0:璇锋眰  1:搴旂瓟
  wire  [47:0]  src_mac         ; //鎺ユ敹鍒扮洰鐨凪AC鍦板潃
  wire  [31:0]  src_ip          ; //鎺ユ敹鍒扮洰鐨処P鍦板潃
  wire          arp_tx_en       ; //ARP鍙戦?佷娇鑳戒俊鍙?
  wire          arp_tx_type     ; //ARP鍙戦?佺被鍨? 0:璇锋眰  1:搴旂瓟
  wire  [47:0]  des_mac         ; //鍙戦?佺殑鐩爣MAC鍦板潃
  wire  [31:0]  des_ip          ; //鍙戦?佺殑鐩爣IP鍦板潃
  wire          arp_tx_done     ; //ARP鍙戦?佸畬鎴愪俊鍙?

  wire          icmp_gmii_tx_en ; //ICMP GMII杈撳嚭鏁版嵁鏈夋晥淇″彿
  wire  [7:0]   icmp_gmii_txd   ; //ICMP GMII杈撳嚭鏁版嵁
  wire          rec_pkt_done    ; //ICMP鍗曞寘鏁版嵁鎺ユ敹瀹屾垚淇″彿
  wire          rec_en          ; //ICMP鎺ユ敹鐨勬暟鎹娇鑳戒俊鍙?
  wire  [ 7:0]  rec_data        ; //ICMP鎺ユ敹鐨勬暟鎹?
  wire  [15:0]  rec_byte_num    ; //ICMP鎺ユ敹鐨勬湁鏁堝瓧鑺傛暟 鍗曚綅:byte
  wire  [15:0]  tx_byte_num     ; //ICMP鍙戦?佺殑鏈夋晥瀛楄妭鏁? 鍗曚綅:byte
  wire          icmp_tx_done    ; //ICMP鍙戦?佸畬鎴愪俊鍙?
  wire          tx_req          ; //ICMP璇绘暟鎹姹備俊鍙?
  wire  [ 7:0]  tx_data         ; //ICMP寰呭彂閫佹暟鎹?
  wire          tx_start_en     ; //ICMP鍙戦?佸紑濮嬩娇鑳戒俊鍙?




  // ------------------------------------------ //
  //            next  is  main  code           //
  // ---------------------------------------- //


  assign   tx_start_en   =   rec_pkt_done  ;
  assign   tx_byte_num   =   rec_byte_num  ;
  assign   des_mac       =   src_mac       ;
  assign   des_ip        =   src_ip        ;
  assign   eth_rst_n     =   sys_rst_n     ;






  // 鏃堕挓
  clk_wiz_0  u_clk_wiz_0 (
               .clk_out1(clk_200m)      ,     // output clk_out1
               .reset(~sys_rst_n)       ,     // input reset
               .locked(locked)          ,     // output locked
               .clk_in1(sys_clk)
             );


  //  gmii_to_rgmii
  gmii_to_rgmii u_gmii_to_rgmii(
                  .idelay_clk   ( clk_200m   ),
                  .gmii_rx_clk  ( gmii_rx_clk  ),
                  .gmii_rx_dv   ( gmii_rx_dv   ),
                  .gmii_rxd     ( gmii_rxd     ),
                  .gmii_tx_clk  ( gmii_tx_clk  ),
                  .gmii_tx_en   ( gmii_tx_en   ),
                  .gmii_txd     ( gmii_txd     ),

                  .rgmii_rxc    ( eth_rxc    ),
                  .rgmii_rx_ctl ( eth_rx_ctl ),
                  .rgmii_rxd    ( eth_rxd    ),
                  .rgmii_txc    ( eth_txc    ),
                  .rgmii_tx_ctl ( eth_tx_ctl ),
                  .rgmii_txd    ( eth_txd    )
                );

 // arp閫氫俊 
arp u_arp(
    .rst_n       ( sys_rst_n       ),
    .gmii_rx_clk ( gmii_rx_clk ),
    .gmii_rx_dv  ( gmii_rx_dv  ),
    .gmii_rxd    ( gmii_rxd    ),
    .gmii_tx_clk ( gmii_tx_clk ),
    .gmii_tx_en  ( arp_gmii_tx_en  ),
    .gmii_txd    ( arp_gmii_txd    ),
    .arp_rx_done ( arp_rx_done ),
    .arp_rx_type ( arp_rx_type ),
    .src_mac     ( src_mac     ),
    .src_ip      ( src_ip      ),
    .arp_tx_en   ( arp_tx_en   ),
    .arp_tx_type ( arp_tx_type ),
    .des_mac     ( des_mac     ),
    .des_ip      ( des_ip      ),
    .tx_done     ( arp_tx_done     )
);


// icmp閫氫俊 
icmp u_icmp(
    .rst_n         ( sys_rst_n         ),
    .gmii_rx_clk   ( gmii_rx_clk   ),
    .gmii_rx_dv    ( gmii_rx_dv    ),
    .gmii_rxd      ( gmii_rxd      ),
    .gmii_tx_clk   ( gmii_tx_clk   ),
    .gmii_tx_en    ( icmp_gmii_tx_en    ),
    .gmii_txd      ( icmp_gmii_txd      ),
    .rec_pkt_done  ( rec_pkt_done  ),
    .rec_en        ( rec_en        ),
    .rec_data      ( rec_data      ),
    .rec_byte_num  ( rec_byte_num  ),
    .tx_start_en   ( tx_start_en   ),
    .tx_data       ( tx_data       ),
    .tx_byte_num   ( tx_byte_num   ),
    .des_mac       ( des_mac       ),
    .des_ip        ( des_ip        ),
    .tx_done       ( icmp_tx_done       ),
    .tx_req        ( tx_req        )
);


鍚屾FIFO
//sync_fifo_2048x8b sync_fifo_2048x8b_inst (
fifo_generator_0 sync_fifo_2048x8b_inst (
  .clk(gmii_rx_clk),      	// input wire clk
  //.rst(~sys_rst_n),      	// input wire rst
  .srst(~sys_rst_n),      	// input wire rst
  .din(rec_data),     		// input wire [7 : 0] din
  .wr_en(rec_en),  			// input wire wr_en
  .rd_en(tx_req),  			// input wire rd_en
  .dout(tx_data),    		// output wire [7 : 0] dout
  .full(),    				// output wire full
  .empty()  				// output wire empty
);



//eth鈥斺?攃trl 
eth_ctrl u_eth_ctrl(
    .clk               ( gmii_rx_clk               ),
    .rst_n             ( sys_rst_n             ),
    .arp_rx_done       ( arp_rx_done       ),
    .arp_rx_type       ( arp_rx_type       ),
    .arp_tx_en         ( arp_tx_en         ),
    .arp_tx_type       ( arp_tx_type       ),
    .arp_tx_done       ( arp_tx_done       ),
    .arp_gmii_tx_en    ( arp_gmii_tx_en    ),
    .arp_gmii_txd      ( arp_gmii_txd      ),
    .icmp_tx_start_en  ( tx_start_en  ),
    .icmp_tx_done      ( icmp_tx_done      ),
    .icmp_gmii_tx_en   ( icmp_gmii_tx_en   ),
    .icmp_gmii_txd     ( icmp_gmii_txd     ),
    
    .gmii_tx_en        ( gmii_tx_en        ),
    .gmii_txd          ( gmii_txd          )
);


endmodule  

gmii_to_rgmii.v

module   gmii_to_rgmii(
    input              idelay_clk  , //IDELAY时钟
    //以太网GMII接口
    output             gmii_rx_clk , //GMII接收时钟
    output             gmii_rx_dv  , //GMII接收数据有效信号
    output      [7:0]  gmii_rxd    , //GMII接收数据
    output             gmii_tx_clk , //GMII发送时钟
    input              gmii_tx_en  , //GMII发送数据使能信号
    input       [7:0]  gmii_txd    , //GMII发送数据
    //以太网RGMII接口
    input              rgmii_rxc   , //RGMII接收时钟
    input              rgmii_rx_ctl, //RGMII接收数据控制信号
    input       [3:0]  rgmii_rxd   , //RGMII接收数据
    output             rgmii_txc   , //RGMII发送时钟
    output             rgmii_tx_ctl, //RGMII发送数据控制信号
    output      [3:0]  rgmii_txd     //RGMII发送数据
  );


  parameter  IDEAY_VALUE =  0 ; 

  // ------------------------------------ //
  // -------------main  code ------------ //
  // ------------------------------------ //

  assign gmii_tx_clk  = gmii_rx_clk  ;  

  // rgmii 
rgmii_rx u_rgmii_rx(
    .idelay_clk    ( idelay_clk    ),
    .rgmii_rxc     ( rgmii_rxc     ),
    .rgmii_rx_ctl  ( rgmii_rx_ctl  ),
    .rgmii_rxd     ( rgmii_rxd     ),
    .gmii_rx_clk   ( gmii_rx_clk   ),

    .gmii_rx_dv    ( gmii_rx_dv    ),
    .gmii_rxd      ( gmii_rxd      )
);


// gmii 
rgmii_tx u_rgmii_tx(
    .gmii_tx_clk   ( gmii_tx_clk   ),
    .gmii_tx_en    ( gmii_tx_en    ),
    .gmii_txd      ( gmii_txd      ),
    .rgmii_txc     ( rgmii_txc     ),
    .rgmii_tx_ctl  ( rgmii_tx_ctl  ),
    .rgmii_txd     ( rgmii_txd     )
);


endmodule 

icmp_rx.v

module icmp_rx(
  input                clk         ,    //时钟信号
  input                rst_n       ,    //复位信号,低电平有效
  
  input                gmii_rx_dv  ,    //GMII输入数据有效信号
  input        [7:0]   gmii_rxd    ,    //GMII输入数据
  output  reg          rec_pkt_done,    //以太网单包数据接收完成信号
  output  reg          rec_en      ,    //以太网接收的数据使能信号
  output  reg  [ 7:0]  rec_data    ,    //以太网接收的数据
  output  reg  [15:0]  rec_byte_num,    //以太网接收的有效字数 单位:byte 

  output  reg  [15:0]  icmp_id     ,    //ICMP标识符
  output  reg  [15:0]  icmp_seq    ,    //ICMP序列号
  output  reg  [31:0]  reply_checksum   //接收数据校验
 
  );

//parameter define
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55; 
//开发板IP地址 192.168.1.10 
parameter BOARD_IP  = {8'd192,8'd168,8'd1,8'd10};

//状态机状态定义
localparam  st_idle             = 7'b000_0001; //初始状态,等待接收前导码
localparam  st_preamble         = 7'b000_0010; //接收前导码状态 
localparam  st_eth_head         = 7'b000_0100; //接收以太网帧头
localparam  st_ip_head          = 7'b000_1000; //接收IP首部
localparam  st_icmp_head        = 7'b001_0000; //接收ICMP首部
localparam  st_rx_data          = 7'b010_0000; //接收有效数据
localparam  st_rx_end           = 7'b100_0000; //接收结束

//以太网类型定义
localparam  ETH_TYPE    = 16'h0800   ; //以太网协议类型 IP协议
localparam  ICMP_TYPE   = 8'd1       ; //ICMP协议类型

//ICMP报文类型:回显请求
localparam ECHO_REQUEST = 8'h08 ; 

//reg define
reg  [6:0]   cur_state       ;
reg  [6:0]   next_state      ;                             
reg          skip_en         ; //控制状态跳转使能信号
reg          error_en        ; //解析错误使能信号

reg  [4:0]   cnt             ; //解析数据计数器
reg  [47:0]  des_mac         ; //目的MAC地址
reg  [15:0]  eth_type        ; //以太网类型
reg  [31:0]  des_ip          ; //目的IP地址
reg  [5:0]   ip_head_byte_num; //IP首部长度
reg  [15:0]  total_length    ; //IP长度 
reg  [1:0]   rec_en_cnt      ; //8bit转32bit计数器
reg  [7:0]   icmp_type        ; //ICMP报文类型:用于标识错误类型的差错报文或者查询类型的报告报文
reg  [7:0]   icmp_code        ; //ICMP报文代码:根据ICMP差错报文的类型,进一步分析错误的原因,代码值不同对应的错误也不同
                //例如:类型为11且代码为0,表示数据传输过程中超时了,超时的具体原因是TTL值为0,数据报被丢弃。
reg  [15:0]  icmp_checksum    ; //接收校验和:数据发送到目的地后需要对ICMP数据报文做一个校验,用于检查数据报文是否有错误
reg  [15:0]  icmp_data_length ; //data length register
reg  [15:0]  icmp_rx_cnt      ; //接收数据计数
reg  [7:0]   icmp_rx_data_d0  ;
reg  [31:0]  reply_checksum_add ;
//****************************************************
//**                    main code
//*****************************************************

//(三段式状态机)同步时序描述状态转移
always @(posedge clk or negedge rst_n) begin
  if(!rst_n)
      cur_state <= st_idle;  
  else
      cur_state <= next_state;
end

//组合逻辑判断状态转移条件
always @(*) begin
  next_state = st_idle;
  case(cur_state)
      st_idle : begin                       //等待接收前导码
          if(skip_en) 
              next_state = st_preamble;
          else
              next_state = st_idle; 
      end
      st_preamble : begin                   //接收前导码
          if(skip_en) 
              next_state = st_eth_head;
          else if(error_en) 
              next_state = st_rx_end; 
          else
              next_state = st_preamble; 
      end
      st_eth_head : begin                   //接收以太网帧头
          if(skip_en) 
              next_state = st_ip_head;
          else if(error_en) 
              next_state = st_rx_end;
          else
              next_state = st_eth_head; 
      end  
      st_ip_head : begin                    //接收IP首部
          if(skip_en)
              next_state = st_icmp_head;
          else if(error_en)
              next_state = st_rx_end;
          else
              next_state = st_ip_head; 
      end 
      st_icmp_head : begin                   //接收ICMP首部
          if(skip_en)
              next_state = st_rx_data;
          else if(error_en)
              next_state = st_rx_end;
          else
              next_state = st_icmp_head;
      end 
      st_rx_data : begin                    //接收有效数据
          if(skip_en)
              next_state = st_rx_end;
          else
              next_state = st_rx_data; 
      end 
      st_rx_end : begin                     //接收结束
          if(skip_en)
              next_state = st_idle;
          else
              next_state = st_rx_end;
      end
      default : next_state = st_idle;
  endcase 
end 

//时序电路描述状态输出,解析以太网数据
always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
      skip_en <= 1'b0;
      error_en <= 1'b0;
      cnt <= 5'd0;
      des_mac <= 48'd0;
      eth_type <= 16'd0;
      des_ip <= 32'd0;
      ip_head_byte_num <= 6'd0;
      
      total_length <= 16'd0;
      
      icmp_type <= 8'd0;
      icmp_code <= 8'd0;
      icmp_checksum <= 16'd0;
      icmp_id <= 16'd0;
      icmp_seq <= 16'd0;
      
      icmp_rx_data_d0 <= 8'd0 ;
      reply_checksum <= 32'd0;  //累加
      reply_checksum_add <= 32'd0;
      icmp_rx_cnt <= 16'd0;
      icmp_data_length <= 16'd0;
      
      rec_en_cnt <= 2'd0;
      rec_en <= 1'b0;
      rec_data <= 32'd0;
      rec_pkt_done <= 1'b0;
      rec_byte_num <= 16'd0;
  end
  else begin
      skip_en <= 1'b0;
      error_en <= 1'b0;  
      rec_pkt_done <= 1'b0;
      case(next_state)
          st_idle : begin
              if((gmii_rx_dv == 1'b1) && (gmii_rxd == 8'h55)) 
                  skip_en <= 1'b1;
      else;
          end
          st_preamble : begin
              if(gmii_rx_dv) begin                         //解析前导码
                  cnt <= cnt + 5'd1;
                  if((cnt < 5'd6) && (gmii_rxd != 8'h55))  //7个8'h55
                      error_en <= 1'b1;
                  else if(cnt==5'd6) begin
                      cnt <= 5'd0;
                      if(gmii_rxd==8'hd5)                  //1个8'hd5
                          skip_en <= 1'b1;
                      else
                          error_en <= 1'b1; 
                  end  
        else;
              end  
          end
          st_eth_head : begin
              if(gmii_rx_dv) begin
                  cnt <= cnt + 5'b1;
                  if(cnt < 5'd6) 
                      des_mac <= {des_mac[39:0],gmii_rxd}; //目的MAC地址
                  else if(cnt == 5'd12) 
                      eth_type[15:8] <= gmii_rxd;          //以太网协议类型
                  else if(cnt == 5'd13) begin
                      eth_type[7:0] <= gmii_rxd;
                      cnt <= 5'd0;
                      //判断MAC地址是否为开发板MAC地址或者公共地址
                      if(((des_mac == BOARD_MAC) ||(des_mac == 48'hff_ff_ff_ff_ff_ff))
                     && eth_type[15:8] == ETH_TYPE[15:8] && gmii_rxd == ETH_TYPE[7:0])
                          skip_en <= 1'b1;
                      else
                          error_en <= 1'b1;
                  end 
        else;
              end 
      else;
          end
          st_ip_head : begin
              if(gmii_rx_dv) begin  //cnt ip首20个byte计数
                  cnt <= cnt + 5'd1;
                  if(cnt == 5'd0)
                      ip_head_byte_num <= {gmii_rxd[3:0],2'd0}; // ip首位长度表示有多少个(4字节 ) 这里就是多少位了
                  else if (cnt == 5'd2) 
                      total_length[15:8] <= gmii_rxd;
                  else if (cnt == 5'd3)
                      total_length[7:0] <= gmii_rxd;
                  else if (cnt == 5'd4)
        //有效数据字节长度,(IP首部20个字节,icmp首部8个字节,所以减去28)
                      icmp_data_length <= total_length - 16'd28; 
        else if(cnt == 5'd9) begin
                      if(gmii_rxd != ICMP_TYPE) begin
                          //如果当前接收的数据不是ICMP协议,停止解析数据                        
                          error_en <= 1'b1;               
                          cnt <= 5'd0;                        
                      end
                  end
                  else if((cnt >= 5'd16) && (cnt <= 5'd18))
                      des_ip <= {des_ip[23:0],gmii_rxd};   //目的IP地址
                  else if(cnt == 5'd19) begin
                      des_ip <= {des_ip[23:0],gmii_rxd}; 
                      //判断IP地址是否为开发板IP地址
                      if((des_ip[23:0] == BOARD_IP[31:8])
                          && (gmii_rxd == BOARD_IP[7:0])) begin  
                          skip_en <=1'b1; 
                          cnt <= 5'd0;
                      end 
                      else begin 
                      //IP错误,停止解析数据 
                          error_en <= 1'b1; 
                          cnt <= 5'd0;
                      end 
                  end 
        else;
              end 
      else;
          end
          st_icmp_head : begin
              if(gmii_rx_dv) begin  //cnt ICMP首8个byte计数
                  cnt <= cnt + 5'd1;
                  if(cnt == 5'd0)
                      icmp_type <= gmii_rxd;
                  else if(cnt == 5'd1)
                      icmp_code <= gmii_rxd ;
                  else if(cnt == 5'd2)
                      icmp_checksum[15:8] <= gmii_rxd;
                  else if(cnt == 5'd3)
                      icmp_checksum[7:0] <= gmii_rxd;
                  else if(cnt == 5'd4)
                      icmp_id[15:8] <= gmii_rxd;
                  else if(cnt == 5'd5)
                      icmp_id[7:0] <= gmii_rxd;
                  else if(cnt == 5'd6)
                      icmp_seq[15:8] <= gmii_rxd;
                  else if(cnt == 5'd7)begin
                      icmp_seq[7:0] <= gmii_rxd;
                      //判断ICMP报文类型是否是回显请求
                      if(icmp_type == ECHO_REQUEST) begin
                              skip_en <=1'b1;
                              cnt <= 5'd0;
                      end 
                      else begin 
                      //ICMP报文类型错误,停止解析数据
                          error_en <= 1'b1; 
                          cnt <= 5'd0;
                      end 
                  end 
        else;
              end 
      else;
          end 
          st_rx_data : begin         
              //接收数据           
              if(gmii_rx_dv) begin
                  rec_en_cnt <= rec_en_cnt + 2'd1;
                  icmp_rx_cnt <= icmp_rx_cnt + 16'd1;
        rec_data <= gmii_rxd;
        rec_en <= 1'b1;
        
        //判断接收到数据的奇偶个数
                 if (icmp_rx_cnt == icmp_data_length - 1) begin                      
                      icmp_rx_data_d0 <= 8'h00;
                      if(icmp_data_length[0])		//判断接收到数据是否为奇数个数
                          reply_checksum_add <= {8'd0,gmii_rxd} + reply_checksum_add;                           
                      else
                          reply_checksum_add <= {icmp_rx_data_d0,gmii_rxd} + reply_checksum_add;    
                  end
        
                  else if(icmp_rx_cnt < icmp_data_length) begin
                      icmp_rx_data_d0 <= gmii_rxd;
                      icmp_rx_cnt <= icmp_rx_cnt + 16'd1;
                      if (icmp_rx_cnt[0] == 1'b1)
                          reply_checksum_add <= {icmp_rx_data_d0,gmii_rxd} + reply_checksum_add; 
                      else
                          reply_checksum_add <= reply_checksum_add; 
                  end
        else;
            
                  if(icmp_rx_cnt == icmp_data_length - 16'd1) begin
                      skip_en <= 1'b1;                    //有效数据接收完成
                      icmp_rx_cnt <= 16'd0;
                      rec_en_cnt <= 2'd0;
                      rec_pkt_done <= 1'b1;               
                      rec_byte_num <= icmp_data_length;
                  end 
        else;
              end
      else;
          end
    
    
          st_rx_end : begin                               //单包数据接收完成
      rec_en <= 1'b0;
              if(gmii_rx_dv == 1'b0 && skip_en == 1'b0)begin
                  reply_checksum <= reply_checksum_add ;
                  skip_en <= 1'b1;
                  reply_checksum_add <= 32'd0;
              end
      else;
          end    
          default : ;
      endcase
  end
end

endmodule

icmp_tx.v

module icmp_tx(    
  input                clk        , //时钟信号
  input                rst_n      , //复位信号,低电平有效
  
  input        [31:0]  reply_checksum, //ICMP数据部分校验和
  input        [15:0]  icmp_id       , //ICMP标识符
  input        [15:0]  icmp_seq      , //ICMP序列号
  input                tx_start_en   , //以太网开始发送信号
  input        [ 7:0]  tx_data       , //以太网待发送数据
  input        [15:0]  tx_byte_num   , //以太网发送的有效字节数
  input        [47:0]  des_mac       , //发送的目标MAC地址
  input        [31:0]  des_ip        , //发送的目标IP地址
  input        [31:0]  crc_data      , //CRC校验数据
  input         [7:0]  crc_next      , //CRC下次校验完成数据
  output  reg          tx_done       , //以太网发送完成信号
  output  reg          tx_req        , //读数据请求信号
  output  reg          gmii_tx_en    , //GMII输出数据有效信号
  output  reg  [7:0]   gmii_txd      , //GMII输出数据
  output  reg          crc_en        , //CRC开始校验使能
  output  reg          crc_clr         //CRC数据复位信号
  );

//parameter define
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55;
//开发板IP地址 192.168.1.123     
parameter BOARD_IP  = {8'd192,8'd168,8'd1,8'd123};
//目的MAC地址 ff_ff_ff_ff_ff_ff
parameter DES_MAC   = 48'hff_ff_ff_ff_ff_ff;
//目的IP地址 192.168.1.102     
parameter DES_IP    = {8'd192,8'd168,8'd1,8'd102};

//状态机状态定义
localparam st_idle       = 8'b0000_0001; //初始状态,等待开始发送信号
localparam st_check_sum  = 8'b0000_0010; //IP首部校验和
localparam st_check_icmp = 8'b0000_0100; //ICMP首部+数据校验
localparam st_preamble   = 8'b0000_1000; //发送前导码+帧起始界定符
localparam st_eth_head   = 8'b0001_0000; //发送以太网帧头
localparam st_ip_head    = 8'b0010_0000; //发送IP首部+ICMP首部
localparam st_tx_data    = 8'b0100_0000; //发送数据
localparam st_crc        = 8'b1000_0000; //发送CRC校验值

//以太网类型定义
localparam  ETH_TYPE     = 16'h0800    ;  //以太网协议类型 IP协议

//以太网数据最小46个字节,IP首部20个字节+ICMP首部8个字节 
//所以数据至少46-20-8=18个字节
localparam  MIN_DATA_NUM = 16'd18;

//parameter define
//ICMP报文类型:回显应答
parameter ECHO_REPLY   = 8'h00;

//reg define
reg  [7:0]   cur_state           ;											//1 				
reg  [7:0]   next_state          ;                          				//1      
reg  [7:0]   preamble[7:0]       ; //前导码
reg  [7:0]   eth_head[13:0]      ; //以太网首部
reg  [31:0]  ip_head[6:0]        ; //IP首部 + ICMP首部                             
reg          start_en_d0         ;                                          //1
reg          start_en_d1         ;   										//1
reg          start_en_d2         ;											//1
reg  [15:0]  tx_data_num         ; //发送的有效数据字节个数					//1
reg  [15:0]  total_num           ; //总字节数								//1
reg          trig_tx_en          ;											//1
reg          skip_en             ; //控制状态跳转使能信号					//1
reg  [4:0]   cnt                 ;											//1
reg  [31:0]  check_buffer        ; //ip首部校验和							//1
reg  [31:0]  check_buffer_icmp   ; //ip首部校验和							//1
reg  [1:0]   tx_bit_sel          ;											//1
reg  [15:0]  data_cnt            ; //发送数据个数计数器						//1
reg          tx_done_t           ;											//1
reg  [4:0]   real_add_cnt        ; //以太网数据实际多发的字节数			//1
                                  
//wire define                       
wire         pos_start_en    ;//开始发送数据上升沿 							//1
wire [15:0]  real_tx_data_num;//实际发送的字节数(以太网最少字节要求)		//1
//*****************************************************
//**                    main code
//*****************************************************

assign  pos_start_en = (~start_en_d2) & start_en_d1;
assign  real_tx_data_num = (tx_data_num >= MIN_DATA_NUM)
                         ? tx_data_num : MIN_DATA_NUM;
                         
//采tx_start_en的上升沿
always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
      start_en_d0 <= 1'b0;
      start_en_d1 <= 1'b0;
  start_en_d2 <= 1'b0;
  end    
  else begin
      start_en_d0 <= tx_start_en;
      start_en_d1 <= start_en_d0;
  start_en_d2 <= start_en_d1;
  end
end 

寄存数据有效字节
always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
      tx_data_num <= 16'd0;
      total_num <= 16'd0;
  end
  else begin
      if(pos_start_en && cur_state==st_idle) begin
          //数据长度
          tx_data_num <= tx_byte_num;
          //IP长度:有效数据+IP首部长度(20bytes)+ICMP首部长度(8bytes)
          total_num <= tx_byte_num + 16'd28;
      end  
  else;
  end
end

//触发发送信号
always @(posedge clk or negedge rst_n) begin
  if(!rst_n) 
      trig_tx_en <= 1'b0;
  else
      trig_tx_en <= pos_start_en;

end

always @(posedge clk or negedge rst_n) begin
  if(!rst_n)
      cur_state <= st_idle;
  else
      cur_state <= next_state;
end

always @(*) begin
  next_state = st_idle;
  case(cur_state)
      st_idle     : begin                               //等待发送数据
          if(skip_en)
              next_state = st_check_sum;
          else
              next_state = st_idle;
      end  
      st_check_sum: begin                               //IP首部校验
          if(skip_en)
              next_state = st_check_icmp;
          else
              next_state = st_check_sum;
      end  
      st_check_icmp: begin                              //ICMP首部校验
          if(skip_en)
              next_state = st_preamble;
          else
              next_state = st_check_icmp;
      end  
      st_preamble : begin                               //发送前导码+帧起始界定符
          if(skip_en)
              next_state = st_eth_head;
          else
              next_state = st_preamble;
      end
      st_eth_head : begin                               //发送以太网首部
          if(skip_en)
              next_state = st_ip_head;
          else
              next_state = st_eth_head;
      end              
      st_ip_head : begin                                //发送IP首部+icmp首部
          if(skip_en)
              next_state = st_tx_data;
          else
              next_state = st_ip_head;
      end
      st_tx_data : begin                                //发送数据
          if(skip_en)
              next_state = st_crc;
          else
              next_state = st_tx_data;
      end
      st_crc: begin                                     //发送CRC校验值
          if(skip_en)
              next_state = st_idle;
          else
              next_state = st_crc;
      end
      default : next_state = st_idle;
  endcase
end

//发送数据
always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
      skip_en <= 1'b0; 
      cnt <= 5'd0;
      check_buffer <= 32'd0;
      check_buffer_icmp <= 32'd0;
      ip_head[1][31:16] <= 16'd0;
      tx_bit_sel <= 2'b0;
      crc_en <= 1'b0;
      gmii_tx_en <= 1'b0;
      gmii_txd <= 8'd0;
      tx_req <= 1'b0;
      tx_done_t <= 1'b0; 
      data_cnt <= 16'd0;
      real_add_cnt <= 5'd0;
      //初始化数组    
      //前导码 7个8'h55 + 1个8'hd5
      preamble[0] <= 8'h55;
      preamble[1] <= 8'h55;
      preamble[2] <= 8'h55;
      preamble[3] <= 8'h55;
      preamble[4] <= 8'h55;
      preamble[5] <= 8'h55;
      preamble[6] <= 8'h55;
      preamble[7] <= 8'hd5;
      //目的MAC地址
      eth_head[0] <= DES_MAC[47:40];
      eth_head[1] <= DES_MAC[39:32];
      eth_head[2] <= DES_MAC[31:24];
      eth_head[3] <= DES_MAC[23:16];
      eth_head[4] <= DES_MAC[15:8];
      eth_head[5] <= DES_MAC[7:0];
      //源MAC地址
      eth_head[6] <= BOARD_MAC[47:40];
      eth_head[7] <= BOARD_MAC[39:32];
      eth_head[8] <= BOARD_MAC[31:24];
      eth_head[9] <= BOARD_MAC[23:16];
      eth_head[10] <= BOARD_MAC[15:8];
      eth_head[11] <= BOARD_MAC[7:0];
      //以太网类型
      eth_head[12] <= ETH_TYPE[15:8];
      eth_head[13] <= ETH_TYPE[7:0];
  end
  else begin
      skip_en <= 1'b0;
      crc_en <= 1'b0;
      gmii_tx_en <= 1'b0;
      tx_done_t <= 1'b0;
      case(next_state)
          st_idle     : begin
              if(trig_tx_en) begin
                  skip_en <= 1'b1; 
                  //版本号:4 首部长度:5(单位:32bit,20byte/4=5)
                  ip_head[0] <= {8'h45,8'h00,total_num};
                  //16位标识,每次发送累加1      
                  ip_head[1][31:16] <= ip_head[1][31:16] + 1'b1;
                  //bit[15:13]: 010表示不分片
                  ip_head[1][15:0] <= 16'h4000;
                  //8'h80:表示生存时间
                  //8'd01:1代表ICMP,2代表IGMP,6代表TCP,17代表UDP
                  ip_head[2] <= {8'h80,8'd01,16'h0000};
                  //源IP地址               
                  ip_head[3] <= BOARD_IP;
                  //目的IP地址    
                  if(des_ip != 32'd0)
                      ip_head[4] <= des_ip;
                  else
                      ip_head[4] <= DES_IP;
                  // 8位icmp TYPE ,8位 icmp CODE 
                  ip_head[5][31:16] <= {ECHO_REPLY,8'h00};
                  //16位identifier 16位sequence
                  ip_head[6] <= {icmp_id,icmp_seq};
                  //更新MAC地址
        
                  if(des_mac != 48'b0) begin
                      //目的MAC地址
                      eth_head[0] <= des_mac[47:40];
                      eth_head[1] <= des_mac[39:32];
                      eth_head[2] <= des_mac[31:24];
                      eth_head[3] <= des_mac[23:16];
                      eth_head[4] <= des_mac[15:8];
                      eth_head[5] <= des_mac[7:0];
                  end
        else;
              end
      else;
          end
          st_check_sum: begin                           //IP首部校验
              cnt <= cnt + 5'd1;
              if(cnt == 5'd0) begin
                  check_buffer <= ip_head[0][31:16] + ip_head[0][15:0]
                                  + ip_head[1][31:16] + ip_head[1][15:0]
                                  + ip_head[2][31:16] + ip_head[2][15:0]
                                  + ip_head[3][31:16] + ip_head[3][15:0]
                                  + ip_head[4][31:16] + ip_head[4][15:0];
              end
              else if(cnt == 5'd1)                      //可能出现进位,累加一次
                  check_buffer <= check_buffer[31:16] + check_buffer[15:0];
              else if(cnt == 5'd2) begin                //可能再次出现进位,累加一次
                  check_buffer <= check_buffer[31:16] + check_buffer[15:0];
              end                             
              else if(cnt == 5'd3) begin                //按位取反 
                  skip_en <= 1'b1;
                  cnt <= 5'd0;            
                  ip_head[2][15:0] <= ~check_buffer[15:0];
              end 
      else;
          end
          st_check_icmp: begin                           //ICMP首部+数据校验
              cnt <= cnt + 5'd1;
              if(cnt == 5'd0) begin
                  check_buffer_icmp <= ip_head[5][31:16] 
                                  + ip_head[6][31:16] + ip_head[6][15:0]
                                  + reply_checksum;
              end
              else if(cnt == 5'd1)                      //可能出现进位,累加一次
                  check_buffer_icmp <= check_buffer_icmp[31:16] + check_buffer_icmp[15:0];
              else if(cnt == 5'd2) begin                //可能再次出现进位,累加一次
                  check_buffer_icmp <= check_buffer_icmp[31:16] + check_buffer_icmp[15:0];
              end                             
              else if(cnt == 5'd3) begin                //按位取反
                  skip_en <= 1'b1;
                  cnt <= 5'd0;
                  // ICMP:16位校验和
                  ip_head[5][15:0] <= ~check_buffer_icmp[15:0];
              end
      else;
          end
          st_preamble : begin                     //发送前导码+帧起始界定符
              gmii_tx_en <= 1'b1;
              gmii_txd <= preamble[cnt];
              if(cnt == 5'd7) begin
                  skip_en <= 1'b1;
                  cnt <= 5'd0;
              end
              else
                  cnt <= cnt + 5'd1;
          end
    
    
    
          st_eth_head : begin                      //发送以太网首部
              gmii_tx_en <= 1'b1;
              crc_en <= 1'b1;
              gmii_txd <= eth_head[cnt];
              if (cnt == 5'd13) begin
                  skip_en <= 1'b1;
                  cnt <= 5'd0;
              end
              else
                  cnt <= cnt + 5'd1;
          end
    
    
          st_ip_head  : begin                        //发送IP首部
              crc_en <= 1'b1;
              gmii_tx_en <= 1'b1;
              tx_bit_sel <= tx_bit_sel + 2'd1;
              if(tx_bit_sel == 3'd0)
                  gmii_txd <= ip_head[cnt][31:24];
              else if(tx_bit_sel == 3'd1)
                  gmii_txd <= ip_head[cnt][23:16];
              else if(tx_bit_sel == 3'd2) begin
                  gmii_txd <= ip_head[cnt][15:8];
                  if(cnt == 5'd6) begin
                      //提前读请求数据,等待数据有效时发送
                      tx_req <= 1'b1;
                  end
              end 
              else if(tx_bit_sel == 3'd3) begin
                  gmii_txd <= ip_head[cnt][7:0];
                  if(cnt == 5'd6) begin
                      skip_en <= 1'b1;
                      cnt <= 5'd0;
                  end    
                  else
                      cnt <= cnt + 5'd1;
              end
      else;
          end
          st_tx_data  : begin                           //发送数据
              crc_en <= 1'b1;
              gmii_tx_en <= 1'b1;
      gmii_txd <= tx_data;
              tx_bit_sel <= 3'd0; 
              if(data_cnt < tx_data_num - 16'd1)
                  data_cnt <= data_cnt + 16'd1;
              else if(data_cnt == tx_data_num - 16'd1)begin
                  //如果发送的有效数据少于18个字节,在后面填补充位
                  //补充的值为最后一次发送的有效数据
                  if(data_cnt + real_add_cnt < real_tx_data_num - 16'd1)
                      real_add_cnt <= real_add_cnt + 5'd1;  
                  else begin
                      skip_en <= 1'b1;
                      data_cnt <= 16'd0;
                      real_add_cnt <= 5'd0;
                  end    
              end
      else;
      
      if(data_cnt == tx_data_num - 16'd2)
        tx_req <= 1'b0; 
      else ;
      
          end 
          st_crc      : begin                          //发送CRC校验值
              gmii_tx_en <= 1'b1;
              tx_bit_sel <= tx_bit_sel + 3'd1;
      tx_req <= 1'b0; 
              if(tx_bit_sel == 3'd0)
                  gmii_txd <= {~crc_next[0], ~crc_next[1], ~crc_next[2],~crc_next[3],
                               ~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};
              else if(tx_bit_sel == 3'd1)
                  gmii_txd <= {~crc_data[16], ~crc_data[17], ~crc_data[18],~crc_data[19],
                               ~crc_data[20], ~crc_data[21], ~crc_data[22],~crc_data[23]};
              else if(tx_bit_sel == 3'd2) begin
                  gmii_txd <= {~crc_data[8], ~crc_data[9], ~crc_data[10],~crc_data[11],
                               ~crc_data[12], ~crc_data[13], ~crc_data[14],~crc_data[15]};
              end
              else if(tx_bit_sel == 3'd3) begin
                  gmii_txd <= {~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],
                               ~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};
                  tx_done_t <= 1'b1;
                  skip_en <= 1'b1;
              end
      else;
          end
          default :;
      endcase
  end
end

//发送完成信号及crc值复位信号
always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
      tx_done <= 1'b0;
      crc_clr <= 1'b0;
  end
  else begin
      tx_done <= tx_done_t;
      crc_clr <= tx_done_t;
  end
end

endmodule

rgmii_rx.v

module  rgmii_rx(
    input             idelay_clk   ,    // 200Mhz鏃堕挓锛孖DELAY鏃堕挓


    // 杩欎竴娈垫槸浠ュお缃慠GNII鎺ュ彛
    input             rgmii_rxc    ,   // RGMII鎺ユ敹鏃堕挓
    input             rgmii_rx_ctl ,   // RGMII鎺ユ敹鏃堕挓鎺у埗淇″彿
    input   [3 : 0]   rgmii_rxd    ,   // RGMII鎺ユ敹鏁版嵁

    // 浠ュお锟�? GMII鎺ュ彛
    output            gmii_rx_clk  ,
    output            gmii_rx_dv   ,
    output  [7 : 0]   gmii_rxd
  );

  // -------------------------------------------//
  // ------parameter  and define--------------- //
  // -------------------------------------------//

  parameter         IDELAY_VALUE  =  0 ;

  wire              rgmii_rxc_bufg     ;   //鍏ㄥ眬鏃堕挓缂撳啿
  wire              rgmii_rxc_bufio    ;   //鍏ㄥ眬鏃堕挓IO缂撳瓨 -- 鎺ュ湪浜咮UFIO鍚庨潰锟�?
  wire              rgmii_rx_ctl_delay ;
  wire     [3 : 0]  rgmii_rxd_delay    ;
  wire     [1 : 0]  gmii_rxdv_t        ;   // 涓や綅GMII鎺ユ敹淇″彿



  // ---------------------------------------------//
  // ------------ main  code ---------------------//
  //----------------------------------------------//

  assign  gmii_rx_clk  =  rgmii_rxc_bufg ;
  assign  gmii_rx_dv   =  gmii_rxdv_t[0] & gmii_rxdv_t[1] ;


  // 鍏ㄥ眬鏃堕挓缂撳瓨
  BUFG BUFG_inst(
         .I       (rgmii_rxc) ,
         .O       (rgmii_rxc_bufg)
       );

  // 鍏ㄥ眬鏃堕挓缂撳瓨
  BUFIO BUFIO_inst (
         .I         (rgmii_rxc),
         .O         (rgmii_rxc_bufio)
       );

  //锟斤拷锟斤拷锟斤拷时锟斤拷锟斤拷
// Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
(* IODELAY_GROUP = "rgmii_rx_delay" *) 
IDELAYCTRL  IDELAYCTRL_inst (
    .RDY(),                      // 1-bit output: Ready output
    .REFCLK(idelay_clk),         // 1-bit input: Reference clock input
    .RST(1'b0)                   // 1-bit input: Active high reset input
);

//rgmii_rx_ctl锟斤拷锟斤拷锟斤拷时锟斤拷双锟截诧拷锟斤拷
(* IODELAY_GROUP = "rgmii_rx_delay" *) 
IDELAYE2 #(
  .IDELAY_TYPE     ("FIXED"),           // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
  .IDELAY_VALUE    (IDELAY_VALUE),      // Input delay tap setting (0-31)
  .REFCLK_FREQUENCY(200.0)              // IDELAYCTRL clock input frequency in MHz 
)
u_delay_rx_ctrl (
  .CNTVALUEOUT     (),                  // 5-bit output: Counter value output
  .DATAOUT         (rgmii_rx_ctl_delay),// 1-bit output: Delayed data output
  .C               (1'b0),              // 1-bit input: Clock input
  .CE              (1'b0),              // 1-bit input: enable increment/decrement
  .CINVCTRL        (1'b0),              // 1-bit input: Dynamic clock inversion input
  .CNTVALUEIN      (5'b0),              // 5-bit input: Counter value input
  .DATAIN          (1'b0),              // 1-bit input: Internal delay data input
  .IDATAIN         (rgmii_rx_ctl),      // 1-bit input: Data input from the I/O
  .INC             (1'b0),              // 1-bit input: Increment / Decrement tap delay
  .LD              (1'b0),              // 1-bit input: Load IDELAY_VALUE input
  .LDPIPEEN        (1'b0),              // 1-bit input: Enable PIPELINE register
  .REGRST          (1'b0)               // 1-bit input: Active-high reset tap-delay input
);

//锟斤拷锟斤拷双锟截诧拷锟斤拷锟侥达拷锟斤拷
IDDR #(
    .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"),// "OPPOSITE_EDGE", "SAME_EDGE" 
                                        //    or "SAME_EDGE_PIPELINED" 
    .INIT_Q1  (1'b0),                   // Initial value of Q1: 1'b0 or 1'b1
    .INIT_Q2  (1'b0),                   // Initial value of Q2: 1'b0 or 1'b1
    .SRTYPE   ("SYNC")                  // Set/Reset type: "SYNC" or "ASYNC" 
) u_iddr_rx_ctl (
    .Q1       (gmii_rxdv_t[0]),         // 1-bit output for positive edge of clock
    .Q2       (gmii_rxdv_t[1]),         // 1-bit output for negative edge of clock
    .C        (rgmii_rxc_bufio),        // 1-bit clock input
    .CE       (1'b1),                   // 1-bit clock enable input
    .D        (rgmii_rx_ctl_delay),     // 1-bit DDR data input
    .R        (1'b0),                   // 1-bit reset
    .S        (1'b0)                    // 1-bit set
);

//rgmii_rxd锟斤拷锟斤拷锟斤拷时锟斤拷双锟截诧拷锟斤拷
genvar i;
generate for (i=0; i<4; i=i+1)
    (* IODELAY_GROUP = "rgmii_rx_delay" *) 
    begin : rxdata_bus
        //锟斤拷锟斤拷锟斤拷时           
        (* IODELAY_GROUP = "rgmii_rx_delay" *) 
        IDELAYE2 #(
          .IDELAY_TYPE     ("FIXED"),           // FIXED,VARIABLE,VAR_LOAD,VAR_LOAD_PIPE
          .IDELAY_VALUE    (IDELAY_VALUE),      // Input delay tap setting (0-31)    
          .REFCLK_FREQUENCY(200.0)              // IDELAYCTRL clock input frequency in MHz
        )
        u_delay_rxd (
          .CNTVALUEOUT     (),                  // 5-bit output: Counter value output
          .DATAOUT         (rgmii_rxd_delay[i]),// 1-bit output: Delayed data output
          .C               (1'b0),              // 1-bit input: Clock input
          .CE              (1'b0),              // 1-bit input: enable increment/decrement
          .CINVCTRL        (1'b0),              // 1-bit input: Dynamic clock inversion
          .CNTVALUEIN      (5'b0),              // 5-bit input: Counter value input
          .DATAIN          (1'b0),              // 1-bit input: Internal delay data input
          .IDATAIN         (rgmii_rxd[i]),      // 1-bit input: Data input from the I/O
          .INC             (1'b0),              // 1-bit input: Inc/Decrement tap delay
          .LD              (1'b0),              // 1-bit input: Load IDELAY_VALUE input
          .LDPIPEEN        (1'b0),              // 1-bit input: Enable PIPELINE register 
          .REGRST          (1'b0)               // 1-bit input: Active-high reset tap-delay
        );
        
        //锟斤拷锟斤拷双锟截诧拷锟斤拷锟侥达拷锟斤拷
        IDDR #(
            .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"),// "OPPOSITE_EDGE", "SAME_EDGE" 
                                                //    or "SAME_EDGE_PIPELINED" 
            .INIT_Q1  (1'b0),                   // Initial value of Q1: 1'b0 or 1'b1
            .INIT_Q2  (1'b0),                   // Initial value of Q2: 1'b0 or 1'b1
            .SRTYPE   ("SYNC")                  // Set/Reset type: "SYNC" or "ASYNC" 
        ) u_iddr_rxd (
            .Q1       (gmii_rxd[i]),            // 1-bit output for positive edge of clock
            .Q2       (gmii_rxd[4+i]),          // 1-bit output for negative edge of clock
            .C        (rgmii_rxc_bufio),        // 1-bit clock input rgmii_rxc_bufio
            .CE       (1'b1),                   // 1-bit clock enable input
            .D        (rgmii_rxd_delay[i]),     // 1-bit DDR data input
            .R        (1'b0),                   // 1-bit reset
            .S        (1'b0)                    // 1-bit set
        );
    end
endgenerate

endmodule

rgmii_tx.v

module   rgmii_tx(
    // GMII 发送端口
    input                gmii_tx_clk  ,   // GMII 发送信号
    input                gmii_tx_en   ,   // GMII 输出数据有效信号
    input    [7 : 0]     gmii_txd     ,

    //RGMII 发送端口
    output               rgmii_txc    ,
    output               rgmii_tx_ctl ,
    output   [3 : 0]     rgmii_txd
  );


  // --------------------------------------------- //
  // -------------- main code -------------------- //
  // --------------------------------------------- //

  assign  rgmii_txc = gmii_tx_clk ; // 输出时钟

  // 输出双沿采样
  ODDR #(
         .DDR_CLK_EDGE ("SAME_EDGE")            ,
         .INIT (1'b0)                           ,
         .SRTYPE ("SYNC")
       ) ODDR_inst (
         .Q (rgmii_tx_ctl)                      ,
         .C (gmii_tx_clk)                       ,
         .CE (1'b1)                             ,
         .D1 (gmii_tx_en)                       ,  //  positive egde
         .D2 (gmii_tx_en)                       ,  //  negative edge
         .R (1'b0)                              ,
         .S (1'b0)
       );

  genvar i;
  generate for (i=0; i<4; i=i+1)
    begin : txdata_bus

      ODDR #(
             .DDR_CLK_EDGE ("SAME_EDGE")    ,
             .INIT (1'b0),
             .SRTYPE ("SYNC")
           ) ODDR_inst (
             .Q (rgmii_txd[i])              ,
             .C (gmii_tx_clk)               ,
             .CE (1'b1)                     ,
             .D1 (gmii_txd[i])              ,
             .D2 (gmii_txd[4+i])            ,
             .R (1'b0)                      ,
             .S (1'b0)
           );
    end
  endgenerate

endmodule

其中icmp_tx.v icmp_rx.v 与icmp_top.v 有tb如下

`timescale  1ns/1ns

module  tb_icmp  ;

  parameter   T           =  8   ;      // 鏃堕挓鍛ㄦ湡 8ns
  parameter   OP_CYCLE    =  100 ;      // 鎿嶄綔鍛ㄦ湡 鍙戦?佸懆鏈熼棿闅?


  //寮?鍙戞澘MAC鍦板潃 00-11-22-33-44-55
  parameter  BOARD_MAC = 48'h00_11_22_33_44_55          ;
  //寮?鍙戞澘IP鍦板潃 192.168.1.10
  parameter  BOARD_IP  = {8'd192,8'd168,8'd1,8'd10}     ;
  //鐩殑MAC鍦板潃 ff_ff_ff_ff_ff_ff
  parameter  DES_MAC   = 48'hff_ff_ff_ff_ff_ff          ;
  //鐩殑IP鍦板潃 192.168.1.10
  parameter  DES_IP    = {8'd192,8'd168,8'd1,8'd10}     ;






  // =============================================================//
  //                define   and  parameter                       //
  // =============================================================//

  //reg define
  reg           gmii_clk        ;    //鏃堕挓淇″彿
  reg           sys_rst_n       ;   //澶嶄綅淇″彿
  reg           tx_start_en     ;
  reg   [31:0]  tx_data         ;
  reg   [15:0]  tx_byte_num     ;
  reg   [47:0]  des_mac         ;
  reg   [31:0]  des_ip          ;
  reg   [3:0]   flow_cnt        ;
  reg   [13:0]  delay_cnt       ;

  //wire define
  wire          gmii_rx_clk     ; //GMII鎺ユ敹鏃堕挓
  wire          gmii_rx_dv      ; //GMII鎺ユ敹鏁版嵁鏈夋晥淇″彿
  wire  [7:0]   gmii_rxd        ; //GMII鎺ユ敹鏁版嵁
  wire          gmii_tx_clk     ; //GMII鍙戦?佹椂閽?
  wire          gmii_tx_en      ; //GMII鍙戦?佹暟鎹娇鑳戒俊鍙?
  wire  [7:0]   gmii_txd        ; //GMII鍙戦?佹暟鎹?
  wire          tx_done         ;
  wire          tx_req          ;



  // ------------------------- main  code --------------------------- //

  assign   gmii_rx_clk  =  gmii_clk    ;
  assign   gmii_tx_clk  =  gmii_clk    ;
  assign   gmii_rx_dv   =  gmii_tx_en  ;
  assign   gmii_rxd     =  gmii_txd    ;



  initial
  begin
    gmii_clk     =    0    ;
    sys_rst_n    =    0    ;
    #(T+1)
     sys_rst_n    =    1    ;
  end


  //
  always #(T/2)  gmii_clk  =  ~gmii_clk  ;

  always@(posedge  gmii_clk  or negedge  sys_rst_n)
  begin
    if(sys_rst_n == 0)
    begin
      tx_start_en   <=    0   ;
      tx_data       <=    0   ;
      tx_byte_num   <=    0   ;
      des_mac       <=    0   ;
      des_ip        <=    0   ;
      delay_cnt     <=    0   ;
      flow_cnt      <=    0   ;
    end
    else
    begin
      case(flow_cnt)
        0 :
        begin
          flow_cnt   <=  flow_cnt  +  1  ;
        end

        1 :
        begin
          tx_start_en  <=  1    ;
          tx_byte_num  <=  20   ;
          flow_cnt     <=  flow_cnt  +  1   ;
        end

        2:
        begin
          tx_start_en  <=  0    ;    // 鑴夊啿淇″彿
          flow_cnt     <=  flow_cnt  + 1   ;
        end

        3 :
        begin
          if(tx_req == 1)
          begin
            tx_data   <=   tx_data  +  1  ;
          end
          if(tx_done == 1)
          begin
            flow_cnt  <=   flow_cnt  +  1  ;
            tx_data   <=   0  ;
          end
        end

        4 :  // 鎷夐珮绛夊緟
        begin
          delay_cnt  <=   delay_cnt  +  1  ;
          if(delay_cnt == OP_CYCLE - 1)
            flow_cnt   <=   flow_cnt  + 1  ;
        end

        5:
        begin
          tx_start_en  <=  1   ;
          tx_byte_num  <=  28  ;
          flow_cnt     <=  flow_cnt  +  1  ;
        end

        6:
        begin
          tx_start_en  <=  0   ;
          flow_cnt     <=  flow_cnt  + 1  ;
        end

        7:
        begin
          if(tx_req == 1)
            tx_data  <=  tx_data  +  1  ;
          if(tx_done == 1)
          begin
            flow_cnt  <=  flow_cnt  +  1  ;
            tx_data   <=  0    ;
          end
        end

        default:
          ;
      endcase
    end
  end


  icmp u_icmp(
         .rst_n         ( sys_rst_n         ),
         .gmii_rx_clk   ( gmii_rx_clk   ),
         .gmii_rx_dv    ( gmii_rx_dv    ),
         .gmii_rxd      ( gmii_rxd      ),
         .gmii_tx_clk   ( gmii_tx_clk   ),
         .gmii_tx_en    ( gmii_tx_en    ),
         .gmii_txd      ( gmii_txd      ),

         .rec_pkt_done  (   ),
         .rec_en        (          ),
         .rec_data      ( ),
         .rec_byte_num  (   ),
         .tx_start_en   ( tx_start_en   ),
         .tx_data       ( tx_data       ),
         .tx_byte_num   ( tx_byte_num   ),
         .des_mac       ( des_mac       ),
         .des_ip        ( des_ip        ),
         .tx_done       ( tx_done       ),
         .tx_req        ( tx_req        )
       );


endmodule

其中的eth_icmp_test.v模块中的clk_wiz_0 与 fifo_generator_0都是xilinx官方IP

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2167497.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux:进程信号】

五个概念&#xff1a; 多个执行流能看到的一份资源叫做共享资源同步和互斥两种方式保护的公共资源叫做临界资源互斥&#xff1a;任何时候只能有一个进程访问该资源如何访问资源&#xff1f;&#xff1a;本质资源访问就是代码访问&#xff0c;代码访问共享资源不访问的共享资源对…

云原生|浅谈云原生中的对象存储之MinIO 的使用

一、什么是对象储存 对象存储&#xff08;Object Storage&#xff09;以对象的形式存储和管理数据&#xff0c;这些对象可以是任何类型的数据&#xff0c;例如 PDF&#xff0c;视频&#xff0c;音频&#xff0c;文本或其他文件类型。对象存储使用分布式存储架构&#xff0c;数据…

基于springboot+vue医院挂号就诊系统设计与实现

基于springbootvue医院挂号就诊系统设计与实现 springboot180基于spring boot的医院挂号就诊系统 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费…

解决macOS MySQL安装后不能远程访问的问题

主要是因为我的后端服务是通过docker部署的, 无法和宿主机的MySQL进行通信. 首先输入 use mysql; 之后输入 update user set host “%” where user “root”; 最后输入 flush privileges; 合起来就是: use mysql;update user set host "%" where user &qu…

视频集成与融合项目中需要视频编码,但是分辨率不兼容怎么办?

在众多视频整合项目中&#xff0c;一个显著的趋势是融合多元化的视频资源&#xff0c;以实现统一监管与灵活调度。这一需求促使项目团队不断探索新的集成方案&#xff0c;确保不同来源的视频流能够无缝对接&#xff0c;共同服务于统一的调看与管理平台&#xff0c;进而提升整体…

工具介绍---效率高+实用

Visual Studio Code (VS Code) 功能特点&#xff1a; 智能代码提示&#xff1a;内置的智能代码提示功能可以自动完成函数、变量等的输入&#xff0c;提高代码编写速度。插件丰富&#xff1a;支持成千上万的扩展插件&#xff0c;例如代码片段、主题、Linting等&#xff0c;能够…

Skip、Compose、Flutter和RN

哈喽&#xff0c;我是老刘 前几天看到一个新的跨平台框架Skip&#xff0c;它的底层技术方案是转译。 感觉还挺巧的&#xff0c;因为不久前刚刚做了一个内部分享&#xff0c;就讲到了虚拟化的转译方案。 其实跨平台开发的技术和虚拟化的技术有很多相通的地方。 正好本文就把跨平…

轻量级日志管理系统SpringBoot3+Loki+grafana的使用实例

目录 文章目录 目录1、简介2、SpringBoot3应用发送日志到Loki2.1、基本介绍2.2、添加依赖2.3、配置文件application.yml2.4、创建logback配置2.5、添加日志示例2.6、运行SpringBoot3 3、在grafana中查看日志3.1、登录grafana3.2、查询日志3.3、查询我们的SpringBoot发送过来的日…

Linux部署python web项目Flask + gunicorn + nginx

文章目录 一、安装python&使用虚拟环境二、python程序重要参数加密2.1 非对称加密&#xff08;RSA&#xff09;2.2 生成密钥对2.4 以连接数据库参数加密为例2.4.1 工具类RSA.py 三、一个简单的Flask项目四、安装配置gunicorn4.1 安装4.2 启动/配置(选择eventlet)4.2.1 命令…

随机信号介绍

一、信号分类 1.1 确定性信号 能够以确定的时间函数表示的信号,比如: 1.2 随机信号 也成为不确定信号或者随机过程,不是时间的确定函数。下面为随机信号的一个样本: 二、随机变量 2.1 定义 随机变量是指变量X的取值由每次随机试验的结果决定。 如果每次随机试验的结果…

JS封装函数转换时间案例

<script>let time prompt("请输入需要转换的秒数总数")function getTime(x) {let h parseInt(x / 60 / 60 % 60)let m parseInt(x / 60 % 60)let s parseInt(x % 60)h h > 10 ? h : 0 hm m > 10 ? m : 0 ms s > 10 ? s : 0 sreturn [h, …

CorePress Pro 网站加载慢 WordPress

一般来说是你用了「CorePress天气模块」 解决方案&#xff1a;这个插件从你右侧边栏里删掉就可以了&#xff08;上方的图中已经是删掉后的效果了&#xff09; 寻找加载时间长的原因&#xff1a; 谷歌浏览器F12->网络->打开录制->ShiftF5 得出结论&#xff1a;和风天气…

【JavaEE】数据链路层协议和DNS

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【Java】登神长阶 史诗般的Java成神之路 &#x1f45c;一.以太网 以太网&#xff08;Ethernet&#xff09;是一种局域网技术&#xff0c;它定义了开放系统互连&#xff08;OSI&#xff09;模型中的物理…

【Pytorch图像+序列双输入网络源代码】

Python&#xff0c;Pytorch构建双输入网络&#xff0c;图像序列双输入&#xff0c;进行二分类。 如何基于Pytorch构建双输入的网络是曾经长时间困扰本人的问题&#xff0c;现已弄明白。本程序示例是一端输入图像&#xff0c;一端输入序列。 整体工作如下&#xff1a; 1、加载数…

CSS清除浮动的多种方法

目录 非 VIP 用户可前往公众号回复“css”进行免费阅读 给浮动元素的祖先元素加高度 给 div 写一个 clear:both; 属性(margin 失效) clear:both; 隔墙法 clear:both; 内墙法 父级 div 定义伪类:after 和 zoom(推荐使用) overflow:hidden;(能够让 margin 生效) 非 …

机器学习-KNN分类算法

1.1 KNN分类 KNN分类算法&#xff08;K-Nearest-Neighbors Classification&#xff09;&#xff0c;又叫K近邻算法。它是概念极其简单&#xff0c;而效果又很优秀的分类算法。1967年由Cover T和Hart P提出。 KNN分类算法的核心思想&#xff1a;如果一个样本在特征空间中的k个最…

【机器学习】——线性回归(自我监督学习)

文章目录 1. 线性回归的定义2. 线性回归的模型3. 线性回归的核心思想4. 线性回归的求解5. 线性回归的假设6. 模型评估7. 线性回归的优缺点8. 线性回归的扩展9. 线性回归的实际应用10. 示例代码&#xff08;Python实现&#xff09; 线性回归详细介绍 1. 线性回归的定义 线性回归…

【已解决】Ubuntu 24.04 修改 ssh 连接端口无效

用编辑器打开 /etc/ssh/sshd_config 文件 sudo vim /etc/ssh/sshd_config将 Port 22 改为你想要的端口号&#xff08;这里演示就设置为4080&#xff09; 用编辑器打开 /lib/systemd/system/ssh.socket 文件 sudo vim /lib/systemd/system/ssh.socket将 ListenStream22 中的 2…

Windows 10 系统安装 FFmpeg 查看、转换、编辑音频文件

1、FFmpeg官网&#xff1a;FFmpeg 点击下载 可以选择下载full版本 下载之后解压到指定目录&#xff0c;在系统环境变量 Path 里面新增环境变量 打开CMD终端运行 ffmpeg -version 查看是否安装成功。 2、基本命令 查看音频基本信息 ffprobe 1.mp3 ##输出 [mp3 000002ab334405…

Xcode16 iOS18 编译问题适配

问题1&#xff1a;ADClient编译报错问题 报错信息 Undefined symbols for architecture arm64:"_OBJC_CLASS_$_ADClient", referenced from:in ViewController.o ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit co…