LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)

LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第1张图片
要想开发出一个完整的基于PCIe通信的FPGA板卡,需要经历以下3个步骤才能算是完成。

首先,用户需要根据实际情况,编写FPGA芯片里面的逻辑代码,比如做一个基于PCIe传输的DAQ采集卡,那么用户需要在FPGA里面利用LabVIEW编写一个ADC采集程序,然后将采集到的数据通过FIFO转移到我们封装出来的PCIe CLIP对应的上行通道里面,或者将上位机下发的数据从FPGA FIFO里面读取出来。这部分代码开发,我们称之为下位机FPGA程序编写,采用LabVIEW进行开发。关于下位机LabVIEW FPGA程序开发,也是贯彻本书的重点,前面每个实验基本上都着重讲解了LabVIEW开发FPGA下位机程序这部分。

接下来,当我们利用LabVIEW编写好FPGA程序(里面包含PCIe CLIP),编译成bit文件,下载到FPGA芯片里面运行后,我们还需要通过重启电脑来识别这个PCIe设备,如果是bit文件,必须热重启,不能通过关机重启,如果是bin文件已经固化到FPGA板子上的Flash芯片里面,可以热重启也可以冷重启。重启之后,如果是第一次接入,系统会将插入到机箱里面的FPGA硬件,在设备管理器里面识别成黄色感叹号,此时用户需要安装一下Xillybus公司提供的PCIe驱动inf文件,这个文件是经过微软数字签名的,不像Xilinx官方给的那个没有经过数字签名的inf那样,必须让系统在测试模式下才能用,所以说,Xillybus提供的驱动inf文件更方便大家使用。当这个inf驱动加载成功后,用户就可以尽情地在上位机享用FPGA PCIe通信了。

最后,我们还需要编写一个上位机应用程序来跟下位机FPGA进行通信,为了方便用户,我们封装了一个8上8下的PCIe DMA DLL,这样对于那些不会LabVIEW的用户也可以使用C\C++\C#\Python等文本语言进行调用;如果用户会用LabVIEW开发上位机应用程序,我们参照NI FPGA板卡的工作方式,重新封装了一个LabVIEW PCIe_DMA.lvlib库,用户直接拖拽就可以了,非常方便。

至此,我们才把一个基于PCIe的FPGA项目或者产品做完了,然后就可以将FPGA板卡、FPGA程序框图、中间层驱动文件和上位机应用程序交付给最终用户。其中,中间层驱动文件inf由Xillybus提供好了,中间层DLL我们神电测控也封装好了,用户只需要会LabVIEW开发FPGA芯片里面的代码以及上位机PC端的程序就可以了,非常简单省事。

下面,我们分别对这3个步骤进行详细的讲解。
7.3.1、下位机FPGA PCIe固件程序开发(bit文件,用LabVIEW FPGA进行开发)
当我们做一个FPGA PCIe硬件时,首先需要准备好一个支持PCIe通信的FPGA板子,比如黑金的AX7103开发板,上面的ARTIX7 FPGA芯片本身就支持PCIe 2.0 X4通信。这块FPGA开发板也就是本书配套的实验平台,关于这个板子各个功能模块的讲解,前面第6章每个实验都详解验证过了。

当FPGA硬件准备好之后,接下来,我们就可以大刀阔斧的按照我们实际的项目或者产品需求,在FPGA芯片里面利用LabVIEW进行编程了。这里用LabVIEW编写的FPGA代码,我们称之为下位机FPGA程序开发。

比如,我们要在FPGA里面开发一个DAQ采集程序,我们可以直接将前面第6章里面的各种ADC采集线程拷贝到本节实验里面来,然后再配上PCIe DMA CLIP节点,就变成了一个带PCIe传输接口的DAQ设备了;如果将前面的DAC信号输出线程拷贝到本节实验里面来,配上PCIe DMA CLIP,就变成了一个任意信号信号发生器板卡了;如果把前面的双目摄像头图像采集框图拷贝到本节实验里面来,配上PCIe DMA CLIP,就变成了一个基于PCIe的图像采集卡了。

