《LabVIEW FPGA开发宝典:TCP网络通信》(入门-->精通-->实战-->应用)

《LabVIEW FPGA开发宝典:TCP网络通信》
第10章:LabVIEW FPGA TCP网络通信开发
(入门-->精通-->实战-->应用)
10.1、FPGA TCP基础知识和通信概述
提到TCP总线通信,其实大家都不陌生,关于TCP协议栈的基本知识和概念,用户可以自行百度一下,这里我们就不赘述了。图10-1显示的是常见的TCP局域网星形拓扑结构,而本书前面第6章提到的UDP通信一般实现的都是点对点通信,比如,FPGA直接跟主机端通过一根网线直连,而TCP则可以很容易实现主机同时对多点进行互联通信。


下面我们重点讲一些平时积累的通信协议应用技巧和经验。
在网络通信领域,TCP/IP可以说是一个非常基础又非常重要的通信协议之一,相比于UDP协议,TCP属于长链接协议,只有服务器与客户端建立起连接之后二者才能进行通信;如果通信过程中发生数据包丢失,TCP协议栈会自动重发,并且另一端收到之后协议内部会自动发送Ack响应,这样的通信方式非常可靠。
因此,只要TCP连接存在,读取端速度足够快,理论上TCP通信就不会丢包,这是因为TCP跟PCIe协议类似,都是握手类协议;如果因为主机端系统抖动(比如,Windows或者Linux),造成读取速度瞬间变慢而丢包,我们可以在中间层加大TCP缓冲区长度,比如,在FPGA芯片里面开辟较大的上行FIFO缓冲区,或者直接使用FPGA旁边的DDR3或者DDR4芯片来缓冲大容量数据;因为上位机所能开辟的TCP缓冲区是有限制的,这个方法也是解决所有握手类协议的终极绝招,特别是高速通信协议,比如TCP和PCIe;系统稍微卡顿一下,即使是ms量级的抖动,也会造成几K甚至几十K的数据溢出,因此,FPGA中间层的TCP/PCIe上行数据FIFO深度,在FPGA资源足够的情况下,设置的越大越好,而下行TCP数据FIFO不需要设置很大,这是因为FPGA芯片里面的程序实际映射的是硬件电路,永远不存在抖动,FPGA的优势就是处理速度快。
而UDP协议最大的问题就在于它不是握手类协议,一端可以无条件发送UDP数据包,不管对方回不回应,优势在于UDP吞吐率要比TCP快一些,缺点在于如果网络条件不好,或者读取端读取速度跟不上的话,容易造成UDP丢包,并且发送端和接收端无法判断是否丢包了,除非是人为添加UDP包头index索引,否则无法判断何时丢包,以及丢了哪些包。对于不需要长连接和不需要分布式并且是固定数据包长度的应用来说,UDP相对于TCP协议栈开销相对小一些。
其实,关于UDP通信的开发和应用,在宝典第6章里面的第9个实验有很详细的讲解,这里不再赘述,本章着重讲解基于纯FPGA实现的TCP协议。
这里提到的纯FPGA实现的TCP协议栈,跟以往大家使用ZYNQ芯片不一样,ZYNQ里面的PS端也就是ARM端可以跑Linux系统,用户可以在系统层面实现TCP协议栈,我们这里要跟大家传递的是利用纯FPGA可编程逻辑资源实现的TCP协议栈,这样只要是FPGA芯片,无论带不带ARM都能实现TCP协议栈了,当然ZYNQ里面的PL部分也可以运行我们这个TCP协议栈。
这里介绍的基于FPGA纯逻辑资源实现的TCP协议栈与传统的ZYNQ里面PS端(ARM)实现的TCP有啥区别呢?我认为有以下几点优势:
1)纯FPGA实现的TCP协议栈,摆脱了FPGA的局限性,只要是FPGA芯片,无论是哪个厂家的,都能加载这个TCP网表文件进行编译,可以选择的FPGA种类更加灵活,硬件成本也更容易控制(比如,Spartan6、Artix7、Kintex7、Virtex7)。
2)纯FPGA芯片内部运行的TCP协议栈,本质上映射的是FPGA芯片内部的硬件电路,因此,不存在芯片死机、卡顿、数据丢包等问题,稳定性和可靠性要比ZYNQ里面的ARM高很多,值得一提的是,很多BP大物理装置里面的核心设备,需要长时间高可靠性运行的设备,往往都是采用纯FPGA实现的TCP协议栈(比如日本的SiTCP),而非ZYNQ和ARM芯片。
3) 纯FPGA实现的TCP协议栈很容易实现百兆、千兆和万兆带宽,这一点ARM芯片和ZYNQ的PS端是无法企及的,特别是万兆协议,除非用ZYNQ里面的PL来实现,本质上也就类似我们的纯FPGA逻辑资源跑TCP协议栈了。
4)相较于UDP通信,TCP网络一般不丢包,更容易实现物联网多节点互传等分布式应用。
5)纯FPGA实现的TCP协议栈更稳定,速度更快,带宽更高,只要对等端读取速度跟的上,FPGA端的TCP速度性能就能发挥到最大,远远超过ARM、ZYNQ以及运行在系统(Windows、Linux)里面的TCP传输速度。
由于UDP协议栈自身的缺陷,现在越来越多的客户希望能在FPGA芯片内部实现TCP协议通信,这也是正是我们决定将TCP协议栈移植到纯FPGA芯片中的原因之一,就像PCIe协议一样好用,至此,我们的LabVIEW FPGA又多了一种可靠的对外交互通信方式。
相对于传统的串口和USB通信,基于FPGA实现的TCP千兆和万兆以太网通信协议支持热插拔和断点续传,接线和拓扑结构更加灵活方便。
如果用户希望自己的FPGA硬件也具备TCP通信能力,一般有两种实现方法:
一是采用专用的TCP协议栈芯片,比如采用外置专用芯片(W5500、W5300)那样,这样处理器的选择余地就很大了,比如ARM、DSP、FPGA都可以,因为TCP通信协议这块交给了专用ASIC芯片来完成了,简化了MCU端的编程复杂度;
另外一种,就是直接采用我们神电测控开发移植的纯FPGA实现的TCP软件IP核(My FPGA TCP Socket CLIP),直接在FPGA芯片内部搞定TCP协议栈所有的通信过程,关于这个FPGA TCP协议栈的具体介绍和应用我们会在后续章节详细展开。
那么具备TCP通信能力的FPGA硬件板卡或者开发板通常长什么样子呢?这里用户可以看一下图10-2展示的实物图,这款黑金ARTIX7 FPGA开发板(AX7103)开发板上面集成了两路千兆PHY芯片(KSZ9031),也就是本书(LabVIEW My FPGA Pro5)配套的实验平台;当然了,如果用户觉得黑金的FPGA开发板价格贵了,也可以选择正点原子的、米联客或者璞致的FPGA开发板或者核心板;图10-3显示的是正点原子的达芬奇Pro开发板(Artix7),上面集成的PHY芯片由原先台湾瑞昱的RTL8211FD换成了国产的YT8511C,进一步降低了硬件成本;图10-4显示的是Kintex7-325T FPGA核心板,上面集成了一颗RTL8211FD芯片。



