FPGA千兆网系列1-----ARP发送与接收(自动)

目录

  • 开发环境
  • GMII接口介绍
  • 以太网帧介绍
  • ARP帧介绍
    • ARP帧存在的目的
    • ARP 帧工作原理
    • ARP数据报格式
      • 使用以太网发包工具组ARP包
      • 用wireshark软件抓包
      • 用CRC计算软件计算CRC校验值
        • 手动计算CRC
        • 自动计算CRC
  • 发送与接收模块设计
    • 发送模块
    • 接收模块
  • 调试
    • 发送
    • 接收
    • 自动CRC
    • wireshark
    • 福利

开发环境

  • 小梅哥AC6102开发板(内含RTL8211E千兆以太网phy芯片)
  • quartus prime17.1
  • win10
  • 千兆网卡 这个需要电脑支持,不支持就玩不了了
  • 本教程是根据小梅哥的百兆网arp教程改编过来的
    FPGA千兆网系列1-----ARP发送与接收(自动)_第1张图片

GMII接口介绍

FPGA千兆网系列1-----ARP发送与接收(自动)_第2张图片

  • gmii_tx_clk是由fpga输出给phy的,注意方向,gmii_rx_clk则是phy输出给fpga,所以我们系统时钟就采用了gmii_rx_clk即可
    FPGA千兆网系列1-----ARP发送与接收(自动)_第3张图片
    FPGA千兆网系列1-----ARP发送与接收(自动)_第4张图片
  • 由于系统时钟采用了gmii_rx_clk,这个引脚并没有连接到全局时钟上,所以我们可以使用altclkctrl ip核,使该时钟连接到全局时钟上,即便这样,系统时钟到了125Mhz,频率比较高,最好是要做一下时钟的约束,我们只需要简单了写个clk的时序约束就可以了,告诉fpga我们的系统时钟是125Mhz,然后他就会按这个频率去优化时序的
    时序约束
  • 绑定引脚时,可以写个引脚的配置文件,但是要注意总线也是要一个一个绑定的,如gmii_tx_data[0],gmii_tx_data[1]…千万不要写成了gmii_tx_data0,gmii_tx_data1…
    FPGA千兆网系列1-----ARP发送与接收(自动)_第5张图片
  • 异步释放,同步复位,避免复位引起亚稳态
    FPGA千兆网系列1-----ARP发送与接收(自动)_第6张图片
