实验111-下位机FPGA TCP客户端-可变采样率-异步模拟ADC需要大缓存

实验111-下位机FPGA TCP客户端-Slope-U8-可变采样率-异步模拟ADC需要大缓存
1、实验内容
现在很多电脑PC、工控机或者其他嵌入式设备都集成了千兆网卡,可以直接接入一根CAT5+或者CAT6类网线即可实现TCP网络互联;以太网通信的优点是传输距离远(网线内部走的是恒流源驱动),支持热插拔(支持断点续传),传输速度快(理论带宽125MB/s)。对于高速采集、分布式测试测量和边缘计算等领域,TCP网络通信是必不可少的,因为UDP无法替代TCP来完成这些应用。
因此,很多用户都希望自己的FPGA芯片能够实现TCP协议栈,这样就可以开发出来很多支持TCP网络通信的工业以太网设备、Gige相机和边缘计算等分布式采集终端了。也为后续基于TCP的Modbus和MQTT等网络应用层协议打下坚实的基础。
黑金设计的ARTIX7-100T FPGA开发板(AX7103)上集成了两路千兆以太网PHY芯片KSZ9031,如图111-1所示,当然如果用户自己做板子的话,也可以将昂贵的KSZ9031换成台湾瑞昱的RTL8211F或者国产廉价的YT8511C这类完全兼容的PHY芯片。

用户可以直接将这个开发板通过一根CAT5+或者CAT6类网线接到工控机或者电脑的网口里面;当然,如果用户是超薄笔记本电脑,没有千兆网口的话,也可以在网上买一个雷电转千兆网口的扩展坞,方便后续做实验。
由于我们已经把复杂的TCP Client通信协议封装进了LabVIEW FPGA下的Socket CLIP,用户只需要根据四线握手协议来调用对应的CLIP端口就可以了。这部分我们在前面第10章已经介绍过了。
本节FPGA TCP Client通信实验重点向大家讲解以下6个方面的内容:
1) 下位机(FPGA)TCP Client客户端通信程序开发。
2) 上位机(PC)服务器端通信应用程序开发。
3) 下位机FPGA内部多线程之间的数据交互与数据缓冲:FIFO。
4) 下位机FPGA内部不同速率之间的匹配机制:四线握手。
5) 下位机FPGA TCP Client通信IP核全双工通信的读写机制。
6) TCP Client通信数据格式和类型转换(大小端与字节数组)(下行和上行都是大端)。
关于下位机(FPGA)TCP Client通信程序开发,指的是FPGA上的代码编写。譬如,用户可以做一个基于TCP传输的高速采集卡,将ADC采集到的数据通过TCP Client实时发送到PC服务器上,这段代码我们可以利用LabVIEW来开发,不需要传统的VHDL/Verilog,这里称之为下位机LabVIEW FPGA编程。本节实验下位机FPGA程序里面我们会模拟一个斜坡信号Slope来代替实际ADC采集的数据,然后发送到上位机服务器进行显示或者流盘。
关于上位机(PC)服务器端的应用程序开发,指的是运行在windows或者linux系统中的LabVIEW上位机程序,可以通过调用LabVIEW软件自身就有的TCP函数选板里面的VI函数与下位机FPGA进行TCP网络数据交互。我们把上位机收发线程分成两个独立的while循环来处理,这样调试起来简单一些。当然,如果有些用户不会LabVIEW,也可以使用C\C++\C#\Python来调用网卡DLL动态库进行上位机程序开发。
关于FIFO的概念和操作,可以参考前面的“实验4-串口通信”,里面有详细的介绍和演示过程。
关于四线握手制的工作过程和原理,也可以参考前面的“实验4-串口通信”,这里不再赘述。
关于TCP Client通信IP核全双工通信机制,我们会通过两个独立的收发线程来演示。发送和接收可以同时并行执行,而且是上行和下行同时全双工工作。
最后一个需要注意的问题是,下位机FPGA 跟上位机之间的TCP通信数据格式和数据类型,需要匹配上才能正确解析,好在我们封装的下位机TCP Client协议栈和上位机LabVIEW里面默认是大端格式的U8字节数组;另外,模拟或者采集的ADC波形,对应的数据类型跟TCP内部的字节数组之间需要通过“强制类型转换”函数转换一下才能看到准确的波形曲线。
下面我们带着大家一起,利用LabVIEW编写1个符合标准四线握手制的TCP Client下位机FPGA客户端程序,来模拟一个TCP千兆以太网采集卡,采集1路斜坡信号,通过TCP网络发送给上位机PC服务器,同时还能接收上位机服务器端下发的指令和参数对FPGA板卡进行采样率和使能采集等控制。
2、硬件资源
打开本书配套的光盘/云盘里面1号文件夹里面的AX7103底板原理图,然后找到PHY网络接口这部分原理图,如图111-2所示。这些引脚其实可以分成4类:PHY芯片复位引脚、PHY芯片寄存器参数配置的IIC引脚(mdc和mdio)、PHY芯片收发125MHz时钟引脚;PHY芯片收发数据引脚(GMII:8bit;RGMII:4bit)。
这4类引脚在前面第10章里面的图10-37定义过了,如果用户自己画的板子或者网上买的其他家的板子上的网口PHY芯片引脚定义不一样,那么照葫芦画瓢对应修改一下这个FPGA终端下的xdc约束文件里面的PHY芯片引脚就可以了。

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

3、FPGA TCP Client下位机客户端通信程序开发
从本节开始,我们会带着用户一起利用LabVIEW开发一个FPGA TCP Client下位机应用程序,这个程序可以下载到FPGA芯片里面运行,为了更加全面形象的展示TCP网络通信的魅力,我们模拟一个TCP高速数据采集卡,将ADC采集到的Slope斜坡信号通过8位位宽的TCP Client上行通道发送到上位机进行显示,上位机通过8位位宽的下行通道下发不同的指令参数来控制FPGA开始采集、停止采集和调整采集频率。以此向大家展示下位机FPGA(客户端)与上位机PC(服务器)之间通过TCP网络进行双向高速通信的过程。
3.1、LabVIEW FPGA项目创建
1)启动LabVIEW 2019 SP1软件,虽然我们可以直接打开前面实验里面已经创建好的“My_FPGA_Starter_Board_Artix7_AX7103.lvproj”这个项目,但是由于我们开发的例程太多了,如果全部放在一个lvprog项目下进行管理的话,每次打开这个项目的时候,加载需要很长时间,同时也会占用很多计算机内存,为此,从本章开始,我们重新新建一个新的LabVIEW项目来开发。用户可以点击左上角的“文件>>新建(N)”或者直接点击“文件”下方的“创建项目”选项。如图111-4所示。点击“确定”按钮后,将新建出来的项目保存一下,命名为“My_FPGA_Starter_Board_Artix7_AX7103_TCP_Client.lvproj”,如图111-5所示。


由于本章我们引入了TCP网络通信,之前创建的FPGA终端里面没有添加TCP Client Socket CLIP,所以,我们需要先右击“我的电脑”选择新建“终端和设备”,如图111-6所示;然后再在弹出来的FPGA终端选择列表里面,选择一个带TCP Client后缀的ARTIX7-100T FPGA终端设备,如图111-7所示;点击“确定”按钮后,创建好的ARTIX7 TCP Client FPGA终端设备,如图111-8所示。



2)右击FPGA终端,选择“新建>>虚拟文件夹”,如图111-9所示。将添加到FPGA终端里面的虚拟文件夹,重命名为“实验111-TCP客户端-通信实验(Slope-U8-可变采样率)-下位机”,如图111-10所示。


3.2、LabVIEW FPGA EIO节点与FIFO创建
1) 新建EIO
1)由于本节所有的TCP通信实验里面都会用到LED指示灯和按键KEY等EIO资源,所以,我们可以在FPGA终端上面右击,选择“新建>>FPGA I/O”,如图111-11所示。然后在弹出来的I/O资源选择对话框里面,找到AX7103 FPGA开发板上的LED和KEY对应的管脚资源,如图111-12所示。单击对话框中间的“向右箭头”按钮,将这些引脚对应的EIO节点添加到右侧的FPGA资源列表里面,如图111-13所示。



点击“确定”按钮后,这些EIO节点会被添加到FPGA终端项目里面,如图111-14所示。

2) 新建FIFO
我们可以将TCP通信IP核看成一个独立的功能模块,类似MCU里面的子函数或者子VI,FPGA程序只需要关心如何将不同类型的数据(U64、U32、U16、U8)通过TCP协议发送出去以及收到的数据怎么反馈给FPGA用户程序,这个中间的桥梁,可以采用FIFO缓冲区来实现。FIFO非常适合在不同的线程之间传递数据,而且设计合理的话,数据不会丢失或者被覆盖。
注意:FPGA用户线程相当于生产者,TCP收发线程相当于消费者,所以,二者之间有个速率匹配的问题,生产者发送数据的速度不能快于消费者(其中,基于纯FPGA实现的TCP传输吞吐率实测最高在95MB/s左右),否则会造成数据丢失。后面我们会根据具体程序进一步分析二者之间的速度匹配问题。
1)右击FPGA终端项目,选择新建1个“虚拟文件夹”,如图111-15所示。然后重命名为“FIFO_TCP”,这个虚拟文件夹用于存放接下来我们要创建的TCP FIFO缓冲区资源,如图111-16所示。


