基于FPGA的百兆以太网ARP测试实验

由于各种原因,如旧式电脑的网卡不支持千兆网等,导致PHY在自协商时速率达不到1000Mbps。而目前主流的FPGA的以太网实验均基于千兆网络编写测试代码,因此根据千兆网络降速编写百兆网的测试实验样例是有必要的,后续可再进一步编写自适应网速切换百兆或千兆的测试例程。

本文将基于正点原子开拓者V2开发板进行适配百兆网络的FPGA中的ARP测试代码编写,主要由正点原子千兆网实验教程改编,千兆网与百兆网的区别并不是很大,因此,主要将记录不同的代码,百兆网下其余与千兆网相同的代码将不再赘述。

实验环境:

Quartus II 13.1 Altera FPGA EP4CE10F17C8N

若是Xilinx环境,则将相关的IP核在基于Xilinx进行更改重新生成,其余RTL级代码基本相同。

代码更改:

正点原子开拓者V2开发板的以太网接口为RGMII接口,数据位宽为4位,在千兆网络下,在时钟上下沿采样,在百兆与十兆网络下,为单个时钟沿采样。

相比GMII接口与MII接口,RGMII至少可以节省8根数据线,因此,现多数PHY侧芯片连接FPGA时多采用RGMII接口。

在RGMII下,百兆网通信时,主要有几根数据线需要注意。

ETH_RXC:接收数据参考时钟,100Mbps 速率下,时钟频率为 25MHz ETH_RXC 由 PHY 侧提供。

ETH_RXCTL(ETH_RX_DV):接收数据控制信号。

ETH_TXC:发送参考时钟,100Mbps 速率下,时钟频率为 25MHz, ETH_TXC 由 MAC 侧提供。

ETH_TXCTL(ETH_TXEN):发送数据控制信号。

rgmii接收模块

由于ARP模块中的arp_rx.v,即 arp接收模块下的输入数据gmii_rxd为为8位,因此arp接收模块的输入时钟应该为12.5Mhz,而为了保持arp接收模块与前一级的rgmii接收模块时钟一致,保证时钟尽量同步。rgmii接收模块的输入时钟也为12.5Mhz。rgmii接收模块百兆时序如下:

rgmii接收模块的输入如果为12.5Mhz,且根据百兆网协议单边沿采样传输数据,则网络速率将只为12.5MhzX4bit=50Mbps,因此为了保证100Mbps的速率,rgmii接收模块将以12.5Mhz的时钟,以双边沿采样的方式,采样RGMII接收数据,将速率提升至100Mbps,具体代码如下:

/************************************************************************ 
 * 项目名称 :  $FPGA实验箱$   
 * 类 名 称 :  $ARP测试实验$ 
 * 版 本 号 :  1.0        
 * 作    者 :  kxq
 * 邮    箱 :  2543697634@qq.com
 * 网    站 :  https://www.cnblogs.com/kxqblog/
 * 创建时间 :  $2022/8/9$
 * 项目描述 :  RGMII接收模块
************************************************************************/

module rgmii_rx(
    //以太网RGMII接口
    input              rgmii_rxc   , //RGMII接收时钟
	 input				  sys_rst_n,
    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接收数据   
    );   

	 
reg 			 cnt;   
wire  [1:0]  gmii_rxdv_t;        //两位GMII接收有效信号			 

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

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

ddi_x4 ddi_x4_inst(
    .datain     (rgmii_rxd    ),
    .inclock    (rgmii_rxc    ),
    .dataout_h  (gmii_rxd[7:4]),
    .dataout_l  (gmii_rxd[3:0])
);

ddi_x1 ddi_x1_inst(
    .datain     (rgmii_rx_ctl),
    .inclock    (rgmii_rxc  ),
    .dataout_h  (gmii_rxdv_t[1]),
    .dataout_l  (gmii_rxdv_t[0])
);

endmodule

这里的ddiIP核是Altera下的ALTDDIO_IN IP,实现双沿数据和单沿数据之间的转换,相关介绍可以参考网络上的一些教程与一些参考书籍。

这里的ddi_x4是在12.5Mhz下将输入的4bit rgmii_rxd转换为8bit gmii_rxd输入给arp接收模块。ddi_x1是将接收数据中的使能信号使能信号异或错误信号的结果输入给arp接收模块。