因此,可以看出,FPGA外围只需要一颗价格很便宜的PHY芯片即可实现TCP通信。特别是对于后期需要自己设计做板子的用户来说,常用的PHY芯片可以选择KSZ9031、RTL8211、YT8511,当然还有博通的B50610系列;现在的PHY芯片基本上都是RGMII接口的,占用引脚少,只需要4bit;当然,对于像RTL8211EG这样的GMII芯片,我们的TCP IP协议栈也是支持的。
本书针对上面提到的这些FPGA硬件提供了配套的资料和例程,其他厂家的FPGA板子,我们的LabVIEW My FPGA也能完美适配,因为我们做的就是开源的RIO解决方案。
本书重点是教会用户使用LabVIEW来开发FPGA芯片里面的TCP通信程序,然后自己根据实际项目和产品功能需求,购买通用的FPGA板子或者使用FPGA核心板+底板的方式,来摆脱NI、简仪、凌华等这些厂商的硬件控制,真正做到硬件自由化,软件开发图形化,移植成本代价最小化,综合效益最大化;同时也是为了下一步纯国产化FPGA方案提供了切实可行的方案,比如用户可以选择深圳国微或者上海复旦微电子的K7、V7芯片来代替Xilinx的FPGA芯片,然后做出百分百国产化带TCP高速接口的自定义LabVIEW FPGA板卡出来,真正为军工企业或者有可能被卡脖子的行业做出一份贡献!!!
10.2、目前主流的3大FPGA TCP网络通信IP核讲解(日本SiTCP IP、GitHub部分开源TCP IP、美国ComBlock公司的TCP IP):结论是美国ComBlock IP简单易用更方便!
要开发一个带TCP网络通信接口的FPGA板卡出来,除了硬件本身外,比如PHY芯片,最重要的就是FPGA芯片里面的TCP通信代码编写,俗称下位机FPGA编程以及上位机PC端的应用程序开发。
其中,主机端(比如上位机PC)一般都可以使用C语言或者LabVIEW或者Python进行开发,但是下位机FPGA里面的TCP通信代码一般会采用VHDL或者Verilog进行编写,难度比较大,尤其是TCP协议栈本身还区分客户端和服务器端两类复杂的通信协议,开发难度和工作量会非常大,这就导致很多应用工程师在做TCP相关的FPGA嵌入式开发时,碰到了不少坑,投入了很多精力和时间也没有把FPGA TCP做稳定。
本章重点就是告诉用户如何使用LabVIEW在FPGA芯片里面编写TCP通信程序,彻底解决下位机FPGA代码编写的痛点,让LabVIEW图形化编程语言深入到FPGA骨髓里面去,降低FPGA TCP嵌入式开发门槛,真正发挥出TCP协议稳定可靠的通信能力。
上面虽然介绍了基于纯FPGA实现的TCP协议栈的诸多优点,唯一的缺点就是纯FPGA TCP协议栈,目前没有看到即好用又免费的,为此,我们调研分析了一下整个FPGA IP核市场,发现了比较有价值的3类TCP协议栈供大家参考。
1) 日本SiTCP IP核
2) GitHub上的开源TCP IP 核
3) 美国ComBlock TCP IP核
下面我们简单介绍一下这3种方案。
1) 说到日本的SiTCP协议栈,估计很多做BP大物理装置设备的用户并不陌生,因为在纯FPGA芯片内部开发这个SiTCP协议栈的作者本身就是从事BP领域的科研人员。随着SiTCP的应用越来越广泛,并且在全球诸多BP领域的大装置里面长年累月不停机的运行测试,使其声名大噪,后来逐步商业化拥抱更为宽阔的自动化和通信领域。缺点就是SiTCP协议栈IP核以网表形式出售,绑定MAC地址,价格非常贵,一般的个体户和小企业不一定能买的起,更不适合广大开源爱好者。
2)说到开源免费,大家首先想到的估计就是GitHub,确实里面有一些基于纯FPGA实现的TCP Demo,但是缺乏维护,而且底层代码似乎也存在不少Bug,例程比较老旧,如果想把GitHub上的TCP协议栈吃透的话,需要大家对精通传统的FPGA开发才行,同时还要花费大量的测试时间以及准备好打补丁工作,为此,我们放弃了GitHub这类没有工程师实时维护的代码。
3)最后,我们发现美国有一个ComBlock软件公司,专门基于纯FPGA开发了各种各样的通信协议栈IP核,源码采用VHDL编写,消耗资源少,执行效率高,关键是授权费用低,并且提供全部源码而非网表,这使得用户可以直接看到底层协议栈编写逻辑,一定程度上可以借鉴和学习。感兴趣的用户登录ComBlock公司网站查看https://www.comblock.com/product_list_IP.html
难能可贵的是,ComBlock这家公司有点像我们之前说过的以色列Xillybus那样,专门在FPGA软件IP核上下功夫,国内目前还缺乏这样专注于FPGA算法和协议栈的纯软件公司,希望国内以后有越来越多的FPGA软件IP协议和算法出现。这样我们就能移植封装到LabVIEW My FPGA软件工具包来了。
为此,我们专门花钱买了ComBlock公司的TCP客户端和服务器端两个版本的源代码授权。然后将其进行模块化封装、移植到我们的LabVIEW My FPGA软件工具包,并且编写了大量基于该TCP客户端和服务器端两个版本的FPGA VI和例程(分为下位机FPGA和上位机PC),大家只要懂一点LabVIEW,就可以直接在FPGA里面调用我们封装好的TCP Socket CLIP VI了,然后自动编译成bit文件、下载到FPGA芯片里面运行,真正做到学以致用。之所以将宝典和例程写的详细并且提供实战应用案例,就是为了照顾一些初学者,让他们也能在短时间内成为FPGA高手。
PS:如果哪位大神有更好的纯FPGA实现的TCP协议栈IP的话,欢迎与我们神电测控联系。
邮箱:DLW30@126.com
微信:myview30
其中,ComBlock开发的TCP Server IP核系统工作框图,如图10-5所示;该TCP Server IP核在Spartan6这样的很low的FPGA芯片里面消耗的资源也是极少的,如图10-6所示。至于TCP Client IP核这里就不展示了,后面我们会讲解在LabVIEW FPGA下面如何调用纯FPGA实现的TCP Client和Server两个版本的IP核。