2)右击虚拟文件夹“FIFO_TCP”,选择“新建>>FIFO”,如图111-17所示。

3)接着会自动弹出FIFO属性设置对话框,如图111-18所示。下面我们逐项解释一下每个选项的含义。
首先是“名称”,这里可以随便输入一个能够体现这个FIFO功能的名称,比如我们要把下位机FPGA实际通过TCP发送给上位机PC的8位数据(U8\I8)存放到这个FIFO,那么可以重命名为“FIFO_TCP_Send_U8”。
其次是“类型”,这个下拉列表里面默认只有一个选项“终端范围”,意思就是我们创建的这个FIFO只能在这个FPGA芯片内部使用,不能用于其他终端,如果用户使用的是NI的RT+FPGA终端设备,那么这个地方会有更多的选择,这里不再赘述。
接着是“请求元素数量”,默认是1023,这个数值就是FIFO的深度,最大可以缓冲的数据长度,数值越大,编译消耗的FPGA资源就越多。很多客户以为这个数值设置越大越好,这样就不容易溢出了,实则不然,如果多线程之间的FIFO读写速度差别很大的话,再大的FIFO对于FPGA这种高速运行的器件来说,也是瞬间就会溢出或者覆盖丢点的。所以问题的关键不在于FIFO深度,而是要设计一个合理的读写匹配机制。
新增重大更新:对于FPGA跟上位机(Windows/Linux)之间存在握手机制的通信协议来说,比如常见的TCP和PCIe协议,FPGA什么时候可以往上行FIFO里面送数据,是可以根据ready信号来判断的,UDP协议不存在握手,所以下位机FPGA不需要大容量缓冲FIFO;通常这些存在握手协议的上行FIFO一般都是异步的,比如ADC采集循环(线程)与TCP或者PCIe传输循序(线程)往往是两个独立的线程,并且时钟域也不一样,因此,这个上行FIFO的深度就显得非常重要,而不是随便设置的。因为我们的上位机特别是Windows系统,抖动非常大,通常都是ms量级的,举个例子,假设下位机FPGA模拟或者实际的ADC采样率是10MS/s,ADC位数是16位,如果上位机系统稍微卡顿或者阻塞的时间是1ms,那么下位机FPGA需要缓冲的FIFO深度至少是10M×0.001=10K,也就是最少需要1万个点的空间或者2万个字节空间;因此,我们建议在FPGA资源足够的情况下,尽量将这类承担高速传输的上行FIFO(FIFO_TCP_Send_U8)深度设置的足够大,比如这里我们可以设置为120K个点,这样后续上位机只要抖动不是很剧烈的话(比如ms量级),是可以保证下位机FPGA数据不溢出丢点的;但是,如果下位机ADC采样率非常高的话,比如100MS/s甚至上GS/s的话,那么光靠下位机FPGA芯片自身的Memory空间显然不足,因为宝贵的LUT资源一般不要轻易作为FIFO用,那么此时,FPGA外围的DDR3或者DDR4就起到了真正的作用;在我们整本书里面虽然有DDR的实验例程和讲解,但是并没有真正说清楚这些DDR颗粒在什么情况下使用,实际上存在高速采集和握手协议的传输或者通信程序中,DDR可以起到大容量数据缓冲的作用,这样上位机即使卡顿或者抖动时间达到几百ms都不用担心了,当然,如果上位机的抖动这么大的话,用户还是需要对上位机程序进行优化的。在我们单独移植开发的纯FPGA TCP Socket CLIP里面也会着重强调上行FIFO空间深度设置的大小合理性。

然后是“实现”方式,里面有3个选项“触发器”、“查找表”和“存储器块”,如图111-19所示。前两个就是FPGA最为宝贵的逻辑门资源了,一般不轻易使用,除非是用户已经将FPGA内部的存储器全部用完了,否则优先使用内部存储器块,这个存储器不占用FPGA内部的逻辑资源,不用也是浪费。
最后是“控制逻辑”,这个特别需要注意,默认选择的是“终端优化”,如图111-20所示。最下方有个提示框,里面有段文字“根据FIFO使用的时钟域和终端类型,应用程序将会通过逻辑片架构或内置控制逻辑实现FIFO,因此当使用“终端优化”控制逻辑时,元素的实际数量可能有所不同。”也就是说LabVIEW在生成VHDL代码的时候,真实生成的FIFO深度并没有达到我们前面实际设置的“请求元素长度”。如果用户忽略这个,可能会导致程序在运行的时候,出现一些跟预期目标不一致的情况。
所以建议用户选择“逻辑片架构”,如图111-21所示。这时下面会提醒用户“元素数量已强制转换为FPGA可高效实现的值。”也就是说LabVIEW生成的FIFO深度会强制为我们申请的长度。
4接下来需要设置FIFO缓冲区单元的数据类型,在左侧的“类别”列表里面选择“数据类型”,然后在右侧的下拉列表里面选择U8无符号整形数据,如图111-22所示。因为我们给用户封装的LabVIEW FPGA TCP Client Socket CLIP里面,数据位宽是8位,用户可以根据实际情况选择U8或者I8,因为本节实验我们模拟的是一个正极性的线性斜坡信号,所以这里将FIFO数据类型设置为U8正好匹配,直接通过TCP传送到上位机PC就不用进行正负转换了,直接在波形显示控件中显示出来就可以了。当然,如果后期用户在实际项目或者产品中需要用到有符号8位,那就选择I8。
5)最后点击左侧的“接口”,切换到仲裁选项,如图111-23所示。将读取和写入的仲裁全部设置为“从不仲裁”,这样编译出来的代码执行速度也就是时钟约束可以提高不少,同时代码的健壮性和确定性也比仲裁来的稳。
6)全部设置完成后,点击“确定”按钮,即可创建出一个发送缓冲区“FIFO_TCP_Send_U8”,然后再以同样的方式创建一个接收缓冲区“FIFO_TCP_Receive_U8”,如图111-24所示。需要注意的是,上位机服务器端下发的指令和参数一般速度不会太快;另外,上位机发送的数据一般会经过“强制类型转换”VI变成字符串或者字节数组,这两种类型的数据表示法默认都是U8,所以,这里我们创建的FPGA接收PC下发数据的FIFO类型也要设置为U8,因此,“FIFO_TCP_Receive_U8”的数据类型要设置为U8,跟FPGA往上发送TCP数据包的Send FIFO位宽一样。另外,由于TCP网络的数据发送和读取属于全双工通信,不需要通过RW读写指令来区分,因此,本节实验不需要创建RW FIFO,这一点与前面USB实验不一样。

7)开始编写程序之前,我们先通过创建虚拟文件夹的方式,将接下来新建的主VI等文件分类进行管理,便于维护和调试。这里用到的方法建议用户在开发自己的项目时,优先分类管理。首先右击虚拟文件夹“实验111-TCP客户端-通信实验(Slope-U8-可变采样率)-下位机”,新建1个子虚拟文件夹(Main),如图111-25所示。这个虚拟文件夹用来存放主VI文件。

3.3、下位机FPGA TCP Client客户端通信主VI编写过程(基于封装好的TCP Client CLIP)
1)右击“实验111-TCP客户端-通信实验(Slope-U8-可变采样率)-下位机”里面的子虚拟文件夹“Main”,选择“新建>>VI”,如图111-26所示。然后,新建一个VI,另存为“实验111-TCP客户端-Slope-U8-可变采样率-异步模拟ADC需要大缓存-FPGA.vi”,如图111-27所示。


2)为了降低FPGA TCP程序开发的难度,我们提前将ComBlock公司提供的VHDL版本的TCP Client IP核,利用NI公司的Socket CLIP技术,将其封装到FPGA终端下面,只把需要进行握手的数据包FIFO和TCP必要的配置参数端口预留出来,如图111-28所示。这样做的好处在于,用户即使完全不懂TCP协议,也能在FPGA VI里面调用CLIP编写TCP通信程序。关于这部分内容,前面10.4节已经做过了详细的讲解,不记得的用户一定要把前面10.4节温习一遍。

3.3.1、编写下位机FPGA TCP Client Send程序框图(FPGA-->PC):上行
1)打开主VI“实验111-TCP客户端-Slope-U8-可变采样率-异步模拟ADC需要大缓存-FPGA.vi”,切换到程序框图,放置一个定时循环结构,然后双击打开这个定时循环的时钟源配置页面,选择125MHz的TCP Client CLIP时钟作为时钟源,如图111-29所示。这个125MHz同步时钟驱动的定时循环,是专门用来处理用户创建的FIFO跟TCP IP核内部的FIFO之间进行数据交互的,支持标准四线握手串联。