关于这个下位机FPGA里面的代码编写,相信用户在学习了本书的第5章和第6章之后,就不会陌生了,而且很多下位机FPGA端的代码都可以直接从前面第6章对应的实验里面进行拷贝,无需重头编写。

当下位机FPGA端的LabVIEW程序框图写完之后,点击运行可以生成对应的FPGA bit文件和bin文件,我们把这个文件称之为下位机可执行文件或者固件程序。当然,如果用户对VHDL或者Verilog十分精通的话,也可以用文本语言编程,但是我们推荐大家用LabVIEW开发,效率更高。

这里我们直接通过一个示意图来表达一下下位机FPGA里面的代码编写过程,如图7-9所示。其中,生成中间VHDL代码和编译成bit文件都是在后台自动完成的,即使用户完全不懂FPGA也没有用过Xilinx任何IDE软件,只要会LabVIEW就可以搞定一切!
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第2张图片
图7-9:LabVIEW下位机FPGA开发过程
备注:详细的LabVIEW FPGA CLIP代码讲解,可以参考后面7.4节的相关内容。

7.3.2、中间层PCIe驱动程序安装(inf文件,Xillybus官方提供)
当我们把开发好的LabVIEW程序框图编译出来的FPGA bit下载到FPGA芯片里面运行之后,点击windows系统开始菜单,选择重启(注意:不能选择关机)。系统重启之后,对于第一次接入到PC或者工控机机箱里面的PCIe板卡来说,在设备管理器里面会出现一个PCI设备黄色感叹号,如图7-10所示。有些系统在重启的时候,右下角还会弹出一个提示框,提醒用户,电脑上有个硬件驱动软件没有安装成功,如图7-11所示。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第3张图片
图7-10:设备管理器出现未知PCI设备黄色感叹号(没有安装Xillybus驱动之前)
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第4张图片
图7-11:重启之后,系统右下角还会提示器件驱动没有成功安装

接下来,我们需要安装一下Xillybus公司提供的PCIe inf驱动文件,右击设备管理器里面的黄色感叹号,选择浏览,如图7-12所示。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第5张图片
图7-12:右击设备管理器,选择浏览计算机上的inf驱动文件

在弹出来的路径选择对话框里面,点击右侧的“Browse”浏览按钮,然后找到本地计算机上的inf驱动文件,这个驱动文件用户可以在Xillybus公司官网上找到,也可以在本书配套的云盘里面下载到,如图7-13所示。选择浏览之后,加载的路径如图7-14所示。然后点击下面的“Next”下一步按钮,又会弹出一个windows安全警告对话框,我们需要勾选“Always trust”总是相信,再点击“Install”按钮进行安装,如图7-15所示。如果不出意外的话,会弹出安装成功提示框,如图7-16所示。最后在设备管理器里面会出现一个名为“Xillybus driver for generic FPGA interface”的PCIe板卡,如图7-17所示。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第6张图片
图7-13:找到Xillybus公司提供的FPGA PCIe驱动文件inf(官网下载或者云盘下载)
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第7张图片
图7-14:选择inf所在的文件夹路径
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第8张图片
图7-15:勾选Always trust再点击Install安装
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第9张图片
图7-16:Xillybus FPGA PCIe驱动安装成功对话框
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第10张图片
图7-17:安装成功后的设备管理器PCIe设备名称(Xillybus)

