【国产FPGA 正点原子DFPGL22G开发板】二、千兆以太网测评【程序设计篇】
[复制链接]
本帖最后由 yyliu 于 2023-1-15 11:29 编辑
声明:针对本帖中可能出现的侵权行为,请及时联系本人修改或删除。未经本人允许,请勿转载。若本帖存在错误或不足之处,烦请指正,本人会及时修改。
0.说明
1.程序设计篇主要介绍千兆以太网通信的程序框图和代码实现,介绍了FPGA开发板与主机之间网速测试的方法。
2.本帖所做的实验为正点原子提供的案例:以太网UDP实验。任务是上位机通过网口调试助手发送数据给FPGA,FPGA通过以太网接口接收数据并将接收到的数据发送给上位机,完成以太网UDP数据的环回。
3.建议学习正点原子《ATK-DFPGL22G之FPGA开发指南_V1.1》的第49~51章后,再阅读本帖的关键代码分析。
1.千兆以太网UDP通信原理
首先了解下以太网通信的基本过程,以生活中常见的场景为例,当你使用电脑WEB网页访问网络资源,你的电脑相当于主机A,为你提供数据的远端服务器相当于主机B,你们之间通信的数据流如下图所示:
应用层:假设你网络请求走的是HTTP协议,那么你的主机将在应用层发出:HTTP数据;
传输层:TCP相对于UDP协议更加可靠,假设传输层采用的是TCP协议,则传输层的数据变为:TCP首部+HTTP数据;
网络层:网络层一般采用IP协议,但是为了获取所访问对象的MAC地址,还有固定采用ARP协议,则网络层传输的数据变为:IP首部+TCP首部+HTTP数据;
数据链路层:数据链路层四个子层为媒体访问控制层(MAC)、无线链路控制层(RLC)、分组数据汇聚协议层(PDCP)和服务数据自适应协议层(SDAP);可以简单认为,数据链路层传输的数据变为:前导码+SFD+以太网帧头+IP首部+TCP首部+HTTP数据+FCS;
与上图相比,该实验的用户数据并不是通过应用层的各种协议实现的,而是直接在FPGA代码中产生所需数据。下图是UDP实验以太网数据包格式:
用户数据打包在UDP协议中,UDP协议又是基于IP协议之上的,IP协议又是走MAC层发送的,即从包含关系来说:MAC帧中的数据段为IP数据报,IP报文中的数据段为UDP报文,UDP报文中的数据段为用户希望传输的数据内容。
IP协议、UDP协议、ARP协议在《ATK-DFPGL22G之FPGA开发指南_V1.1》中有详细介绍,学习了解这些协议是理解本实验程序的前提,重点需要了解各协议的数据格式,占用字节数和各类型数据的顺序。本帖将三种协议的数据格式截取下来,相关内容请学习正点原子提供的文档。
IP数据格式见下图:
UDP数据格式见下图:
ARP数据格式见下图:
2.关键代码分析
下图是UDP通信的系统框图。
2.1 GMII TO RGMII 模块:GMII TO RGMII 模块负责将双沿(DDR)数据和单沿(SDR)数据之间的转换。
我们为什么要设计GMII TO RGMII 模块呢?而不是将RGMII接口的数据直接与ARP顶层模块和UDP顶层模块相连?首先,RGMII采用的是双边延采样,我们需要解析RGMII接收到的数据,因此采用GMII TO RGMII 模块去将双边沿数据解析出来,用统单一周期的时钟去表示1bit的数据,与其他模块的时钟更容易同步。其次,RGMII接口的数据直接与ARP顶层模块和UDP顶层模块相连也是可以的,只要处理好RGMI需要发送和接收的数据即可。
GMII和RGMII区别:在GMII接口中,它是用8根数据线来传送数据的,这样在传送1000M数据时,时钟就会125MHz。虽然RGMII接口中,信号线减半,同时GTX_CLK和RX_CLK还是125MHz,为了达到1000Mbit的传输速率,TXD和RXD信号线上在时钟的上升沿发送GMII接口中的TXD[3:0]/RXD[3:0],在时钟的下降沿发送GMII接口中TXD[7:4]/RXD[7:4],并且信号TX_CTL反映了TX_EN和TX_ER的状态,即在GTX_CLK上升沿发送TX_EN,下降沿发送TX_ER。同样的道理适用于信号RX_CTL,它反映了RX_EN和RX_ER的状态,即在RX_CLK上升沿发送RX_EN,下降沿发送RX_ER。
该模板例化了rgmii_rx和rgmii_tx两个模块,rgmii_rx用于将GMII数据转换为RGMII数据发送出去,rgmii_rx用于将RGMII接收的数据转化为GMII的单边沿数据。本帖只对rgmii_rx进行分析,rgmii_tx和rgmii_rx是类似的。下述代码是rgmii_rx的端口声明。
module rgmii_rx(
//以太网RGMII接口
input rgmii_rxc , //RGMII接收时钟
input rgmii_rx_ctl , //RGMII接收数据控制信号
input [3:0] rgmii_rxd , //RGMII接收数据
//以太网GMII接口
output gmii_rx_clk , //GMII接收时钟
output reg gmii_rx_dv , //GMII接收数据有效信号
output reg [7:0] gmii_rxd , //GMII接收数据
output gmii_tx_clk_deg,
output pll_lock
);
rgmii_rx模块的rgmii用于接收上位机发送过来的数据,在上升下降沿进行采样,gmii用于将双边沿数据转化为单边沿数据,送到ARP或者UDP模块。
该模块的核心是使用了PDS原语GTP_ISERDES;不同于RTL级设计可以通过综合变成门级电路,原语也就是门级语言,可以直接自定义数字电路以达到对指定电路的设计。
wire [5:0] nc1;
GTP_ISERDES #(
.ISERDES_MODE ("IDDR"), //"IDDR","IMDDR","IGDES4","IMDES4","IGDES7","IGDES8","IMDES8"
.GRS_EN ("TRUE"), //"TRUE"; "FALSE"
.LRS_EN ("TRUE") //"TRUE"; "FALSE"
) igddr1(
.DI (rgmii_rxd[0]),
.ICLK (1'd0 ),
.DESCLK (gmii_rx_clk ),
.RCLK (gmii_rx_clk ),
.WADDR (3'd0 ),
.RADDR (3'd0 ),
.RST (1'b0 ),
.DO ({gmii_rxd_s[4],gmii_rxd_s[0],nc1})
);
其中,IDDR工作模式的时序图如下:
当配置成IDDR模式时,DO[5:0]是无效数据,因此赋值给nc1;DO[7:6]是有效数据,因此将RGMII的四根数据线的其中一根的上升下降沿数据赋值给GMII的两个bit;也就是rgmii_rxd[0]在上升沿的数据赋值给gmii_rxd_s[0],在下降沿的数据赋值给gmii_rxd_s[4]。不理解的可以看下RGMII接收端的接口时序图,就容易理解了。
2.2 ARP模块:知道目标主机的IP后,本地主机是不知道目标主机的MAC地址,这样就无法完成以太网数据的发送和请求。ARP模块的目的是为了自动获取目标主机的MAC地址。整个以太网帧格式与ARP协议的实现由ARP顶层模块完成;ARP控制模块负责检测输入的触摸按键是否被按下,控制ARP顶层模块发起请求与产生应答等操作。
ARP模块的接口见代码:GMII收到数据以后,会将数据传送给ARP模块;ARP模块根据对方的数据请求类型,决定是否需要返回自己的MAC地址。因此ARP模块也分为发送模块arp_tx和接收模块arp_rx。
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 //以太网发送完成信号
);
理解ARP发送模块和接收模块的代码,需要根据ARP协议规范的格式,明白先发什么数据,后发什么数据,总体来说难度不大,但是需要花一些时间去理解代码状态机的跳转。本帖不再介绍。
ARP模块还包括了CRC校验模块crc32_d8。
//输入待校验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];
......
CRC码是由两部分组成的,前部分是信息字段,就是需要校验的信息,后部分是校验字段,如果CRC码共长n个bit,信息字段长k个bit,就称为(n,k)码。因此crc32_d8相当于n=32,k=8。
它的编码规则是:
- 首先将信息字段值(k bit)左移r位(k+r=n)
- 运用一个生成多项式g(x) (也可看成二进制数) 模2除上面的式子,得到的余数就是校验字段值。
- 生成的CRC码值为:信息字段值+校验字段值(单位:位bit,次序:高位到低位),例如字段值为1001,校验字段值为110,则CRC码为1001110
关于CRC校验的原理,可以参考博客园的帖子:https://www.cnblogs.com/masonzhang/p/10261855.html;写的比较清晰。
3.3 UDP模块:UDP顶层模块实现了以太网UDP数据包的接收、发送以及CRC校验的功能。
UDP模块和ARP模块类似,包括发送模块、接收模块和CRC校验模块。需根据UDP通信协议去理解状态机的跳转,分析用户数据是如何被解析出来的。
3.Modelsim仿真
本实验仿真的原理是开发板的udp的环回,既将udp的发送数据直接发送给udp的接收数据。由于FPGA开发板的发送和接收的MAC地址都是相同的,因此不需要调用ARP模块去获取目的主机的MAC地址。
下述代码可以看出时钟周期为8ns,等价于RGMII和GMII的125MHz时钟频率。
//parameter define
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地址 192168.1.10
parameter DES_IP = {8'd192,8'd168,8'd1,8'd10};
下述是例化UDP模块的代码:
-
tx_data是需要发送的数据,testbench定初始化该数据为32'h_00_11_22_33;
-
gmii_rxd是环回的接收数据,
大家在做Modelsim仿真时,可以着重注意发送和接收的数据。
//例化UDP模块
udp
#(
.BOARD_MAC (BOARD_MAC), //参数例化
.BOARD_IP (BOARD_IP ),
.DES_MAC (DES_MAC ),
.DES_IP (DES_IP )
)
u_udp(
.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)
);
配置好Modelsim和PDS联合仿真后,执行下图操作,查看仿真代码:
大家可以看到该实验发送的数据如下:
接收的数据如下:
另外,根据时钟周期和波形图,大家可以对着波形图、MAC层协议,分析数据传输过程是否和状态机跳转过程一致。
4.实物仿真和网速测试
实物仿真包括两个部分,一是正点原子教程上的上位机网络调试助手与FPGA通信实验;大家可以自行参考手册;
另一部分是正点原子没提到的,如何进行网速测试,使用抓包工具wireshark测试FPGA通信时以太网的速率。
我们可以通过抓包工具wireshark测量以太网部分的数据发送的速度,因为千兆以太网理想模式网络的速度可以达到1Gbps,但实际因为每个数据包中有包头,CRC等非数据字符,而且每包之间有空隙,一般千兆以太网的数据传输速度最高在950Mbps左右。传输是上下对称传输,就是说上行和下行都能达到950Mbps左右。在这里,因为抓包wireshark只能统计接收的数据包,而不会发送数据,所以只是测试FPGA发给电脑的输送数据的速度。
使用抓包工具wireshark 测试PGA通信时以太网的速率,需要重新编写testbench文件,由于本人电脑无法使用网线和开发板相连,因此编写testbench文件和网速测试的工作留个尾巴。如果大家有需求,后续有时间可以将这块内容补上。网速测试相关细节可参考资料:https://www.cnblogs.com/alinx/p/14605179.html。
5.提出建议
正点原子的UDP通信实验例程下载完成后,红框所示的黄色指示灯一直是亮的。在没插网线时,黄色灯长灭比较合理
这个黄色等表示的是ACT灯,一般在没插网线是不会亮的。一般网络指示灯包括LINK灯和ACT灯,常用规范如下:
- 连接指示灯(绿色)。
连接指示灯亮就代表线路连接正常。
- 信号指示灯(黄色)。
在连接指示灯亮的情况下,信号指示灯的含义如下:
a) 如果信号指示灯闪烁,代表信号正常,正在通信;
b) 如果信号指示灯灭,代表没有通信;
c) 如果信号指示灯长亮,代表网线短路。
|