2)接下来,我们可以将FPGA终端下“TCP_Client Data”里面的前8个节点(TCP Client IP核复位和TCP网络配置参数)和“lv_tcp_client_connect_status”拖拽到刚刚创建的定时循环里面,然后创建相应的复位按钮和连接指示灯控件以及相应的IP参数,如图111-30所示。

其中,“TCP_Client Data\lv_tcp_client_reset”拉高(True),复位TCP Client IP核;如果TCP连接成功,“TCP_Client Data\lv_tcp_client_connect_status”指示灯会点亮;中间的7个TCP参数含义分别是:
l 下位机FPGA 客户端MAC地址:01-02-03-04-05-06
l 下位机FPGA客户端IP地址:192.168.1.16
l 下位机FPGA客户端端口:1028
l 下位机FPGA客户端网关:192.168.1.1
l 下位机FPGA客户端子网掩码:255.255.255.0
l 下位机FPGA需要连接的远程服务器IP地址:192.168.1.10
l 下位机FPGA需要连接的远程服务器端口:1024
3)接下来,我们需要编写一下FPGA(客户端)传递数据给TCP IP核然后发送到上位机PC(服务器)的代码。首先,我们把FPGA终端下的TCP_Client Data里面的3个发送信号端口“lv_tcp_client_tx_data_in”、“lv_tcp_client_tx_data_in_vld”、“lv_tcp_client_tx_data_req_en”,拖拽到程序框图里面来,如图111-31所示。这3个信号端口其实符合标准的四线握手。

4)然后把我们前面创建好的8位位宽的用户FIFO(FIFO_TCP_Send_U8)也拖到程序框图里面,然后根据四线握手的方式,将TCP Client CLIP上行通道的3个握手信号接到FIFO上面去,完整的TCP Client数据上行发送程序框图,如图111-32所示。

(FPGA-->PC上行数据发送程序框图)
下面我们来讲解一下上面红色框框里面的程序框图含义:当TCP Client IP核内部的FIFO没有满时,这个“lv_tcp_client_tx_data_req_en”信号会拉高,也就是通知FPGA,可以将采集的数据发送给TCP IP核内部的FIFO;我们可以将这个信号接到FPGA里面用户创建的FIFO(FIFO_TCP_Send_U8)的“输出就绪”端口,然后把“元素”和“输出有效”两个端口分别接到TCP Client IP核的“lv_tcp_client_tx_data_in”和“lv_tcp_client_tx_data_in_vld”两个信号端口上;当用户FIFO里面有数据输出时,就会直接通过TCP传输到上位机PC端了。
提醒:为了让上位机PC(服务器)能够自由动态的控制下位机FPGA(客户端)使能采集,我们加了一个布尔型控件“开始/停止”按钮,这个控件可以由上位机下发指令和参数进行控制,通过取反之后跟准备就绪“lv_tcp_client_tx_data_req_en”信号“相或”之后接到用户FIFO上面,同时还直接接到case结构上面。这样设计的目的是:是为了让下位机FPGA能够自动把用户FIFO(FIFO_TCP_Send_U8)残留剩余的数据读走等效于清空掉,比如上位机此时下发了一个停止采集指令,然后将布尔控件“开始/停止”设置为“假”,取反之后为“真”,接到了用户FIFO上面,如果此时用户FIFO里面依然残留了一些数据,直接就读出来,通过后面的case结构“假”分支过滤掉了;如果此时上位机PC再下发一个开始采集指令,那么用户FIFO里面一定是最新的数据,通过TCP发送到上位机的数据也就是最新的了。利用这种巧妙的方式可以模拟一个真实的DAQ数据采集设备。
3.3.2、编写下位机FPGA TCP Client Receive程序框图(PC-->FPGA):下行
1)接下来,我们还需要编写一个下行(PC-->FPGA)数据接收的程序框图。由于发送的下行数据一般都是经过“强制类型转换”函数转成了字节数组或者字符串形式,本质上数据类型属于U8,所以这里,我们可以使用FPGA终端下TCP_Client Data这个CLIP里面的8位下行通道来接收PC下发的字节数组。比如,本节实验我们使用下行通道来接收PC下发的指令和参数。
2)首先,用户可以将FPGA终端下行通道的2个握手信号(lv_tcp_client_rx_data_out、lv_tcp_client_rx_data_out_vld)拖拽到FPGA程序框图里面,如图111-33所示。

3)然后根据四线握手的方式,将TCP Client CLIP下行通道的2个握手信号接到用户创建的Receive FIFO(FIFO_TCP_Receive_U8)上面去,完整的FPGA TCP数据下行接收程序框图,如图111-34所示。

(PC --> FPGA下行数据接收程序框图)
至此,关于下位机FPGA TCP Client四线握手的通信线程代码就编写完成了,这个线程也是最为核心的一个程序,今后,用户可以直接拷贝这个线程到自己的程序里面,而无需从头编写。
3.3.3、编写下位机FPGA斜坡信号采集程序框图:用户线程
1)现在我们还需要编写一个斜坡信号或者Sine信号采集线程,来模拟一个真实的TCP DAQ数据采集卡,然后将这些实时采集的波形数据通过TCP传输到上位机PC端进行显示,看看采集的数据是否正确。本节实验我们模拟一个简单的8位位宽的斜坡信号,后面的实验我们全部用Sine信号来充当采集的波形。不同位宽的数据会涉及到并串转换和串并转换的问题,这个在前面已经重点提醒过了。
2)首先,我们放置一个定时循环,将定时循环时钟源设置为50MHz,也就是20ns(50MHz的倒数),再给这个定时循环加一个计数器和分频系数的输入控件“Count(20nSec)”,通过软件分频的方式来控制ADC采样率,当计数达到分频系数之后,计数器又会重新从0开始计数;这样后续就可以通过上位机PC下发参数来控制这个循环里面case结构轮询的速度,相当于变相的控制了采集卡的采样率。配置过程如图111-35所示。注意:除了这种方式外,在后续的实验里面我们还可以采用普通while循环+循环定时器来代替这里的定时循环,实现变采样率采集效果。

需要注意的是:“普通while循环+循环定时器”这种方式是没有办法达到最小时钟约束的,这是因为普通while循环里面的代码在编译的时候,LabVIEW会给其分配一定的冗余时间,以防止出现时钟约束编译报错。举个例子,假设循环定时器单位设置为滴答,也就是5ns步进,如果循环定时器赋值1,实际上编译完成后的普通while循环运行速度是到不了5ns的,如果是定时循环的话,时钟源设置为200MHz,只要编译通过,实际下载到FPGA芯片里面bit代码运行的效果就是严格的5ns,大家切记!
3)然后,在定时循环里面,放置一个布尔型的按钮,和一个实体按键对应的EIO节点,二者“相或”之后,再跟上面的软件分频计数“≥”号相与之后连到case条件结构上面,这样既能通过手动按下FPGA开发板上的实体按键来触发采集也可以通过上位机PC下发开始采集指令来控制采集,实体按键如果替换成外部采样脉冲信号,那就实现了同步时钟采集了;接下来,在case条件为“真”的分支里面放置一个8位位宽的加1计数器,通过移位寄存器的方式实现计数到最大值,溢出之后会自动重新从0开始计数;然后将这个计数器累加值连到上行发送数据FIFO上面(FIFO_TCP_Send_U8),最后再把这个FIFO的“超时”输出取反之后连到第4个LED指示灯上面(LED4_D15),如果这个LED灯点亮,说明采集线程的速度快于TCP上行发送的速度,FIFO溢出,数据就会丢失,因此,必须要保证采样率不能大于TCP上行的最大吞吐率(95MB/s)。
需要注意的是:由于本节实验模拟的是异步通信,也就是ADC采集线程跟TCP通信线程属于两个不同的时钟域,中间隔了一层FIFO缓冲区;为了方便后续分析ADC采样率与TCP吞吐率之间的关系,我们将上行发送FIFO(FIFO_TCP_Send_U8)的超时拉出来进行+1计数,并在前面板上放置一个“溢出点数”显示控件,这样就可以直观的判断出当前ADC采集的数据是否丢点,如果因为上位机系统抖动或者读取速度慢导致下位机FPGA TCP发送阻塞超时,那么溢出点数会不断增加,只要上位机系统抖动小读取速度足够快,理论上是不会发生溢出的,当然,用户做TCP极限吞吐率测试时,最好把占用上位机CPU和网络带宽的应用软件全部关掉,特别是像todesk、teamview、向日葵这样的软件。
最后,完整的用户线程(模拟斜坡信号采集)程序框图,如图111-36所示。注意,在case条件为“假”的分支里面,将移位寄存器首尾相连,这样,上位机PC端采集到的波形数据就是连续的锯齿波。

