利用FPGA实现UDP网络高速可靠传输
FPGA数据处理中常用的一些IP核有网口ETH核、DDR核、CORDIC核、DDS核、加法器、乘法器、滤波器IP、FFT等IP核,这些IP核熟练使用能减少很多工作量和提高代码的稳定性。这些IP核的配置比较简单,但是如何正确使确是个很大的问题。最近一直在用网口、DDR、DDS、CORDIC这些IP。一下子很难把这些IP的用法全部写完,后续会一个一个的去介绍。
1、网口IP的框架结构和配置验证
我用的是XILINX的Virtex-5系列的芯片,这里就以这个配置为例。这里些的配置方法主要是参考UG340这个官方文档。
首先要对这个Tri-Mode Ethernet
MAC IP有个全局的直观把握,这样对理解IP核的层次核代码会有一个很好的指导。
从这个图来看,这是一个网口数据回环例程结构。从外到内可以分为四个层次:
1、外部接口,这里主要是IP核的输入输出以及时钟核复位接口。这里由于是回环传输的一个例程框架图,这个模块还包含Address Swap Module地址交换模块。
2、LocalLink wrapper模块,这部分其实就是把几个单独的模块例化到同一个模块下组成一个相对完备的功能。这里主要由两个大的模块组成,一个是收发的数据缓存FIFO,一个是MAC的数据收发转化的模块。
3、Block Level Wrapper 这层就是把第四层的两个核心模块进行例化。
4、MAC层,这个是网络IP的核心模块,这个主要是实现从PHY芯片接收到的数据IDDR核ODDR的转换和网络链路层(MAC层)协议的转换,这部分是整个IP核的核心。
上面这个结构能清晰的对网络Tri-Mode Ethernet MAC IP进行一个直观的描述,从上面可以看出数据的流向核各个模块的作用。具体到IP核的配置其实很简单。
这里面有三个选项,主要是选择共享接口和主机类型的选择,以及选择一个还是两个网口。
其中DCR设备控制寄存器,这个主要是通过总线桥对不同MAC进行控制。而在无特殊要求的时候一般选用NONE模式。
这部分主要是配置PHY的接口类型和传输速度。这个需要根据硬件情况进行配置。
这部分一般是对输入输出的控制和对一些MAC地址进行过滤。
以上就是Tri-Mode Ethernet MAC IP的基本配置。
完成了基本的配置只是实现网络传输的一小部分,要想灵活的运用这个IP还需要对底层代码进行了解,明白各个模块的功能。
打开仿真源码并结合上面的结构框图可以清晰的看到IP的四层结构框架。
这部分代码需要仔细的去看看,看明白数据的流向,ODDR和IDDR的转化。其中最重要的就是接收和发送时钟的来源以及去向。就不细讲里面的代码了,这部分需要自己看呀,不然讲讲也没啥用。
从MODELSIM仿真中可以看到完成了一个UDP包的回环收发。通过上面一系列的操作完成了IP核的配置并且通过仿真验证了数据能正确传输。
1、
基础的传输协议
利用FPGA实现UDP数据传输时需要明白UDP一种无连接的传输层协议,这个就是说都是单项传输的,不想TCP那样有握手机制在里面。又因为UDP在TCP/IP五层模型中处在第四层(传输层),处于IP协议的上一层,因此传输时需要知道对方的MAC地址、网络地址。而上位机也需要知道FPGA板卡的MAC地址、网络地址。解决这个问题可以有两种方式:
1、若是点对点的数据传输,可以通过在FPGA内部初始化对应计算机的MAC地址、网络地址、端口号等必要的信息,而计算机也通过修改网络协议栈中的信息,把对应IP的信息由动态改为静态即可,也就是PC机IP-MAC绑定。
更改方法为:一、首先,以管理员权限运行命令提示符(CMD)。
二、在命令提示符中运行“netsh
i i show in”查看要进行ARP绑定的网卡的idx编号。
三、在命令提示符中运行“netsh -c “i i” add neighbors idx IP MAC”进行ARP绑定,这里的idx就是上一步查到的网卡的idx编号,IP和MAC就是你要绑定的IP地址和MAC地址。例如:netsh -c “i i” add neighbors 12 192.168.1.1 D0-27-88-C9-7C-A4
四、最后在命令提示符下用“arp -a”命令查看下自己所添加的ARP项是否在列表中并且为静态就可以了。
“arp -a”命令检测为静态
五、解除绑定:netsh -c “i i” delete neighbors IDX (IDX改为相应的数字) 才可删除MAC地址绑定,然后重启系统。
2、通过使用ARP来实现动态的获知对方IP-MAC地址。这种方式比较灵活,实现起来也不难。
要灵活的实现UDP数据传输不仅需要实现UDP协议,还需要实现ARP来获取必要的信息。然而在实际的调试应用过程中不仅需要知道网络是否还需要知道连接是否正常,这就需要一个简单的方式来实现,一般用ICMP协议来实现。这种方式就是可以利用上位机Ping下位机IP,通过是否又回复来判断网络连接是否正常。
从上面的论述可知要想相对灵活的实现UDP传输还需要实现ARP和ICMP这两个协议。而这些协议是实现需要明白网络的OSI模型,这个其实不太难理解(其实挺难的,但是实现UDP传输需要理解的部分就相对简单点)。下面的图就是网络的层级结构和一些基础的常用的协议。
由于每一层实际都是一个复杂的层。后面我也会根据个人实际的项目应用展开部分层的深入学习。根据我的理解我简单的说下每个层的作用。
1、应用层则是拿到了数据想怎么用就怎么用。
2、传输层可以区分数据包属于哪一个应用程序,实现了数据从端到端的传递(应用程序到应用程序的传递)。
3、网络层实现了数据在主机之间的传递,每台网络设备都应该有自己的网络地址,网络层规定了主机的网络地址该如何定义,以及如何在网络地址和MAC 地址之间进行映射,即ARP 协议。
4、链路层主要是规定了数据帧能被网卡接收的条件,常见的方式是利用网卡的MAC地址识别。
5、物理层规定了传输需要的物理电平标准、介质特征。
在知道每个层的作用后就需要知道每个层的组成,其实这几个层是通过层层封装来组成的,他们的封装包格式如下图:
上面说了各个层的功能,下面就逐个来描述下要用到的ARP、ICMP和UDP这三个协议的组成。
ARP协议是地址解析协议,每一个PC都设有一个ARP高速缓存,这个缓存是存储所在的网段上的各主机和路由器的IP地址到硬件地址的映射表,这个表是动态刷新的,也可以把其中一个地址由动态改为静态。
这里需要注意的是
1、网络上其他主机并不响应 ARP 询问,只有接收端主机接收到这个帧时,才向发送端主机做出这样的回应。
2、ARP 是解决同一个局域网上的主机或路由器的IP地址和硬件地址的映射问题.若所要找的主机和源主机不在同一个局域网上,那么就要通过ARP找到一个位于本局域网上的某个路由器的硬件地址,然后把分组发送给这个路由器,让这个路由器把分组转发给下一个网络.剩下的工作就由下一个网络来做。
3、从IP地址到硬件地址的解析是自动进行的,主机的用户对这种地址解析过程是不知道的,也就是说这个是协议自动完成的,是随机的不可控的。
4、主机或路由器要和本网络上另一个已知 IP 地址的主机或路由器进行通信,ARP 协议会自动地将该IP地址解析为链路层所需要的硬件地址。
在FPGA中只需要进行ARP响应就行。在接收到ARP请求时需要把对方的MAC地址和IP信息记录保存下来,在进行回应时应把自己的MAC和IP地址上报。ARP报文格式如下:
主机的ARP请求利用wireshark抓包可以得到一个完整的ARP请求包类型如下图
FPGA内对ICMP的包类型和实现方式是和ARP一样的。这里主要是要理解ARP和ICMP的关系以及为什么要在FPGA里实现这两个协议。
在FPGA内使用ICMP目的有两个:1、为了确保系统物理链路连接通畅。2、为了提高 IP 数据包交付成功的机会。在我们上位机(PC机)和FPGA进行ICMP通信时上位机得到一下两方面从信息:1、传输是否通畅、传输延时和包的生存时间。2、可以得到目标IP的MAC地址(这个MAC地址实际是ARP获得的)。
这里在得到目标MAC地址时是存在ICMP和ARP协同配合得到的。这两个协议的工作方式由以下步骤实现:
1、PC1
在应用层发起个目标IP
为192.168.1.2
的Ping
请求。
2、直接使用网际层的ICMP
协议,不经过传输层。
3、 网际层接收来处上层的数据后,根据ICMP
协议进行封装,添加PC1
的IP
为源IP
和PC2
的IP
为目标IP
后封装成数据包。下传到网络接口层。
4、网络接口层接收数据包后,进行封装,源MAC
地址为PC1
的MAC
地址,目标MAC
地址则查询自己的ARP
缓存表获取。如果PC1
的ARP
缓存表中没有目标IP
对应的MAC
地址,
则PC1
发出一个ARP
广播报文。ARP
报文中源MAC
地址为PC1
的MAC
地址,源IP
地址为PC1
的IP
,所要请求的是PC2
的IP
对应的MAC
地址。
5、 PC2
收到ARP
广播后,进行解封装,发现所请求的MAC
地址是自己的。则PC2
将PC1
的MAC
地址写入ARP
缓存表中。然后向PC1
发送一个 ARP
应答单播。该单播消息包括目标IP
为PC1
的IP
,目标MAC
为PC1
的MAC
地址,源IP
为PC2
的IP
,源MAC
为PC2
的MAC
。
6、 PC1
接收到PC2
的ARP
应答报文后,将PC2
的MAC
地址存入ARP
缓存中,并将PC2
的MAC
地址作为目标地址封装到数据帧中。发给下层进行网络传输。
7、 PC2
接收这个帧后,在网络接口层查看目标MAC
地址是否指向自己。是,PC2
则将帧头去掉,向上层传输。
8、PC2
网际层接收到这个信息包,查看包头,发现目标IP
和自己匹配,则解封装,将数据向上层传输。
9、 传输层接收来自下层的Ping
请求的UDP
报文,则去掉UDP
报头,向应用层传送。
10、 应用层收到Ping
请求后,发送一个Ping
回应报文给PC1
。
这个过程针对于FPGA来说其实并不复杂,FPGA作为从机只需要回复ARP请求和ICMP请求即可,并且ARP回应是在ICMP回应之前。
在FPGA内部实现UDP协议其实是和ARP和ICMP是一样的,只是包的长度发生了变化。UDP包的格式如下图所示:
在进行UDP传输时需要注意的是局域网环境下,建议将UDP数据控制在1472字节以下。
简单的实现ARP、ICMP、UDP并不难。如何在较大的数据传输速率下稳定可靠传输是一个很重要的要求。在数据传输过程中会出现以下三种情况,
合理的处理这几种情况是保证数据高速无断点传输必须面对的。为了解决这个几种状况,首先要明白数据接收和发送的规律。数据接收完全是随机的、不可控的,而数据的发送是通过FPGA来实现的,自主可控的。并且ARP回应和ICMP回应并不要求实时的进行回复,因此为了保证UDP数据包的传输不会受到干扰,因此可以在UDP包传输间隔内进行ARP和ICMP的回应。如下图所示:
在数据UDP发送的前后端都预留一段时间禁止ARP回应。UDP数据包前端预留时间是为了解决收到ARP请求而没有足够时间空间回复,所以留了一段时间。UDP数据包后端预留一段时间是为了预防PHY芯片发送过于繁忙而导致数据出错。通过上面的方式能实现数据的有效传输。
通过上面的设计能解决FPGA在UDP网络传输中的ARP和ICMP请求。然而在实际的解决过程中肯定会有更加优化的方法来实现这些问题。目前新的思路是通过FIFO来实现,就是所有的请求回应都按照先后顺序来写入一个公共FIFO中,然后顺序读出,这样就可以解决ARP和UDP包冲突的问题。这些方法其实都是殊途同归的,目的都是一样的。
然而虽然把上面的步骤都一一实现了在实际测试中还会存在网络在传输或者复位后自动断开现象,这些情况在实际的应用中会带来很大的麻烦。解决FPGA网络断开连接这个问题需要注意方方面面的情况。总的来说是三个方面:1、硬件的完好,包括网线、PC机网口、FPGA板卡的PHY芯片配置等。2、FPGA代码的时序设计和布局布线的时序约束。3、上位机网口的一些设置。
在实际的调试中出现过以下现象:现象一:FPGA部分条件都不变下,通过网线分别连接两台电脑,WIN10电脑速率自适应匹配为1000Mbps,而WIN7电脑速率自适应匹配为100 Mbps。WIN10电脑能实现ARP、ICMP与FPGA的交互,并且能正确的接收FPGA上发的数据。而WIN7电脑不能与FPGA进行数据的交互。
现象二:FPGA部分条件都不变下,通过更改WIN7电脑速率为1000 Mbps固定值,连接FPGA和WIN7电脑,但无法联通,网卡显示如图所示。
这个现象就是由于网线和上位机网卡驱动存在问题导致,后续更换了网线和更新了网卡驱动测试正常。这些都需要在实际调试时通过对比试验来确定不同点,最终来实现问题定位。
现象三:能实现FPGA和PC机的通信,但PC机下发ARP或ICMP时FPGA接收到的数据经常出现错误。这个问题要分开来看,首先是每次PC机发送数据FPGA都能接到,但存在接收到的数据是错误的。因此问题定位带FPGA应用层和PC网络芯片发出端之间存在问题,通过chipscope抓取FPGARX端IO口的数据可知数据是正确的,因此问题定位在Tri-Mode Ethernet MAC IP的输入和输出存在问题,然而xilinx官方IP核很难出现问题,因此应该是例化中出现错误。经过查找发现是时序约束存在错误。
而IP的约束主要是周期约束、偏移约束、多周期约束这三个大的方面。时序约束后面会展开专门的例程来说。这里需要把官方产生的约束核自己的代码层次结合起来,然后根据自己编译的时序分析报告进行修改。
通过对信号约束,解决了上面提到的数据错误,并且还大大减少了网络断开连接的问题。
在进行通信时上位机网口的配置也会对数据的传输产生很大的影响。比如说开启ARP减负、接收缓冲区设置、电源管理设置等都会对数据交互产生影响。
通过上面的设计基本上能实现数据的传输。在实际的测试中由于项目实际数据速率只有600MB/s,PC机实时的接收数据并通过ping命令不停的发送ICMP请求这样的条件下连续运行10个小时,基本满足了设计要求,后续改进还需要根据实际的需求进行改进。