以太网ARP协议——FPGA学习笔记23

一、简介

FPGA千兆网口数据传输MDIO接口——FPGA学习笔记3_yt8531sh原理图-CSDN博客

1、以太网帧类型

2、以太网帧格式

前导码(Preamble) : 为了实现底层数据的正确阐述,物理层使用 7 个字节同步码(0 和 1 交替(55-55-55-55-55-55-55))实现数据的同步。
帧起始界定符(SFD, Start Frame Delimiter):使用 1 个字节的 SFD(固定值为 0xd5)来表示一帧的开始,即后面紧跟着传输的就是以太网的帧头。
目的 MAC 地址: 即接收端物理 MAC 地址,占用 6 个字节。 MAC 地址从应用上可分为单播地址、组播地址和广播地址。单播地址:第一个字节的最低位为 0,比如 00-00-00-11-11-11,一般用于标志唯一的设备;组播地址:第一个字节的最低位为 1,比如 01-00-00-11-11-11,一般用于标志同属一组的多个设备;广播地址:所有 48bit 全为 1,即 FF-FF-FF-FF-FF-FF,它用于标志同一网段中的所有设备。
源 MAC 地址:即发送端物理 MAC 地址,占用 6 个字节。
长度/类型: 上图中的长度/类型具有两个意义,当这两个字节的值小于 1536(十六进制0x0600)时,代表该以太网中数据段的长度;如果这两个字节的值大于 1536,则表示该以太网中的数据属于哪个上层协议,例如 0x0800 代表 IP 协议(网际协议) 、 0x0806 代表 ARP 协议(地址解析协议)等。
数据:以太网中的数据段长度最小 46 个字节, 最大 1500 个字节。

3、以太网MAC帧格式

二、以太网报文

目的IP :ff ff ff ff ff ff(广播IP地址)     类型:ARP (0806)   源MAC地址:74 7d 24 92 fb df

单播                                      广播                                      组播

单播:MAC 00 XX  XX  XX  XX  XX

组播:MAC 01 XX XX  XX  XX  XX

广播:MAC FF  FF  FF  FF  FF  FF 

三、ARP协议

ARP根据IP地址获取MAC地址的一种TCP/IP协议

1、ARP映射

        ARP 映射是指将 IP 地址和 MAC 地址映射起来,分为静态映射和动态映射。
静态映射指手动创建一张 ARP 表,把 IP 地址和 MAC 地址关联起来。手动绑定之后,源主机在通信之前,就可以直接从 ARP 表中直接找到 IP 地址对应的 MAC 地址。

动态映射指使用协议来获取相对应的物理地址,之所以用动态这个词是因为这个过程是自动完成的,一般应用程序的用户或系统管理员不必关心。

2、ARP请求

3、ARP应答


4、免费ARP(检测IP冲突)

5、以太网ARP数据包

6、ARP协议

硬件类型(Hardware type):硬件地址的类型, 0x0001 表示以太网地址。
协议类型(Protocol type):要映射的协议地址类型, ARP 协议的上层协议为 IP 协议,因此该协议类型为 IP 协议,其值为 0x0800。
硬件地址长度(Hardware size):硬件地址(MAC 地址)的长度,以字节为单位。对于以太网上 IP 地址的 ARP 请求或者应答来说,该值为 6。
协议地址长度(Protocol size): IP 地址的长度,以字节为单位。对于以太网上 IP 地址的 ARP 请求或者应答来说,该值为 4。
OP(Opcode):操作码,用于表示该数据包为 ARP 请求或者 ARP 应答。 1 表示 ARP 请求, 2 表示 ARP应答。
源 MAC 地址:发送端的硬件地址。
源 IP 地址:发送端的协议(IP)地址,如 192.168.1.102。

