LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)

当FPGA硬件被系统识别成功后,我们就可以编写一个上位机PC端的应用程序来与之通信,比如用来监控下位机FPGA前面板上的控件值或者下发控制指令给FPGA了。为了方便广大用户的使用,我们将2上2下共计4个通道的中间层Memory读写通道传输也封装到前面给用户介绍过的那个DLL动态链接库里面了,这样对于使用不同编程语言(C\C++\C#\Python)开发上位机应用程序的用户来说,直接调用我们封装好的DLL驱动就可以了。这个DLL位于本书配套的云盘里面,如图7-74所示。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第1张图片
图7-74:我们给用户封装好的包含8对DMA FIFO和2对Memory通道的DLL动态链接库

注意:不管是win7还是win10系统,基本上都没有ucrtbased.dll和vcruntime140d.dll,因此,如果用户直接调用我们生成的“Xillybus_PC_for_LV_DLL_8Chs.dll”,win7系统会弹框提示你少了的库的名称,其实就是前面两个依赖库,但是win10系统不提示,直接报错,从这一点看,win10系统的用户体验做的太烂。因此,为了方便用户使用,我们直接将这两个系统依赖库跟我们的“Xillybus_PC_for_LV_DLL_8Chs.dll”放在同一个目录下,这样用户就不用操心了,无论通过什么软件去调用dll都能正常了!!!

下面再回顾一下前面我们针对LabVIEW软件封装好的一个lvlib库文件,对于熟悉LabVIEW编程的工程师来说,直接将lvlib里面的VI拖拽到自己的程序里面就可以完成跟下位机FPGA之间的PCIe通信了,一目了然。图7-75显示的是我们封装好的上位机LabVIEW My FPGA PCIe Toolkit软件工具包项目浏览器截图。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第2张图片
图7-75:封装出来的FPGA PCIe DMA和Memory上位机LabVIEW项目库

里面主要包含两大块,分别是PCIe DMA FIFO相关的多态VI和Memory通道读写相关的多态VI;DMA FIFO相关函数我们在前面7.3.3节已经做了详细介绍,本节先着重介绍一下我们封装的lvlib库里面的Memory相关函数(VI),这些函数位于这个虚拟文件夹“PC_Memory_RW_2Chs_Func”里面。

为了提高Memory读写的效率,我们特地有针对性的封装,将Memory上行读数据(FPGA–>Host)和下行写数据(Host–>FPGA)的传输过程分成3个步骤,分别对应6个子VI,也就是打开、读写、关闭,如图7-76所示。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第3张图片
图7-76:lvlib库里面的7个PCIe Memory通道读写函数

 PCIe Memory读通道初始化(FPGA_Memory_Read_Poly_Init_DLW30.vi)
 PCIe Memory读通道数据读取(FPGA_Memory_Read_Poly_Receive_DLW30.vi)
 PCIe DMA读通道关闭(FPGA_Memory_Read_Poly_Close_DLW30.vi)

 PCIe Memory写通道初始化(FPGA_Memory_Write_Poly_Init_DLW30.vi)
 PCIe Memory写通道数据下发(FPGA_Memory_Write_Poly_Send_DLW30.vi)
 PCIe DMA写通道关闭(FPGA_Memory_Write_Poly_Close_DLW30.vi)

再次强调:所有调用DLL的子VI都要设置成“任意线程”,切不可设置成“用户线程”,如图7-77所示。否则一旦DLL里面的函数出现读写阻塞之后,会导致LabVIEW界面无响应,卡死状态,更没有办法进行探针调试,切记! LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第4张图片
图7-77:LabVIEW调用底层有阻塞函数的DLL时,一定要设置成“任意线程运行”

下面我们对这6个多态子VI分别进行讲解,之所以设计成多态VI,是为了方便用户编程的时候,可以直接选择切换通道。下面我们逐一给大家讲解一下这6个PCIe Memory通道通信的VI含义和用法。

1)FPGA_Memory_Read_Poly_Init_DLW30.vi
这个子VI可以用来完成对指定的FPGA PCIe Memory读通道进行初始化,本质上就是打开Xillybus里面的Memory通道文件名,如果Memory读通道初始化成功,该VI会返回一个唯一的句柄“fd_read”,后面所有跟读通道相关的函数VI都需要借助这个引用指针“fd_read”,如图7-78所示。用户可以根据实际情况,在左侧的下拉列表“Pipe_Read_Name”里面选择16位或者32位位宽的Memory通道作为读取通道,其中,后缀mem0对应16位位宽,mem1对应32位位宽,也就是每次操作的数据是双字节还是4字节。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第5张图片
图7-78:PCIe Memory读通道初始化VI