总结:对于我们传统的嵌入式工程师来说,选择一个合适的FPGA TCP驱动IP非常重要,不仅可以节约大量开发时间,关键是稳定性要比我们自己写的更好,毕竟很多商业FPGA驱动IP核和驱动inf文件都是经过了大量企业实践检验过的,而很多所谓的开源代码,实际上用起来总是有这样或者那样的bug和坑,需要用户自己花费大量的时间去调试、修改和维护,有时候得不偿失。如果是做产品的话,个人愚见,还是用成熟的企业版软件工具包和FPGA驱动IP核,毕竟稳定胜于一切,社会分工不一样。
经过上面的讲解和分析,最终我们选择了美国ComBlock作为我们LabVIEW FPGA底层TCP驱动IP核的候选,我们将整个TCP客户端和服务器端两个版本的FPGA TCP IP核以CLIP的方式重新封装到LabVIEW My FPGA工具包下面,这样用户就能通过LabVIEW图形化的方式直接调用这个IP核了(关于这个FPGA TCP CLIP的详细介绍,后面专门会有一节进行讲解),用户只需要会一点点LabVIEW就可以将最为复杂的FPGA TCP通信轻松拿下。
旁白:我们也衷心地希望国内有更多的牛人、高手或者企业能够像以色列Xillybus和美国ComBlock公司那样专注于做一件事情,然后把这件事做到极致,做出属于我们中国人自己的底层FPGA TCP IP核。我相信这一天早晚会到来!
10.3、FPGA TCP网络通信开发过程(2个步骤)
要想开发出一个完整的基于TCP网络通信的FPGA板卡,需要经历以下2个步骤才能算是完成。
首先,用户需要根据实际情况,编写FPGA芯片里面的逻辑代码,比如做一个基于TCP网络传输的DAQ采集卡,那么用户需要在FPGA里面利用LabVIEW编写一个ADC采集程序,然后将采集到的数据通过FIFO转移到我们封装出来的TCP CLIP对应的上行通道里面,或者将上位机下发的数据从FPGA FIFO里面读取出来。这部分代码开发,我们称之为下位机FPGA程序编写,采用LabVIEW进行开发。关于下位机LabVIEW FPGA程序开发,也是贯彻本书的重点,前面每个实验基本上都着重讲解了LabVIEW开发FPGA下位机程序这部分。
接下来,当我们利用LabVIEW编写好FPGA程序(里面包含TCP CLIP),编译成bit文件,下载到FPGA芯片里面运行后,然后,我们还需要编写一个上位机应用程序来跟下位机FPGA进行通信,如果上位机大家也准备用LabVIEW开发的话,可以直接参考LabVIEW自带的原始以太网TCP通信例程,当然,推荐大家参考我们重写的上位机LabVIEW网络通信例程,有能力的用户也可以直接调用LabVIEW TCP函数选板里面的VI自己实现;对于那些不会LabVIEW的用户也可以使用C\C++\C#\Python等文本语言实现TCP网络通信,关于这方面的资料和例程,网上有很多,上位机TCP通信本身不复杂, 基本都是现成的。
至此,我们就把一个基于TCP的FPGA项目或者产品做完了,然后就可以将FPGA板卡、FPGA程序框图和上位机应用程序交付给最终用户。用户只需要会LabVIEW开发FPGA芯片里面的代码以及上位机PC端的程序就可以了,非常简单省事。
特别需要注意的是:部分用户在做后续FPGA跟上位机之间的TCP通信实验时,发现下位机FPGA TCP带宽超过20MB/s就会产生FIFO溢出,数据丢包,无法达到我们宝典里面测试的75MB/s~95MB/s(如图10-7和10-8所示),这是因为这类用户测试的上位机电脑中,安装了消耗CPU和网络资源的软件,特别是像todesk、anydesk、teamview、向日葵这类远程软件;虽然这些软件不占用直连的网卡资源,但是这类软件会造成CPU卡顿和侵占部分网络资源。


因此,建议大家:在测试我们封装的FPGA TCP极限传输带宽(吞吐率),最好把上位机所有影响CPU和网络的软件统统关闭,并且关闭上位机应用程序里面的波形图显示也会进一步提高下位机FPGA的TCP传输带宽。
提醒:最好把上位机网卡属性里面的接收缓冲区和传输缓冲区改到最大值,也就是2048,如图10-9所示,这样可以一定程度上提高TCP的传输带宽,更重要的是前面我们提到的,要在FPGA下位机里面增加TCP上行FIFO深度,因为上位机能改的Buffer深度太小了,只有2048,下位机FPGA可以申请的FIFO深度可以到几十KB都行,必要的时候还可以动用DDR3或者DDR4来做更大的数据缓冲。