目的 MAC 地址:接收端的硬件地址,在 ARP 请求时由于不知道接收端 MAC 地址,因此该字段为广播地址, 即 48’hff_ff_ff_ff_ff_ff。
目的 IP 地址:接收端的协议(IP)地址,如 192.168.1.10。

四、RGMII接口

1、接口说明

ETH_TXCTL 和 ETH_RXCTL 控制信号同样采用 DDR 的方式在一个时钟周期内传输两位控制信号,即上升沿发送/接收数据使能(TX_EN/RX_ DV)信号,下降沿发送/接收使能信号与错误信号的异或值(TX_ERR xor TX_EN、 RX_ERR xor RX_DV)。当 RX_DV 为高电平(表示数据有效), RX_ERR 为低电平(表示数据无错误),则异或的结果值为高电平,因此只有当 ETH_RXCTL和 ETH_TXCTL 信号的上下沿同时为高电平时,发送和接收的数据有效且正确。

2、接口时序

(1)接收时序:


        RGMII 发送端口正常模式下,需要满足 TXC 的上下边沿与 TXD 和 TX_CTL 信号的中间位置对齐, TXC 的时钟周期为 8ns,单个高电平或者低电平为 4ns, TXC 相对于 TXD 和TX_CTL 延时约 2ns。

(2)发送时序


        RGMII 发送端口延时模式下,需要满足 TXC 的上下边沿与 TXD 和 TX_CTL 信号对齐,相位相同。本次实验 RTL8211FD RGMII 发送端口采用延时模式,其模式由硬件上的特殊引脚外接上下拉电阻进行配置。

五、硬件设计

已做上拉处理,芯片已经处于延时模式,代码中无需在额外添加延时


六、xilinx原语

Xilinx原语——FPGA学习笔记4_fpga 源语作用-CSDN博客