2)FPGA_Memory_Read_Poly_Receive_DLW30.vi
当上行的Memory读取通道打开成功之后,我们就可以利用这里的“FPGA_Memory_Read_Poly_Receive_DLW30.vi”函数按照指定的地址把下位机FPGA里面的数组或者Memory元素读取出来了,如图7-79所示。该VI除了错误簇外,还有4个连接端口,最上面的左右两侧分别是Memory读通道打开后的引用句柄,另外就是地址输入控件“address_read_U16”和数据输出控件“data_read_U16”。就是用户可以将FPGA里面的数组或者Memory指定位置(地址)处的元素读取到上位机来。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第6张图片
图7-79:PCIe Memory读通道读取下位机FPGA Memory函数

这个VI本质上调用的是一个DLL函数,里面实际上包含了两个系统函数,分别是lseek文件位置偏移函数和read文件内容读取函数。如果用户不会LabVIEW的话,也可以使用其他语言,比如Python、C++、C#等直接调用系统里面的lseek和read函数就可以实现图7-79这个VI一样的功能和效果,因此我们开发的FPGA PCIe CLIP不限制操作系统,无论是Windows还是Linux都支持。

下面我们双击打开这个VI,看看程序框图里面有没有什么需要注意的地方,如图7-80所示。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第7张图片
图7-80:PCIe Memory读通道读取VI程序框图

为了让用户在执行读取操作的时候,地址是连续的,也就是地址0对应的U16或者U32数组的第1个元素,地址1对应第2个元素,地址2对应第3个元素,以此类推,而不需要考虑什么双字节或者4字节的倍数整除问题,为此,我们特地在这个VI里面加入了一个转换公式,如图7-81~7-82所示,其中,16位位宽的Memory地址,我们下发之前给FPGA的地址做了×2+1偏移;对于32位位宽的Memory地址做了×4+3偏移。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第8张图片
图7-81:16位的Memory地址偏移算法 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第9张图片
图7-82:32位的Memory地址偏移算法

最后需要注意的一点是:PCIe Memory跟FIFO一样,对超过8位位宽的数据,存在大小端格式转换问题,在前面7.6节已经强调过了,为了减少FPGA资源的消耗,鉴于Memory读写速度很慢,我们特地将大小端格式转换代码放在了上位机,也就是本节介绍的Memory通道读写VI里面,如图7-83所示。将读出来的字节数组进行翻转,然后再利用“强制类型转换”函数变成U16或者U32就可以了。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第10张图片
图7-83:将Memory大小端格式转换放在上位机VI里面处理,下位机FPGA则不需要

3)FPGA_Memory_Read_Poly_Close_DLW30.vi
当我们需要退出上位机应用程序之前,需要利用这里的“FPGA_Memory_Read_Poly_Close_DLW30.vi”函数来把先前打开的PCIe Memory读通道引用句柄指针关掉,如图7-84所示。防止出现内存泄露,更重要的是,如果不执行关闭的话,下次再打开这个通道的时候,会提示这个通道被占用了,所以务必要检查一下之前开启的通道句柄是否销毁了。另外,为了保证Memory通道引用被强制关闭,我们在输入错误簇之前加入一个“清除错误簇”函数,防止上游的错误导致该VI不执行关闭功能。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第11张图片
图7-84:关闭PCIe Memory上行读通道引用句柄,释放通道占用