//异步释放,同步复位
always  @(posedge clk or negedge s_rst_n)begin
    if(s_rst_n==1'b0)begin
        rst_n_r <=  0;
    end
    else begin
        rst_n_r <=  {rst_n_r[0],1'b1};
    end
end
 
assign  rst_n   =   rst_n_r[1];
  • 以上是我自己遇到过的部分bug,如果实验失败了,首先要检查时钟是否有效,引脚是否绑定正确,时序是否违规。
    FPGA千兆网系列1-----ARP发送与接收(自动)_第7张图片
    FPGA千兆网系列1-----ARP发送与接收(自动)_第8张图片

以太网帧介绍

FPGA千兆网系列1-----ARP发送与接收(自动)_第9张图片

ARP帧介绍

ARP帧存在的目的

在网络通讯时,源主机的应用程序知道目的主机的 IP 地址和端口号,却不知道目的主机的硬件地址,
而数据包首先是被网卡接收到再去处理上层协议的,如果接收到的数据包的硬件地址与本机不符,则直接丢弃。
因此在通讯前必须获得目的主机的硬件地址。 ARP 协议就起到这个作用。

ARP 帧工作原理

源主机发出 ARP 请求,询问“IP 地址是 192.168.0.1 的主机的硬件地址是多少”,并将这
个请求广播到本地网段(以太网帧首部的硬件地址填 FF:FF:FF:FF:FF:FF 表示广播),目的主机
接收到广播的 ARP 请求,发现其中的 IP 地址与本机相符,则发送一个 ARP 应答数据包给源
主机,将自己的硬件地址填写在应答包中。
每台主机都维护一个 ARP 缓存表,可以用 arp -a 命令查看。缓存表中的表项有过期时间
(一般为 20 分钟),如果 20 分钟内没有再次使用某个表项,则该表项失效,下次还要发 ARP
请求来获得目的主机的硬件地址。想一想,为什么表项要有过期时间而不是一直有效?
ARP 数据报的格式如下图所示(该图出自[TCPIP]):

ARP数据报格式

使用以太网发包工具组ARP包

FPGA千兆网系列1-----ARP发送与接收(自动)_第10张图片
FPGA千兆网系列1-----ARP发送与接收(自动)_第11张图片
注意到源 MAC 地址、目的 MAC 地址在以太网首部和 ARP 请求中各出现一次,对于链路层为以太网的情况
是多余的,但如果链路层是其它类型的网络则有可能是必要的。硬件类型指链路层网络类型, 1 为以太网,
协议类型指要转换的地址类型, 0x0800 为 IP 地址,后面两个地址长度对于以太网地址和 IP 地址分别为
6 和 4(字节), op 字段为 1 表示 ARP 请求, op字段为 2 表示 ARP 应答。
FPGA千兆网系列1-----ARP发送与接收(自动)_第12张图片

FPGA千兆网系列1-----ARP发送与接收(自动)_第13张图片

用wireshark软件抓包

FPGA千兆网系列1-----ARP发送与接收(自动)_第14张图片

用CRC计算软件计算CRC校验值

手动计算CRC

完整的以太网数据包结尾是帧校验(FCS), 即 CRC32 值, 由于整个过程中都没有出现
CRC 校验字段的内容,而我们使用 FPGA 发包需要计算 CRC 校验字段, CRC 校验字段的产生
可以使用软件或硬件的形式产生,本节实验,为了降低难度, 使用 CRC 计算软件手动计算出
数据报的 CRC 值, 因此使用专用 CRC 校验工具对数据内容进行计算以得到校验字:
CRC 校验软件计算校验内容
FPGA千兆网系列1-----ARP发送与接收(自动)_第15张图片

第一次发的时候,我将arp中的des_mac默认为00_00_00_00_00_00的,后面通过arp应答包,解析出des_mac的值后,crc就要做相应的更新,为了方便,我是直接用crc计算校正值的,不同的电脑mac不一样,所以需要自己计算一下。

always	@(posedge clk or negedge rst_n)begin
	if(rst_n == 1'b0)begin
		crc_value	<=	32'h967d1d69;
	end
	else begin
		crc_value	<=	32'h24495d2f;	//这个值要根据你们自己电脑的mac来计算
	end
end

自动计算CRC

自己看代码研究一下吧,关键点是要时序对齐,注意下降沿采集的技巧(可提前半拍把crc送走,否则时序会出问题!!!),这个crc算法倒是不用过于深究,直接用就可以了。

/************************************************************************
 * Author        : Wen Chunyang
 * Email         : [email protected]
 * Create time   : 2018-11-19 22:27
 * Last modified : 2018-11-19 22:27
 * Filename      : crc.v
 * Description   : 
 * *********************************************************************/
module  crc(
    input                       clk                             ,
    input                       rst_n                           ,
    input         [ 7: 0]       din                             ,
    input                       en                              ,
    input                       init                            ,
    output  reg   [31: 0]       crc                             
);
//======================================================================\
//************** Define Parameter and Internal Signals *****************
//======================================================================/
wire    [ 7: 0]                 data                            ;
wire    [31: 0]                 crc_next                        ;
//======================================================================\
//**************************** Main Code *******************************
//======================================================================/
assign data={din[0],din[1],din[2],din[3],din[4],din[5],din[6],din[7]};

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

//crc 这里用下降沿采集,是为了crc能提前半拍送到eth_mac_send模块中,这个很重要
//否则crc高8位会发送出错,自己可以修改代码试试看
always  @(negedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        crc <=  {32{1'b1}};
    end
    else if(init)begin
        crc <=  {32{1'b1}};
    end
    else if(en)begin
        crc <=  crc_next;
    end
end


endmodule

发送与接收模块设计

发送模块

  • 首先是上电等待10ms,电压稳定后,再去准备arp包,并且每隔1s发送一次arp包
/************************************************************************
 * Author        : Wen Chunyang
 * Email         : [email protected]
 * Create time   : 2018-11-16 22:20
 * Last modified : 2018-11-16 22:20
 * Filename      : eth_send_test.v
 * Description   : 
 * *********************************************************************/
module  eth_send(
        input                   clk                             ,
        input                   rst_n                           ,
        //Eth
        input         [47: 0]   des_mac                         ,
        input                   update                          ,
        output  wire            tx_trig                         ,
        output  wire  [31: 0]   crc                             ,
        output  reg   [ 9: 0]   wr_addr                         ,
        output  wire            wr_clk                          ,
        output  reg             wr_en                           ,
        output  reg   [ 7: 0]   wr_data 
);
//======================================================================\
//************** Define Parameter and Internal Signals *****************
//======================================================================/
parameter   DELAY_1S    =       27'd125_000_000                 ;
parameter   DELAY_10MS  =       25'd125_000_0                   ;
//cnt0
reg     [24: 0]                 cnt0                            ;
wire                            add_cnt0                        ;
wire                            end_cnt0                        ;
//cnt
reg     [27: 0]                 cnt                             ;
wire                            add_cnt                         ;
wire                            end_cnt                         ;
//cnt1
reg     [ 9: 0]                 cnt1                            ;
wire                            add_cnt1                        ;
wire                            end_cnt1                        ;

reg                             flag_wr                         ;
reg                             flag                            ;
reg     [31: 0]                 crc_value                       ;
//======================================================================\
//**************************** Main Code *******************************
//======================================================================/
assign  crc         =   {crc_value[7:0],crc_value[15:8],crc_value[23:16],crc_value[31:24]};

//crc_value 后续的会专门写crc生成电路的,现在只是为了简单起见,比较刚刚入门
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        crc_value   <=  32'h967D1D69;
    end
    else if(update)begin
        crc_value   <=  32'h24495D2F;
    end
end

//cnt0 上电后等待10ms,使电平稳定后
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt0 <= 0;
    end
    else if(add_cnt0)begin
        if(end_cnt0)
            cnt0 <= 0;
        else
            cnt0 <= cnt0 + 1;
    end
end

assign  add_cnt0        =       flag;       
assign  end_cnt0        =       add_cnt0 && cnt0 == DELAY_10MS-1;

//flag
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        flag    <=  1'b1;
    end
    else if(end_cnt0)begin
        flag    <=  1'b0;
    end
end

//cnt 1s发送一次
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt <= 0;
    end
    else if(add_cnt)begin
        if(end_cnt)
            cnt <= 0;
        else
            cnt <= cnt + 1;
    end
end

assign  add_cnt     =       flag == 1'b0;       
assign  end_cnt     =       add_cnt && cnt == DELAY_1S-1;   

assign  tx_trig     =       end_cnt;

//cnt1 
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt1 <= 0;
    end
    else if(add_cnt1)begin
        if(end_cnt1)
            cnt1 <= 0;
        else
            cnt1 <= cnt1 + 1;
    end
end

assign  add_cnt1        =       flag_wr;       
assign  end_cnt1        =       add_cnt1 && cnt1 == 46-1;

//flag_wr
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        flag_wr <=  1'b0;
    end
    else if(end_cnt0 || update)begin
        flag_wr <=  1'b1;
    end
    else if(end_cnt1)begin
        flag_wr <=  1'b0;
    end
end

//wr_en
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        wr_en   <=  1'b0;
    end
    else begin
        wr_en   <=  flag_wr;
    end
end

//wr_addr
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        wr_addr <=  10'd0;
    end
    else begin
        wr_addr <=  cnt1;
    end
end

//wr_clk
assign  wr_clk      =   clk;

//wr_data
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        wr_data   <=  8'h00;
    end
    else if(flag_wr)begin
        case(cnt1)
            //hdwr_type
            00:wr_data    <=  8'h00;
            01:wr_data    <=  8'h01;
            //protocol_type
            02:wr_data    <=  8'h08;
            03:wr_data    <=  8'h00;
            //hdwr_size
            04:wr_data    <=  8'h06;
            //protocol_size
            05:wr_data    <=  8'h04;
            //opcode
            06:wr_data    <=  8'h00;
            07:wr_data    <=  8'h01;
            //sender_mac
            08:wr_data    <=  8'h00;
            09:wr_data    <=  8'h0a;
            10:wr_data    <=  8'h35;
            11:wr_data    <=  8'h01;
            12:wr_data    <=  8'hfe;
            13:wr_data    <=  8'hc0;
            //sender_ip
            14:wr_data    <=  8'd192;
            15:wr_data    <=  8'd168;
            16:wr_data    <=  8'd0;
            17:wr_data    <=  8'd2;
            //target_mac
            18:wr_data    <=  des_mac[47:40];
            19:wr_data    <=  des_mac[39:32];
            20:wr_data    <=  des_mac[31:24];
            21:wr_data    <=  des_mac[23:16];
            22:wr_data    <=  des_mac[15: 8];
            23:wr_data    <=  des_mac[ 7: 0];
            //target_ip
            24:wr_data    <=  8'd192;
            25:wr_data    <=  8'd168;
            26:wr_data    <=  8'd0;
            27:wr_data    <=  8'd3;
            //填充字段
            28:wr_data    <=  8'h00;
            29:wr_data    <=  8'h00;
            30:wr_data    <=  8'hff;
            31:wr_data    <=  8'hff;
            32:wr_data    <=  8'hff;
            33:wr_data    <=  8'hff;
            34:wr_data    <=  8'hff;
            35:wr_data    <=  8'hff;
            36:wr_data    <=  8'h00;
            37:wr_data    <=  8'h23;
            38:wr_data    <=  8'hcd;
            39:wr_data    <=  8'h76;
            40:wr_data    <=  8'h63;
            41:wr_data    <=  8'h1a;
            42:wr_data    <=  8'h08;
            43:wr_data    <=  8'h06;
            44:wr_data    <=  8'h00;
            45:wr_data    <=  8'h01;
            default:begin
                wr_data   <=  8'h00;
            end
        endcase
    end
end

endmodule

接收模块

  • 接收模块设计的比较简单,因为pc发送的arp应答包大小是固定的,我设计了一个计数器用来计数,以此来定位,然后我解析的标准是前导码正确,帧分界符正确,fpga mac地址正确,arp应答包,这些都满足,我就把pc的mac存下来,并且更新到des_mac变量中,用于下次fpga发arp中的des_mac中。这个模块其实也可以用状态机来实现。后续的udp教程应该会使用状态机来做哦。
/************************************************************************
 * Author        : Wen Chunyang
 * Email         : [email protected]
 * Create time   : 2018-11-18 16:37
 * Last modified : 2018-11-18 16:37
 * Filename      : eth_receive.v
 * Description   : 
 * *********************************************************************/
module  eth_receive(
        input                   clk                             ,
        input                   rst_n                           ,
        //gmii
        input                   gmii_rx_dv                      ,
        input         [ 7: 0]   gmii_rx_data                    ,
        input                   gmii_rx_er                      ,
        output  reg             update                          ,
        output  reg   [47: 0]   des_mac 
);
//======================================================================\
//************** Define Parameter and Internal Signals *****************
//======================================================================/
reg     [ 6: 0]                 cnt                             ;
wire                            add_cnt                         ;
wire                            end_cnt                         ;
reg                             preamble_ok                     ;
reg                             frame_start                     ;
reg     [47: 0]                 src_mac                         ;
reg     [47: 0]                 des_mac_tmp                     ;
reg                             src_mac_ok                      ;
reg     [15: 0]                 eth_type                        ;
reg                             is_arp                          ;
reg     [15: 0]                 opcode                          ;
reg                             opcode_ack                      ;
//======================================================================\
//**************************** Main Code *******************************
//======================================================================/
//cnt
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt <= 0;
    end
    else if(add_cnt)begin
        if(end_cnt)
            cnt <= 0;
        else
            cnt <= cnt + 1;
    end
    else begin
    	cnt <= 0;
    end
end

assign  add_cnt     =       gmii_rx_dv && (gmii_rx_er == 1'b0);       
assign  end_cnt     =       add_cnt && cnt == 72-1;   

//preamble_ok
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        preamble_ok <=  1'd0;
    end
    else if(add_cnt && (cnt <= 7-1) && (gmii_rx_data == 8'h55))begin
        preamble_ok <=  1'b1;
    end
    else begin
        preamble_ok <=  1'b0;
    end
end

//frame_start
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        frame_start <=  1'b0;
    end
    else if(preamble_ok && add_cnt && (cnt == 8-1) && (gmii_rx_data == 8'hd5))begin
        frame_start <=  1'b1;
    end
    else if(end_cnt)begin
        frame_start <=  1'b0;
    end
end

//src_mac fpga的mac地址 
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        src_mac <=  48'd0;
    end
    else if(frame_start && add_cnt && (cnt >= 9-1) && (cnt <= 14-1))begin
        src_mac <=  {src_mac[39:0],gmii_rx_data};
    end
    else if(end_cnt)begin
        src_mac <=  48'd0;
    end
end

//src_mac_ok
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        src_mac_ok  <=  1'b0;
    end
    else if(add_cnt && (cnt == 15-1) && (src_mac == 48'h00_0a_35_01_fe_c0))begin
        src_mac_ok  <=  1'b1;
    end
    else if(end_cnt)begin
        src_mac_ok  <=  1'b0;
    end
end

//eth_type
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        eth_type    <=  16'd0;
    end
    else if(src_mac_ok && add_cnt && (cnt >= 21-1) && (cnt <= 22-1))begin
        eth_type    <=  {eth_type[7:0],gmii_rx_data};
    end
    else if(end_cnt)begin
        eth_type    <=  16'd0;
    end
end

//is_arp
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        is_arp  <=  1'b0;
    end
    else if(src_mac_ok && add_cnt && (cnt == 23-1) && (eth_type == 16'h0806))begin
        is_arp  <=  1'b1;
    end
    else if(end_cnt)begin
        is_arp  <=  1'b0;
    end
end

//opcode
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        opcode  <=  16'd0;
    end
    else if(is_arp && add_cnt && (cnt >= 29-1) && (cnt <= 30-1))begin
        opcode  <=  {opcode[7:0],gmii_rx_data};
    end
    else if(end_cnt)begin
        opcode  <=  16'd0;
    end
end

//opcode_ack
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        opcode_ack  <=  1'b0;
    end
    else if(add_cnt && (cnt == 31-1) && (opcode == 16'h0002))begin
        opcode_ack  <=  1'b1;
    end
    else if(end_cnt)begin
        opcode_ack  <=  1'b0;
    end
end

//des_mac_tmp
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        des_mac_tmp <=  48'd0;
    end
    else if(add_cnt && (cnt >= 31-1) && (cnt <= 36-1))begin
        des_mac_tmp <=  {des_mac_tmp[39:0],gmii_rx_data};
    end
end

//des_mac
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        des_mac <=  48'd0;
    end
    else if(opcode_ack && add_cnt && (cnt == 37-1))begin
        des_mac <=  des_mac_tmp;
    end
end

//update
always  @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        update  <=  1'b0;
    end
    else if(opcode_ack)begin
        update  <=  end_cnt;
    end
end

endmodule

调试

发送

发送

接收

FPGA千兆网系列1-----ARP发送与接收(自动)_第16张图片

自动CRC

FPGA千兆网系列1-----ARP发送与接收(自动)_第17张图片

wireshark

FPGA千兆网系列1-----ARP发送与接收(自动)_第18张图片

福利

为了能及时回复大家,现在获取源码方式如下:
微信扫描下面的二维码关注【春哥笔记】公众号,回复“arp”即可Get源码的获取方式:
FPGA千兆网系列1-----ARP发送与接收(自动)_第19张图片

你可能感兴趣的:(CRC,FPGA,千兆网,ARP,以太网,FPGA千兆网系列)