IDDR原语:

    IDDR #(
        .DDR_CLK_EDGE   ("SAME_EDGE_PIPELINED"    ),    //模式选择
        .INIT_Q1        (1'b0                     ),    //设置初值
        .INIT_Q2        (1'b0                     ),    //设置初值
        .SRTYPE         ("SYNC"                   )     //复位  同步/异步
    )   
    IDDR_u0     
    (   
        .Q1             (w_rec_data[rxd_i]          ), // 1-bit output for positive edge of clock 
        .Q2             (w_rec_data[rxd_i +4]       ), // 1-bit output for negative edge of clock
        .C              (w_rxc_bufio                ),    //时钟
        .CE             (1                          ),    //输入时钟使能
        .D              (w_rxd_idly[rxd_i]          ),    //输入管脚
        .R              (0                          ),    //复位
        .S              (0                          )     //置位
    );

ODDR原语:

ODDR #(
    .DDR_CLK_EDGE    ("OPPOSITE_EDGE"       ),    //模式配置
    .INIT            (1'b0                  ),    //初值
    .SRTYPE          ("SYNC"                )     //复位   同步/异步
) 
ODDR_u 
(
    .Q               (o_txd[txd_i]          ),    //输出数据
    .C               (w_txc                 ),    //输入时钟
    .CE              (1                     ),    //时钟输入使能
    .D1              (w_send_d1[txd_i]      ),    //数据1  上升沿
    .D2              (w_send_d2[txd_i]      ),    //数据2  下降沿
    .R               (0                     ),    //复位
    .S               (0                     )     //置数
);

​IDLAY:

七、程序设计

1、RGMII2GMII

 (1)RX

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/12/14 21:19:29
// Design Name: 
// Module Name: rgmii_rx
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module rgmii_rx(
    input           idelay_clk      ,   //200M
    //以太网RGMII接口   
    input           rgmii_rxc       ,   //RGMII接收时钟
    input           rgmii_rx_ctl    ,   //RGMII接收数据控制信号
    input   [3:0]   rgmii_rxd       ,   //RGMII接收数据
    //以太网GMII接口    
    output          gmii_rx_clk     ,   //GMII接收时钟
    output          gmii_rx_dv      ,   //GMII接收数据有效信号
    output  [7:0]   gmii_rxd            //GMII接收数据
    );

parameter   IDELAY_VALUE = 0;

wire        rgmii_rxc_bufg     ;//全局时钟缓存
wire        rgmii_rxc_bufio    ;//全局时钟IO缓存
wire        rgmii_rx_ctl_delay ;//
wire  [1:0] gmii_rxdv_t        ;
wire  [3:0] rgmii_rxd_delay    ;

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

//全局时钟缓存
BUFG BUFG_inst (
   .O(rgmii_rxc_bufg), // 1-bit output: Clock output
   .I(rgmii_rxc)  // 1-bit input: Clock input
);

//全局时钟IO缓存
BUFIO BUFIO_inst (
   .O(rgmii_rxc_bufio), // 1-bit output: Clock output (connect to I/O clock loads).
   .I(rgmii_rxc)  // 1-bit input: Clock input (connect to an IBUF or BUFMR).
);

//输入延时控制
// 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" *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
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 (190.0-210.0, 290.0-310.0).
)
IDELAYE2_inst (
   .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: Active high enable increment/decrement input
   .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 input
   .LD            (1'b0                ),                   // 1-bit input: Load IDELAY_VALUE input
   .LDPIPEEN      (1'b0                ),       // 1-bit input: Enable PIPELINE register to load data input
   .REGRST        (1'b0                )            // 1-bit input: Active-high reset tap-delay input
);

//rgmii_rx_ctl输入延时与双沿采样
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" 
) IDDR_inst (
   .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" *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
   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 (190.0-210.0, 290.0-310.0).
   )
   IDELAYE2_inst (
      .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: Active high enable increment/decrement input
      .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_rxd[i]        ),         // 1-bit input: Data input from the I/O
      .INC           (1'b0                ),                 // 1-bit input: Increment / Decrement tap delay input
      .LD            (1'b0                ),                   // 1-bit input: Load IDELAY_VALUE input
      .LDPIPEEN      (1'b0                ),       // 1-bit input: Enable PIPELINE register to load data input
      .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" 
   ) IDDR_inst (
      .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
      .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

(2)TX

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/12/15 12:52:31
// Design Name: 
// Module Name: rgmii_tx
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

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

    //RGMII发送端口 
    output          rgmii_txc       ,   //RGMII发送数据时钟
    output          rgmii_tx_ctl    ,   //RGMII输出数据有效信号
    output  [3:0]   rgmii_txd           //RGMII输出数据
);

assign      rgmii_txc = gmii_tx_clk;

//输出双沿采样寄存器   rgmii_tx_ctl
ODDR #(
   .DDR_CLK_EDGE    ("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE" 
   .INIT            (1'b0       ),    // Initial value of Q: 1'b0 or 1'b1
   .SRTYPE          ("SYNC"     ) // Set/Reset type: "SYNC" or "ASYNC" 
) ODDR_inst (
   .Q       (rgmii_tx_ctl   ),   // 1-bit DDR output
   .C       (gmii_tx_clk    ),   // 1-bit clock input
   .CE      (1'b1           ), // 1-bit clock enable input
   .D1      (gmii_tx_en     ), // 1-bit data input (positive edge)
   .D2      (gmii_tx_en     ), // 1-bit data input (negative edge)
   .R       (1'b0           ),   // 1-bit reset
   .S       (1'b0           )    // 1-bit set
);

genvar  i ; 
generate for ( i = 0 ; i < 4  ; i = i + 1 ) 
    begin : txdata_bus
        ODDR #(
           .DDR_CLK_EDGE    ("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE" 
           .INIT            (1'b0       ),    // Initial value of Q: 1'b0 or 1'b1
           .SRTYPE          ("SYNC"     ) // Set/Reset type: "SYNC" or "ASYNC" 
        ) ODDR_inst (
           .Q       (rgmii_txd[i]   ),   // 1-bit DDR output
           .C       (gmii_tx_clk    ),   // 1-bit clock input
           .CE      (1'b1           ), // 1-bit clock enable input
           .D1      (gmii_txd[i]    ), // 1-bit data input (positive edge)
           .D2      (gmii_txd[4+i]  ), // 1-bit data input (negative edge)
           .R       (1'b0           ),   // 1-bit reset
           .S       (1'b0           )    // 1-bit set
        );
end
endgenerate

endmodule

2、ARP

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/12/15 14:41:05
// Design Name: 
// Module Name: arp_top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module arp_top(
    input                idelay_clk ,
    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      //以太网发送完成信号    
);

//开发板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            crc_en      ;
wire            crc_clr     ;
wire    [32:0]  crc_data    ;
wire    [32:0]  crc_next    ;

//
arp_rx 
  #(
    //开发板MAC地址 00-11-22-33-44-55
    .BOARD_MAC(BOARD_MAC), 
    //开发板IP地址 192.168.1.10   
    .BOARD_IP(BOARD_IP)      
    )
    u_arp_rx
    (
        //input
        .clk            (gmii_rx_clk    )   , //时钟信号
        .idelay_clk     (idelay_clk     )   ,
        .rst_n          (rst_n          )   , //复位信号,低电平有效       
        .gmii_rx_dv     (gmii_rx_dv     )   , //GMII输入数据有效信号
        .gmii_rxd       (gmii_rxd       )   , //GMII输入数据
        //output
        .arp_rx_done    (arp_rx_done    )   , //ARP接收完成信号
        .arp_rx_type    (arp_rx_type    )   , //ARP接收类型 0:请求  1:应答
        .src_mac        (src_mac        )   , //接收到的源MAC地址
        .src_ip         (src_ip         )     //接收到的源IP地址
);

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

crc32_d8 u_crc32_d8(
    .clk            (gmii_tx_clk    )  ,  //时钟信号
    .rst_n          (rst_n          )  ,  //复位信号,低电平有效
    .data           (gmii_txd       )  ,  //输入待校验8位数据
    .crc_en         (crc_en         )  ,  //crc使能,开始校验标志
    .crc_clr        (crc_clr        )  ,  //crc数据复位信号            
    .crc_data       (crc_data       )  ,  //CRC校验数据
    .crc_next       (crc_next       )     //CRC下次校验完成数据
);











endmodule

(1)RX

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/12/15 14:43:57
// Design Name: 
// Module Name: arp_rx
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments: 
// 
//////////////////////////////////////////////////////////////////////////////////


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                idelay_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
localparam  ALL_MAC     = 48'hff_ff_ff_ff_ff_ff;


reg             skip_en     ;
reg             error_en    ;
reg     [4:0]   cur_state   ;
reg     [4:0]   next_state  ;
reg     [4:0]   cnt         ;
reg     [47:0]  des_mac_t   ;
reg     [47:0]  sou_mac_t   ;
reg     [15:0]  eth_type    ;
reg     [15:0]  op_data     ;
reg     [47:0]  src_mac_t   ;   //源MAC
reg     [31:0]  src_ip_t    ;   //源IP
reg     [31:0]  des_ip_t    ;   //目标IP   

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

//
always @(*) begin
    if (!rst_n) begin
        next_state <= st_idle;
    end 
    else begin
        case (cur_state)
            st_idle     :begin                  //等待前导码
                if (skip_en) begin
                    next_state = st_preamble;
                end 
                else begin
                    next_state = st_idle;
                end
            end
            st_preamble :begin                  //接收前导码
                if (skip_en) begin
                    next_state = st_eth_head;
                end 
                else if(error_en)begin
                    next_state = st_rx_end;
                end
                else begin
                    next_state = st_preamble;
                end
            end
            st_eth_head :begin                  //接收以太网帧头
                if (skip_en) begin
                    next_state = st_arp_data;
                end 
                else if(error_en)begin
                   next_state =  st_rx_end;
                end
                else begin
                    next_state = st_eth_head;
                end
            end
            st_arp_data :begin                  //接收ARP数据
                if (skip_en) begin
                    next_state = st_rx_end;
                end 
                else if(error_en)begin
                    next_state = st_rx_end;
                end
                else begin
                    next_state = st_arp_data;
                end
            end
            st_rx_end   :begin                  //接收结束
                if (skip_en) begin
                    next_state = st_idle;
                end else begin
                    next_state = st_rx_end;
                end
            end    
            default: next_state = st_idle ;
        endcase    
    end
end

//
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        skip_en     <=  1'b0;
        cnt         <=  6'd0;
        error_en    <=  1'b0;
        des_mac_t   <=  48'd0;
        sou_mac_t   <=  48'd0;
        eth_type    <=  16'd0;
        op_data     <=  16'd0;
        des_ip_t    <=  32'd0;
        src_ip_t    <=  32'd0;
        arp_rx_done <=  1'd0;
        src_mac     <=  48'd0;
        src_ip      <=  32'd0;
        arp_rx_type <=  1'd0;
        src_mac_t   <=  48'd0;
    end 
    else begin
        skip_en <=  1'b0;
        error_en <= 1'b0;  
        arp_rx_done <= 1'b0; 
        case (next_state)
            st_idle     :begin
                if ((gmii_rx_dv == 1'b1)&&(gmii_rxd == 8'h55)) begin
                    skip_en <= 1'b1;
                end else begin
                    skip_en <= 1'b0; 
                end
            end
            st_preamble :begin
                if (gmii_rx_dv == 1'b1) begin
                    cnt <= cnt + 1'b1;
                    if ((cnt< 5'd6)&&(gmii_rxd != 8'h55)) begin
                        error_en <= 1'b1;
                    end 
                    else if(cnt == 5'd6)begin//1+6+1
                        cnt <= 5'd0;
                        if (gmii_rxd == 8'hd5) begin
                            skip_en     <=  1'b1;
                            error_en    <=  1'b0;
                        end 
                        else begin
                            skip_en     <=  1'b0;
                            error_en    <=  1'b1;
                        end
                    end
                end else begin
                    skip_en     <=  skip_en ;
                    error_en    <=  error_en; 
                end
            end
            st_eth_head :begin
                if (gmii_rx_dv == 1'b1) begin
                    cnt <= cnt + 1'b1;
                    if (cnt < 5'd6) begin
                        des_mac_t   <=  {des_mac_t[39:0],gmii_rxd};
                    end 
                    else if (cnt == 5'd6)begin
                        if ((des_mac_t != BOARD_MAC ) && ( des_mac_t != ALL_MAC)) begin
                            error_en    <=  1'b1;
                        end else begin
                            error_en    <=  1'b0; 
                        end
                    end
                    else if ((cnt >= 5'd6)&&(cnt < 5'd12))begin
                        sou_mac_t   <=  {sou_mac_t[39:0],gmii_rxd};
                    end
                    else if (cnt == 5'd12) begin
                        eth_type[15:8]  <=  gmii_rxd;
                    end 
                    else if (cnt == 5'd13)begin
                        eth_type[7:0]   <=  gmii_rxd;
                        cnt             <=  5'd0;
                        if ((eth_type[15:8] != ETH_TPYE[15:8])&&(gmii_rxd != ETH_TPYE[7:0])) begin  //eth_type[7:0]尚未同步 所以判断gmii_rxd
                            skip_en     <= 1'b0;
                            error_en    <= 1'b1;
                        end else begin
                            skip_en     <= 1'b1;
                            error_en    <= 1'b0;
                        end
                    end
                end
                else begin
                    skip_en     <= skip_en  ;
                    error_en    <= 1'b1     ;
                end
            end
            st_arp_data :begin
                if (gmii_rx_dv == 1'b1) begin
                    cnt <= cnt + 1'b1;
                    if (cnt == 5'd6) begin
                        op_data[15:8]   <=  gmii_rxd;
                    end 
                    else if (cnt == 5'd7)begin
                        op_data[7:0]    <=  gmii_rxd;
                    end
                    else if ((cnt >= 5'd8)&&(cnt < 5'd14)) begin
                        src_mac_t   <=  {src_mac_t[36:0],gmii_rxd};      //源MAC
                    end 
                    else if ((cnt >= 5'd14)&&(cnt < 5'd18))begin
                        src_ip_t    <=  {src_ip_t[23:0],gmii_rxd};      //源IP
                    end
                    else if ((cnt >= 5'd24)&&(cnt < 5'd28)) begin
                        des_ip_t    <=  {des_ip_t[23:0],gmii_rxd};      //目标IP
                    end 
                    else if (des_ip_t == BOARD_IP) begin
                        cnt = 5'd0;
                        if ((op_data == 16'd1)||(op_data == 16'd2)) begin   //01 请求    10 应答
                            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) begin     //请求
                                arp_rx_type <=  1'b0    ;   //APR请求
                            end 
                            else begin
                                arp_rx_type <=  1'b1    ;   //ARP应答
                            end
                        end 
                        else begin
                            error_en    <=  1'b1    ;
                            skip_en     <=  1'b0    ;
                        end
                    end
                    else begin
                        error_en    <=  error_en    ;
                    end
                end 
                else begin
                    error_en    <=  1'b1    ;
                    skip_en     <=  skip_en     ;
                end
            end
            st_rx_end   :begin
                cnt         <=  5'd0    ;
                error_en    <=  1'b0    ;
                //传输数据包
                if ((gmii_rx_dv == 1'b0)&&(skip_en == 1'b0)) begin
                    skip_en <= 1'b1;
                end
                else begin
                    skip_en <= 1'b0;
                end
            end  
            default: ;
        endcase   
    end
end

ila_0 u_ila_0 (
	.clk(idelay_clk), // input wire clk


	.probe0(gmii_rxd), // input wire [7:0]  probe0  
	.probe1(cnt), // input wire [4:0]  probe1
    .probe2(next_state), // input wire [4:0]  probe1
    .probe3(skip_en), // input wire [4:0]  probe1
    .probe4(error_en) // input wire [4:0]  probe1
);

endmodule

(2)TX

 

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/12/15 20:16:45
// Design Name: 
// Module Name: arp_tx
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module arp_tx(
    input                clk        , //时钟信号
    input                idelay_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协议
localparam  MIN_DATA_NUM = 6'd46   ; //以太网数据最小为46个字节,不足部分填充数据   

reg             tx_en_d0            ;
reg             tx_en_d1            ;
reg             tx_en_d2            ;
reg     [4:0]   cur_state           ;
reg     [4:0]   next_state          ;
reg             skip_en             ;
reg     [7:0]   preamble [7:0]      ;
reg     [7:0]   eth_head [13:0]      ;
reg     [7:0]   arp_data [27:0]      ;
reg     [5:0]   cnt                 ;
reg     [5:0]   data_cnt            ;
reg             tx_done_t           ;


assign  pos_tx_en   =   (!tx_en_d2) & tx_en_d1; //触发信号上升沿

//对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) begin
        cur_state   <=  st_idle     ;
    end 
    else begin
        cur_state   <=  next_state  ;
    end
end

//
always @(*) begin
    if (!rst_n) begin
        next_state  =  st_idle ;
    end 
    else begin
        case (cur_state)
            st_idle     :begin
                if (skip_en) begin
                    next_state  =   st_preamble ;
                end 
                else begin
                    next_state  =   st_idle     ;
                end
            end
            st_preamble :begin
                if (skip_en) begin
                    next_state  =   st_eth_head ;
                end 
                else begin
                    next_state  =   st_preamble ;
                end
            end
            st_eth_head :begin
                if (skip_en) begin
                    next_state  =   st_arp_data ;
                end else begin
                    next_state  =   st_eth_head ;
                end
            end
            st_arp_data :begin
                if (skip_en) begin
                    next_state  =   st_crc      ;
                end else begin
                    next_state  =   st_arp_data ;
                end
            end
            st_crc      :begin
                if (skip_en) begin
                    next_state  =   st_idle     ;
                end else begin
                    next_state  =   st_crc      ;
                end
            end
            default: next_state  =   st_idle    ;
        endcase
    end
end

//
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        skip_en     <=  1'b0;
        gmii_tx_en  <=  1'b0;
        cnt         <=  6'b0;
        data_cnt    <=  6'b0;
        gmii_txd    <=  8'd0;
        crc_en      <=  1'b0;
        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;   
        tx_done_t   <=  1'b0;
        case (next_state)
            st_idle     :begin
                gmii_tx_en  <=  1'b0;
                if (pos_tx_en) begin
                    skip_en <=  1'b1;
                    if ((des_mac != 48'd0)||(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 
                end 
                else begin
                    skip_en <= 1'b0;
                end
                if (arp_tx_type == 1'b0) begin
                    arp_data[7] <= 8'h01;           //请求
                end 
                else begin
                    arp_data[7] <= 8'h02;           //应答
                end
            end
            st_preamble :begin                      //前导码 7个8'h55 + 1个8'hd5 
                gmii_tx_en  <=  1'b1;
                gmii_txd    <=  preamble[cnt];
                if (cnt == 6'd7) begin
                    skip_en <=  1'b1;
                    cnt     <=  6'd0;
                end 
                else begin
                    cnt     <= cnt + 1'b1;    
                end
            end
            st_eth_head :begin                      //以太网帧头
                gmii_tx_en  <=  1'b1;
                gmii_txd    <=  eth_head[cnt];
                crc_en      <=  1'b1;
                if (cnt == 6'd13) begin
                    skip_en <=  1'b1;
                    cnt     <=  6'd0;
                end 
                else begin
                    cnt     <= cnt + 1'b1; 
                end
            end
            st_arp_data :begin                      //ARP数据
                gmii_tx_en  <=  1'b1;
                crc_en      <=  1'b1;
                if (cnt == MIN_DATA_NUM - 1'b1) begin
                    skip_en <=  1'b1;
                    cnt     <=  6'b0;
                    data_cnt<=  6'b0;
                end else begin
                    cnt <= cnt + 1;
                end

                if (data_cnt <= 6'd27) begin
                    data_cnt <= data_cnt + 1'b1;
                    gmii_txd <= arp_data[data_cnt];
                end 
                else begin
                    gmii_txd  <= 8'd0;    
                end
            end
            st_crc      :begin
                gmii_tx_en  <= 1'b1;
                cnt         <= cnt + 1'b1;
                crc_en      <=  1'b0;
                if(cnt == 6'd0)
                    // gmii_txd <= {~crc_next[24], ~crc_next[25], ~crc_next[26],~crc_next[27],
                    //              ~crc_next[28], ~crc_next[29], ~crc_next[30],~crc_next[31]};
                    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 
            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

ila_1 u_ila_1 (
	.clk(idelay_clk), // input wire clk

	.probe0(gmii_txd), // input wire [7:0]  probe0  
	.probe1(cnt), // input wire [4:0]  probe1
    .probe2(next_state), // input wire [4:0]  probe1
    .probe3(skip_en), // input wire [4:0]  probe1
    .probe4(error_en) // input wire [4:0]  probe1
);

endmodule

(3)CRC

八、仿真

发送请求包:

发送应答包:

九、源码获取、工程移植、请后台私信

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值