接下来,我们还需要检验一下系统是否正常加载了FPGA下位机里面添加的PCIe DMA通道号。比如我们在下位机FPGA里面封装了8上8下的PCIe DMA CLIP节点,那么上位机PC端必须要识别出来所有的通道号名称和具体路径,这样后续才能跟FPGA通信。
具体怎么操作呢?用户可以到微软官方网站上,下载一个小工具“WinObj.exe”,直接双击运行,它可以将我们电脑上所有正常安装的硬件,统统识别出来,比如我们平时常用的串口设备,如图7-18所示。而我们刚刚加载安装成功的Xillybus PCIe设备端口读写通道号也被枚举出来了,如图7-19所示。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第11张图片
图7-18:通过WinObj工具可以查看计算机所有的串口设备号
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第12张图片
图7-19:通过WinObj工具可以查看计算机枚举出来的Xillybus PCIe端口通道号

这些枚举出来的端口通道名称就是后续第三个步骤上位机应用程序需要访问的端口名称,比如“xillybus_write_32_ch0”,意为下行(Host–>FPGA)的写端口,位宽是32位,通道号是0。

那么问题来了,这里面8上8下的设备端口名称是不是固定的呢?还是人为可以修改的呢?实际上这些端口名称就是前面我们展示的下位机FPGA CLIP里面的名称,这些名称是在Xillybus公司官网的IP核Web配置页面里面提前写好的,如前面的图7-6所示。当我们在Web上生成PCIe IP核之后,加载到下位机LabVIEW FPGA CLIP里面之后,编译成bit文件下载到FPGA芯片里面运行之后,主机端系统就会将FPGA Memory里面存放的这些端口号名称通过中间层驱动全部识别枚举出来,然后我们通过WinObj这个工具就能将其全部观察到。一般情况下,我们建议用户使用Xillybus官方推荐的命名规则,这样不同的用户生成的FPGA PCIe驱动对应的端口号就会一目了然。

到这里,说明我们的FPGA下位机运行正常,并且PCIe驱动也已成功安装!

7.3.3、上位机PC端DMA FIFO应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)

当FPGA硬件被系统识别成功后,我们就可以编写一个上位机PC端的应用程序来与之通信,进行数据交互了。为了方便广大用户的使用,我们将8上8下共计16个通道的中间层DMA高速传输封装成了DLL动态链接库,这样对于使用不同编程语言(C\C++\C#\Python)开发上位机应用程序的用户来说,直接调用我们封装好的DLL驱动就可以了。这个DLL位于本书配套的云盘里面,如图7-20所示。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第13张图片
图7-20:我们给用户封装好的8上8下共计16个通道的DLL动态链接库

下面重点给用户讲解一下我们针对LabVIEW软件封装好的一个lvlib库文件,对于熟悉LabVIEW编程的工程师来说,直接将lvlib里面的VI拖拽到自己的程序里面就可以完成跟下位机FPGA之间的PCIe通信了,一目了然。图7-21显示的是我们封装好的上位机LabVIEW My FPGA PCIe Toolkit软件工具包项目浏览器截图。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第14张图片
图7-21:封装出来的FPGA PCIe DMA上位机LabVIEW项目库

里面主要包含两大块,分别是PC端的FPGA PCIe上位机范例程序(Example-PC虚拟文件夹)和中间层的lvlib库文件。其中PC端的范例程序在后续的PCIe基础和高级实验里面再给大家做详细的讲解;本节先着重介绍一下我们封装的lvlib库函数(VI)。
首先,在参照了NI FPGA 板卡的DMA传输工作流程之后,我们特地有针对性的封装,将一个上行(FPGA–>Host)DMA传输过程分成7个步骤,分别对应7个子VI,下行则不需要DMA机制:
 PCIe DMA通道初始化并开辟指定深度的缓冲区(PC_FIFO_DMA_Poly_Init_DLW30.vi)
 PCIe DMA通道启动传输(PC_FIFO_DMA_Poly_Start_DLW30.vi)
 PCIe DMA通道缓冲区存在的字节数数量(PC_FIFO_Poly_Buffer_Length_DLW30.vi)
 PCIe DMA通道缓冲区读取指定长度的数据(PC_FIFO_DMA_Poly_Read_DLW30.vi)
 PCIe DMA通道停止传输(PC_FIFO_DMA_Poly_Stop_DLW30.vi)
 PCIe DMA通道等待退出(PC_FIFO_DMA_Poly_Exit_Wait_DLW30.vi)
 PCIe DMA通道销毁(PC_FIFO_DMA_Poly_Destroy_DLW30.vi)
**注意:**所有调用DLL的子VI都要设置成“任意线程”,切不可设置成“用户线程”,如图7-22所示。否则一旦DLL里面的函数出现读写阻塞之后,会导致LabVIEW界面无响应,卡死状态,更没有办法进行探针调试,切记!
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第15张图片
图7-22:LabVIEW调用底层有阻塞函数的DLL时,一定要设置成“任意线程运行”

下面我们对这7个多态子VI分别进行讲解,之所以设计成多态VI,是为了方便用户编程的时候,可以直接选择切换通道。
1)PC_FIFO_DMA_Poly_Init_DLW30.vi
这个子VI可以用来完成对指定的FPGA PCIe通道进行初始化,同时还可以按照用户输入的数值向计算机申请开辟一个指定深度(长度或者大小,单位字节)的缓冲区,如图7-22所示。图中所示可以这样理解:针对32位位宽的PCIe DMA ch0(通道0)向计算机(主机)申请开辟一个500M字节的缓冲区,缓冲区首地址或者说指针放在第3个DMA引用当中,如果申请的缓冲区成功,那么对应输出的指示灯会点亮。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第16张图片
图7-23:PCIe DMA上位机PC端通道初始化VI函数