3.3.4、编写下位机FPGA TCP Client数据解析程序框图:下行数据解析线程
1)为了能够真实的模拟上位机PC(服务器)和下位机FPGA(客户端)之间的交互运行,我们还需要编写一个FPGA数据解析线程,将PC下发的指令和参数正确的解析出来,然后赋值给FPGA里面相应的控件。关于这个数据解析线程的框架,其实我们在本书前面的串口通信和千兆以太网通信实验里面都有提到,不熟悉的用户可以回顾一下前面的相关内容。
2)数据解析线程的整体框架,其实很简单:就是一个普通while循环,首先通过FIFO“获取待读取元素数量”方法节点,轮询TCP Receive FIFO里面是否接收到指定长度的数据(比如,做定长解析),如果达到了指定长度的数据,则利用for循环一次性将这些字节数组读取出来,然后进行拼接合并转换,变成实际意义的参数或者指令。
3)比如本节实验里面,上位机PC每次下发的指令和参数一共是9个字节数据,那么我们在下位机FPGA程序里面,就可以将case结构的条件设置为9..,也就是说,只要下位机FPGA接到了至少有9个字节的数据,就会进入case里面,然后将for循环的长度也设置为9,这样就能一次性把这9个字节数据全部读取出来变成一个字节数组,然后再对这个一维数组进行索引拼接合并,甚至于进行加减乘除计算。完整的数据解析线程框图,如图111-37所示。通过程序框图可以看出,我们将前面4个字节进行拼接之后赋值给了前面的用户线程(模拟斜坡信号采集)里面的分频系数(Count(20nSec)),用来改变用户线程里面的case条件轮询速度,相当于改变了采样率;第5个字节跟0进行比较,如果大于0就启动采集,小于等于0则停止采集,都是通过给布尔控件(开始/停止)进行赋值实现的;最后4个字节是预留的,用户可以根据实际情况下发更多的参数或者指令给FPGA,只要修改一下对应的数据长度就行了,这里我们就不再赘述了。

4)将前面的FPGA TCP Client通信线程(上行+下行)、模拟斜坡信号采集线程(用户线程)、下发的数据解析线程这3个线程(循环)放在一起,就形成了一个完整的基于LabVIEW TCP Socket CLIP实现的下位机FPGA TCP客户端通信程序,这个FPGA VI程序前面板和程序框图分别如图111-38和111-39所示。


4、LabVIEW FPGA VI仿真
本节实验涉及的是FPGA TCP网络通信,无法在计算机上进行虚拟,所以这里的功能性仿真可以跳过,直接将LabVIEW FPGA TCP主VI程序编译下载到FPGA芯片里面运行,然后再利用上位机通过下发指令和参数来测试通过网线连到上位机PC端(服务器)的黑金AX7103开发板(客户端)上的TCP网口能否正常发送和接收数据包,并在上位机前面板上把采集到的波形数据显示出来。
5、LabVIEW FPGA VI编译下载
1)直接点击FPGA主VI“实验111-TCP客户端-Slope-U8-可变采样率-异步模拟ADC需要大缓存-FPGA.vi”上方工具栏里面的“运行”箭头按钮,保存VI之后,弹出Xilinx 编译服务器选择对话框,如图111-40所示。选择本地编译器,然后单击“取消”按钮,此时,在FPGA项目下的程序生成规范里面就会多出来一个与TCP通信VI同名的程序生成规范(实验111-TCP客户端-Slope-U8-可变采样率-异步模拟ADC需要大缓存-FPGA),如图111-41所示。双击打开这个程序生成规范属性配置页面,勾选“加载至FPGA时运行”复选框,最后点击“生成”或者“确定”按钮,如图111-42所示。如果点击的是“确定”按钮,那么需要重新运行一下VI即可再次启动Xilinx Vivado 2017.2编译器,进入编译状态窗口,如图111-43所示。




2)编译很顺利,没有报定时错误了也没有报编译未完成的错误,因为我们已经突破了7系列FPGA的编译、下载和在线前面板交互式运行和调试功能。等待FPGA VI程序编译完成后,就可以看到最终消耗的FPGA硬件资源,如图111-44所示。由于我们默认将TCP网络通信IP核也封装到这个FPGA终端下面,所以编译消耗的资源显的非常多,实际上,纯TCP协议栈消耗的FPGA资源很少,不到10%。

3)当上面编译完成后,会弹出一个“正在准备交互式执行”对话框,如图111-45所示,这个想必用过NI或者看过我们Pro1开发宝典的用户很熟悉,正是LabVIEW FPGA启动在线前面板交互式运行和调试功能的提示框;如果大家还没有将Xilinx JTAG下载器和FPGA开发板接到这台电脑或者接的不是Xilinx JTAG下载器而是Digilent HS系列下载器,过一会会弹出一个超时错误对话框,提醒用户找不到下载通信线缆,也就是说主机找不到Xilinx JTAG下载器,如图111-46所示。


重大更新:很多用户关心的是Vivado编译器编译出来的原始bit文件,而不是NI的lvbitx文件,因为原始FPGA bit文件更灵活更方便,将这个原始的bit文件发给任何人都可以通过任何手段下载到FPGA里面运行;虽然上面我们没有连接下载器和FPGA板子到电脑上导致下载超时,但是LabVIEW项目浏览器所在路径下的“FPGA Bitfiles”文件夹里面,依然会自动生成一个同名的原始bit文件,如图111-47所示。

注意1:这个自动生成原始FPGA bit文件的功能是我们在后台帮大家实现的,也就是说,用户再也不需要像前面第5章5.3节那样总是开着获取bit文件的程序才能得到原始bit文件。很明显经过我们重大更新后的宝典和LabVIEW My FPGA工具包更方便、更易用,体验效果跟NI完全一样。
注意2:lvbitx不是FPGA原始的bit位文件,而是NI在bit基础上又封装了一层,因此,不能通过其他软件进行下载。但是,lvbitx里面包含了FPGA VI前面板上的控件类型和FPGA寄存器地址等信息,所以在后续FPGA VI在线前面板交互式运行里面会用得到lvbitx,这个也是LabVIEW后台自动完成下载和解析的,用户了解一下即可。
6、TCP通信上位机PC服务器端程序编写
下面我们需要编写一个TCP上位机(服务器端)应用程序来测试下位机FPGA TCP客户端程序是否满足设计需求,相当于开发一个TCP采集卡上位机程序。这里用户可以直接调用LabVIEW软件自身就有的TCP函数选板里面的VI,如图111-48所示,不熟悉的用户可以自行研究一下,这里不再赘述。当然了,喜欢用C、C#、Python、QT等文本编程语言开发上位机TCP服务器测试程序的话,可以上网搜索一下或者问问ChatGPT。

下面我们简单介绍一下上位机TCP服务器程序的编写过程。
1)右击项目浏览器里面的“我的电脑”,选择新建一个“虚拟文件夹”,如图111-49所示。

2)然后对新建出来的虚拟文件夹重命名为“实验111-TCP服务器端-通信实验(Slope-U8-可变采样率)-上位机”,如图111-50所示。

3)然后右击上面的虚拟文件夹,选择新建一个VI,将这个VI保存为“实验111-TCP服务器端-通信程序-Slope-U8-PC.vi”,如图111-51所示。

4)为了加快讲解速度,这里我们直接给出TCP上位机服务器通信程序框图,如图111-52所示。程序框图由两个相互独立的线程组成,也就是两个while循环,分别是TCP写线程(PC服务器下发数据给客户端FPGA)和TCP读线程(PC服务器读取FPGA客户端上传的数据)。对应的前面板控件布局,我们也先贴出来,如图111-53所示。


下面我们来分别介绍一下这两个线程对应的程序框图编写过程及其注意事项。
5)首先,程序启动的时候,一般都会有一个初始化过程,这里也不例外。由于前面板上有一些按键和指示灯等控件,为了让程序每次运行的时候,都能恢复到一个默认状态,这里利用顺序结构,对需要设置的控件进行赋值操作,如图111-54所示。

6)接下来,我们看看上位机TCP写线程代码是怎么实现的。由于下位机FPGA跑的是TCP Client客户端程序,那么上位机的角色就是服务器;所以,我们需要从TCP函数选板里面选择侦听函数“TCP listen.vi”,侦听来自下位机FPGA的TCP连接请求;这个“TCP Listen.vi”要侦听的下位机FPGA客户端IP地址是192.168.1.10,端口是1024,如图111-55所示,这两个参数就是前面下位机FPGA VI程序里面设置的PC IP和port;同时为了防止上位机阻塞死机,将TCP侦听超时设置为30s。

7)一旦上位机TCP侦听成功,说明上位机PC跟下位机FPGA成功建立起TCP网络连接,同时,这个“TCP Listen.vi”会返回一个非零的TCP引用句柄出来,用户可以通过这个引用实现TCP网络数据的下发和读取。为了人为控制下发的指令和参数,可以利用case条件结构将下行数据的发送代码框起来,如图111-56所示。