从之前的rgmii接收模块百兆时序图来看,时钟边沿与数据传输边沿对齐,这样将在采样时发生错误,因此,有必要对于输入时钟进行偏移,对于千兆网来说,可以通过硬件引脚上拉电阻的方式,硬件延时2ns来偏移1/4个周期。而对百兆网,千兆网硬件延时的时间较短,不适用于百兆网的rgmii接收模块,这里,在生成输入时钟的PLL的IP核中对于12.5Mhz进行偏移45度,如下图所示。

rgmii发送模块

相比较正点原子中的千兆速率下的rgmii发送模块,改动幅度较大。在百兆rgmii发送模块中,数据发送时钟ETH_TXC为25Mhz,且为上边沿采样方式。因此rgmii发送模块百兆时序如下:

从图中可以看出,数据发送时钟为25Mhz时,时钟采样发送数据将为25MhzX4bit=100Mbps,恰好为百兆的发送速率,具体代码如下,需要有一定的Verilog代码基础。

/************************************************************************ 
 * 项目名称 :  $FPGA实验箱$   
 * 类 名 称 :  $ARP测试实验$ 
 * 版 本 号 :  1.0        
 * 作    者 :  kxq
 * 邮    箱 :  2543697634@qq.com
 * 网    站 :  https://www.cnblogs.com/kxqblog/
 * 创建时间 :  $2022/8/9$
 * 项目描述 :  RGMII发送模块
************************************************************************/

module rgmii_tx(
    //GMII发送端口
    input              gmii_tx_clk ,   //GMII发送时钟
	 input				  sys_rst_n, 
    input              gmii_tx_en  ,   //GMII输出数据有效信号
    input       [7:0]  gmii_txd    ,   //GMII输出数据        
    
    //RGMII发送端口
    output             rgmii_txc   ,   //RGMII发送数据时钟    
    output   reg          rgmii_tx_ctl,   //RGMII输出数据有效信号
    output   reg   [3:0]  rgmii_txd       //RGMII输出数据     
    );

reg 									cnt;
//*****************************************************
//**                    main code
//*****************************************************

