基于FPGA的千兆以太网ARP和UDP的实现

1 以太网原理介绍1.1 以太网帧在以太网链路上的数据包称作以太网帧。以太网帧起始部分由前导码和帧开始符组成。后面紧跟着一个以太网报头,以mac地址说明目的地址和源地址。帧的中部是该帧负载的包含其他协议报头的数据包(例如ip协议、arp协议)。以太帧由一个32位冗余校验码结尾。它用于检验数据传输是否出现损坏。以太网帧格式如下图所示。
1.前导码和帧开始符是固定的,为7个0x55紧跟着1个0xd52.目的mac地址指明帧的接受者3.源mac地址指明帧的发送者4.以太网类型,指示帧的类型,比如0x0800表示该帧是ip数据包,0x0806表示该帧是arp协议数据包5.数据和填充就是所承载的数据包,跟前面以太网类型对应。6.帧校验序列是一个32位的循环校验码(fcs)。每一个设备都有一个不同的mac地址,当一个设备a发送一个以太网帧,源mac地址是自己的mac地址,目的mac地址如果是0xffffff,此时就是广播,所有与之连接的设备都会收到该帧,如果目的mac地址是一个独特的mac地址,那么本地mac地址与之相同的设备将会接收到该以太网帧,然后通过判断以太网帧类型,进行下一步数据包解析。
1.2 arp协议arp协议,全称为address resolution protocol,即地址解析协议,arp协议属于以太网帧的一种,前面以太网帧介绍中有说到,我们如果从设备a发送以太网帧到设备b,我们不可能每次都进行广播,那么设备a如何知道设备b的物理地址呢?arp协议就是为了解决这个问题。首先设备a广播,发送arp请求,等收到设备b的arp应答以后就能知道设备b的mac地址。arp帧格式如下图所示
arp字段就是前面以太网帧待填充的数据。硬件类型、上层协议类型、mac地址长度、ip地址长度均固定不变。假设设备a的ip地址为192.168.0.2,mac地址为0x00_0a_35_01_fe_c0,我们知道目的ip地址为192.168.0.3,不知道该ip地址对应的mac地址,如果设备a想要和ip地址为192.168.0.3的设备b进行通信(如udp或者ip通信),就必须知道它的mac地址。此时设备a就需要广播发送arp请求,接收方mac地址填0xff_ff_ff_ff_ff_ff。这样ip地址为192.168.0.3的设备就会解析出这是一个arp请求,它询问自身的mac地址,此时它就会做出arp应答,将自身的mac地址发送给对应ip地址的设备a。注意发送arp请求时,操作码为0x0001,应答时操作码为0x0002。
1.3 ip协议tcp/ip协议定义了一个在因特网上传输的包,称为ip数据包,而ip数据报(ip datagram)是个比较抽象的内容,是对数据包的结构进行分析。由首部和数据两部分组成,其格式如下图图所示。首部的前一部分是固定长度,共20字节,是所有ip数据报必须具有的。在首部的固定部分的后面是一些可选字段,其长度是可变的。首部中的源地址和目的地址都是ip协议地址。
1.4 udp协议udp 协议是一种不可靠传输,发送方只负责将数据发送出去,而不管接收方是否正确的接收。非常类似于 uart 串口传输。但是,在很多场合,是可以接受这种潜在的不可靠性的,例如视频实时传输显示。在这类系统中,由于数据并不需要进行运算并得到非常精确的结果用于其他功能,而仅仅是显示在屏幕上,因此可以接受一定程度的丢包或者误码。此类应用在 led 大屏显示系统中应用非常广泛。udp帧组成如下图所示
2 gmii接口介绍在芯航线 ac6102 开发板上,设计了一路 gmii 接口的千兆以太网电路,通过该以太网电路,用户可以将 fpga 采集或运算得到的数据传递给其他设备如 pc 或服务器,或者接收其他设备传输过来的数据并进行处理。首先介绍一下gmii接口。
gmii_rx_clk是phy发送过来的时钟,fpga通过该时钟进行采样gmii_rx_dv是接收数据有效标志,与gmii_rx_data对齐gmii_rx_er是错误标志,当它有效时,说明发送帧错误gmii_rx_data是phy发送过来的数据phy_rst_n是低电平复位信号gmii_tx_clk是fpga发送时钟,这里直接使用gmii_rx_clk即可gmii_tx_en发送数据有效标志,与gmii_tx_data对齐gmii_tx_er是错误标志gmii_tx_data是fpga发送给phy的数据以上就是gmii接口所有的信号。在发送数据时,只需要按以下时序发送。
3 arp协议实现在本工程中,arp实现分两部分,一是由fpga发送arp请求,二是对发送过来的arp应答进行解析,得到目的ip地址对应的mac地址。这样我们就可以知道,发送arp请求完整的帧内容了。如下图所示。(数据格式为16进制)
除了crc32校验值之外,其他都是固定的,crc32需要进行计算。关于crc32的8位并行计算,大家可以参考下面这篇论文。千兆以太网mac中crc算法的设计与实现用crc计算软件可以算出,也可以用fpga实现。
由计算软件看出crc32校验结果为0x63f9a3ca,在发送时,按7-0、15-8、23-16、31-24的顺序发送。下面给出crc32的fpga实现代码:
module  crc(    input                       clk                             ,    input                       rst_n                           ,    input         [ 7: 0]       din                             ,    input                       crc_en                          ,    input                       crc_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(crc_init)begin        crc <=  {32{1'b1}};    end    else if(crc_en)begin        crc <=  crc_next;    endend
endmodule
下面是用signaltap抓取到的信号,如下图所示。
可以看出发送的校验码为0xca_a3_f9_63与crc计算机软件计算结果一致。
3.1 arp请求fpga实现arp请求非常简单。首先定义模块信号。
module  arp_request(        input                   clk                             ,//125m        input                   rst_n                           ,        input         [31: 0]   src_ip_addr                     ,        input         [31: 0]   dest_ip_addr                    ,        input         [47: 0]   src_mac_addr                    ,        input         [47: 0]   dest_mac_addr                   ,        input         [31: 0]   crc                             ,        output  reg             crc_init                        ,        output  reg             crc_en                          ,        //gmii         output  reg             gmii_tx_en                      ,        output  reg   [ 7: 0]   gmii_tx_data                            );
用计数器,在计数变化时,将每一个字节通过gmii_tx_data发送即可。具体请看工程源码。
3.2 arp应答解析在fpga发送arp请求后,对应ip地址将会做出apr应答发送其对应mac地址,此时通过fpga的arp应答解析模块即可正确解析出目标mac地址,从而为udp协议传输奠定基础。具体实现:①判断目标mac地址是否是fpga设备的mac地址②判断操作码是否是0x0002③判断pc端ip地址是否是192.168.0.3④判断目的ip地址是否是192.168.0.2只有以上条件均满足时,我们认为这是一条arp应答包,从而更新ip为192.168.0.3对应的mac地址。具体请看工程源码
4 udp协议传输实现在这里,我通过按键,生成1200个字节的数据,从0-255循环变化,然后封装成udp包发送到pc端。从而实现udp协议传输。由上面udp介绍,我们可以知道发送一个udp包的主要构成,如下图所示:
ip数据包的报头校验和:0x4500+0x04cc+0x0000+0x0000+0x4011+0xc0a8+0x0002+0xc0a8+0x0003 = 0x20b320x0002+0x0b32 = 0x0b34checksum = ~(0x0b34) = 0xf4cb同样的,我们使用计数器来发送对应数据。udp发送模块如下:
module  udp_send(        input                   clk                             ,        input                   clk_50m                         ,        input                   rst_n                           ,        input         [31: 0]   src_ip_addr                     ,            input         [31: 0]   dest_ip_addr                    ,        input         [47: 0]   src_mac_addr                    ,        input         [47: 0]   dest_mac_addr                   ,        //fifo        input         [10: 0]   fifo_rdusedw                    ,        input         [ 7: 0]   fifo_rd_data                    ,        output  reg             fifo_rdreq                      ,        //crc        input         [31: 0]   crc                             ,        output  reg             crc_init                        ,        output  reg             crc_en                          ,        //gmii        output  reg             gmii_tx_en                      ,        output  reg   [ 7: 0]   gmii_tx_data                     );//======================================================================//************** define parameter and internal signals *****************//======================================================================/parameter   data_len        =   16'd1200                        ;//发送的udp数据长度
parameter   preamble        =   8'h55                           ;parameter   sfd             =   8'hd5                           ;parameter   len_type        =   16'h0800                        ;
parameter   ver_hdr_len     =   8'h45                           ;parameter   tos             =   8'h00                           ;parameter   total_len       =   data_len+28                     ;parameter   id              =   16'h0000                        ;parameter   rsv_df          =   8'h00                           ;parameter   mf_frag_offset  =   8'h00                           ;parameter   ttl             =   8'h40                           ;parameter   protocol        =   8'h11                           ;
parameter   src_port        =   16'd5000                        ;parameter   dst_port        =   16'd6000                        ;parameter   udp_len         =   data_len+8                      ;parameter   udp_checksum    =   16'h0000                        ;
wire    [19: 0]                 ip_check_sum1                   ;wire    [19: 0]                 ip_check_sum2                   ;wire    [15: 0]                 ip_check_sum                    ;
reg                             trig_udp_send                   ;reg                             flag_udp_send                   ;
reg     [10: 0]                 udp_byte_cnt                    ;wire                            add_udp_byte_cnt                ;wire                            end_udp_byte_cnt                ;
reg     [ 7: 0]                 udp_data                        ;//======================================================================//**************************** main code *******************************//======================================================================///check_sum1 进位为17'hxff这种情况时,check_sum1,前16与后16位相加可能还会在第//17位进1assign  ip_check_sum1  =   {ver_hdr_len, 8'h00} + total_len + {ttl, protocol} + src_ip_addr[31:16] + src_ip_addr[15:0] + dest_ip_addr[31:16] + dest_ip_addr[15:0];//width<=20
//check_sum2assign  ip_check_sum2   =   ip_check_sum1[19:16] + ip_check_sum1[15:0];//width<=17assign  ip_check_sum    =   ~(ip_check_sum2[19:16] + ip_check_sum2[15:0]);//width<=16
//trig_udp_sendalways  @(posedge clk or negedge rst_n)begin    if(!rst_n)begin        trig_udp_send   <=  1'b0;    end    else if(fifo_rdusedw == data_len-1)begin//        trig_udp_send   <=  1'b1;    end    else begin        trig_udp_send   <=  1'b0;    endend
//flag_udp_sendalways  @(posedge clk or negedge rst_n)begin    if(!rst_n)begin        flag_udp_send   <=  1'b0;    end    else if(trig_udp_send)begin        flag_udp_send   <=  1'b1;    end    else if(end_udp_byte_cnt)begin        flag_udp_send   <=  1'b0;    endend
//udp_byte_cntalways @(posedge clk or negedge rst_n)begin    if(!rst_n)begin        udp_byte_cnt <= 0;    end    else if(add_udp_byte_cnt)begin        if(end_udp_byte_cnt)            udp_byte_cnt <= 0;        else            udp_byte_cnt <= udp_byte_cnt + 1;    endend
assign  add_udp_byte_cnt    =       flag_udp_send;       assign  end_udp_byte_cnt    =       add_udp_byte_cnt && udp_byte_cnt == data_len+54-1;//前导码+分隔符8个,mac帧头14,crc校验4   
//fifo_rdreqalways  @(posedge clk or negedge rst_n)begin    if(!rst_n)begin        fifo_rdreq   <=  1'b0;     end    else if(udp_byte_cnt == 50-1)begin        fifo_rdreq   <=  1'b1;      end    else if(udp_byte_cnt == data_len+50-1)begin//校验码之前        fifo_rdreq   <=  1'b0;     endend
//--------------------------------------------------------------------//gmii_tx_enalways  @(posedge clk or negedge rst_n)begin    if(!rst_n)begin        gmii_tx_en  <=  1'b0;    end    else if(flag_udp_send)begin        gmii_tx_en  <=  1'b1;    end    else begin        gmii_tx_en  <=  1'b0;    endend
//gmii_tx_dataalways  @(posedge clk or negedge rst_n)begin    if(!rst_n)begin        gmii_tx_data    <=  8'h00;    end    else if(flag_udp_send)begin        case(udp_byte_cnt)            0,1,2,3,4,5,6:  gmii_tx_data    <=  preamble;            7:              gmii_tx_data    <=  sfd;            //帧头            8:              gmii_tx_data    <=  dest_mac_addr[47:40];            9:              gmii_tx_data    <=  dest_mac_addr[39:32];            10:             gmii_tx_data    <=  dest_mac_addr[31:24];            11:             gmii_tx_data    <=  dest_mac_addr[23:16];            12:             gmii_tx_data    <=  dest_mac_addr[15: 8];            13:             gmii_tx_data    <=  dest_mac_addr[ 7: 0];            14:             gmii_tx_data    <=  src_mac_addr[47:40];            15:             gmii_tx_data    <=  src_mac_addr[39:32];            16:             gmii_tx_data    <=  src_mac_addr[31:24];            17:             gmii_tx_data    <=  src_mac_addr[23:16];            18:             gmii_tx_data    <=  src_mac_addr[15: 8];            19:             gmii_tx_data    <=  src_mac_addr[ 7: 0];            20:             gmii_tx_data    <=  len_type[15: 8];            21:             gmii_tx_data    <=  len_type[ 7: 0];            //ip            22:             gmii_tx_data    <=  ver_hdr_len;//8'h45                23:             gmii_tx_data    <=  tos;                       24:             gmii_tx_data    <=  total_len[15: 8];                 25:             gmii_tx_data    <=  total_len[ 7: 0];              26:             gmii_tx_data    <=  id[15: 8];                        27:             gmii_tx_data    <=  id[ 7: 0];                   28:             gmii_tx_data    <=  rsv_df;                    29:             gmii_tx_data    <=  mf_frag_offset;            30:             gmii_tx_data    <=  ttl;                       31:             gmii_tx_data    <=  protocol;            32:             gmii_tx_data    <=  ip_check_sum[15: 8];                       33:             gmii_tx_data    <=  ip_check_sum[ 7: 0];             34:             gmii_tx_data    <=  src_ip_addr[31:24];                       35:             gmii_tx_data    <=  src_ip_addr[23:16];            36:             gmii_tx_data    <=  src_ip_addr[15: 8];                       37:             gmii_tx_data    <=  src_ip_addr[ 7: 0];            38:             gmii_tx_data    <=  dest_ip_addr[31:24];                       39:             gmii_tx_data    <=  dest_ip_addr[23:16];             40:             gmii_tx_data    <=  dest_ip_addr[15: 8];                       41:             gmii_tx_data    <=  dest_ip_addr[ 7: 0];             //udp                42:             gmii_tx_data    <=  src_port[15: 8];            43:             gmii_tx_data    <=  src_port[7 : 0];                44:             gmii_tx_data    <=  dst_port[15: 8];                45:             gmii_tx_data    <=  dst_port[7 : 0];                 46:             gmii_tx_data    <=  udp_len[15: 8];            47:             gmii_tx_data    <=  udp_len[7 : 0];            48:             gmii_tx_data    <=  udp_checksum[15: 8];             49:             gmii_tx_data    <=  udp_checksum[7 : 0];                       //crc                     data_len+50:    gmii_tx_data    <=  ~{crc[24], crc[25], crc[26], crc[27], crc[28], crc[29], crc[30], crc[31]};            data_len+51:    gmii_tx_data    <=  ~{crc[16], crc[17], crc[18], crc[19], crc[20], crc[21], crc[22], crc[23]};            data_len+52:    gmii_tx_data    <=  ~{crc[8], crc[9], crc[10], crc[11], crc[12], crc[13], crc[14], crc[15]};            data_len+53:    gmii_tx_data    <=  ~{crc[0], crc[1], crc[2], crc[3], crc[4], crc[5], crc[6], crc[7]};                        default:        gmii_tx_data    <=  fifo_rd_data;         endcase    end     else begin        gmii_tx_data    <=  8'h00;    endend
//---------------------------crc-----------------------------------//crc_initalways  @(posedge clk or negedge rst_n)begin    if(!rst_n)begin        crc_init    <=  1'b0;    end    else if(udp_byte_cnt == (8-1))begin        crc_init    <=  1'b1;    end    else begin        crc_init    <=  1'b0;    endend
//crc_en    always  @(posedge clk or negedge rst_n)begin    if(!rst_n)begin        crc_en  <=  1'b0;    end    else if(udp_byte_cnt == (9-1))begin//9-1        crc_en  <=  1'b1;    end    else if(udp_byte_cnt == (data_len+51-1))begin//1251-1        crc_en  <=  1'b0;    endendendmodule
下面是用signaltap抓取信号示意图帧头部分,rdreq就是从fifp读取的数据然后通过封装成udp发送
精彩推荐至芯科技12年不忘初心、再度起航2月11日北京中心fpga工程师就业班开课、线上线下多维教学、欢迎咨询!fpga与cpu、gpu、asic的区别,fpga在云计算中的应用方案
fpga学习-基于fpga的图像处理
扫码加微信邀请您加入fpga学习交流群
欢迎加入至芯科技fpga微信学习交流群,这里有一群优秀的fpga工程师、学生、老师、这里fpga技术交流学习氛围浓厚、相互分享、相互帮助、叫上小伙伴一起加入吧!
点个在看你最好看
原文标题:基于fpga的千兆以太网arp和udp的实现
文章出处:【微信公众号:fpga设计论坛】欢迎添加关注!文章转载请注明出处。

DeepNude惊现!一键“脱衣”
基于FPGA的电网实时数据采集与控制
泰州市人民政府与中国移动携手打造“泰州智造”工业名片
EDA产业目前面临哪些问题?
戴维南等效电路受控源分析
基于FPGA的千兆以太网ARP和UDP的实现
国民技术因前海旗隆相关人员失联事件欲计提5亿坏帐
小米X1最新消息:月底发布,小米新品确认曝光:骁龙660处理器+6英寸屏幕
怎么判断单片机是否起振 如何判断晶振好坏
浅谈膨体聚四氟乙烯(ePTFE)微孔防水透气膜薄膜的制备
基于晶体管和UJT的简单锯齿发生器电路
大联大诠鼎推出基于高通的QCC3031 TWS蓝牙音箱设计
二位二通电磁阀工作原理 二位五通和三位五通的区别
美参议员要求脸书不记录用户的位置数据
PUR热熔胶机注意事项有哪些_PUR热熔胶机换胶时注意事项详解
关于5G+工业互联网的思考与实践
尼龙导热塑料应对LED散热问题的解决方案
权威发布2019年物联网十大趋势
纳米多孔锌助力二次碱性锌基电池
深圳率先完成5G覆盖 美国全面封锁华为第三方芯片供应来源