为了让上位机PC(服务器端)能够控制下位机FPGA(客户端)里面的采样率和信号采集的启动和停止,我们将上位机前面板上的U32类型的“采样率(20ns)”控件通过“强制类型转换”函数变成字节数组;再把前面板上的布尔型“开始/停止采集”按钮转成U8类型的0或者1;然后再把预留的一个参数“size_read_I32”控件通过“强制类型转换”变成字节数组;最后将这3个转换后的数据进行数组拼接(实际上就是字节数组),赋给“写入TCP数据.vi”,如图111-57所示。这样就可以将上位机服务器端的数据通过TCP下发给FPGA客户端。

8)下面,我们再来看看上位机PC服务器端是如何把FPGA采集上传的数据通过TCP函数读取出来的。为了提高TCP读取效率或者为了方便定长数据的解析,我们可以给“TCP读取数据.vi”函数设置一个长度阈值,只有当上位机PC端的TCP缓冲区里面接收到指定长度的字节数之后,再把这些字节数组一次性读取出来。因为TCP传输的数据都是以字节为单位,所以如果用户希望读取的数据位宽是字节的倍数,比如后续16位或者24位ADC网络数采卡实验,下位机FPGA上传的数据是U16类型的ADC数据,正好就是2个字节,那么在判断之前,我们需要将前面板读取点数×2,再做比较判断,这样读取出来的数据点数才能被正确解析恢复出来。
LabVIEW里面自带的“TCP读取数据.vi”这个函数非常强大,它提供了4种内在读取模式,分别是Standard、Buffered、CRLF、Immediate这4种模式:

这里很多用户经常搞不清楚这些模式的含义以及用法,这个属于基本功问题了。下面我们简单讲解一下这4种模式。
1)Standard:标准模式,也是默认模式,因为这个“TCP读取数据.vi”拖拽到LabVIEW程序框图里面之后,这个模式参数默认是不显示的,如果用户不去手动创建的话,那么默认就是第一个“标准模式”。这种模式下,如果TCP缓冲区里面接收到的字节数量少于指定读取的长度,那么该VI就是有多少读取多少,并且产生超时错误,虽然产生了超时错误,但是读出来的数据都是有效的,并不妨碍数据本身,只是我们可以通过是否超时来判断读取的数据长度是否达到我们指定的长度。这种模式内部存在阻塞机制,直到发生超时退出为止。
2)Buffered:缓冲模式,这种模式相比于标准模式,它的优势更明显,那就是当缓冲区里面的字节数据没有达到指定读取的长度,那么这个VI读出来的数据是无效的,TCP数据依然很好的保留在TCP缓冲区里面,通过超时错误告知用户,当前返回的数据可以直接忽略。
3)CRLF:回车换行模式,这个模式带有一定的协议分段功能在里面,特别适合不定长数据的读取和解析,比如下位机每次发送的TCP数据有效内容长度不固定,那么可以在TCP发送每包有效数据的尾巴加上CR(回车)LF(换行)结束符,这样,TCP对等端只需要将读取长度设置为一个超级大的数,该函数内部自己轮询TCP缓冲区收到的字节数据,一旦发现有CRLF结束符,立刻输出当前有效内容,可以看出,这个模式非常适合不定长数据和自定义协议解析等应用。
4)Immediate:立即模式,这个模式是所有模式里面读取效率最高的,如果用户将超时设置为0的话,那么实际效果就是,每次执行这个TCP读取VI的话,缓冲区有多少字节数据,就会立刻读取出来,不管有没有超时产生,读出来的数据都是有效的,这个模式配上LabVIEW里面自带的队列函数,可以快速构建出来一个“生产者-消费者”模式,将TCP报文通过队列存放到计算机内存中。
总结:如果想要实现上面指定长度字节数据的读取、解析和信号波形恢复,应该选择Buffered缓冲模式最佳,这样如果下位机FPGA采集的是一个周期性信号,那么只要上位机读取的点数能够整除波形周期点数,那么波形图里面就不会存在左右滑动跳动的不稳定效果了,因为我们需要观察一个相对稳定的周期信号,类似示波器周期信号触发功能一样。当然了,如果想要测试TCP传输的极限吞吐率,应该选择Immediate立即模式,同时还要关闭所有影响CPU效率的代码,比如波形图显示。
基于以上分析,编写的上位机服务器端TCP读取程序框图,如图111-58所示。

9)一般情况下,如果下位机FPGA发送的TCP数据吞吐率很高,那么上位机读取的频率和每次读取的字节长度(size_read_I32)要满足一定的范围才能保证所有的数据不会丢失,如果每次读取的字节长度很短,那么需要读取的速度就要很快,这样不仅会极大的消耗CPU的资源,还有可能会造成数据溢出丢失;关于这个数值取多少合适,用户可以根据实际情况做实验决定。
10)“TCP读取数据.vi”函数读取返回的数据类型是字节数组,如果从FPGA里面接收到的数据物理意义并不是字节类型,那我们还需要借助“强制类型转换”函数将原始的字节数组转换真实的数据,本节实验刚好凑巧,下位机FPGA发送的斜坡信号数据类型也是U8,程序如图111-59所示;在后续很多实验里面,我们会通过“强制类型转换vi”将原始字节数组转成实际指定的物理数据,比如16位8通道AD7606以太网TCP采集实验。

11)当斜坡信号读到之后,我们可以在前面板上放置一个波形图显示控件,将FPGA客户端发送上来的数据以波形的方式呈现出来,但是考虑到实际应用中,如果波形图控件里面每次显示的点数过大的话,会导致电脑CPU、内存和显卡压力过大,CPU运行速度会降低,这样不利于TCP快速读取数据,因此,我们在TCP-读线程里面加了两个控制按钮,可以人为控制读取出来的波形是否进行拼接以及是否开启波形显示功能,如图111-60所示。关于这几个功能的演示,我们在后续的实验演示环节里面再给用户做详细的介绍。

12)当用户需要将这个程序停止运行的时候,可以通过TCP发送停止指令给下位机FPGA,让FPGA内部的TCP Client发送线程进入case为假的分支,也就是停止给上位机服务器发送数据了,同时下位机FPGA里面的“FIFO_TCP_Send_U8”也会自动清空,这样下次再运行这个上位机程序的时候,采集的又是当前最新的数据了;最后关闭上位机TCP引用句柄,防止出现内存泄漏,如图111-61所示。

注意:细心的用户发现了我们的“TCP-读线程”while循环里面有一个禁用结构,里面是ms倍数延时,如图111-62。这个延时的作用是什么呢?这个ms倍数延时函数主要是用来控制我们的while循环读取频率,可以释放CPU资源。假设,当FPGA采样率很低的时候,也就意味着上位机PC端接收到的数据吞吐率低,那么我们就没有必要让“TCP-读线程”跑的太快,这样会极大消耗CPU资源,如果主程序里面还有其他的事情要处理,那么CPU会显得吃力;如果下位机FPGA采样率很高的时候,为了避免TCP缓冲区溢出,我们需要以最快的速度将里面的数据全部读取出来,这时我们可以给ms倍数延时函数赋值0或者直接将这延时函数禁用掉,这样“TCP-读线程”这个while循环就会全速运行。

13)最后,再来介绍一下这个上位机PC TCP服务器端通信程序前面板上的控件有哪些功能和注意事项,完整的上位机程序前面板,如图111-63所示。

上位机程序运行之后,一旦侦听到下位机FPGA客户端发来的连接请求后,成功建立连接之后,前面板上的“Running_W”和“Running_R”两个显示控制里面的数值就会快速递增;然后用户就可以通过“采样率(20ns)”控件里面的数值参数控制下位机FPGA里面的采样率,比如当我们设置为50,换算一下也就是50*20ns=1000ns=1us<=>1MS/s采样率,换算成字节为单位的采样率的话,也就是1MByte/s,这个采样率算是很低的了;所以我们可以将前面板上的“开始/停止采集”和“显示波形?”两个按钮全部点亮,前者是用来通知FPGA启动采集发送数据,后者是把上位机接收到的波形数据在波形图控件中显示出来;然后可以在“size_read_I32”控件里面输入一个点数,比如128000,也就是128K个U8(因为本节实验下位机FPGA里面模拟的是U8的斜坡信号采集)点;最后,当我们点击一下“Send”按钮之后,上位机PC就会把指令和参数通过TCP下发给FPGA,然后就能看到波形图里面立刻出现一个长度为128K个点的斜坡信号。整个过程可以很好地反应下位机FPGA客户端采集数据发送到PC服务器端的整个动态展示,更多现象我们在后续的实验演示环节再给用户讲解。
需要注意的是:如果用户提高了FPGA采样率,比如,把“采样率(20ns)”这个参数设置为1,也就是20ns,相当于50MS/s,换算成字节单位就是50MB/s,对于我们封装的ComBlock TCP IP核来说,可以完全胜任;不过对于性能很差的电脑,建议用户最好把前面板上的“显示波形?”熄灭关掉,这样可以节省CPU资源,加快LabVIEW应用程序读取速度,减少上位机的TCP缓冲区溢出造成的数据丢失。
7、实验操作
7.1、准备工作
1)接好硬件设备。利用一根CAT5+或者CAT6类网线,一头接到黑金AX7103开发板第2路网口里面(ETH2),另外一头接到电脑的网口里面;要想实现FPGA VI在线下载和在线前面板交互式运行,必须使用Xilinx JTAG下载器(不能使用任何第三方Digilent HS系列下载器,比如黑金的红色下载器本质上就是高仿的Digilent HS1下载器),将JTAG线缆一头插到黑金AX7103开发板上的JTAG下载口,USB一头接到电脑上(注意:如果用户使用的是不带RJ45网口的超薄笔记本电脑的话,可以在网上买一个雷电转千兆网口扩展坞,相当于给笔记本扩展了一个RJ45千兆网口,这样上位机PC(服务器)和下位机FPGA(客户端)之间的TCP网络通信实验就可以在一台笔记本上完成了,更方便);此时,笔记本的设备管理器里面识别出来一个Xilinx JTAG下载器设备,如图111-64所示。关于Xilinx下载器驱动安装方法在前面,我们已经给用户介绍过了,这里不再赘述。实际AX7103 FPGA开发板跟电脑之间的接线实物图,可参考图111-65所示。


