用户工具

站点工具


千兆以太网实验

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
千兆以太网实验 [2019/12/17 18:36]
zgf
千兆以太网实验 [2022/03/18 15:49] (当前版本)
sean
行 1: 行 1:
-  ***银杏科技有限公司/Gingko Technology Co.,Ltd.**     +|  ​**银杏科技有限公司旗下技术文档发布平台**  |||| 
-  ***技术支持论坛:http://​www.eeschool.org** +|技术支持电话|**0379-69926675-801**||| 
-  ***旗舰店:http://​icore.taobao.com** +|技术支持邮件|Gingko@vip.163.com||| 
-  ***技术支持邮件:GINGKO@vip.163.com**  + 版本 ​ ^  ​日期 ​ ​^ ​ 作者 ​ ^  修改内容 ​ ^ 
-  ​***电话:0379-69926675** + V1.0  ​|  2019-12-25 ​ |  gingko ​ |  初次建立 ​ |
-  ***更新日期:12/​17/​2019** +
-  ​***版本号:v1.0** +
-  ***更新说明:无**+
  
 ===== 实验三十二:千兆以太网传输 ===== ===== 实验三十二:千兆以太网传输 =====
行 15: 行 12:
   -掌握GMII接口千兆以太网的UDP协议通信模块设计。   -掌握GMII接口千兆以太网的UDP协议通信模块设计。
 === 二、实验设备及平台 ===  === 二、实验设备及平台 === 