4)FPGA_Memory_Write_Poly_Init_DLW30.vi
这个子VI可以用来完成对指定的FPGA PCIe Memory写通道进行初始化,本质上就是打开Xillybus里面的Memory通道文件名,如果Memory写通道初始化成功,该VI会返回一个唯一的句柄“fd_write”,后面所有跟写通道相关的函数VI都需要借助这个引用指针“fd_write”,如图7-85所示。用户可以根据实际情况,在左侧的下拉列表“Pipe_Write_Name”里面选择16位或者32位位宽的Memory通道作为写入通道,其中,后缀mem0对应16位位宽,mem1对应32位位宽,也就是每次操作的数据是双字节还是4字节。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第12张图片
图7-85:PCIe Memory写通道初始化VI

5)FPGA_Memory_Write_Poly_Send_DLW30.vi
当下行的Memory写取通道打开成功之后,我们就可以利用这里的“FPGA_Memory_Write_Poly_Send_DLW30.vi”函数将上位机的地址和数据下发给下位机FPGA,按照索引值(地址)将数组或者Memory里面的元素覆盖更新,如图7-86所示。该VI除了错误簇外,还有4个连接端口,最上面的左右两侧分别是Memory写通道打开后的引用句柄,另外就是地址输入控件“address_write_U16”和数据输入控件“data_write_U16”。就是用户可以将上位机的数据按照给定的地址对FPGA里面的数组或者Memory指定位置(地址)处的元素进行覆盖更新。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第13张图片
图7-86:PCIe Memory写通道下位机FPGA Memory写入更新函数

这个VI本质上调用的是一个DLL函数,里面实际上包含了两个系统函数,分别是lseek文件位置偏移函数和write文件内容写入函数。如果用户不会LabVIEW的话,也可以使用其他语言,比如Python、C++、C#等直接调用系统里面的lseek和write函数就可以实现图7-86这个VI一样的功能和效果,因此我们开发的FPGA PCIe CLIP不限制操作系统,无论是Windows还是Linux都支持。

下面我们双击打开这个VI,看看程序框图里面有没有什么需要注意的地方,如图7-87所示。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第14张图片
图7-87:PCIe Memory写通道写入VI程序框图

为了让用户在执行写入操作的时候,地址是连续的,也就是地址0对应的U16或者U32数组的第1个元素,地址1对应第2个元素,地址2对应第3个元素,以此类推,而不需要考虑什么双字节或者4字节的倍数整除问题,为此,我们特地在这个VI里面加入了一个转换公式,如图7-88~7-89所示,其中,16位位宽的Memory地址,我们下发之前给FPGA的地址做了×2+1偏移;对于32位位宽的Memory地址做了×4+3偏移。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第15张图片
图7-88:16位的Memory地址偏移算法 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第16张图片
图7-89:32位的Memory地址偏移算法

最后需要注意的一点是:PCIe Memory跟FIFO一样,对超过8位位宽的数据,存在大小端格式转换问题,在前面7.6节已经强调过了,为了减少FPGA资源的消耗,鉴于Memory读写速度很慢,我们特地将大小端格式转换代码放在了上位机,也就是本节介绍的Memory通道读写VI里面,如图7-90所示。先利用“强制类型转换”函数将U16或者U32转换成字节数组,再利用“反转一维数组”函数进行反转就可以了。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第17张图片
图7-90:将Memory大小端格式转换放在上位机VI里面处理,下位机FPGA则不需要

6)FPGA_Memory_Write_Poly_Close_DLW30.vi
当我们需要退出上位机应用程序之前,需要利用这里的“FPGA_Memory_Write_Poly_Close_DLW30.vi”函数来把先前打开的PCIe Memory写通道引用句柄指针关掉,如图7-91所示。防止出现内存泄露,更重要的是,如果不执行关闭的话,下次再打开这个通道的时候,会提示这个通道被占用了,所以务必要检查一下之前开启的通道句柄是否销毁了。另外,为了保证Memory通道引用被强制关闭,可以在输入错误簇前面加一个“清除错误簇”函数,防止上游的错误导致该VI不执行关闭功能。 LabVIEW FPGA PCIe开发讲解-7.7节:上位机PC端Memory应用程序开发(LabVIEW/C调用DLL文件,神电提供lvlib库)_第18张图片
图7-91:关闭PCIe Memory下行写通道引用句柄,释放通道占用

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

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