下面,我们分别对这2个步骤进行详细的讲解。
10.3.1、下位机FPGA TCP通信程序开发(bit文件,用LabVIEW FPGA进行开发)
当我们做一个FPGA TCP硬件时,首先需要准备好一个支持TCP通信的FPGA板子,比如黑金的AX7103开发板,上面的ARTIX7 FPGA芯片外加1个PHY芯片(比如KSZ9031、RTL8211FD、YT8511C)即可实现TCP或者UDP通信。这块FPGA开发板也是本书配套的实验平台,关于这个板子各个功能模块的讲解,前面第6、7、8章每个实验都详细验证过了。
当FPGA硬件准备好之后,接下来,我们就可以大刀阔斧的按照我们实际的项目或者产品需求,在FPGA芯片里面利用LabVIEW进行编程了。这里用LabVIEW编写的FPGA代码,我们称之为下位机FPGA程序开发。
比如,我们要在FPGA里面开发一个DAQ采集程序,我们可以直接将前面第6章里面的各种ADC采集线程拷贝到本节实验里面来,然后再配上我们封装的FPGA TCP IP节点,就变成了一个带TCP传输接口的DAQ设备了;如果将前面的DAC信号输出线程拷贝到本节实验里面来,配上FPGA TCP IP核,就变成了一个任意信号信号发生器板卡了;如果把前面的双目摄像头图像采集框图拷贝到本节实验里面来,配上FPGA TCP IP核,就变成了一个支持TCP网络通信的图像采集卡了。
关于这个下位机FPGA里面的代码编写,相信用户在学习了本书的第5章和第6章之后,就不会陌生了,而且很多下位机FPGA端的代码都可以直接从前面第6章对应的实验程序里面进行拷贝,无需重头编写。
当下位机FPGA端的LabVIEW程序框图编写完成之后,点击运行可以自动编译生成对应的FPGA bit文件和bin/mcs文件,我们把这个文件称之为下位机可执行文件或者固件程序。当然,如果用户对VHDL或者Verilog十分精通的话,也可以用文本语言编程,但是我们推荐大家使用LabVIEW开发,效率更高。
下面我们直接通过一个示意图来表达一下下位机FPGA里面的代码编写过程,如图10-10所示。其中,生成中间VHDL代码和编译成bit文件都是在后台自动完成的,即使用户完全不懂FPGA也没有用过Xilinx任何IDE软件,只要会LabVIEW就可以搞定一切!

备注:详细的LabVIEW FPGA TCP CLIP代码讲解,可以参考后面10.4节的相关内容。
10.3.2、上位机PC端TCP通信应用程序开发(LabVIEW/C/Python都有完善的例程)
当下位机FPGA TCP网络通信程序编译下载运行后,我们就可以编写一个上位机PC端的应用程序来与之通信,进行数据交互了。如果下位机FPGA跑的是TCP Client客户端协议栈的话,那么上位机PC就要跑TCP Sever服务器程序,反之亦然。
本身上位机程序我们以LabVIEW为例进行讲解,因为LabVIEW是图形化编程语言,相比于传统的C和Python等文本编程语言,更加简单易懂,并且LabVIEW软件自身就有大量的网络通信函数VI和丰富的例程;图10-11显示的LabVIEW软件里面自带的TCP函数选板;图10-12显示的是LabVIEW范例查找器里面自带的TCP通信Demo例程,双击其中Simple TCP例程,可以看到该项目浏览器同时具备Server和Client两个Demo程序,如图10-13所示。



PS:关于上位机采用LabVIEW编写的TCP客户端和服务器端Demo例程,我们会在后续实验环节里面给大家做详细的讲解;如果大家擅长C或者Python这样的编程语言,上位机网络通信程序自行脑补(也可以问问ChatGPT)。
10.4、LabVIEW FPGA TCP Socket CLIP 讲解(神电测控独家提供TCP客户端和服务器两个版本)
本节我们重点给用户讲解一下我们封装好的LabVIEW FPGA下的TCP CLIP组件功能和用法。这个TCP CLIP也是我们My FPGA软件工具包的核心组件之一,我们花费了近3个月的时间,将美国COmBlock公司提供的TCP VHDL协议栈代码进行移植、修改和适配,并进行了大量的测试验证,最终才把底层客户端和服务器两个版本的TCP IP核封装到LabVIEW FPGA CLIP里面来,并且给用户提供了极其简单的四线握手FIFO接口,所有关于TCP网络协议自带的通信功能都封装到底层去了,用户不需要深入了解底层FPGA TCP代码,一样可以使用LabVIEW FPGA来调用这个TCP Socket CLIP进而快速开发出一个属于自己的TCP FPGA设备来。
ComBlock官方提供了2个版本的TCP通信IP核,分别是Client客户端版本和Server服务器版本。其中,TCP Client协议栈版本在FPGA里面运行之后,可以将FPGA变成TCP客户端角色,最大极限吞吐率是95MB/s;TCP Server协议栈版本在FPGA里面运行之后,可以将FPGA变成TCP服务器角色,最大极限吞吐率也是95MB/s。
目前我们将下位机FPGA TCP客户端和服务器两个版本的协议栈都封装成了LabVIEW FPGA Socket CLIP组件供大家使用。
对于目前大多数工业自动化和测试测量等领域的应用来说,95MB/s的TCP网络传输带宽完全足够了,虽然只比UDP巅峰时期的118MB/s少一点,但是TCP属于握手类长链接传输协议,更加可靠和稳定。
下面重点给用户介绍一下我们神电测控封装好的FPGA TCP Client和Server Socket CLIP,首先在LabVIEW项目浏览器下的“我的电脑”上右击,选择新建“终端和设备”,如图10-14所示。

然后在弹出来的终端设备新建对话框里面,展开ARTIX7-100T,找到后缀带TCP_Server和TCP_Client的黑金AX7103或者正点原子达芬奇Pro对应的FPGA终端,如图10-15所示。