always@(posedge gmii_tx_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		cnt <= 1'b0;
	else
		cnt <= cnt + 1'b1;
end

always@(posedge gmii_tx_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		rgmii_txd <= 4'd0;
	else if(cnt == 1'b1)
		rgmii_txd <= gmii_txd[3:0];
	else 
		rgmii_txd <= gmii_txd[7:4];
end

always@(posedge gmii_tx_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		rgmii_tx_ctl <= 1'b0;
	else if(cnt == 1'b1)
		rgmii_tx_ctl <= gmii_tx_en;
	else 
		rgmii_tx_ctl <= gmii_tx_en;
end

assign rgmii_txc = gmii_tx_clk;

endmodule

这里,GMII发送时钟与RGMII发送数据时钟速率设置保持一致。同样地,rgmii发送模块百兆时序中可以看出,时钟边沿与数据传输边沿对齐,这样将在采样时发生错误。因此需要在生成输入时钟的PLL的IP核中对于25Mhz进行偏移90度,如下图所示。

arp测试顶层模块

相比较于正点原子手册中的arp测试顶层模块,在本改进的百兆网模块下,主要是对rgmiigmii模块与时钟锁相环进行了更改,具体可以参考以下代码。

/************************************************************************ 
 * 项目名称 :  $FPGA实验箱$   
 * 类 名 称 :  $ARP测试实验$ 
 * 版 本 号 :  1.0        
 * 作    者 :  kxq
 * 邮    箱 :  2543697634@qq.com
 * 网    站 :  https://www.cnblogs.com/kxqblog/
 * 创建时间 :  $2022/8/9$
 * 项目描述 :  顶层模块,实现ARP协议
************************************************************************/

module eth_arp_top(
    input              sys_rst_n , //系统复位信号,低电平有效 
    input              touch_key , //KEY0按键开关
    //以太网RGMII接口   
    input              eth_rxc   , //RGMII接收数据时钟
    input              eth_rx_ctl, //RGMII输入数据有效信号
    input       [3:0]  eth_rxd   , //RGMII输入数据
    output             eth_txc   , //RGMII发送数据时钟    
    output             eth_tx_ctl, //RGMII输出数据有效信号
    output      [3:0]  eth_txd   , //RGMII输出数据          
    output             eth_rst_n , //以太网芯片复位信号,低电平有效 

	 output				  sample_clk
    );

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

//wire define      
wire          eth_rxc_deg   ; //eth_rxc时钟相位偏移 
 
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       ; //接收到目的MAC地址
wire  [31:0]  src_ip        ; //接收到目的IP地址    
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			  clk_25Mhz;

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

assign des_mac = src_mac;
assign des_ip = src_ip;
assign eth_rst_n = sys_rst_n;

//PLL
pll my_pll(
    .inclk0  (eth_rxc),
    .c0      (eth_rxc_deg),
	 .c1		 (sample_clk),
	 .c2		 (clk_25Mhz),
    .locked  ()
    );

//GMII接口转RGMII接口
gmii_to_rgmii my_gmii_to_rgmii(

    .gmii_rx_clk       (gmii_rx_clk ),
    .gmii_rx_dv        (gmii_rx_dv  ),
    .gmii_rxd          (gmii_rxd    ),
    .gmii_tx_clk       (clk_25Mhz  ),   //gmii_tx_clk
    .gmii_tx_en        (gmii_tx_en  ),
    .gmii_txd          (gmii_txd    ),
        
    .rgmii_rxc         (eth_rxc_deg ),
    .rgmii_rx_ctl      (eth_rx_ctl  ),
    .rgmii_rxd         (eth_rxd     ),
    .rgmii_txc         (eth_txc     ),
    .rgmii_tx_ctl      (eth_tx_ctl  ),
    .rgmii_txd         (eth_txd     ),
	 .sys_rst_n			  (sys_rst_n)
    );

//ARP通信
arp                                             
   #(
    .BOARD_MAC     (BOARD_MAC),      //参数例化
    .BOARD_IP      (BOARD_IP ),
    .DES_MAC       (DES_MAC  ),
    .DES_IP        (DES_IP   )
    )
   my_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   (eth_rxc_deg),
    .gmii_tx_en    (gmii_tx_en ),
    .gmii_txd      (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       (tx_done    )
    );

//ARP控制
arp_ctrl my_arp_ctrl(
    .clk           (gmii_rx_clk),
    .rst_n         (sys_rst_n),
                   
    .touch_key     (~touch_key),
    .arp_rx_done   (arp_rx_done),
    .arp_rx_type   (arp_rx_type),
    .arp_tx_en     (arp_tx_en),
    .arp_tx_type   (arp_tx_type)
    );

endmodule

其中,PLL中的C1时钟100Mhz,是SignalTapII测试抓波时的采样时钟,可以不用。C0时钟eth_rxc_deg为之前所说的偏移后的12.5Mhz时钟。GMII接口转RGMII接口中对gmii_tx_clk进行了更改,同时再添加一路复位信号。ARP通信中对gmii_tx_clk进行了更改,上层的arp模块下的子发送模块的参考时钟同样要与GMII接口转RGMII接口中的发送时钟同步。

测试结果

最终经过测试,修改后的代码无论是在板子发送ARP消息给电脑,或是电脑给FPGA发送ping消息下,电脑均能正确解析,并动态加载板子的Mac地址。

板子发送消息给电脑并正确识别:

电脑ping板子,其中ARP消息被正确解析。

代码链接 :

https://pan.baidu.com/s/1kQ24aYFQdKwEA1BUmWDgzA?pwd=vgue

参考博客:

https://blog.csdn.net/weixin_55796564/article/details/122459134

https://www.likecs.com/show-204584212.html

https://www.jianshu.com/p/46b762fee7ad

http://t.zoukankan.com/571328401--p-12686528.html

https://blog.csdn.net/weixin_42464967/article/details/112781420

https://www.cnblogs.com/amxiang/p/16288835.html

以及小梅哥,野火等一些论坛中查找的资料,这里就不一一详细列举。

posted @ 2022-12-10 21:51  Deceiver_Ker  阅读(1960)  评论(7编辑  收藏  举报