注意:初始化函数的缓冲区深度设置,跟后面的(PC_FIFO_DMA_Poly_Read_DLW30.vi)读取长度之间需要满足一定的关系,否则会出现读取丢点的情况。原因是我们开辟的缓冲区(假设FIFO深度为M),实际上是一个异步的环形FIFO,FPGA端会把数据源源不断的往这个FIFO里面写,然后应用层,比如LabVIEW会从这个FIFO里面取数据,至于什么时候读,读多少?取决于这个FIFO里面现有的数据长度,一般的上位机读取机制是:当上位机判断到这个FIFO已经有了至少N个点时,我们就调用这个函数(PC_FIFO_DMA_Poly_Read_DLW30.vi)把N个点读取出来,余下的数据还会留在FIFO里面,由于开辟的是环形FIFO,所以读取位置会不断的从开头到末尾扫描,因此,在跨越边界长度的时候,会出现读取截断或者丢点的现象。结论是M要能整除N才行。

举例说明:比如我们开辟了一个500MByte深度的缓冲区,然后每次读取2048个Byte点,实际上500M/2048无法整除,这样就会导致在读取最后一帧的时候,读出来的数据有问题,为了避免这个问题,我们有两种解决方法:如果读取的长度事先确定了,那么我们在申请开辟缓冲区长度的时候,将读取长度乘上一个整数就可以了,比如开辟2048×100000=204.8MByte;如果读取长度是变化的,那么需要满足整除这个条件,比如我们开辟的环形FIFO大小是500M,那么读取长度可以选择5、10、100、1000之类的,不能选择1024或者3之类的。上面讲解的机制和原理,用户一定要记在心里!!!

小心:在后续的PCIe+OV5640摄像头例程里面,可以看到,720p RGB565格式下的每帧图像字节是1280×720×2=1843200Byte,如果用户想要每次读取一幅图像刷新显示,那么在调用初始化函数(PC_FIFO_DMA_Poly_Init_DLW30.vi)开辟缓冲区长度的时候,一定要设置为1843200Byte的整数倍,比如184.32MByte或者368.64MByte。