这里我们选择“ARTIX7_XC7A100T_2FGG484_AX7103_PCIe_X4_B_8CMs_TCP_Client”和“ARTIX7_XC7A100T_2FGG484_AX7103_PCIe_X4_B_8CMs_TCP_Server”这两个终端进行讲解。点击“确定”按钮后,这两个版本的TCP通信Socket CLIP组件会自动添加到FPGA终端里面来,图10-16显示的是带TCP Client客户端版本的LabVIEW FPGA CLIP(TCP_Client Data),图10-17显示的是TCP Server服务器版本的LabVIEW FPGA CLIP(TCP_Server Data)。
展开这两个TCP Socket CLIP之后,可以看到一共有15个EIO节点,虽然多,但是有规律可循,除去第一个TCP CLIP复位(lv_tcp_client_reset)和最后的125MHz时钟外(Clock lv_tcp_client_125M_clk_out),余下的就是TCP MAC地址和IP端口等参数设置,最后就是TCP收发数据的四线握手信号。
可以看出,这两个TCP Socket CLIP与我们前面封装的UDP CLIP接口形式上面差不多,这样做的目的是为了让大家更容易理解和方便调用。


可以看出,TCP Client版本与TCP Server版本封装出来的接口和参数除了名称不一样,功能上面很类似,这样方便大家使用和记忆。也就说,用户只要掌握了其中一个版本的调用,另外一个也就触类旁通了;如果要改变下位机FPGA的TCP通信角色,比如FPGA原先跑的TCP客户端协议,现在想变成服务器,那就直接右击FPGA VI里面的TCP Client Socket CLIP节点,逐个替换成TCP Server Socket CLIP下面的节点即可。
为了支持更高端的FPGA芯片以及国产化FPGA(深圳国微和上海复旦微),我们特地将Xilinx Kintex7家族也进行了TCP协议栈的LabVIEW Socket CLIP封装,如图10-18(abcdef)所示。图10-19和10-20显示的是分别集成了TCP Client和TCP Server Socket CLIP节点的Kintex7 FPGA终端和端口信息。








下面我们给大家逐一讲解一下TCP Client和TCP Server两个版本的LabVIEW FPGA TCP Socket CLIP里面每个信号端口的含义和注意事项。通过虚线将15个CLIP端口划分成4类:分别是TCP IP核服务信号、TCP CLIP 125M运行时钟、TCP链接状态信号、上行(FPGA-->Host)通道以及下行(Host-->FPGA)通道的数据和握手信号。其中,Host可以直观的理解成上位机PC。
10.4.1、TCP Client客户端版本的FPGA EIO节点讲解(下位机):
1)lv_tcp_client_reset
-------------------------------------------------------------------
2)Clock lv_tcp_client_125M_clk_out
--------------------------------------------------------------------
3)lv_tcp_client_connect_status
--------------------------------------------------------------------
4)lv_tcp_client_board_mac
5)lv_tcp_client_board_ip
6)lv_tcp_client_board_port
7)lv_tcp_client_board_gate_way
8)lv_tcp_client_board_subnet_mask
9)lv_tcp_client_pc_ip
10)lv_tcp_client_pc_port
---------------------------------------------------------------------
11)lv_tcp_client_tx_data_in
12)lv_tcp_client_tx_data_in_vld
13)lv_tcp_client_tx_data_req_en
14)lv_tcp_client_rx_data_out
15)lv_tcp_client_rx_data_out_vld
------------------------------------------------------------------
下面我们给用户详细讲解一下TCP Client每个端口的含义。
1)lv_tcp_client_reset(下位机FPGA TCP客户端IP核复位信号)
为了防止FPGA芯片内部的TCP Client协议栈IP核在某些极端条件下出现死机等问题,我们特地将底层TCP Client IP核的复位信号拉到LabVIEW FPGA里面来,这样用户就能通过LabVIEW随时随地对这个TCP Client IP核进行复位清零等工作。需要复位的时候,将这个“lv_tcp_client_reset”信号端口拉高(True),平时给个低电平(Flase)即可。当然,最直接的方式就是在FPGA VI前面板上创建一个布尔型的按键,连到“lv_tcp_client_reset”端口上,如图10-21所示;这样就可以利用我们特有的LabVIEW FPGA在线前面板调试模式,直接利用鼠标对TCP IP核进行动态复位,方便用户测试下位机FPGA程序。

2)Clock lv_tcp_client_125M_clk_out(下位机FPGA TCP客户端IP核运行时钟域)
FPGA TCP Client IP核运行时钟,默认是125MHz,这个时钟是路由出来给用户自己写程序用的FIFO同步时钟。众所周知,千兆模式下的TCP运行时钟和用户时钟都是125MHz,也就是说,用户如果要把FPGA里面的数据通过TCP发送给PC(上行)或者接收PC下发的数据(下行),必须要使用这个125MHz时钟源,才能保持FIFO同步握手。为此,我们将这个125MHz时钟直接封装到我们的LabVIEW FPGA TCP Client CLIP里面来了,时钟信号名为“Clock lv_tcp_client_125M_clk_out”。
如何使用TCP Client CLIP路由进来的时钟呢?跟前面我们封装的PCIe总线、UDP千兆以太网、USB时钟一样,用户可以直接在FPGA定时循环左边创建一个时钟源常量,然后在下拉列表里面选择,如图10-22所示;或者直接双击FPGA定时循环,在弹出来的时钟配置页面里面选择“TCP_Client Data\ Clock lv_tcp_client_125M_clk_out”,如图10-23所示。


3)lv_tcp_client_connect_status(下位机FPGA客户端是否与服务器建立起连接状态)
由于TCP协议跟PCIe协议都是握手类协议,也就是说,只有与对等端建立起链接之后才能进行后续的发送和接收等传输工作,为了方便用户观察和调试TCP通信过程,我们将TCP Client IP核底层的是否建立起链接这个状态封装到LabVIEW FPGA里面来了;比如,这里下位机FPGA运行TCP Client协议之后,上位机PC充当服务器,那么当上位机服务器端侦听到下位机FPGA的TCP连接请求之后,一旦成功建立起连接之后,这个“lv_tcp_client_connect_status”端口就会输出高电平(True),因此,我们可以将这个信号端口连接到FPGA VI前面板上面的LED指示灯,如图10-24所示;这样就能通过这个LED灯的亮灭来判断下位机FPGA(TCP客户端)与主机端(比如上位机PC服务器端)是否建立起TCP通信连接。