7.2、手动下载bit文件(传统手动下载方式,效率低,不推荐,跳过,直接看7.3节)
7.3、自动下载bit文件(支持在线前面板交互式运行和调试,效率高,更方便,推荐)
接下来,就是见证奇迹的时刻了,大家屏住呼吸哈
硬件接好之后,给FPGA开发板上电,然后再点击一下编译过的“实验111-TCP客户端-Slope-U8-可变采样率-异步模拟ADC需要大缓存-FPGA.vi”FPGA VI前面板左上角的运行箭头,此时,LabVIEW会在后台自动将编译好的bit文件下载到FPGA芯片里面,几秒之后,奇迹发生了,可以看到就像我们开发的LabVIEW STM32工具包那样,LabVIEW前面板成功地进入了FPGA在线交互式运行模式,如图111-85所示。

几秒之后,可以看到上位机“控制面板\网络和Internet\网络连接”里面的网卡由断开状态变成了连接状态,如图111-86所示。说明,上位机识别到了下位机FPGA网络。

7.4、观察现象(运行上位机TCP服务器网络通信测试程序)
1)首先,需要根据下位机FPGA Client VI里面设置的远程服务器端IP地址和端口,把对等端上位机的网络地址也做相应的设置,如图111-87所示。这里,上位机服务器端IP是192.168.1.10,端口是1024.


2)为了提高TCP的极限传输吞吐率,建议将上位机网卡里面的“传输缓冲区”和“接收缓冲区”深度改到最大值2048,如图111-88所示。

3)为了测试下位机FPGA Client客户端与上位机服务器端是否建立起网络通信,最简单的办法就是利用Windows系统自带的cmd命令窗口,利用ping指令来测试下位机FPGA,比如这里,我们可以输入ping 192.168.1.16,如图111-89所示。可以看出,下位机FPGA对于上位机的ping指令立马做出了回应。提醒:如果用户ping指令没有反应,也可以点击一下下位机FPGA VI前面板上的“reset”按钮对TCP IP核复位一下即可。

4)为了验证下位机FPGA Client客户端程序的功能是否符合前面的预期设计,需要一个对等端,比如本节实验需要一个上位机作为服务器端来与下位机FPGA进行通信测试。 为此,我们准备了3种上位机测试软件,分别是:网络调试助手、基于C生成的TCPTool.exe、基于LabVIEW编写的测试VI。其中,读取效率最低的就是网络调试助手,压根就跟不上我们下位机FPGA TCP发送数据的速度,因此,我们才单独用C写了一个只读取不做任何处理的exe小程序,最后是我们最强大的LabVIEW TCP上位机Demo程序,无论是执行效率还是灵活性还是可阅读性都是最强的。下面我们分别进行演示。
7.4.1、利用“网络调试助手”测试下位机FPGA Client网络通信
1)首先,我们利用大家最为熟悉的“网络调试助手”来测试一下下位机FPGA Client,大家都可以自行在网上搜索下载一下,也可以联系我们提供。双击打开安装好的“网络调试助手”,将“协议类型”选择为TCP Server,因为下位机FPGA跑的是Client客户端程序,所以上位机就是服务器端了;然后将本地IP地址设置为192.168.1.10,端口设置为1024;点击“连接”按钮,可以看到按钮点亮了,如图111-90所示。同时,下位机FPGA VI前面板上的“connect_status”指示灯点亮了,如图111-91所示,说明,上位机网络助手已经成功的侦听到了下位机FPGA发起的连接并且成功建立起网络通信了。并且,“data_req_en”数据请求指示灯也点亮了,说明下位机FPGA准备好了,随时可以将数据发送给服务器端。


2)由于网络调试助手下发16进制字符串比较麻烦不直观,但是好在我们的下位机FPGA VI支持在线前面板运行,因此,即使上位机不下发任何指令控制下位机FPGA VI,我们也能通过手动来控制下位机FPGA VI上的按钮发送数据给上位机。当然,再看完后续我们的LabVIEW上位机TCP Demo程序之后,也可以把LabVIEW强制类型转换出来的16进制字符串拷贝到网络调试助手里面下发,这个留给用户自己测试。
3)将下位机FPGA VI前面板上的分频系数“Count(20nSec)”设置为500,也就是下位机发送的数据吞吐率是50MB/500=100KB/s,然后按下“开始/停止”发送按钮,如图111-92所示。同时上位机“网络调试助手”里面快速收到了很多数据,为了提高调试助手的读取速度,勾选“暂停接收显示”,如图111-93所示。由于这个网络调试助手没有内置网络传输速度的计算功能,为此,我们可以打开Windows任务管理器,找到以太网通信页面,如图111-94,可以看出,当前条件下的网络上行和下行吞吐率,如果是win7系统的话,只能看到一个总的网络使用率,win10系统单独给出了接收和发送速率。


提醒:如果不关闭接收显示的话,“网络调试助手”一会就会卡死崩溃,这是因为接收的数据太多了,缓冲区快爆了,同时也会影响网络助手的运行速度。

虽然,我们下位机FPGA(客户端)只发送数据给上位机(服务器),但是图111-94里面的发送速度(下发)依然有16Kbps,也就是2KB/s,这是因为TCP属于握手类通信协议,客户端发送任何数据,服务器端都需要Ack响应,否则客户端底层会自动重发TCP数据包。这一点与UDP不一样,UDP是单方面通信,不存在Ack数据包。
上位机任务管理器的以太网接收速度是840Kbps,换算成Byte字节就是840Kbps/8=105KB/s,很接近FPGA实际发送数据的100KB/s,为啥还会多出来5KB/s,这是因为TCP除了我们的100KB/s斜坡数据,还存在包头、MAC地址、IP地址、端口和CRC校验位等额外的参数,因此,网卡实际每秒接收到的数据一定会大于TCP数据包里面的有效数据吞吐率。
4)接下来,减小下位机FPGA VI前面板上的分频系数“Count(20nSec)”,将其设置为50,等效提高下位机FPGA发送数据吞吐率(50MB/500=1MB/s),保持“开始/停止”发送按钮常亮状态,如图111-95所示;网络调试助手右下角的“接收”字节数也在快速飙升,如图111-96;同时,Windows任务管理器的接收速度变成了8.4Mbps,相当于提高了10倍(1.05MB/s),如图111-97所示。



5)最后,我们将下位机FPGA VI前面板上的分频系数“Count(20nSec)”进一步降低为2,也就是等效的FPGA Client客户端发送TCP有效数据速度为50MBps/2=25MB/s,如图111-98所示;此时,可以看到下位机FPGA VI前面板上的“溢出点数”显示控件里面的数字不断累加,说明下位机FPGA模拟ADC采集的异步FIFO出现了溢出,数据丢失了,同时“data_req_en”指示灯也由常亮变成了闪烁,这是因为对等端上位机PC服务器(网络调试助手)读取速度跟不上下位机FPGA发送数据的速度了,理论上只要对等端读取速度足够快,TCP是可以达到极限传输带宽的(800Mbps)。虽然下位机FPGA数据溢出了,但是上位机Windows任务管理器里面显示的接收速度是209Mbps,等效为26.125MB/s,如图111-99所示。


结论:可以看出,“网络调试助手”目前能够读取下位机FPGA发送TCP数据的最大速度不能超过25MB/s,因此,通过网络调试助手是无法测试我们的FPGA TCP Client极限吞吐率的。但是不用担心,下面两种方式可以帮助我们测试下位机FPGA TCP传输速度。
7.4.2、利用“TCPTool.exe”测试下位机FPGA Client网络通信
1)为了解决网络调试助手的读取效率问题,我们专门用C语言编写了一个只读不做任何处理的TCP专用工具.exe,感兴趣的用户可以联系我们索取。双击打开桌面上的“TCPTool.exe”,将本地IP设置为192.168.1.10,本地端口设置为1024,点击“开启”按钮,如图111-100所示;此时,下位机FPGA Client VI前面板上的两个指示灯点亮了,说明下位机FPGA成功的连上了PC服务器,如图111-101所示。