2)PC_FIFO_DMA_Poly_Start_DLW30.vi
这个多态子VI的作用是用来控制上位机PC端的PCIe DMA FIFO通道是否接收下位机FPGA发送的数据,相当于启动了DMA传输,一旦调用了这个VI,那么只要下位机FPGA有数据在发送,通过任务管理器就能看到我们前面申请开辟的缓冲区里面就会有源源不断的数据进来,如果下位机FPGA不发送上行数据,那么这个DMA通道也会一直处于等待接收状态,相当于线程阻塞了。这个子VI不需要输入参数,如图7-24所示。通过显示控件“DMA启动传输成功?”指示灯状态,可以判断PCIe DMA通道是否成功开启传输模式,由于我们支持8个DMA通道同时并行传输数据,所以前面我们初始化了哪些DMA通道,这里我们就需要利用“PC_FIFO_DMA_Poly_Start_DLW30.vi”函数来开启这些通道的数据传输使能。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第17张图片
图7-24:PCIe DMA上位机PC端启动通道数据传输VI函数

3)PC_FIFO_Poly_Buffer_Length_DLW30.vi
这个子VI的功能跟我们平时常用的一个VISA属性节点“串口缓冲区字节数”功能类似,就是用户可以通过轮询的方式,查看当前PC端DMA通道对应的缓冲区里面存在多少字节的数据,比如,当字节数量大于10KBytes的时候,我们再调用读取VI将这些数据从FIFO缓冲区里面取走,这样可以提高读取效率,同时也能降低读取频率,释放CPU给其他线程使用。通过调用这个子VI可以实时观察到缓冲区里面的数据增加或者减少,对于不定长传输也会起到一定的作用。这个子VI返回的长度单位是Byte,通常会跟FIFO Read函数一起使用。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第18张图片
图7-25:查询PCIe DMA上位机PC端缓冲区里面存在的字节数量

4)PC_FIFO_DMA_Poly_Read_DLW30.vi
当我们通过前面的“PC_FIFO_Poly_Buffer_Length_DLW30.vi”函数查询到缓冲区里面已经有了我们需要的数据时,接下来就可以利用这个“PC_FIFO_DMA_Poly_Read_DLW30.vi”函数从FIFO里面把数据读取出来,给到我们的应用程序进行处理、显示或者流盘保存等操作了。如图7-26所示,这个VI需要用户给定读取数据的长度值,单位是Byte字节,指定读取的长度不能超过缓冲区已有的数据长度;其次,从缓冲区里面读取出来的数据都是最原始的字节数组或者说是字符串,用户可以根据实际情况,利用数值选板里面的“强制类型转换”函数将原始的字节数组转成需要的数据类型,比如下位机FPGA发送的是有符号32位类型的波形数据,那么我们可以参考图7-26所示的那样,将真实的数据转换出来,如果用户在界面上给定读取的是数据是波形点数,那么Size_Read应该×4比较好,这样转换后的点数就跟实际的点数对应上了。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第19张图片
图7-26:从上位机PCIe DMA通道FIFO缓冲区里面读取指定长度的字节数据

注意:初始化函数的缓冲区深度设置,跟后面的(PC_FIFO_DMA_Poly_Read_DLW30.vi)读取长度之间需要满足一定的关系,否则会出现读取丢点的情况。原因是我们开辟的缓冲区(假设FIFO深度为M),实际上是一个异步的环形FIFO,FPGA端会把数据源源不断的往这个FIFO里面写,然后应用层,比如LabVIEW会从这个FIFO里面取数据,至于什么时候读,读多少?取决于这个FIFO里面现有的数据长度,一般的上位机读取机制是:当上位机判断到这个FIFO已经有了至少N个点时,我们就调用这个函数(PC_FIFO_DMA_Poly_Read_DLW30.vi)把N个点读取出来,余下的数据还会留在FIFO里面,由于开辟的是环形FIFO,所以读取位置会不断的从开头到末尾扫描,因此,在跨越边界长度的时候,会出现读取截断或者丢点的现象。结论是M要能整除N才行。