4)lv_tcp_client_board_mac(下位机FPGA客户端MAC地址)
5)lv_tcp_client_board_ip(下位机FPGA客户端IP地址)
6)lv_tcp_client_board_port(下位机FPGA客户端端口)
7)lv_tcp_client_board_gate_way(下位机FPGA客户端网关)
8)lv_tcp_client_board_subnet_mask(下位机FPGA客户端子网掩码)
9)lv_tcp_client_pc_ip(下位机FPGA客户端发起连接的远程服务器IP地址)
10)lv_tcp_client_pc_port(下位机FPGA客户端发起连接的远程服务器端口)
上面这7个信号端口(4~10)一看名称就明白,它们是用来配置TCP通信的参数,依次是下位机FPGA客户端的MAC地址、IP地址、Port端口、网关、子网掩码和需要连接的远程服务器端(比如上位机PC)IP地址和Port端口。需要注意的是,为了方便大家记忆,我们将下位机FPGA形象的称呼为板子board,服务器端称呼为pc电脑,因此,名称里面带board的都是指下位机FPGA参数,带pc的都是指上位机参数,不要被前缀“lv_tcp_client”迷惑了,这个前缀是指FPGA当前封装的TCP协议栈是Client客户端。通过下面图10-25所示的程序框图,对TCP通信参数的设置和理解更加直观和一目了然。

提醒:如果下位机FPGA接入的是局域网,MAC地址一般可以随便设置,如果是广域网,那么设置的MAC地址不能与公网上的MAC相同,否则会产生冲突。
某些用户英文不是很好,为了照顾大家,我们特地在上图10-25的参数旁边加了备注,特别是下位机FPGA里面的IP地址不支持点号,必须要用16进制表示。
11)lv_tcp_client_tx_data_in(下位机FPGA TCP客户端需要发送的上行数据)
12)lv_tcp_client_tx_data_in_vld(下位机FPGA TCP客户端发送的上行数据是否有效)
13)lv_tcp_client_tx_data_req_en(下位机FPGA TCP客户端发送上行数据的请求信号)
上面这3个FPGA TCP客户端发送通道(11~13)的四线握手信号,其实就是上行数据发送端口,跟本书前面第7章提到的8位PCIe发送通道类似,这里的TCP端口数据位宽也是8位的,也就是1个Byte字节,同样,每个上行通道都有3个握手端口组成。为了让用户更加形象的理解和掌握这些握手信号的用法,我们先给出一个FPGA TCP客户端数据发送程序框图,如图10-26所示。毕竟LabVIEW框图看起来显而易见,一图胜千言!

其中,“lv_tcp_client_tx_data_req_en”信号就是TCP Client Socket IP核内部的接收FIFO准备就绪信号或者称之为主动向FPGA用户程序请求数据信号,如果这个“lv_tcp_client_tx_data_req_en”拉高,说明TCP Client IP核可以接收FPGA给过来的数据,然后将其发送到上位机PC服务器端。因此,我们可以将这个“lv_tcp_client_tx_data_req_en”接到FPGA里面用户创建的数据源FIFO(FIFO_TCP_Send_U8)的“输出就绪”端口上面;再把该FIFO输出的数据和有效信号分别接到TCP Client Socket CLIP的“lv_tcp_client_tx_data_in”和“lv_tcp_client_tx_data_in_vld”端口上面。
需要注意的是:如果对等端(比如服务器端)读取速度慢,那么需要加大下位机FPGA里面的Send FIFO(FIFO_TCP_Send_U8)深度,否则TCP数据包容易溢出丢失,因为上位机网卡自身往往没有办法增加TCP缓冲区,只有下位机FPGA能够加大FIFO或者利用DDR来缓冲数据;通常情况下,握手类协议的上行数据缓冲区FIFO一般设置大一些比较好,这个我们在前面也多次提到过了,切记!
14)lv_tcp_client_rx_data_out
15)lv_tcp_client_rx_data_out_vld
最后,剩下的这2个TCP Client接收数据通道(14~15)的握手信号,其实就是下行数据接收端口,这里TCP接收到服务器下发的下行数据位宽也是8位,跟PCIe类似,TCP下行通道同样是2个握手端口组成。为了让用户更加形象的理解和掌握这些下行数据握手信号的用法,我们先给出一个FPGA TCP Client客户端数据接收程序框图,如图10-27所示。毕竟LabVIEW框图看起来更直观易懂!

其中,一旦下位机FPGA客户端接收到上位机PC服务器端下发的TCP数据包后,那么对应的“lv_tcp_client_rx_data_out”和“lv_tcp_client_rx_data_out_vld”就会有效;因此,我们可以将“lv_tcp_client_rx_data_out”和“lv_tcp_client_rx_data_out_vld”分别接到FPGA里面用户创建的接收缓冲区FIFO(FIFO_TCP_Receive_U8)对应的数据输入和输入有效信号端口上。
需要注意的是:与上行数据FIFO缓冲区不一样,握手类协议的下行数据缓冲区FIFO一般不需要设置很大,避免过多浪费FPGA Block RAM资源,这是因为FPGA自身的读取和处理速度是非常快的,不存在卡顿或者抖动一说;因此,可以看出,如果其中一方速度慢于另一方,那么速度快的这一方通常需要加大FIFO深度;假设双方是两个读取和处理速度都很快的FPGA进行互联的话,那么无论是发送FIFO还是接收FIFO,实际上都不需要设置很大,因为FPGA可以完全高速的逐点处理,而上位机PC通常做不到。
提醒:我们封装的TCP客户端上行和下行data都是U8字节整形数据,如果用户需要发送或者接收小数或者定点数,那么需要提前进行转换,比如,将定点数通过“数值至布尔数组”将符号和小数点特性去掉,然后再利用“布尔数组至数值”转换成整形数据,最后再进行拆分组合变成U8字节数组,反之亦然。
结论:可以看出,虽然TCP协议本身很复杂,但是对于用户来说,只要理解了FIFO的四线握手编程,就可以搞定所有线程之间的数据传输问题了,这也就是为什么我们在书中每个实验里面都反复强调四线握手和并转串以及串转并的重要性。
10.4.2、TCP Server服务器版本的FPGA EIO节点讲解(下位机):
1)lv_tcp_server_reset
------------------------------------------------------------------
2)Clock lv_tcp_server_125M_clk_out
------------------------------------------------------------------
3)lv_tcp_server_connect_status
-----------------------------------------------------------------
4)lv_tcp_server_board_mac
5)lv_tcp_server_board_ip
6)lv_tcp_server_board_port
7)lv_tcp_server_board_gate_way
8)lv_tcp_server_board_subnet_mask
9)lv_tcp_server_pc_ip
10)lv_tcp_server_pc_port
-----------------------------------------------------------------
11)lv_tcp_server_tx_data_in
12)lv_tcp_server_tx_data_in_vld
13)lv_tcp_server_tx_data_req_en
14)lv_tcp_server_rx_data_out
15)lv_tcp_server_rx_data_out_vld
----------------------------------------------------------------
下面我们给用户详细讲解一下TCP Server每个端口的含义。
1)lv_tcp_server_reset(下位机FPGA TCP服务器IP核复位信号)
为了防止FPGA芯片内部的TCP Server协议栈IP核在某些极端条件下出现死机等问题,我们特地将底层TCP Server IP核的复位信号拉到LabVIEW FPGA里面来,这样用户就能通过LabVIEW随时随地对这个TCP Server IP核进行复位清零等工作。需要复位的时候,将这个“lv_tcp_server_reset”信号端口拉高(True),平时给个低电平(Flase)即可。当然,最直接的方式就是在FPGA VI前面板上创建一个布尔型的按键,连到“lv_tcp_server_reset”端口上,如图10-28所示;这样就可以利用我们特有的LabVIEW FPGA在线前面板调试模式,直接利用鼠标对TCP IP核进行动态复位,方便用户测试下位机FPGA程序。