2)低速下的TCP传输测试,这里我们就不测试了,直接测试TCP高吞吐率。将下位机FPGA VI前面板上的分频系数“Count(20nSec)”设置为2,等效的FPGA发送速度是25MB/s,按下“开始/停止”按钮,如图111-102所示;此时,TCPTool.exe软件界面里面显示的接收速度为24MB/s左右,如图111-103所示,低于下位机FPGA发送的TCP速度,这个不影响,这个软件本身对测速这块相对粗糙一些;更为准确的TCP接收数据带宽可以参考Windows任务管理器,如图111-104所示,根据千兆网卡1Gbps和网络使用率21.19%计算出当前的TCP接收速度为1Gbps×21.19/100=211.9Mbps=26.48MB/s,符合我们的预期。



3)接下来,直接将下位机FPGA VI前面板上的分频系数“Count(20nSec)”设置为1,也就是本节实验最大的FPGA发送速度50MB/s,“开始/停止”按钮保持按下常亮状态,如图111-105所示;即使FPGA处在50MB/s发送速度下,“溢出点数”显示控件里面的数字依然是0,同时“data_req_en”指示灯常亮,始终高电平状态,说明上位机PC服务器端读取的速度足够快,下位机FPGA的TCP Client FIFO才没有发生溢出;同时,上位机TCPTool.exe软件里面显示的接收速度为48.30MB/s,如图111-106所示;Windows任务管理器里面显示的网络使用率为41.96%,如图111-107所示,换算一下就是1Gbps×41.96/100=419.6Mbps=52.45MB/s,说明我们封装的TCP Client IP核非常成功,50MB/s的TCP网络通信轻松拿下。



提醒:一定要把上位机影响CPU和网络性能的软件全部关闭,特别是todesk、teamview、向日葵等。本节实验下位机FPGA我们设计的定时循环最快是50MHz,没有体现出我们实际封装的TCP FPGA Client IP核的极限传输带宽,下一节实验112,我们会单独利用125MHz时钟域编写一个125MB/s极限传输程序来验证我们封装的FPGA TCP极限传输速度(108MB/s),尽请期待!
7.4.3、利用“LabVIEW TCP Demo”测试下位机FPGA Client网络通信
1)打开运行上位机TCP服务器端网络测试程序,位于FPGA项目浏览器“我的电脑”下面的“实验111-TCP服务器端-通信程序-Slope-U8-PC.vi”,如图111-108所示。

2)运行上位机程序之后,可以看到下位机FPGA Client VI前面板上的两个指示灯控件点亮了,说明上位机PC服务器端侦听到了下位机FPGA发起的连接,并成功建立起了TCP网络通信,如图111-109所示;同时,上位机VI前面板上的“Running_W”和“Running_R”显示控件里面的数字快速增加,说明上位机TCP读线程和写线程运行起来了。

3)在上位机前面板上的控件“采样率(20nSec)”里面输入5,相当于模拟ADC采集的等效采样率是5×20ns=100ns<=>10MS/s<=>80Mbps;按下“波形显示?”按钮,这样后面就能在波形图里面看到每次读取的斜坡信号了;再在输入控件“size_read_I32”里面输入每次从PC端TCP网络缓冲区里面需要读取的数据长度(以带阻塞的Buffered方式读取),由于前面我们设置的采样率不高,所以这里输入的读取长度相对自由一些,比如我们设置128000,也就是128K个点,单位是U8,换算成字节的话,就是每次读取128K个Byte;最后再按下点亮“开始/停止采集”这个按钮。
当一切设置就绪后,点击一下前面板上的“Send”发送按钮(这是一个触发型的控件),每点击一次,上位机(服务器)都会把前面板上设置的这些参数转换成字节数组通过TCP写通道发送给下位机FPGA(客户端),当FPGA接收完成并解析出来指令和参数后,会立刻把采集到的斜坡信号通过TCP Client上行通道源源不断的发送给上位机PC服务器。
实际观察到的情况是:每隔12.8ms,上位机TCP网络缓冲区会清空一次,同时,波形图里面的曲线会刷新一次,这是因为下位机FPGA的采样率是10MB/s,上位机LabVIEW程序每次批量读取128KB数据,所以刷新周期就是12.8ms。
此时,上位机PC服务器端的LabVIEW TCP程序前面板上的波形图里面出现了锯齿波(周期性的斜坡信号),如图111-110所示;如果下位机FPGA TCP Client VI程序没有发生溢出丢点的话,上位机接收到的斜坡信号就是一个稳定的从0开始的信号,如果因为上位机抖动或者截图操作会阻塞CPU导致下位机FPGA溢出,那么上位机还原的锯齿波波形会发生偏移。

实际上,当下位机FPGA接收到上位机通过TCP下发的指令和参数后,也可以在下位机FPGA Client VI前面板上看到这些控件参数自动发生更新了,并且下位机FPGA VI前面板上的“溢出点数”始终为0,说明原始的ADC FIFO没有数据溢出,也就不存在丢点,非常好,如图111-111所示;同时,Windows任务管理器里面显示的网络使用率是8.52%,如图111-112所示,换算一下就是1Gbps×8.52/100=85.2Mbps=10.65MB/s,符合预期。


注意:如果用户按下上位机VI前面板上的“拼接”按钮,可以将TCP读取的所有波形进行收尾拼接相连,但是如果让这个LabVIEW上位机程序一直运行着,过一会会弹出一个错误提示:系统内存不足,请释放内存空间。这是因为我们在程序框图里面开启了波形首尾拼接功能,随着时间的延长,程序框图里面的数组长度和波形里面的数据量越来越大,一旦超越了LabVIEW软件本身的承受能力,就会导致LabVIEW报警甚至卡死崩溃。所以,一般情况下,我们都是人为控制一下程序框图里面的数组长度以及波形控件里的数据点数,不能太大,否则不仅会造成计算机卡顿甚至造成软件崩溃;或者利用“生产者-消费者”模式将从网络缓冲区里面读取到的TCP数据转移到队列里面,本质上相当于从网卡转移到计算机内存里面,因为计算机内存一般都很大,而计算机网络缓冲区则很小,因此这种方式可以实现数据流盘和无损回放。
4)下面我们来提高一下FPGA采样率,看看会有什么情况发生:首先,将输入控件“采样率(20nSec)”里面的5改成2,相当于等效的采样率提高了2.5倍:2×20ns = 40ns <=> 25MS/s <=> 200Mbps(位宽是8位U8);按下“波形显示?”按钮,这样后面就能在波形图里面看到每次读取的斜坡信号了,熄灭“拼接?”按钮;其他参数保存不变,比如,输入控件“size_read_I32”里面还是输入128000,也就是128K个点,单位是U8,换算成字节的话,就是每次读取128K个Byte;“开始/停止采集”这个按钮要一直点亮。
设置完成后,再点击一下前面板上的“Send”发送按钮(这是一个触发型的控件),每点击一次,上位机都会把前面板上设置的这些参数转换成字节数组通过TCP写通道发送给下位机FPGA,当FPGA接收完成并解析出来指令和参数后,会立刻把采集到的斜坡信号通过TCP Client上行通道源源不断的发送给上位机PC服务器端。
此时,下位机FPGA Client VI前面板上的“Count(20nSec)”分频系数变成了2,如图111-113所示,说明上位机下发的TCP参数和指令成功了,并且“溢出点数”始终为0,说明下位机FPGA TCP上行通道即使在25MB/s传输速度下,依然稳健,没有发生数据丢失。

同时,上位机PC服务器端程序前面板上的波形图里面出现了从0开始的周期性斜坡信号,如图111-114所示,这是因为下位机FPGA Client VI只要没有发生溢出,并且上位机读取的长度始终是下位机斜坡信号的整数倍,波形图里面的波形就不会出现乱跳或者左右滑动效果。

通过打开Windows任务管理器,可以看到当前时刻的以太网传输速度,如图111-115所示,其中网络使用率为21.12%,换算一下就是1Gbps×21.12/100=211.2Mbps=26.4MB/s,算上TCP包头、MAC、IP地址和CRC等信息,大于有效数据的25MB/s完全合理。