举例说明:比如我们开辟了一个500MByte深度的缓冲区,然后每次读取2048个Byte点,实际上500M/2048无法整除,这样就会导致在读取最后一帧的时候,读出来的数据有问题,为了避免这个问题,我们有两种解决方法:如果读取的长度事先确定了,那么我们在申请开辟缓冲区长度的时候,将读取长度乘上一个整数就可以了,比如开辟2048×100000=204.8MByte;如果读取长度是变化的,那么需要满足整除这个条件,比如我们开辟的环形FIFO大小是500M,那么读取长度可以选择5、10、100、1000之类的,不能选择1024或者3之类的。上面讲解的机制和原理,用户一定要记在心里!!!

小心:在后续的PCIe+OV5640摄像头例程里面,可以看到,720p RGB565格式下的每帧图像字节是1280×720×2=1843200Byte,如果用户想要每次读取一幅图像刷新显示,那么在调用初始化函数(PC_FIFO_DMA_Poly_Init_DLW30.vi)开辟缓冲区长度的时候,一定要设置为1843200Byte的整数倍,比如184.32MByte或者368.64MByte。

5)PC_FIFO_DMA_Poly_Stop_DLW30.vi
当我们不需要PCIe DMA通道或者退出应用程序之前,用户可以调用这里的“PC_FIFO_DMA_Poly_Stop_DLW30.vi”函数来关停上位机PC端的DMA通道的数据接收功能,此时,即使下位机FPGA还在发送数据,上位机PC里面的FIFO缓冲区也不会接收任何数据,处于关闭状态。这个VI跟前面的第2个函数(PC_FIFO_DMA_Poly_Start_DLW30.vi)其实是一对功能互反的VI。用户调用的时候,只需要选择想要关闭的DMA通道就可以了,如图7-27所示。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第20张图片
图7-27:关闭PCIe DMA通道的数据传输

6)PC_FIFO_DMA_Poly_Exit_Wait_DLW30.vi
虽然前面我们把上位机PC端的DMA缓冲区接收功能关闭了,但是中间层的驱动里面,还有一些人为开辟的线程没有关掉,我们特地给用户封装了一个等待线程退出函数“PC_FIFO_DMA_Poly_Exit_Wait_DLW30.vi”,通过这个函数可以确保中间层的DLL里面所有线程都退出了,防止出现线程阻塞卡死等状态。这个VI的调用非常简单,用户只需要选择想要退出的DMA通道就可以了,如图7-28所示。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第21张图片
图7-28:等待中间层的DLL里面的PCIe DMA通道退出

7)PC_FIFO_DMA_Poly_Destroy_DLW30.vi
当DLL里面的DMA Read线程退出之后,最后我们还需要调用一下这里的“PC_FIFO_DMA_Poly_Destroy_DLW30.vi”函数将DLL里面申请开辟的FIFO缓冲区句柄、读取和状态轮训线程的引用以及对应的句柄全部销毁掉,防止出现内存泄露导致系统崩溃。这个VI跟前面的第一个函数“PC_FIFO_DMA_Poly_Init_DLW30.vi”功能是反的,前面那个函数在DLL里面负责申请开辟资源,创建线程等等,这里的VI则负责将其全部销毁,否则一旦发生内存泄露,后果不堪设想。这个VI的用法也很简单,用户只需要选择想要销毁的DMA通道资源就可以了,如图7-29所示。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第22张图片
图7-29:销毁中间层DLL里面开辟的FIFO缓冲区指针、引用句柄和线程句柄
**注意:**最后这3个VI,一般是在退出应用程序之前,连在一起使用的,如图7-30所示。但是千万需要注意的是,在执行DMA停止、退出、销毁这3个VI的时候,下位机FPGA里面的PCIe DMA Write不能停发数据,也就是说,下位机FPGA要一直往PCIe DMA CLIP对应的通道里面写数据,否则会造成中间层DLL里面的DMA缓冲区Read读取线程处于-1,永不超时状态,也就是线程阻塞了,上位机应用程序会陷入卡死状态。因此,我们建议用户,等到DLL里面的PCIe DMA FIFO和读取线程销毁之后,上位机再发送“停止写入指令”给下位机FPGA,最后再关闭下行的PCIe通道资源。
在这里插入图片描述
图7-30:退出应用程序之前,依次调用FIFO停止、等待线程退出、销毁FIFO和引用句柄