-  ​-iCore3 双核心板( FPGA型号为EP4CE10F17) + 
-  -iCore3千兆网传输模块 +  ​-iCore3 双核心板( FPGA型号为EP4CE10F17)[[https://​item.taobao.com/​item.htm?​id=524229438677|点击购买]] 
-  -Blaster(或相同功能的)仿真器和USB线缆+  -千兆网传输模块[[https://​item.taobao.com/​item.htm?​id=574787244706|点击购买]] 
 +  -Blaster(或相同功能的)仿真器和USB线缆[[https://​item.taobao.com/​item.htm?​id=554869837940|点击购买]]
   -Micro USB线缆和千兆速率网线。   -Micro USB线缆和千兆速率网线。
   -QuartusII开发软件(本实验中使用的是13.1版本)、TCP&​UDP测试工具软件、wireshark抓包软件。   -QuartusII开发软件(本实验中使用的是13.1版本)、TCP&​UDP测试工具软件、wireshark抓包软件。
   -一台带有千兆网卡的电脑。   -一台带有千兆网卡的电脑。
-{{ :​图32-1_硬件连接实物图.jpg?​direct&​600 |图32-1_硬件连接实物图}}+{{ :icore3:​图32-1_硬件连接实物图.jpg?​direct&​600 |图32-1_硬件连接实物图}}
 **注意事项1:** 注意FPC转接板和千兆网卡模块下边缘对齐连接,如图30-1红圈内标注所示。\\ ​ **注意事项1:** 注意FPC转接板和千兆网卡模块下边缘对齐连接,如图30-1红圈内标注所示。\\ ​
 **注意事项2:** 确认PC端网卡和网线是千兆速率,网线的水晶头连接紧密稳定。\\ ​ **注意事项2:** 确认PC端网卡和网线是千兆速率,网线的水晶头连接紧密稳定。\\ ​
行 31: 行 29:
    ​*2)FPGA打包数据,并发送至PC。 =    ​*2)FPGA打包数据,并发送至PC。 =
   *具体细分的话,要实现FPGA单次收发数据的功能,和测试FPGA大量发送数据至PC端的极限速率。    *具体细分的话,要实现FPGA单次收发数据的功能,和测试FPGA大量发送数据至PC端的极限速率。
-  + 
-{{ :​图32-2_数据收发传输路径.png?​direct&​600 |图32-2_数据收发传输路径}}+{{ :icore3:​图32-2_数据收发传输路径.png?​direct&​600 |图32-2_数据收发传输路径}}
   *围绕实验目的,构建本次实验的开发环境。上图是本次实验需要构建的一个开发环境。图中绿色箭头代表PC端的TCP&​UDP测试工具软件,发送数据至FPGA内部千兆网数据接收模块的数据流路径,这是数据接收过程(相对于FPGA来说)。红色箭头代表FPGA的数据发送模块,将数据传送至PC端TCP&​UDP测试工具软件的数据传送路径,这是FPGA的数据发送过程。其中千兆网模块和iCore3开发板之间是通过排针连接, PC和千兆网模块是通过网线连接。PHY芯片和FPGA之间的数据传输,采用的是GMII接口。   *围绕实验目的,构建本次实验的开发环境。上图是本次实验需要构建的一个开发环境。图中绿色箭头代表PC端的TCP&​UDP测试工具软件,发送数据至FPGA内部千兆网数据接收模块的数据流路径,这是数据接收过程(相对于FPGA来说)。红色箭头代表FPGA的数据发送模块,将数据传送至PC端TCP&​UDP测试工具软件的数据传送路径,这是FPGA的数据发送过程。其中千兆网模块和iCore3开发板之间是通过排针连接, PC和千兆网模块是通过网线连接。PHY芯片和FPGA之间的数据传输,采用的是GMII接口。
 == 2、PHY芯片RTL8211EG == == 2、PHY芯片RTL8211EG ==
   *本实验中千兆网模块上的PHY芯片是realtek(瑞昱半导体)公司的RTL8211EG,采用64引脚封装,引脚分布如下图所示:   *本实验中千兆网模块上的PHY芯片是realtek(瑞昱半导体)公司的RTL8211EG,采用64引脚封装,引脚分布如下图所示:
-  + 
-{{ :​图32-3_rtl8211eg引脚示意图.png?​direct&​600 |图32-3_rtl8211eg引脚示意图}}+{{ :icore3:​图32-3_rtl8211eg引脚示意图.png?​direct&​600 |图32-3_rtl8211eg引脚示意图}}
   *RTL8211EG作为高集成度的网络收发芯片,支持多种收发模式,支持GMII和RGMII两种接口。可以通过配置,使其工作在RGMII或者GMII模式。GMII接口使用引脚较多,采用时钟单边沿采样数据。而RGMII则在时钟的上升沿和下降沿都采样数据,节约引脚但增加了时序控制的复杂度。至于选用哪种接口,视需要而定。本实验的千兆网模块已经通过硬件配置使RTL8211EG工作在GMII模式,寄存器配置采用的是默认设置。因此,了解PHY芯片的硬件驱动原理和处理好数据的收发即可。   *RTL8211EG作为高集成度的网络收发芯片,支持多种收发模式,支持GMII和RGMII两种接口。可以通过配置,使其工作在RGMII或者GMII模式。GMII接口使用引脚较多,采用时钟单边沿采样数据。而RGMII则在时钟的上升沿和下降沿都采样数据,节约引脚但增加了时序控制的复杂度。至于选用哪种接口,视需要而定。本实验的千兆网模块已经通过硬件配置使RTL8211EG工作在GMII模式,寄存器配置采用的是默认设置。因此,了解PHY芯片的硬件驱动原理和处理好数据的收发即可。
   *通过阅读RTL8211EG芯片手册中GMII模式下对应引脚的介绍,可以知道各引脚的编号,输入输出类型以及功能描述等。包括数据收发对应的引脚以及模式配置引脚。   *通过阅读RTL8211EG芯片手册中GMII模式下对应引脚的介绍,可以知道各引脚的编号,输入输出类型以及功能描述等。包括数据收发对应的引脚以及模式配置引脚。
-  + 
-{{ :​图32-4_gmii模式引脚命名及定义1.png?​direct&​600 |图32-4_gmii模式引脚命名及定义}} +{{ :icore3:​图32-4_gmii模式引脚命名及定义1.png?​direct&​800 |图32-4_gmii模式引脚命名及定义1}} 
-{{ :​图32-5_gmii模式引脚命名及定义2.png?​direct&​600 |图32-5_gmii模式引脚命名及定义2}}+{{ :icore3:​图32-5_gmii模式引脚命名及定义2.png?​direct&​800 |图32-5_gmii模式引脚命名及定义2}}
  
   *从芯片手册的表格中可以看到,RGMII模式和GMII模式是通过配置31号引脚来控制的。如下图红框中所示,拉低31号引脚的电平,则表示选择GMII模式,拉高则表示选择RGMII模式。从表格下方的原理图可以看到31号引脚接地,接口模式为 GMII接口。 ​   *从芯片手册的表格中可以看到,RGMII模式和GMII模式是通过配置31号引脚来控制的。如下图红框中所示,拉低31号引脚的电平,则表示选择GMII模式,拉高则表示选择RGMII模式。从表格下方的原理图可以看到31号引脚接地,接口模式为 GMII接口。 ​
  
-{{ :​图32-6_phy芯片模式选择控制.png?​direct&​600 |图32-6_phy芯片模式选择控制}} +{{ :icore3:​图32-6_phy芯片模式选择控制.png?​direct&​600 |图32-6_phy芯片模式选择控制}} 
-{{ :​图32-7_rtl8211eg电路原理图.png?​direct&​600 |图32-7_rtl8211eg电路原理图}}+{{ :icore3:​图32-7_rtl8211eg电路原理图.png?​direct&​600 |图32-7_rtl8211eg电路原理图}}
  
-  *表6.7中也包含了其他的配置信号,包括PHY地址和工作电压配置等,这些信号在千兆网模块上通过硬件电路已经配置完成,这里不再详解。有兴趣的同学可以结合手册和原理图,看一下是如何配置的。+  *图32-4和图32-5中也包含了其他的配置信号,包括PHY地址和工作电压配置等,这些信号在千兆网模块上通过硬件电路已经配置完成,这里不再详解。有兴趣的同学可以结合手册和原理图,看一下是如何配置的。
 **3、GMII接口** **3、GMII接口**
   *GMII接口采用8位数据通道,125MHz的工作时钟,可达1000Mbps的传输速率。符合IEEE802.3-2000标准以太网协议。在实际应用中,绝大多数GMII接口都是兼容MII接口的,所以,一般的GMII接口都有两个发送参考时钟:TX_CLK和GTX_CLK。本次实验是千兆网实验,使用的是GTX_CLK。   *GMII接口采用8位数据通道,125MHz的工作时钟,可达1000Mbps的传输速率。符合IEEE802.3-2000标准以太网协议。在实际应用中,绝大多数GMII接口都是兼容MII接口的,所以,一般的GMII接口都有两个发送参考时钟:TX_CLK和GTX_CLK。本次实验是千兆网实验,使用的是GTX_CLK。
   *TXCLK也是发射参考时钟,跟PHY芯片的传输速率有关。这个时钟是由PHY芯片提供,指向MAC侧的。100Mbps速率下,TXCLK的频率为25MHz,10Mbps速率下,TXCLK频率为2.5MHz。   *TXCLK也是发射参考时钟,跟PHY芯片的传输速率有关。这个时钟是由PHY芯片提供,指向MAC侧的。100Mbps速率下,TXCLK的频率为25MHz,10Mbps速率下,TXCLK频率为2.5MHz。
   *RXC也是PHY芯片提供的连续接收参考时钟。100Mbps速率下,时钟频率为25MHz,10Mbps速率下,时钟频率为2.5MHz。把FPGA看成MAC端,千兆网模块看成PHY端,他们之间的信号关联如下图所示:   *RXC也是PHY芯片提供的连续接收参考时钟。100Mbps速率下,时钟频率为25MHz,10Mbps速率下,时钟频率为2.5MHz。把FPGA看成MAC端,千兆网模块看成PHY端,他们之间的信号关联如下图所示:
-  + 
-{{ :​图32-8_gmii接口的相关信号.png?​direct&​600 |图32-8_gmii接口的相关信号}}+{{ :icore3:​图32-8_gmii接口的相关信号.png?​direct&​600 |图32-8_gmii接口的相关信号}}
   *关于GMII接口的信号定义,RTL8211EG的手册里有相关介绍,可以详参阅。   *关于GMII接口的信号定义,RTL8211EG的手册里有相关介绍,可以详参阅。
   *在GMII接口信号中,125Mhz的GTX_CLK作为发射参考时钟,由MCA侧提供给PHY芯片,即由FPGA内部PLL输出,提供给PHY芯片用于采集FPGA发送的数据。GTX_CLK上升沿要保证在数据的稳定期以确保采集数据的准确性。RTL8211EG的芯片手册中关于此信号和数据的规范如下图: ​   *在GMII接口信号中,125Mhz的GTX_CLK作为发射参考时钟,由MCA侧提供给PHY芯片,即由FPGA内部PLL输出,提供给PHY芯片用于采集FPGA发送的数据。GTX_CLK上升沿要保证在数据的稳定期以确保采集数据的准确性。RTL8211EG的芯片手册中关于此信号和数据的规范如下图: ​
  
-{{ :​图32-9_gmii接口时序定义.png?​direct&​600 |图32-9_gmii接口时序定义}}+{{ :icore3:​图32-9_gmii接口时序定义.png?​direct&​600 |图32-9_gmii接口时序定义}}
   *图中可以看出,tGCC即RXCLK时钟周期,典型值是8ns,​因为时钟频率是125MHz。tGSUR到时钟上升沿的最小值是2ns,​也就是TXD的跳变沿要比GTXCLK的上升沿最少要早2ns。我们代码中命名为GTX_CLK(tGCC)的时钟,​由PLL IP核输出。在PLL IP核关于此时钟的设定中我们设置了时钟沿延后2ns。如下图红框中所示:   *图中可以看出,tGCC即RXCLK时钟周期,典型值是8ns,​因为时钟频率是125MHz。tGSUR到时钟上升沿的最小值是2ns,​也就是TXD的跳变沿要比GTXCLK的上升沿最少要早2ns。我们代码中命名为GTX_CLK(tGCC)的时钟,​由PLL IP核输出。在PLL IP核关于此时钟的设定中我们设置了时钟沿延后2ns。如下图红框中所示:
-  + 
-{{ :​图32-10_时钟gtx_clk相位偏移量设置.png?​direct&​600 |图32-10_时钟gtx_clk相位偏移量设置}}+{{ :icore3:​图32-10_时钟gtx_clk相位偏移量设置.png?​direct&​600 |图32-10_时钟gtx_clk相位偏移量设置}}
 === 四、以太网 ===  === 四、以太网 === 
   *本实验所使用的千兆网模块基于PHY芯片RTL8211EG。作为物理层芯片,只负责数据的收发,不能对MAC层数据进行处理以及对协议的解析。这里就可以通过FPGA实现MAC层功能,按照互联网协议,解析出帧结构的数据部分,提取IP数据包。然后对IP数据包进行解析,读取IP数据包的数据部分,提取UDP数据包。再然后对UDP数据包进行解析,取出UDP包中的数据,也就是帧结构中的有效数据。从而实现FPGA解析以太网帧结构,解析IP协议,解析UDP数据包,以获取帧结构中的有效数据。   *本实验所使用的千兆网模块基于PHY芯片RTL8211EG。作为物理层芯片,只负责数据的收发,不能对MAC层数据进行处理以及对协议的解析。这里就可以通过FPGA实现MAC层功能,按照互联网协议,解析出帧结构的数据部分,提取IP数据包。然后对IP数据包进行解析,读取IP数据包的数据部分,提取UDP数据包。再然后对UDP数据包进行解析,取出UDP包中的数据,也就是帧结构中的有效数据。从而实现FPGA解析以太网帧结构,解析IP协议,解析UDP数据包,以获取帧结构中的有效数据。
行 83: 行 81:
   *接收端首先接收到的是由7个字节的0x55构成的前导码,然后收到一个0xD5作为帧起始符。起始符之后收到目标MAC地址,源MAC地址,然后是网络协议类型。本实验采用的是IPv 4网络协议,这个协议类型字段的值就应该是0x0800。然后是数据长度以及CRC-32的校验数据。  ​   *接收端首先接收到的是由7个字节的0x55构成的前导码,然后收到一个0xD5作为帧起始符。起始符之后收到目标MAC地址,源MAC地址,然后是网络协议类型。本实验采用的是IPv 4网络协议,这个协议类型字段的值就应该是0x0800。然后是数据长度以及CRC-32的校验数据。  ​
   *发送端与此类似,先发送7个字节的0x55,接着发送一个字节的0xd5,​然后按顺序发送MCA地址协议类型以及数据和CRC校验值。   *发送端与此类似,先发送7个字节的0x55,接着发送一个字节的0xd5,​然后按顺序发送MCA地址协议类型以及数据和CRC校验值。
-  *前导码的作用是告诉接收节点有数据发送来了,做好接收准备。而SFD就代表一帧数据的起始。这里有个地方需要注意,就是数据的长度。标准以太网帧长度最小值为64 字节,​最大值1518字节,有兴趣的可以查一下这两个数是怎么算的。这里的64和1518都是从目标地址开始算起的,不包括前导码和SFD,这也是为什么帧数据除了头部和校验以外的长度范围是46字节到1500字节。+  *标准以太网帧长度最小值为64 字节,​最大值1518字节,有兴趣的可以查一下这两个数是怎么算的。这里的64和1518都是从目标地址开始算起的,不包括前导码和SFD,这也是为什么帧数据除了头部和校验以外的长度范围是46字节到1500字节。
 == 2、IP协议 == == 2、IP协议 ==
   *IP数据包在以太网帧结构中是以帧数据的形式存在的。所以解析完以太网的帧结构,得到的以太网帧的数据部分,便是IP协议的数据包。按照IP协议的头部格式,对帧的数据部分依次进行比对判断。识别完IP首部,接下来读取的便是IP数据包的数据部分。那么,IP数据包以什么样的结构在以太网帧中存在的呢?请看下表:   *IP数据包在以太网帧结构中是以帧数据的形式存在的。所以解析完以太网的帧结构,得到的以太网帧的数据部分,便是IP协议的数据包。按照IP协议的头部格式,对帧的数据部分依次进行比对判断。识别完IP首部,接下来读取的便是IP数据包的数据部分。那么,IP数据包以什么样的结构在以太网帧中存在的呢?请看下表:
行 131: 行 129:
 === 五、代码讲解 ===  === 五、代码讲解 === 
   *首先来看一下这张图:   *首先来看一下这张图:
-  + 
-{{ :​图32-11_数据接收流程图.png?​direct&​300 |图32-11_数据接收流程图}}+{{  :​icore3:​图32-11_数据接收流程图.png?​direct&​300 |图32-11_数据接收流程图}}
   *这是数据接收时,FPGA内部接收模块数据做处理的流程。FPGA作为接收方,要判断接收的数据是否为一个帧数据包,就要按照帧结构,依次对接收的数据进行判断。只有数据符合帧结构,且MAC地址、IP地址、以及端口信息均和预期的吻合,才开始接收有效数据并缓存到寄存器组变量buffer中。否则就返回到初始状态继续等待数据的到来。   *这是数据接收时,FPGA内部接收模块数据做处理的流程。FPGA作为接收方,要判断接收的数据是否为一个帧数据包,就要按照帧结构,依次对接收的数据进行判断。只有数据符合帧结构,且MAC地址、IP地址、以及端口信息均和预期的吻合,才开始接收有效数据并缓存到寄存器组变量buffer中。否则就返回到初始状态继续等待数据的到来。
-  + 
-{{ :​图32-12_数据发送流程图.png?​direct&​400 |图32-12_数据发送流程图}}+{{ :icore3:​图32-12_数据发送流程图.png?​direct&​400 |图32-12_数据发送流程图}}
   *上图是帧数据发送流程图。一旦SD_EN为高的时候,进入帧数据发送流程。发送数据的时候要按照协议协议规定,以125MHz时钟信号做参考,将数据依次发送出去。按照顺序发送的思想,这里使用case语句控制状态的跳转,并通过计数对帧头部数据的发送进行控制。发送完UDP首部以后判断flag信号电平的高低。控制state是进入单次发送状态(send_data)还是连续发送状态(send_data1)。   *上图是帧数据发送流程图。一旦SD_EN为高的时候,进入帧数据发送流程。发送数据的时候要按照协议协议规定,以125MHz时钟信号做参考,将数据依次发送出去。按照顺序发送的思想,这里使用case语句控制状态的跳转,并通过计数对帧头部数据的发送进行控制。发送完UDP首部以后判断flag信号电平的高低。控制state是进入单次发送状态(send_data)还是连续发送状态(send_data1)。
   *有了上面的思路就可以构思代码逻辑了。   *有了上面的思路就可以构思代码逻辑了。
行 174: 行 172:
   *在解析UDP包头的时候,也对目标端口进行了判断。要保证TCP&​UDP测试软件设置的时候的端口值和此处代码中的端口值保持一致,否则会导致数据接收失败。   *在解析UDP包头的时候,也对目标端口进行了判断。要保证TCP&​UDP测试软件设置的时候的端口值和此处代码中的端口值保持一致,否则会导致数据接收失败。
 <code verilog> <code verilog>
-UDP_R:// 接收UDP包头 8 byte +    ​UDP_R:// 接收UDP包头 8 byte 
- begin +        begin 
- if(cnt_R == 8'd7)+     ​if(cnt_R == 8'd7) 
 +         begin 
 +     udp_head <= {udp_head[55:​0],​data1};​ 
 +     cnt_R <= 8'​d0;​  
 +     if(udp_head[39:​24] == 16'​h7530)//​16进制的7530是十进制的30000,即设定的端口号 
 +         begin 
 +     data_lenght <= udp_head[23:​8] - 16'​d8;//​提取数据长度,udp_head的[23:​8]定义了UDP包的数据长度,包括了UDP头部,所以这里要减去UDP头部的数据长度8  
 +     STATE_R <= APP_R; 
 + end 
 +     else
  begin  begin
- udp_head <= {udp_head[55:​0],​data1};​ +     ​STATE_R <= PRE_R; //​如果端口不匹配,则返回PRE_R状态
- cnt_R <= 8'​d0;​  +
- if(udp_head[39:​24] == 16'​h7530)//​16进制的7530是十进制的30000,即设定的端口号 +
- begin +
- data_lenght <= udp_head[23:​8] - 16'​d8;//​提取数据长度,udp_head的[23:​8]定义了UDP包的数据长度,包括了UDP头部,所以这里要减去UDP头部的数据长度8  +
- STATE_R <= APP_R; +
- end +
- else +
- begin +
- STATE_R <= PRE_R; //​如果端口不匹配,则返回PRE_R状态 +
- end+
  end  end
- else + end 
- begin +    else 
- udp_head <= {udp_head[55:​0],​data1};​ +         ​begin 
- cnt_R <= cnt_R +1'​d1;​  +     ​udp_head <= {udp_head[55:​0],​data1};​ 
- end  +     ​cnt_R <= cnt_R +1'​d1;​  
- end+ end  
 +     ​end
 </​code>​ </​code>​
   *在这个状态机中还有个标志信号UDP_SD_EN,在存取有效数据完毕的同时,拉高UDP_SD_EN这个信号,在帧结束状态拉低这个信号,如果感兴趣,可以用SignalTap II Logic Anlyzer(quartus II 自带的逻辑分析仪)查看一下这个信号持续了几个时钟周期。这个标志位的作用是表示一个完整的帧数据包接收完成了。在发送数据的时候,以这个信号判断数据是否接收完毕。   *在这个状态机中还有个标志信号UDP_SD_EN,在存取有效数据完毕的同时,拉高UDP_SD_EN这个信号,在帧结束状态拉低这个信号,如果感兴趣,可以用SignalTap II Logic Anlyzer(quartus II 自带的逻辑分析仪)查看一下这个信号持续了几个时钟周期。这个标志位的作用是表示一个完整的帧数据包接收完成了。在发送数据的时候,以这个信号判断数据是否接收完毕。
行 207: 行 205:
 always@(posedge RX_CLK or negedge rst_N ) always@(posedge RX_CLK or negedge rst_N )
  if(rst_N==1'​b0)  if(rst_N==1'​b0)
 +     flag_h<​=1'​b0;​
 + else if((buffer[0]==8'​h33)&&​(STATE_UT==APP_R))//​连发开始位标志3
 + flag_h<​=1'​b1;​
 +      ​else ​
  flag_h<​=1'​b0;​  flag_h<​=1'​b0;​
- else if((buffer[0]==8'​h33)&&​(STATE_UT==APP_R))//​连发开始位标志3 
- flag_h<​=1'​b1;​ 
- else ​ 
- flag_h<​=1'​b0;​ 
 always@(posedge RX_CLK or negedge rst_N ) always@(posedge RX_CLK or negedge rst_N )
  if(rst_N==1'​b0)  if(rst_N==1'​b0)
 +     flag_l<​=1'​b0;​
 + else if((buffer[0]==8'​h73)&&​(STATE_UT==APP_R))//​连发停止位标志s
 + flag_l<​=1'​b1;​
 +      else
  flag_l<​=1'​b0;​  flag_l<​=1'​b0;​
- else if((buffer[0]==8'​h73)&&​(STATE_UT==APP_R))//​连发停止位标志s 
- flag_l<​=1'​b1;​ 
- else 
- flag_l<​=1'​b0;​ 
   
 always@(posedge RX_CLK or negedge rst_N ) always@(posedge RX_CLK or negedge rst_N )
  if(rst_N==1'​b0)  if(rst_N==1'​b0)
- flag<​=1'​b0;​+     ​flag<​=1'​b0;​
  else if(flag_l) //​连发状态停止  else if(flag_l) //​连发状态停止
- flag<​=1'​b0;​ + flag<​=1'​b0;​ 
- else if(flag_h==1'​b1)//​连发状态开始 +      else if(flag_h==1'​b1)//​连发状态开始 
- flag<​=1'​b1;​+       ​flag<​=1'​b1;​
 </​code>​ </​code>​
   *看这段代码,定义了另外两个寄存器变量,用于控制flag信号,那么控制flag变量的flag_h和flag_l又是如何产生的呢?在代码中,通过对数据接收模块状态和接收的第一个字符的值进行综合判断,当接收模块处于接收有效数据,且第一个接受到的数值为“3”的时候,拉高一个时钟周期的flag_h信号。flag_l则根据接收数据的第一个字符为“s”的时候拉高一个时钟周期。这里大家应该有注意到,当我们对数据接收缓存器的第一个值buffer[0]进行判断时,对比值是8’h33和8’h73。这里的8’h33和8’h73分别是ASCII码“3”和“s”对应的16进制数。这是因为我们使用TCP&​UDP测试工具给FPGA发送数据的时候选择的是ASCII格式。   *看这段代码,定义了另外两个寄存器变量,用于控制flag信号,那么控制flag变量的flag_h和flag_l又是如何产生的呢?在代码中,通过对数据接收模块状态和接收的第一个字符的值进行综合判断,当接收模块处于接收有效数据,且第一个接受到的数值为“3”的时候,拉高一个时钟周期的flag_h信号。flag_l则根据接收数据的第一个字符为“s”的时候拉高一个时钟周期。这里大家应该有注意到,当我们对数据接收缓存器的第一个值buffer[0]进行判断时,对比值是8’h33和8’h73。这里的8’h33和8’h73分别是ASCII码“3”和“s”对应的16进制数。这是因为我们使用TCP&​UDP测试工具给FPGA发送数据的时候选择的是ASCII格式。
行 235: 行 233:
  always@(posedge clk_125M or negedge rst_N)  always@(posedge clk_125M or negedge rst_N)
  if(!rst_N)  if(!rst_N)
- SD_EN<​=1'​b0;​+     ​SD_EN<​=1'​b0;​
  else if(flag)  else if(flag)
- SD_EN<​=1'​b1;​ + SD_EN<​=1'​b1;​ 
- else if(!flag) +      else if(!flag) 
- SD_EN<​=UDP_SD_EN;​+       ​SD_EN<​=UDP_SD_EN;​
 </​code>​ </​code>​
   *进入发送状态以后,是将帧头部和IP头部以及校验和的值存入对应的寄存器。准备工作做好之后,开始进入发送状态。在发送数据之前,先进行IP首部校验和的运算。IP校验的目的是为了保证IP首部的完整性。算法也比较简单,反码求和。反码求和的意思是先将IP首部的数据每两个字节拼成一个16位数,并对这些16位数求和。将求和的结果存入一个32位寄存器中,然后将寄存器的高16位和低16位求和取反即可得到IP首部校验和。代码如下:   *进入发送状态以后,是将帧头部和IP头部以及校验和的值存入对应的寄存器。准备工作做好之后,开始进入发送状态。在发送数据之前,先进行IP首部校验和的运算。IP校验的目的是为了保证IP首部的完整性。算法也比较简单,反码求和。反码求和的意思是先将IP首部的数据每两个字节拼成一个16位数,并对这些16位数求和。将求和的结果存入一个32位寄存器中,然后将寄存器的高16位和低16位求和取反即可得到IP首部校验和。代码如下:
行 245: 行 243:
 check1: check1:
  begin  begin
- if(cnt == 8'​d6) +     ​if(cnt == 8'​d6) 
- begin//​IP计算首部校验和:以16位相加方式加到32位校验和中 + begin//​IP计算首部校验和:以16位相加方式加到32位校验和中 
- ip_check_sum <= {ip_send[0],​ip_send[1]} + {ip_send[2],​ip_send[3]}+{ip_send[4],​ip_send[5]} + {ip_send[6],​ip_send[7]} + {ip_send[8],​ip_send[9]}+{ip_send[12],​ip_send[13]}+{ip_send[14],​ip_send[15]}+{ip_send[16],​ip_send[17]}+{ip_send[18],​ip_send[19]};​ +     ​ip_check_sum <= {ip_send[0],​ip_send[1]} + {ip_send[2],​ip_send[3]}+{ip_send[4],​ip_send[5]} + {ip_send[6],​ip_send[7]} + {ip_send[8],​ip_send[9]}+{ip_send[12],​ip_send[13]}+{ip_send[14],​ip_send[15]}+{ip_send[16],​ip_send[17]}+{ip_send[18],​ip_send[19]};​ 
- crc_reset <= 1'​d1;​ +     ​crc_reset <= 1'​d1;​ 
- cnt <= 8'​d0;​ +     ​cnt <= 8'​d0;​ 
- STATE_UT <= check2; +     ​STATE_UT <= check2; 
- end + end 
- else +     ​else 
- begin + begin 
- cnt <= cnt  + 1'​d1;​ +     ​cnt <= cnt  + 1'​d1;​ 
- end+ end
  end  end
 check2: check2:
  begin//​IP计算首部校验和:16位相加取反  begin//​IP计算首部校验和:16位相加取反
- {ip_send[10],​ip_send[11]} = ~(ip_check_sum[15:​0] + ip_check_sum[31:​16]);​ +     ​{ip_send[10],​ip_send[11]} = ~(ip_check_sum[15:​0] + ip_check_sum[31:​16]);​ 
- STATE_UT <= send55;+     ​STATE_UT <= send55;
  end  end
 </​code>​ </​code>​
行 267: 行 265:
 <code verilog> <code verilog>
  send_udp://​发送UDP包头  send_udp://​发送UDP包头
- begin +     ​begin 
- if(cnt == 8'​d7) + if(cnt == 8'​d7) 
- begin +     ​begin 
- data_out <= udp_send[cnt];​ + data_out <= udp_send[cnt];​ 
- cnt <= 8'​d0;​ + cnt <= 8'​d0;​ 
- data_counter <= 16'​d0;​ +         ​data_counter <= 16'​d0;​ 
- if(flag) //​控制发送状态跳转至send_data1 + if(flag) //​控制发送状态跳转至send_data1 
- STATE_UT<​=send_data1;​ +     ​STATE_UT<​=send_data1;​ 
- else +         ​else 
- STATE_UT <= send_data;​ +     ​STATE_UT <= send_data;​ 
- end +     ​end 
- else + else 
- begin +     ​begin 
- data_out <= udp_send[cnt];​ + data_out <= udp_send[cnt];​ 
- cnt <= cnt +1'​d1;​ +         ​cnt <= cnt +1'​d1;​ 
- end+     ​end
  end  end
 </​code>​ </​code>​
行 288: 行 286:
 <code verilog> <code verilog>
 send_data://​发送数据 send_data://​发送数据
- begin  +     begin  
- if(data_lenght < 16'​d19) + if(data_lenght < 16'​d19) 
- begin +     ​begin 
- if(data_counter == 16'​d20) +         ​if(data_counter == 16'​d20) 
- begin +                    begin 
- data_out <= buffer[data_counter];​ +         ​data_out <= buffer[data_counter];​ 
- data_counter <= 16'​d0;​ + data_counter <= 16'​d0;​ 
- STATE_UT <= send_crc; + STATE_UT <= send_crc; 
- end +     ​end 
- else + else 
- begin  +     ​begin  
- data_out <= buffer[data_counter];​  + data_out <= buffer[data_counter];​  ​               ​data_counter <= data_counter + 1'​d1;​ 
- data_counter <= data_counter + 1'​d1;​ +     ​end 
- end +     ​end 
- end + else  
- else  +            begin 
- begin + if(data_counter == data_lenght - 16'​d1) 
- if(data_counter == data_lenght - 16'​d1) +     ​begin 
- begin + data_out <= buffer[data_counter];​ 
- data_out <= buffer[data_counter];​ + data_counter <= 16'​d0;​ 
- data_counter <= 16'​d0;​ + STATE_UT <= send_crc; 
- STATE_UT <= send_crc; +             ​end 
- end + else 
- else +     ​begin  
- begin  + data_out <= buffer[data_counter];​ data_counter <= data_counter + 1'​d1;​ 
- data_out <= buffer[data_counter];​ data_counter <= data_counter + 1'​d1;​ +             ​end 
- end +     ​end
- end+
  end  end
 +
 send_data1: send_data1:
- begin +       begin 
- if(cnt_l==1200) + if(cnt_l==1200) 
- begin + begin 
- STATE_UT <​=send_crc;​ + STATE_UT <​=send_crc;​ 
- cnt_l<​=12'​d0;​ + cnt_l<​=12'​d0;​ 
- end + end 
- else + else 
- begin +     ​begin 
- data_out<​=8'​h38;//​输出数值随便定义,这里设定的“8” + data_out<​=8'​h38;//​输出数值随便定义,这里设定的“8” 
- cnt_l<​=cnt_l+1'​b1;​ + cnt_l<​=cnt_l+1'​b1;​ 
- end+     ​end
  end  end
 </​code>​ </​code>​
行 343: 行 341:
      ​*点击【设置】选项,在弹出窗口中的以太网界面点击①处【更改适配器】选项。弹出网络连接界面,右键单击②处【以太网】,选择【属性】。在弹出界面点击③处的【协议版本4】,点击【属性】。弹出界面后在④处选择使【用下面的IP地址】,并设置IP地址和子网掩码,完成后点击【OK】并退出。如图中①②③④所示:      ​*点击【设置】选项,在弹出窗口中的以太网界面点击①处【更改适配器】选项。弹出网络连接界面,右键单击②处【以太网】,选择【属性】。在弹出界面点击③处的【协议版本4】,点击【属性】。弹出界面后在④处选择使【用下面的IP地址】,并设置IP地址和子网掩码,完成后点击【OK】并退出。如图中①②③④所示:
      *      *
-{{ :​图32-13_设置ip地址.png?​direct&​600 |图32-13_设置ip地址}}+{{ :icore3:​图32-13_设置ip地址.png?​direct&​600 |图32-13_设置ip地址}}
  
   *3)连接好网线和调试工具,供电并编译下载。   *3)连接好网线和调试工具,供电并编译下载。
   *4)打开wireshark抓包软件,双击以太网那行,进入抓数据包界面。   *4)打开wireshark抓包软件,双击以太网那行,进入抓数据包界面。
-  + 
-{{ :图32-4_gmii模式引脚命名及定义1.png?​direct&​600 |图32-4_gmii模式引脚命名及定义1}}+{{ :icore3:图32-14_wireshark界面.png?​direct&​600 |图32-14_wireshark界面}}
  
   *5)打开PC端的任务管理器,在性能界面点击【以太网】,在此界面可以实时动态显示PC端以太网接口发送和接收数据的速率。   *5)打开PC端的任务管理器,在性能界面点击【以太网】,在此界面可以实时动态显示PC端以太网接口发送和接收数据的速率。
行 354: 行 352:
     *(1)打开软件,点击客户端模式,创建连接。     *(1)打开软件,点击客户端模式,创建连接。
    
-{{ :​图32-15_tcp_udp测试工具创建连接.png?​direct&​600 |图32-15 TCP&​UDP测试工具创建连接}}+{{ :icore3:​图32-15_tcp_udp测试工具创建连接.png?​direct&​600 |图32-15 TCP&​UDP测试工具创建连接}}
     *(2)配置为UDP格式,并指定目标IP和端口号,注意此处IP和端口号要跟代码中的相互对应。点击【创建】按钮。     *(2)配置为UDP格式,并指定目标IP和端口号,注意此处IP和端口号要跟代码中的相互对应。点击【创建】按钮。
    
-{{ :​图32-16_tcp_udp测试工具参数设置.png?​direct&​600 |图32-16 TCP&​UDP测试工具参数设置}}+{{ :icore3:​图32-16_tcp_udp测试工具参数设置.png?​direct&​600 |图32-16 TCP&​UDP测试工具参数设置}}
  
   *出现下图界面,从界面中红框处可以看到IP、端口、类型等信息,点击蓝色框中的【创建】创立连接。如果打开软件后发送区为灰色,可以点击绿色框中的【√】,取消发送文件选项(本实验暂不支持文件的传输)。这时会看到TCP&​UDP测试工具软件的发送区可以输入内容了。在发送区输入任意内容,点击发送或者自动发送(此处自动发送为PC端持续向iCore3发送数据,iCore3接收并返回数据到PC端),即可实现PC端向FPGA发送数据。   *出现下图界面,从界面中红框处可以看到IP、端口、类型等信息,点击蓝色框中的【创建】创立连接。如果打开软件后发送区为灰色,可以点击绿色框中的【√】,取消发送文件选项(本实验暂不支持文件的传输)。这时会看到TCP&​UDP测试工具软件的发送区可以输入内容了。在发送区输入任意内容,点击发送或者自动发送(此处自动发送为PC端持续向iCore3发送数据,iCore3接收并返回数据到PC端),即可实现PC端向FPGA发送数据。
    
-{{ :​图32-17_tcp_udp测试工具测试界面.png?​direct&​600 |图32-17 TCP&​UDP测试工具测试界面}}+{{ :icore3:​图32-17_tcp_udp测试工具测试界面.png?​direct&​600 |图32-17 TCP&​UDP测试工具测试界面}}
   *本实验中使用3和s(小写)作为高速发送模式的标识符,即当PC发送的内容第一个字符为3这个数字时,FPGA开始连续大量发送数字8到PC,此时可以测试FPGA的千兆网模块发送数据的能力。   *本实验中使用3和s(小写)作为高速发送模式的标识符,即当PC发送的内容第一个字符为3这个数字时,FPGA开始连续大量发送数字8到PC,此时可以测试FPGA的千兆网模块发送数据的能力。
   *FPGA大量发送数据的过程中,如果PC端通过TCP&​UDP测试软件给千兆网模块发送的内容第一个字符为s(小写),则FPGA停止发送数字8,返回到普通模式。   *FPGA大量发送数据的过程中,如果PC端通过TCP&​UDP测试软件给千兆网模块发送的内容第一个字符为s(小写),则FPGA停止发送数字8,返回到普通模式。
行 367: 行 365:
   *不勾选【自动发送】选项,则点击发送的时候,发送的内容可以在接收区看到。这里以数字“4”为例,在TCP&​UDP测试工具的发送区输入数字“4”,并点击发送。可以在接收区看到数字“4”,表示FPGA接收到数字4,并将其返回至PC端。同时可以在wireshark界面看到PC端网口抓到的本次发送和接收的数据包信息。如下图红框中所示,是本次发送和接收的IP地址信息和数据包格式信息,点击任意一行,会深色显示,表示选中,在界面的中间窗口有源端口和目标端口的显示,对照TCP&​UDP测试工具软件的蓝色框内信息可以确认我们的端口信息。底端窗口橙色框中所示,是数字“4”的16进制和ASCII码格式。   *不勾选【自动发送】选项,则点击发送的时候,发送的内容可以在接收区看到。这里以数字“4”为例,在TCP&​UDP测试工具的发送区输入数字“4”,并点击发送。可以在接收区看到数字“4”,表示FPGA接收到数字4,并将其返回至PC端。同时可以在wireshark界面看到PC端网口抓到的本次发送和接收的数据包信息。如下图红框中所示,是本次发送和接收的IP地址信息和数据包格式信息,点击任意一行,会深色显示,表示选中,在界面的中间窗口有源端口和目标端口的显示,对照TCP&​UDP测试工具软件的蓝色框内信息可以确认我们的端口信息。底端窗口橙色框中所示,是数字“4”的16进制和ASCII码格式。
    
-{{ :​图32-18_tcp_udp测试工具和wireshark软件联合调试.png?​direct&​600 |图32-18 ​ TCP&​UDP测试工具和wireshark软件联合调试}}+{{ :icore3:​图32-18_tcp_udp测试工具和wireshark软件联合调试.png?​direct&​600 |图32-18 ​ TCP&​UDP测试工具和wireshark软件联合调试}}
   *勾选【连续发送】后可以看到接收区不断的接收和发送区相同的内容。这是TCP&​UDP测试软件持续将发送区内容发送至FPGA、FPGA再把接收到的内容发送至PC端的过程。   *勾选【连续发送】后可以看到接收区不断的接收和发送区相同的内容。这是TCP&​UDP测试软件持续将发送区内容发送至FPGA、FPGA再把接收到的内容发送至PC端的过程。
   *此时在TCP&​UDP测试工具的发送区输入第一个字符为3的内容,并点击发送。在任务管理器界面可以看到接收速率达980 Mbps。同时可以在TCP&​UDP测试工具软件的接收区看到连续的数字“8”接收进来,说明成功实现了FPGA向PC端以接近千兆的速率传输数据。   *此时在TCP&​UDP测试工具的发送区输入第一个字符为3的内容,并点击发送。在任务管理器界面可以看到接收速率达980 Mbps。同时可以在TCP&​UDP测试工具软件的接收区看到连续的数字“8”接收进来,说明成功实现了FPGA向PC端以接近千兆的速率传输数据。
    
-{{ :​图32-19_测试结果.png?​direct&​600 |图32-19 ​ 测试结果}}+{{ :icore3:​图32-19_测试结果.png?​direct&​600 |图32-19 ​ 测试结果}}
   *在FPGA高速发送数据模式下,如果在TCP&​UDP测试工具软件的发送区输入第一个字符为“s” (小写)的内容并点击发送,可以在任务管理器界面看到数据接收变为0Kbps。由于TCP&​UDP测试工具软件会缓存接收的数据并显示,等待一会儿之后可以看到接收区停止显示数字“8”,并且结尾显示有我们发送的字符“s”。如果有丢包现象,可以多点击两次【发送】按钮。目前千兆以太网已经很常见了,理论网速可达1000Mbit/​s,实际传输时接近该值,但几乎达不到该值。本次例程中传输速率实测可达980Mbit/​s。至此,本实验实现了基于FPGA的千兆网实验,包括数据的单次收发,连续收发以及接近千兆的发送能力测试。   *在FPGA高速发送数据模式下,如果在TCP&​UDP测试工具软件的发送区输入第一个字符为“s” (小写)的内容并点击发送,可以在任务管理器界面看到数据接收变为0Kbps。由于TCP&​UDP测试工具软件会缓存接收的数据并显示,等待一会儿之后可以看到接收区停止显示数字“8”,并且结尾显示有我们发送的字符“s”。如果有丢包现象,可以多点击两次【发送】按钮。目前千兆以太网已经很常见了,理论网速可达1000Mbit/​s,实际传输时接近该值,但几乎达不到该值。本次例程中传输速率实测可达980Mbit/​s。至此,本实验实现了基于FPGA的千兆网实验,包括数据的单次收发,连续收发以及接近千兆的发送能力测试。
 === 七、拓展实验: ===  === 七、拓展实验: === 
千兆以太网实验.1576578975.txt.gz · 最后更改: 2019/12/17 18:36 由 zgf