2)Clock lv_tcp_server_125M_clk_out(下位机FPGA TCP服务器IP核运行时钟域)
FPGA TCP Server IP核运行时钟,默认是125MHz,这个时钟是路由出来给用户自己写程序用的FIFO同步时钟。众所周知,千兆模式下的TCP运行时钟和用户时钟都是125MHz,也就是说,用户如果要把FPGA里面的数据通过TCP发送给PC(上行)或者接收PC下发的数据(下行),必须要使用这个125MHz时钟源,才能保持FIFO同步握手。为此,我们将这个125MHz时钟直接封装到我们的LabVIEW FPGA TCP Server CLIP里面来了,时钟信号名为“Clock lv_tcp_server_125M_clk_out”。
如何使用TCP Server CLIP路由进来的时钟呢?跟前面我们封装的PCIe总线、UDP千兆以太网、USB时钟一样,用户可以直接在FPGA定时循环左边创建一个时钟源常量,然后在下拉列表里面选择,如图10-29所示;或者直接双击FPGA定时循环,在弹出来的时钟配置页面里面选择“TCP_Server Data\ Clock lv_tcp_server_125M_clk_out”,如图10-30所示。


3)lv_tcp_server_connect_status(下位机FPGA服务器是否与客户端建立起连接状态)
由于TCP协议跟PCIe协议都是握手类协议,也就是说,只有与对等端建立起连接之后才能进行后续的发送和接收等传输工作,为了方便用户观察和调试TCP通信过程,我们将TCP Server IP核底层的是否建立起链接这个状态封装到LabVIEW FPGA里面来了;比如,这里下位机FPGA运行TCP Server协议之后,上位机PC充当客户端,那么当下位机FPGA(服务器端)侦听到上位机(客户端)的TCP连接请求之后,一旦成功建立起连接之后,这个“lv_tcp_server_connect_status”端口就会输出高电平(True),因此,我们可以将这个信号端口连接到FPGA VI前面板上面的LED指示灯,如图10-31所示;这样就能通过这个LED灯的亮灭来判断下位机FPGA(TCP服务器端)与主机端(比如上位机PC客户端)是否建立起TCP通信连接。

4)lv_tcp_server_board_mac(下位机FPGA服务器端MAC地址)
5)lv_tcp_server_board_ip(下位机FPGA服务器端IP地址)
6)lv_tcp_server_board_port(下位机FPGA服务器端端口)
7)lv_tcp_server_board_gate_way(下位机FPGA服务器端网关)
8)lv_tcp_server_board_subnet_mask(下位机FPGA服务器端子网掩码)
9)lv_tcp_server_pc_ip(下位机FPGA服务器侦听来自远程客户端的IP地址)
10)lv_tcp_server_pc_port(下位机FPGA服务器侦听来自远程客户端的端口)
上面这7个信号端口(4~10)一看名称就明白,它们是用来配置TCP通信的参数,依次是下位机FPGA服务器的MAC地址、IP地址、Port端口、网关、子网掩码和侦听来自远程客户端(比如上位机PC)的IP地址和Port端口。需要注意的是,为了方便大家记忆,我们将下位机FPGA形象的称呼为board(板子),对等端称呼为pc电脑,因此,名称里面带board的都是指下位机FPGA参数,带pc的都是指上位机参数,不要被前缀“lv_tcp_server”迷惑了,这个前缀是指FPGA当前封装的TCP协议栈是Server服务器。通过下面图10-32所示的程序框图,对TCP通信参数的设置和理解更加直观和一目了然。

提醒:如果下位机FPGA接入的是局域网,MAC地址一般可以随便设置,如果是广域网,那么设置的MAC地址不能与公网上的MAC相同,否则会产生冲突。
某些用户英文不是很好,为了照顾大家,我们特地在上图10-32的参数旁边加了备注,特别是下位机FPGA里面的IP地址不支持点号,必须要用16进制表示。
11)lv_tcp_server_tx_data_in(下位机FPGA TCP服务器需要发送的上行数据)
12)lv_tcp_server_tx_data_in_vld(下位机FPGA TCP服务器发送的上行数据是否有效)
13)lv_tcp_server_tx_data_req_en(下位机FPGA TCP服务器发送上行数据的请求信号)
上面这3个FPGA TCP服务器发送通道(11~13)的四线握手信号,其实就是上行数据发送端口,跟本书前面第7章提到的8位PCIe发送通道类似,这里的TCP端口数据位宽也是8位,也就是1个Byte字节,同样,每个上行通道都有3个握手信号组成。为了让用户更加形象的理解和掌握这些握手信号的用法,我们先给出一个FPGA TCP服务器端数据发送程序框图,如图10-33所示。毕竟LabVIEW框图看起来显而易见,一图胜千言!