介绍完上行的PCIe DMA Read这7个函数之后,下面来看看我们给大家封装的3个下行(PC–>FPGA)Write子VI,如图7-31所示。这3个函数就可以实现把上位机PC端的数据或者指令或者参数直接通PCIe总线下发传输写到FPGA芯片里面。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第23张图片
图7-31:lvlib库里面的3个下行PCIe通道写函数

 FPGA_FIFO_Write_Pipe_Init_DLW30.vi
 FPGA_FIFO_Write_Pipe_Send_DLW30.vi
 FPGA_FIFO_Write_Pipe_Close_DLW30.vi
下面我们逐一给大家讲解一下这3个完成PCIe下行通信的VI含义和用法。

1)FPGA_FIFO_Write_Pipe_Init_DLW30.vi
这个VI可以打开用户指定的PCIe下行(Host–>FPGA)通道并完成初始化工作,如图7-32所示。因为我们给用户封装了8个下行写通道(当然了,还有前面介绍的8个上行读通道),因此,调用这个VI的时候,需要根据实际情况选择一个合适的下行通道作为上位机PC发送数据给FPGA的Pipe管道,比如,我们发送的是一些指令形式的数据,或者数据量非常小的一些参数等等,那么可以选择低速的8位位宽的Channel4~7作为载体;如果我们需要快速下发一些诸如任意信号发生器的波形数据,那么可以选择吞吐率高的64位或者32位位宽的Channel0或者Channel1作为下行传输通道。如果下行写通道初始化成功,这个VI会返回一个唯一的句柄“fd_write”,后面所有跟写通道相关的函数VI都需要借助这个引用指针“fd_write”。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第24张图片
图7-32:PCIe下行写通道初始化VI

2)FPGA_FIFO_Write_Pipe_Send_DLW30.vi
当下行的写通道成功打开之后,我们就可以利用这里的“FPGA_FIFO_Write_Pipe_Send_DLW30.vi”函数将上位机PC端的数据下发给FPGA了,如图7-33所示。需要注意的地方是,这个通道写VI的数据输入类型是字节数组,所以,如果外部的波形信号或者参数或者指令等数据类型不是字节,那么我们需要通过数值选板里面的“强制类型转换”VI先转一下,图7-33中显示的是把上位机一个浮点型的正弦信号先通过I64转成有符号64位整形数组,然后通过“强制类型转换”变成U8字节数组给到我们的“FPGA_FIFO_Write_Pipe_Send_DLW30.vi”函数,写到FPGA芯片里面去。
LabVIEW FPGA PCIe开发讲解-7.3节:FPGA PCIe DMA总线通信开发过程(3个步骤)_第25张图片
图7-33:PCIe下行通道数据发送VI(Host–>FPGA)

3)FPGA_FIFO_Write_Pipe_Close_DLW30.vi
当我们需要退出上位机应用程序之前,需要利用这里的“FPGA_FIFO_Write_Pipe_Close_DLW30.vi”函数来把先前打开的PCIe写通道引用句柄指针关掉,如图7-34所示。防止出现内存泄露,更重要的是,如果不执行关闭的话,下次再打开这个通道的时候,会提示这个通道被占用了,所以务必要检查一下之前开启的通道句柄是否销毁了。
在这里插入图片描述
图7-34:关闭PCIe下行写通道引用句柄,释放通道占用

总结:用户在熟悉和掌握了上面介绍的这10个子函数(VI)的含义和用法之后,就可以大刀阔斧的开始编写PCIe上位机应用程序了。关于这部分内容,我们会在后面的7.6和7.7节实验部分,做详细的讲解。

你可能感兴趣的:(labview,fpga,pci-e,嵌入式,编程语言)