5)最后,我们直接一步到位将FPGA采样率提升到本节实验最大值50MS/s,看看会有什么情况发生:先将输入控件“采样率(10ns)”里面的2改成1,等效采样率:1×20ns = 20ns <=> 50MS/s <=>400Mbps(位宽是8位U8);其他保持不变,比如,“波形显示?”按钮处于点亮状态,这样后面就能在波形图里面看到每次读取的斜坡信号了;“拼接?”按钮依然设置为熄灭状态,防止内存溢出;输入控件“size_read_I32”里面的256000,也就是256K个点保持不变,单位是U8,换算成字节的话,还是每次读取256K个Byte;“开始/停止采集”这个按钮要一直点亮。
设置完成后,再点击一下前面板上的“Send”发送按钮(这是一个触发型的控件),每点击一次,上位机都会把前面板上设置的这些参数转换成字节数组通过TCP写通道发送给下位机FPGA,当FPGA接收完成并解析出来指令和参数后,会立刻更新前面板上的控件值,如图111-116所示,可喜的是,“溢出点数”依然是0,说明下位机FPGA模拟的ADC采集信号FIFO没有溢出丢点;同时会将采集到的斜坡信号通过TCP上行通道发送给上位机PC服务器端,此时,上位机前面板中的波形图里面显示出来连续多个周期性斜坡信号,并且起点也是从0开始的,如图111-117所示。并且,Windows任务管理器里面的网络使用率也变成了42%左右,换算一下就是1Gbps×42/100=420Mbps=52.5MB/s,如图111-118所示。



有些用户的反馈,TCP传输带宽还远没有达到我们预期的最大105MB/s时,就出现了数据丢失呢?这是因为上位机LabVIEW应用程序“TCP-读线程”循环运行太慢了(虽然读线程里面的延时已经禁用了),导致了上位机没有及时从TCP网络缓冲区里面把数据取走导致的。
那么下面我们就要把影响这个“TCP-读线程”读取速度的原因找出来,通过仔细分析发现:程序框图里面有两个因素会导致while循环变慢,一是“强制类型转换”函数,二是前面板上的波形图显示功能。于是,我们先把前面板上的“显示波形?”按钮关闭,如果数据依然丢失,再把“强制类型转换”函数也禁用掉,因为这个强制类型转换时间过长也会导致TCP读取变慢,在转换过程中,该VI函数也会占据大量的CPU电脑资源。
于是,我们先点击一下前面板上的“停止”按钮,把上位机程序停下来,然后切换到程序框图,直接将程序框图里面的“强制类型转换”和“波形显示”代码全部禁用掉,如图111-119所示。然后据此才能真正测到我们的TCP极限传输吞吐率。

实际上,无论是在Xillybus官网上还是NI官网上,所有测试任何总线(USB、千兆以太网、PCIe等)极限传输带宽吞吐率时,都需要排除一切干扰因素,否则无法真正测到极限传输速度。
7.5、实验分析(至关重要)
1)如果某些用户的电脑性能一般,可以将上位机PC程序前面板上的“波形显示”功能关闭掉,这样可以进一步提高上位机的TCP读取效率,以保证下位机FPGA Client的模拟的ADC数据不会溢出丢失。
2)重要结论:只要上位机TCP读线程足够快,就不会发生下位机FPGA FIFO溢出,所有采集的数据都可以通过TCP网络传输到计算机内存里面来;在后续的实验112极限测试中发现:当FPGA采样率设置为100MB/s,也就是800Mbps,TCP传输都是正常的,因为我们封装的FPGA TCP IP核底层协议支持的吞吐率最大可以到1Gbps千兆模式。
3)有些细心的用户发现了,如果上位机每次读取长度不是256的整数倍时,上位机波形图里面的锯齿波就会出现左右滑动,而不是一个稳定状态,这个跟示波器原理类似,因为下位机FPGA里面模拟的斜坡信号周期点数是256,上位机要想稳定的显示出来,每次从TCP网络缓冲区里面读取的点数就需要满足整除。
4)一定要把上位机影响CPU和网络性能的软件全部关闭,特别是todesk、teamview、向日葵等。本节实验下位机FPGA我们设计的定时循环最快是50MHz,没有体现出我们封装的TCP FPGA Client IP核的真实极限传输带宽,下一节实验112,我们会单独利用125MHz时钟域编写一个125MB/s极限传输程序来验证我们封装的FPGA TCP极限传输速度,尽请期待!
8、手动固化FPGA VI程序(传统手动固化方式,效率低,不推荐,跳过,直接看第9节)
9、自动固化FPGA VI程序(右击VI直接自动下载固化,效率高,更方便,推荐)
当我们借助LabVIEW FPGA强大的在线交互式运行前面板调试完FPGA VI程序后,就可以将这个FPGA VI进行批量部署了,也就是把编译出来的FPGA原始bit文件转成mcs或者bin文件固化到FPGA开发板上的Flash芯片里面。
前面第8节我们向大家介绍了可以通过Vivado手动将FPGA bit文件固化到Flash芯片里面,但是那个方法过于复杂,操作起来非常不方便!感兴趣的用户可以自行了解一下即可,无须深入学习第8节,除非是一种特殊的情况,那就是你设计的FPGA产品卖给了你的用户,当你的FPGA bit文件升级了,你把最新的bit文件发给远在其他城市的客户,需要将这个bit文件重新固化到你的FPGA板子里面去,但是你的客户又不懂LabVIEW,此时,你可以教他如何利用Vivado进行手动下载和固化。
为了让我们的LabVIEW My FPGA工具包,跟NI FPGA硬件开发有着相同的用户体验,我们将整个FPGA bit文件到mcs/bin文件格式的转换、下载器速度的设置以及各种Flash芯片的选择,全部在后台自动完成,极大的方便了用户的使用!
需要注意的是:有些厂家做的FPGA板子为了降低硬件成本,实际焊接的Flash芯片跟其提供的说明书有时候对不上,导致固化总是不成功,此时,保险起见,最好用眼睛实际观察一下FPGA板子上Flash芯片的具体型号和厂家,这种情况时有发生!经验之谈、少走弯路!
下面进入实战操作:将“实验111-TCP客户端-Slope-U8-可变采样率-异步模拟ADC需要大缓存-FPGA.vi”FPGA VI固化到黑金FPGA开发板的Flash芯片里面。
1)首先根据黑金提供的FPGA开发板原理图找到上面的Flash芯片具体型号,比如黑金AX7103开发板上的Flash芯片都是N25Q128,如图111-130所示。可以看出,这款Flash容量是128Mbit,换成大B就是16MByte。

2)然后找到黑金FPGA开发板AX7103对应的终端模板文件所在的路径,如图111-131所示。到这个文件夹里面找到xdc约束文件,打开xdc文件,发现里面定义了这句话:set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design],如图111-132所示,那就意味着FPGA VI编译生成的bit文件内部将读写位宽设置的是x4模式。也就是当FPGA上电从Flash里面可以通过SPI x4模式并行读取,提高了bit文件加载和启动速度。
通常情况下,只要FPGA板子上的Flash芯片引脚支持x4的话,建议大家在xdc约束文件加上set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]这句话,这样后续FPGA程序加载启动速度更快,体验更好!
3)知道了上面的信息,我们就可以在LabVIEW FPGA VI固化页面的下拉列表里面选择对应的类型即可。右击该终端下编译过的“实验111-TCP客户端-Slope-U8-可变采样率-异步模拟ADC需要大缓存-FPGA.vi”FPGA VI名称,选择菜单里面的“Download Bit to Flash”,如图111-133所示;然后在弹出来的页面里面,Flash芯片型号选择N25Q128;容量设置为16MByte;SPI数据读写位宽选择x4模式,如图111-134所示。


4)最后,点击页面“Download”下载按钮,出现一个bit文件下载进度条,如图111-135所示;大约等待几十秒,会弹出Flash烧写成功提示框,如图111-136所示。然后将黑金FPGA开发板重新上电,就能看到先前我们编写的“实验111-TCP客户端-Slope-U8-可变采样率-异步模拟ADC需要大缓存-FPGA.vi”FPGA VI程序成功的被加载和运行了。
10、总结
本节实验内容写的非常细致,难度不大,因为涉及到很多下位机FPGA TCP Client客户端和上位机PC服务器端通信的知识点和内容,用户需要细心学习研究。在学习研究本节实验之前,有些用户是跳着看的,建议大家最好先把本章前面的TCP基础内容看一遍,否则有些概念都不清楚的话,是很难真正掌握LabVIEW FPGA下的TCP网络通信开发的。
本节实验重点需要理解和掌握TCP Client网络通信过程、原理;学会我们封装的下位机FPGA TCP Client Socket CLIP里面的读写通道的调用及其注意事项;学习模拟斜坡信号采集的用户线程编写;掌握控制FPGA采样率的编程方法;学会接收并解析上位机下发的指令和参数;熟练使用FIFO四线握手制编程方式;最后就是学习和掌握LabVIEW上位机软件自带的TCP函数选板里面的所有函数(VI)的功能和注意事项,做到熟练使用。
虽然本节实验我们只是模拟了一个正数8位位宽(U8)的斜坡信号采集,并且在后续的实验当中,我们还会继续教用户如何模拟一个带有正负号的Sine信号采集;等到这些实验全部学习完成后,我们再把本书前面真实的AD采集模块和摄像头模块对应的采集线程跟本章的TCP通信融合到一起,实现一个真正的基于FPGA的TCP以太网数据采集卡或者Gige图像采集卡。