其中,“lv_tcp_server_tx_data_req_en”信号就是TCP Server Socket IP核内部的接收FIFO准备就绪信号或者称之为主动向FPGA用户程序请求数据的信号,如果这个“lv_tcp_server_tx_data_req_en”拉高,说明TCP Server IP核可以接收FPGA给过来的数据,然后将其发送到上位机PC客户端。因此,我们可以将这个“lv_tcp_server_tx_data_req_en”接到FPGA里面用户创建的数据源FIFO(FIFO_TCP_Send_U8)的“输出就绪”端口上面;再把该FIFO输出的数据和有效信号分别接到TCP Server Socket CLIP的“lv_tcp_server_tx_data_in”和“lv_tcp_server_tx_data_in_vld”端口上面。
需要注意的是:如果对等端(比如客户端)读取速度慢,那么需要加大下位机FPGA服务器里面的Send FIFO(FIFO_TCP_Send_U8)深度,否则TCP数据包容易溢出丢失,因为上位机网卡自身往往没有办法增加TCP缓冲区,只有下位机FPGA能够加大FIFO或者利用DDR来缓冲数据;通常情况下,握手类协议的上行数据缓冲区FIFO一般设置大一些比较好,这个我们在前面也多次提到过了,切记!
14)lv_tcp_server_rx_data_out
15)lv_tcp_server_rx_data_out_vld
最后,剩下的这2个TCP Server接收数据通道(14~15)的握手信号,其实就是下行数据接收端口,这里TCP接收到远程客户端下发的下行数据位宽也是8位,跟PCIe类似,TCP下行通道同样是2个握手端口组成。为了让用户更加形象的理解和掌握这些下行数据握手信号的用法,我们先给出一个FPGA TCP Server服务器端数据接收程序框图,如图10-34所示。毕竟LabVIEW框图看起来更直观易懂!

其中,一旦下位机FPGA服务器接收到上位机PC客户端下发的TCP数据包后,那么对应的“lv_tcp_server_rx_data_out”和“lv_tcp_server_rx_data_out_vld”就会有效;因此,我们可以将“lv_tcp_server_rx_data_out”和“lv_tcp_server_rx_data_out_vld”分别接到FPGA里面用户创建的接收缓冲区FIFO(FIFO_TCP_Receive_U8)对应的数据输入和输入有效信号端口上。
需要注意的是:与上行数据FIFO缓冲区不一样,握手类协议的下行数据缓冲区FIFO一般不需要设置很大,避免过多浪费FPGA Block RAM资源,这是因为FPGA自身的读取和处理速度是非常快的,不存在卡顿或者抖动一说;因此,可以看出,如果其中一方速度慢于另一方,那么速度快的这一方通常需要加大FIFO深度;假设双方是两个读取和处理速度都很快的FPGA进行互联的话,那么无论是发送FIFO还是接收FIFO,实际上都不需要设置很大,因为FPGA可以完全高速的逐点处理,而上位机PC通常做不到。
提醒:我们封装的TCP服务器上行和下行data都是U8字节整形数据,如果用户需要发送或者接收小数或者定点数,那么需要提前进行转换,比如,将定点数通过“数值至布尔数组”将符号和小数点特性去掉,然后再利用“布尔数组至数值”转换成整形数据,最后再进行拆分组合变成U8字节数组,反之亦然。
结论:可以看出,虽然TCP协议本身很复杂,但是对于用户来说,只要理解了FIFO的四线握手编程,就可以搞定所有线程之间的数据传输问题了,这也就是为什么我们在书中每个实验里面都反复强调四线握手和并转串以及串转并的重要性。
10.5、修改底层XDC约束文件以适配用户自己做的或者第三方的FPGA TCP硬件
本节我们重点讲解一下一个非常重要的问题:那就是如果用户自己做的板子没有参考黑金AX7103原理图,或者网上买了其他家成熟的带PHY网口的FPGA硬件,那么用户需要注意哪些地方才能让我们封装的LabVIEW FPGA TCP Socket CLIP完美适配呢?
不知道大家还记不记得我们曾在第五章里面,告诉大家如何修改FPGA引脚来适配自己板子上的晶振时钟,让FPGA能够运行起来;同理,TCP与UDP对应的PHY芯片也有几个非常重要的引脚可以在xdc顶层约束文件里面修改。编译的时候会自动覆盖底层网表文件里面的引脚定义。
首先,找到我们需要修改的FPGA终端模板文件里面的xdc约束文件,比如本书配套的ARTIX7开发板AX7103,默认的路径如图10-35所示,找到里面的xdc约束文件,如图10-36所示:
C:\Program Files (x86)\National Instruments\LabVIEW 2018\Targets\NI\FPGA\NiAdept
C:\Program Files (x86)\National Instruments\LabVIEW 2019\Targets\NI\FPGA\NiAdept


然后,右击通过notepad++打开这个xdc约束文件,找到PHY网口芯片引脚定义的地方,如图10-37所示。

因为每个厂家做的FPGA板子给外设分配的引脚可能都不尽相同,所以用户需要根据板子实际的原理图来修改这个xdc顶层文件。以黑金AX7103开发板举例说明,先打开对应底板原理图,找到PHY网络芯片相关针脚定义,如图10-38所示。这些引脚其实可以分成4类:PHY芯片复位引脚、PHY芯片寄存器参数配置的IIC引脚(mdc和mdio)、PHY芯片收发125MHz时钟引脚;PHY芯片收发数据引脚(GMII:8bit;RGMII:4bit)。
这4类引脚在前面图10-37里面都定义过了,如果用户自己画的板子或者网上买的其他家的板子引脚定义不一样,那么照葫芦画瓢对应修改一下就可以了。

然后,打开AX7103开发板上插着的核心板AC7100原理图,如图10-39所示,可以看到黑金AX7103开发板上第1路和第2路KSZ9031 PHY芯片都挂在了FPGA BANK16上面。

