千兆以太网TCP协议的FPGA实现
Lzx
2017/4/20
写在前面,这应该是我大四最后一个工程性的作品了,以后要养成写文档记录的习惯。说明下,本工程为纯verilog实现的硬件TCP收发器,不同于其他的使用MCU构建软件协议栈的方案,如有同学学习实验需要用到,可以找我拿代码。
联系方式QQ:929259243
本文将从以下几个方面进行讲述:
1、 以太网基础(主要讲下tcp协议)。
2、 基于FPGA的TCP协议实现方案。
3、 调试方法。
4、 问题记录。
5、 实验过程
代码链接:https://download.csdn.net/download/lzx6901152/11884597
一、以太网基础:(这里只是说下大概念,具体详细的讨论、认知,推荐看《计算机网络》(谢希仁))
以太网主要分为数据链路层、网络层、运输层、应用层。
数据链路层,某种程度可以理解成以太网的MAC层。我们来看下它的具体定义:
“所谓链路,就是从一个结点到相邻结点的一段物理线路,中间没有任何其他的交换结点。数据链路则是另外一个概念。因为当需要在一条线路上传送数据时,必须有一条物理线路外,还必须有一些必要的通信协议来控制这些数据的传输。把实现这些协议的硬件和软件加到链路上,就构成了数据链路”简而言之,也就是说数据链路层负责处理物理硬件接口细节。它定义了将数据组成正确帧的规范和在网络中传输帧的规范。
网络层,网络层位于数据链路层之上,它负责处理分组在网络中的活动。同时,它负责提供互联网的“虚拟网络”镜像(可以理解成IP地址)。本层定义了网络中传输的“信息包”格式,以及从一个终端到达另一个终端的路由转发机制(路由器转发)。本层包括IP、ICMP、IGMP、ARP等协议。
运输层,在以太网通讯中我们面临这样一个问题,前面所述的协议可以实现电脑A到电脑B的通讯。然而A、B上都有很多的进程在跑,光用上面的协议是不能实现电脑A上的进程1到电脑B上的进程2的通信,也就是我们说的端到端的通讯。为解决这个问题便有了TCP协议和UDP协议。TCP协议是面向连接的(两个进程要传输数据要先建立一个链接),它提供可靠的数据传输服务,提供诸如流量控制、拥塞控制、超时重传等功能。UDP协议是面向无连接的传输协议,不提供可靠的传输,但保证实时性。举个例子,如果你通过以太网发送一封邮件,那么你的侧重点一定是对方收到邮件一定是跟我发的一模一样才好。这时候传输用的就是TCP协议了,它能保证可靠性。再比如,如果一公司正在开网络视频会议,你一定希望画面能是及时画面,而不是因为某些原因导致的10s、20s前的画面,这时候使用的UDP协议。
应用层:定义了应用程序使用互联网的规范,负责处理特定的应用程序细节。如FTP、SMTP、Telnet等。
讲了这么多概念,我们来看下一次完整的互联网是数据传输是如何进行的。
主机H1将数据通过层层打包后传输给R1(路由器),R1对从H1传来的包解析(解析道网络层),并根据提取出的目的IP找寻到下一个可转发路由器的硬件地址R2(或者主机硬件地址)。然后将数据包重新打包,改变源地址、目的地址,发给R2。以此类推,发送给主机H2,解包后交给应用进程。值得注意的是,这个过程中,数据包的源、目的IP始终不变,但它的源、目的硬件地址却是一直在改变的。从这个大家应该也可以体会出IP地址和硬件地址的区别了吧。
上面用了一个“层层打包”的笼统词汇,然而具体的打包是如何进行的呢?
举个例子:如上图这是从网络层到数据链路层的打包,IP数据包包裹了成MAC帧发出。其他各层也是如此的包含关系。(如下图)
以上为一点基础知识,让大家大概的了解下以太网,如果要想有点更深入的体会,还是要多读下《计算机网络》这本书。
下面详细说下TCP通信过程。
1、 建立链接。
如上图,TCP建立连接,也就是我们常说的三次握手,它需要三步完成。在TCP的三次握手中,发送第一个SYN的一端执行的是主动打开。而接收这个SYN并发回下一个SYN的另一端执行的是被动打开。
这里以客户端向服务器发起连接来说明。
1) 第1步:客户端向服务器发送一个同步数据包请求建立连接,该数据包中,初始序列号(seq)是客户端随机产生的一个值,确认号是0;
2) 第2步:服务器收到这个同步请求数据包后,会对客户端进行一个同步确认。这个数据包中,序列号(seq)是服务器随机产生的一个值,确认号是客户端的初始序列号+1;
3) 第3步:客户端收到这个同步确认数据包后,再对服务器进行一个确认。该数据包中,序列号是上一个同步请求数据包中的确认号值,确认号是服务器的初始序列号+1。
注意:因为一个SYN将占用一个序号,所以要加1。
初始序列号(seq)随时间而变化的,而且不同的操作系统也会有不同的实现方式,所以每个连接的初始序列号是不同的。TCP连接两端会在建立连接时,交互一些信息,如窗口大小、MSS等,以便为接着的数据传输做准备。
RFC793指出ISN可以看作是一个32bit的计数器,每4ms加1,这样选择序号的目的在于防止在网络中被延迟的分组在以后被重复传输,而导致某个连接的一端对它作错误的判断。
2、传输:(以客户端向服务器发数据说明)
第1次传输数据包时(记为A),其序列号(seq)和应答号(ack)应与建立连接第3步发出的包的相同。
服务器返回的应答包(第一次到第N次)(记为B)的seq应与A的ack相同。它的ack为A的seq+所传数据长度。
第二次到第N次传输包C,它的seq与上一次的应答包的ack相同,ack与上一次的应答包的seq相同。
例:传输态:
Client(发送数据包) Sever(返回的应答包)
1、S=01 0F C9 65 S=68EF 8D 53
A=68 EF 8D 53 A=01 0FC9 81
2、S=01 0F C9 81 S=68EF 8D 53
A=68 EF 8D 53 A=010F C9 9D
……
3、释放连接。
如图,这里我就不过多累述了。简要说明一下,图中的关闭时双向的,即AàB关闭了链接,BàA仍然可以传输数据。所以关闭时候记得全都关了,它才会释放链接。
二、FPGA实现方案
这里,我们实现的是FPGA TCP协议的自环测试,框图如下:
注:代码中MDIO没有实现,因为后来我发现用PHY默认的配置即可。PHY其实可以理解成一个AD+DA+具有自协商功能的控制器。
其他都很好理解,TCP控制模块负责接收端和发送端的协调沟通。
其中TCP控制模块状态机如图:
这里给出发送状态机:
接收的与发送的类似,就不上图了。这里贴上各包的包头格式,方便查阅:
TCP首部
三、调试工具和方法:
软件:
1、Wireshark:从网卡上直接抓取数据,并可以对数据进行简单的分析检查。如校验校验和。注(这里抓出来的看不到前导码和MAC帧的校验和。显示出来说明上述都正确,若不显示,最基本的MAC帧也是错误的)
2、 NetAssist:上位机软件。可以设置端口,用于显示传到该端口的数据,可以设成服务器、客户机、UDP三种工作方式。
3、 Chipscope:dataport设小一些,这个很占资源,不然编译出来的东西在125M下跑很容易出问题,最好就去掉它,少用。
4、 Modelsim:有时候逻辑上的问题去做仿真是个好选择。
硬件:
1、 交叉网线一条:不是普通网线。为什么不是普通网线呢,因为普通网线不能实现PC对PC对发。对发可以让我们采集到一些标准数据,可以跟我们的实验数据对比,方便我们排查问题。
2、 两台电脑。
3、 ALX516开发板一块。
调试的方法:
采集标准数据,对照调试自己的代码。如,你想看正确的建立链接这个过程和你写的有什么区别,可以用两台PC对发,用wireshark抓取建立链接的包,在与自己代码产生的对比,往往就能知道自己写的哪里出了问题。
一般先做仿真,确认逻辑无误后,再上板调试。
四、问题记录:
1、TCP协议不支持广播。
2、建立连接时第一个数据包一定要带OPTION选项。
3、由于频率较高(125M),缺少FPGA片内信号直接观察的手段。(加上chipscope常常会使时序恶化,本身这个东西就是ram,非常占资源。)外部只有500M采样的示波器,看不了。调试只能使用插入标志位,让led灯亮灭显示状态的笨办法来确定问题。
4、发送端有一处设计缺陷,即从ram中取出数据计算校验和时候,是以双字为单位,判断门限设置不恰当,造成发送模块只能发送以4字节为倍数的字符串。尚待改进。
5、wireshark看别人发来的数据一般完整,看自己发给别人的数据一般校验和都有问题。不过无大碍,可以用。
五、实验步骤:
1、在发送模块目的地址,请修改成你pc的网卡地址,并重新编译。如图:
2、 请将网络适配器的IPV4设成如图:
3、 打开NetAssiant,打开TCP Sever,设置成如图,点连接。
4、 这时可以打入8字节、12字节(4的倍数)的字符串,点发送,完成自环测试。如果不按4的倍数发送的话,请重新输入。
5、 最后请不要忘记点断开按钮,毕竟TCP协议是个严谨的东西。