发布时间:2024-05-15 13:01
1、实验内容
现在很多电脑PC或者工控机主板上面都集成了PCIe插座,可以直接插入PCIe板卡,优点是卡槽标准,插拔简单,传输速度极快。对于高速采集测试测量领域,PCIe用途非常广泛,最大极限带宽可以到6.6GB/s,这个速度可以直接用来做高速示波器卡、数字化仪、RF射频板卡和视频采集卡了。
本节实验我们准备采用黑金提供的AN706模块(AD7606),上面有1颗16位8通道高精度ADC芯片,结合PCIe总线实现8路并行采样率(最大200KS/s)可调节的PCIe数据采集卡,直接插到PC或者工控机或者工业树莓派上使用。
下面我们先来回顾一下PCIe接口和AN706硬件模块等相关内容。
黑金设计的ARTIX7-100T FPGA开发板(AX7103)上已经把PCIe接口引出来了,做成了X4接口模式,如图63-1所示。
图63-1:带PCIe X4接口的Aritx7 FPGA开发板(黑金AX7103)
用户可以直接将这个开发板插到工控机或者电脑主板上的PCIe母座里面,由于PCIe不支持热插拔,所以用户接入的时候,一定把机箱电源关掉,防止烧坏板卡;当然,如果用户闲PCIe插拔比较麻烦,还可以到淘宝上买一根PCIe延长线,将机箱里面的PCIe母座引出来,方便做实验。
由于我们已经把复杂的PCIe DMA通信协议封装成了LabVIEW FPGA下的Socket CLIP,用户只需要根据四线握手协议来调用对应的CLIP端口就可以了。 这部分我们在前面几节里面已经介绍过了。
本节PCIe DMA通信实验重点向大家讲解以下8个方面的内容:
关于PCIe DMA下位机(FPGA)通信应用程序开发,指的是FPGA芯片中的代码编写。譬如,本节实验我们准备开发一个基于PCIe 传输的高速采集卡,将ADC采集到的数据通过PCIe DMA实时发送到PC上,这段代码我们可以利用图形化的LabVIEW编程语言来开发,替代传统的VHDL/Verilog语言,这里我们称之为下位机LabVIEW FPGA编程。本节实验下位机FPGA程序里面的用户线程,我们会利用AD7606芯片(16位)采集外部真实的Sine正弦信号,然后发送到上位机进行显示或者流盘。
关于PCIe 硬件驱动加载与识别,可以参考前面7.3.2节里面的内容,不熟悉的用户一定要看一下,因为这个起到承上启下的作用。
关于PCIe DMA通信上位机(PC端)应用程序开发,指的是运行在windows或者linux系统中的LabVIEW上位机程序,用户可以通过LabVIEW调用我们封装好的FPGA PCIe lvlib库里面的多态VI与下位机FPGA进行PCIe DMA通信交互。我们把上位机收发线程分成两个独立的while循环来处理,这样调试起来方便一些。当然,如果有些用户不会LabVIEW,也可以使用C\C++\C#\Python来调用我们封装好的DLL驱动进行上位机程序开发。
关于FIFO的概念和操作,可以参考前面的“实验4-串口通信”,里面有详细的介绍和演示过程。
关于四线握手制的工作过程和原理,也可以参考前面的“实验4-串口通信”,这里不再赘述。
关于PCIe DMA通信IP核全双工通信机制,我们会通过两个独立的收发线程来演示。发送和接收可以同时并行执行,而且是8个上行和8个下行同时全双工工作。
最后一个需要注意的问题是,PCIe DMA下位机跟上位机之间的通信数据格式和数据类型,需要匹配上才能正确解析,因为Xillybus底层IP核走的是小端格式,而上位机LabVIEW里面默认是大端格式,因此,在前面7.4节里面,强调了FPGA里面数据格式转换的重要性;另外,模拟或者采集的AD波形,对应的数据类型跟PCIe内部的字节数组之间需要通过“强制类型转换”函数转换一下才能看到正确的波形曲线。
下面带着大家一起,利用LabVIEW编写符合标准四线握手制的PCIe DMA总线收发FPGA程序,来实现一个基于FPGA的PCIe 16位8通道数据采集卡(最大采样率200KS/s,采集电压范围±5V或者±10V),采集外部真实的Sine正弦信号,通过PCIe总线发送给上位机PC,同时还能接收上位机PC下发的指令和参数对FPGA板卡进行采样率和使能采集等控制。
2、硬件资源
2.1、先回顾一下PCIe总线接口方面的硬件知识
黑金设计的带PCIe接口的ARTIX7开发板,内部走线连接示意图如图63-2所示。可以看出,PCIe时钟走线是经过AC耦合的。一共使用了4对差分收发总线,所以最大位宽支持X4模式。
图63-2:Aritx7 FPGA开发板内部的PCIe走线连接示意图
打开本书配套的光盘/云盘里面1号文件夹里面的AX7103底板原理图,然后找到PCIe接口这部分原理图,如图63-3所示。
图63-3:打开PCIe底板原理图,找到PCIe总线互联的3大类引脚
这些引脚其实可以分成3类:PCIe总线复位引脚;PCIe总线差分时钟输入引脚;PCIe总线不同位宽的差分数据引脚。这3类引脚在前面图7-52里面都定义过了,如果用户自己画的板子或者网上买的其他家的板子引脚定义不一样,那么照葫芦画瓢对应修改一下就可以了。
然后,打开插在AX7103开发板上的核心板AC7100原理图,找到PCIe复位引脚,就是J20,如图63-4所示。接着需要寻找一下PCIe的差分时钟引脚,这个引脚非常重要,如果找不到或者设置不对,那么PCIe总线肯定初始化不了;在核心板原理图上可以看到有两个MGT_CLK信号,但是实际接到PCIe插槽的是MGT_CLK1,也就是F10,如图63-5所示。
图63-4:在AC7100核心板上找到PCIe复位引脚:J20
图63-5:在核心板AC7100原理图上找到PCIe差分时钟输入引脚:F10
但是细心的用户可能发现了,黑金的AC7100核心板在画板子布局布线的时候,将PCIe X4模式下的数据收发差分对引脚0和1弄反了,2和3是对的,如图63-6所示。这个细节导致无数用户付出了惨痛代价。很多用传统Verilog开发FPGA的用户,看原理图不仔细,或者说黑金太随意了,为了自己的布局方便,把0跟1故意调换了一下,那么在Vivado里面默认的PCIe引脚就不对了,必须要人为调整才行。
图63-6:仔细检查一下PCIe X4或者X8数据差分对顺序是否正确
为了方便我们广大LabVIEW FPGA用户开发,我们特地在顶层xdc里面将PCIe X4全部引脚定义拉出来,用户只要自己根据实际情况修改就行,而无需在网表里面定义,简化了编程的繁琐,也避免了一些不必要的错误。希望这一点能够引起开发者的注意!!!重要的事情说三遍,0跟1互换了,所以引脚定义也要变,如图63-7所示。
图63-7:PCIe数据差分对引脚要跟原理图保持一致
2.2、再回顾一下16位8通道高精度ADC芯片AD7606相关的硬件内容
本节实验需要用到的ADC采集模块是黑金设计的AN706高精度16位8通道AD模块,这款模块上面集成了一片AD7606芯片,并且预留了8个SMA焊接口,可以直接插到AX7103开发板右上角的扩展口J11上面,简单易用。如图63-8所示。
图63-8:AN706模块上的AD7606电路实物图
AD706模块的等效示意图,如图63-9所示。可以看出,AD7606芯片内置了2.5V的基准参考电压,只要给这个芯片提供5V和3.3V单电源供电就可以了。根据芯片RANGE引脚接法,可以直接采集±10V或者±5V真双极性输入信号;并且所有通道均可达到200KS/s的吞吐速率;输入钳位保护电路可以耐受最高达±16.5V的电压;无论采样率多少,AD7606的模拟输入阻抗均为1M欧姆。
图63-9:AD7606等效电路示意图
打开本书配套的光盘/云盘1号文件夹里面的AN706模块原理图,找到AD7606芯片电路接线原理图,如图63-10所示。可以看出,黑金AN706模块上的RANGE引脚是做接地处理了,所以芯片的输入电压范围就是固定的±5V,如果将来用户自己做板子,可以将RANGE引脚预留出来,由FPGA芯片来控制采集的电压范围,这样更灵活一些。
图63-10:AD7606电路原理图
黑金AN706这个模块正好有40pin的母座,可以直接插到AX7103开发板右上角的扩展口J11上面,如图63-11所示。
图63-11:AN706模块插到FPGA主板扩展口
通过查看AN706模块与AX7103扩展口之间的线序关系,可以看到1片AD7606基本上快把40pin占满了,如图63-12所示。然后在AX7103主板原理图上找到这些引脚序号,如图63-13所示。
图63-12:AN706模块上的AD7606占用的扩展口引脚序号
图63-13:FPGA开发板上的扩展口引脚序号图
3、FPGA PCIe DMA下位机ADC采集程序开发(AD7606)
下面,我们带着用户一起利用LabVIEW开发一个FPGA PCIe DMA下位机FPGA数据采集应用程序,这个程序可以下载到FPGA芯片里面运行,为了更加全面形象的展示PCIe总线通信的魅力,我们模拟一个PCIe多通道高精度数据采集卡,将8路16位AD芯片采集到的Sine正弦信号通过16位位宽的PCIe DMA上行通道ch2发送到上位机进行显示,上位机通过8位位宽的下行通道ch4下发不同的指令参数来控制FPGA开始采集、停止采集和调整采集频率。以此向大家展示FPGA与PC之间通过PCIe总线进行双向高速通信的过程。
提醒:之所以使用16位位宽的ch2作为ADC数据上行通道,而没有选择其他位宽的ch0/1/4/5/6/7,是因为我们提供的B版本PCIE Data CLIP模板里面,ch4/5/6/7通道的吞吐率比较低,而ch0(720MB/s)和ch1(50MB/s)又太高,有点浪费带宽;而AD7606芯片的最大采样率可以到200KS/s,I16换算成字节为单位,也就是200K×2=0.4MB/s,8个通道同时并行采集的话,就是3.2MB/s;对于ch2和ch3来说,我们默认设置的带宽是10MB/s,速度方面刚好适合,同时位宽也可以直接匹配,就不需要像前面两个实验那样进行复杂的拼凑了,这样可以极大简化我们的FPGA编程,同时只需要一个并转串线程将8个通道的I16数据直接转移到LabVIEW PCIe CLIP的ch2或者ch3里面就可以了,非常简单。如果今后用户碰到更高采样率的16位ADC,有两种方式可以解决传输问题:一是提高PCIe通道2或者3的传输带宽,二是可以采取前面两个实验的拼凑机制变成U64或者U32再通过通道0或者通道1发送出去。
3.1、LabVIEW FPGA项目创建
(1)启动LabVIEW 2015 SP1软件,用户可以点击左上角的“文件>>新建”或者直接点击“新建”下方的“项目”选项。这里我们也可以直接打开前面实验里面已经创建好的“My_FPGA_Starter_Board_Artix7_AX7103.lvproj”这个项目,如图63-14所示。
图63-14:打开前面创建过的LabVIEW项目
由于本章我们引入了PCIe总线通信,之前创建的FPGA终端里面没有添加PCIe Socket CLIP,所以,我们需要先右击“我的电脑”选择新建“终端和设备”,如图63-15所示;然后再在弹出来的FPGA终端选择列表里面,选择一个带PCIe X4接口8通道的ARTIX7-100T FPGA终端设备,如图63-16所示。其中,B版本的PCIe总线带宽要比A版本的高一倍,也就是800MB/s,本书我们选择B版本进行测试,A版本感兴趣的用户可以自行测试。点击“确定”按钮后,创建好的ARTIX7 PCIe FPGA终端设备如图63-17所示。
提醒:新建含有FPGA PCIe Socket CLIP的过程,在前面实验54里面已经创建过了,所以这里可以忽略,如果是用户自己新建的项目,那么参考上面的步骤走一遍就可以了。
图63-15:右击我的电脑选择新建“终端和设备”
图63-16:选择一个PCIe X4 8通道的B版本ARTIX7-100T FPGA终端
图63-17:创建成功后的ARTIX7 FPGA PCIe终端设备
(2)右击FPGA终端,选择“新建>>虚拟文件夹”,如图63-18所示。将添加到FPGA终端里面的虚拟文件夹,重命名为“实验63-PCIe DMA+16位ADC(模拟数据采集卡)”,如图63-19所示。
图63-18:右击FPGA终端,新建一个虚拟文件夹
图63-19:将虚拟文件夹重命名为“实验63-PCIe DMA+16位ADC(模拟数据采集卡)”
3.2、LabVIEW FPGA EIO节点与FIFO创建
1) 新建按键KEY和LED灯的EIO节点
(1)由于本节所有的PCIe通信实验里面都会用到LED指示灯和按键KEY等EIO资源,所以,我们可以在FPGA终端上面右击,选择“新建>>FPGA I/O”,如图63-20所示。然后在弹出来的I/O资源选择对话框里面,找到AX7103 FPGA开发板上的LED和KEY对应的管脚资源,如图63-21所示。单击对话框中间的“向右箭头”按钮,将这些引脚对应的EIO节点添加到右侧的FPGA资源列表里面,如图63-22所示。
提醒:实际上LED灯和按键KEY的EIO节点在前面实验54里面已经创建过了,所以这里可以忽略,如果是用户自己新建的项目,那么参考上面的步骤做一遍就可以了。
图63-20:新建FPGA I/O EIO节点
图63-21:找到AX7103开发板上LED指示灯和KEY按键对应的EIO节点
图63-22:将LED和KEY EIO节点全部添加到右侧的可用列表中
点击“确定”按钮后,这些EIO节点会被添加到FPGA终端项目里面,如图63-23所示。
图63-23:LED灯和KEY按键的EIO节点被添加到FPGA终端项目里面(在前面做实验54的时候已经添加过了)
2) 新建AD9280芯片对应的EIO节点
(1)右击“FPGA终端 3”,选择“新建>>FPGA I/O”,如图63-24所示。然后在弹出来的I/O资源选择对话框里面,找到AN706模块上的AD7606芯片对应到AX7103 FPGA开发板J11扩展口关联的管脚资源,如图63-25所示。单击对话框中间的“向右箭头”按钮,将这些引脚资源添加到右侧的FPGA资源列表里面,如图63-26所示。因为,Vivado编译器不支持同一个物理IO在xdc约束文件里面有多处定义,所以这里我们直接使用原始的扩展口IO名称。
图63-24:新建FPGA I/O EIO节点
图63-25:找到AD7606对应FPGA(XC7A100T)芯片上的引脚EIO节点
图63-26:将AD7606芯片的EIO节点添加到右侧的可用列表中
点击“确定”按钮后,这些EIO节点被添加到FPGA终端项目里面,如图63-27所示。
图63-27:AD7606芯片EIO节点被添加到FPGA终端项目里面
注意:细心的用户会发现,这些EIO节点实际上已经在前面2个实验里面添加过了,所以本节实验里面的图63-25实际上里面并没有这些EIO节点。如果是用户自己新建的一个FPGA终端项目,那么可以完整的添加。
3) 新建FIFO
我们可以将PCIE通信IP核看成一个独立的功能模块,类似MCU里面的子函数或者子VI,FPGA程序只需要关心如何将不同类型的数据(U64、U32、U16、U8)通过PCIe总线发送出去以及收到的数据怎么反馈给FPGA用户程序,这个中间的桥梁,可以采用FIFO缓冲区来实现。FIFO非常适合在不同的线程之间传递数据,而且设计合理的话,数据不会丢失或者被覆盖。
注意:FPGA用户线程相当于生产者,PCIe DMA发送线程相当于消费者,所以,二者之间有个速率匹配的问题,生产者发送数据的速度不能快于消费者。比如,本节实验里面,我们使用的是16位8通道的AD7606芯片,每通道最大采样率是200KS/s,因此,需要的通道带宽至少大于8×200KS×2=3.2MB/s的上行通道(默认情况下,能满足这个指标的有720MB/s的ch0、50MB的ch1、10MB的ch2和ch3这4个通道)才能把下位机FPGA采集的数据无损的传输给上位机PC,否则会造成数据丢失。由于ch2和ch3的位宽跟AD7606正好匹配,都是16位的,所以本节实验我们选择ch2作为AD7606采集数据的上行通道。
(1)右击FPGA终端项目,选择新建一个子的“虚拟文件夹”,如图63-28所示。然后重命名为“FIFO_ADC”,这个虚拟文件夹专门用来存放接下来我们创建的各种ADC芯片的FIFO缓冲区资源,如图63-29所示。
图63-28:新建一个子虚拟文件夹
图63-29:将虚拟文件夹重命名为“FIFO_ADC”(这个虚拟文件夹在前面实验61里面创建过了)
(2)右击子虚拟文件夹“FIFO_ADC”,选择“新建>>FIFO”,如图63-30所示。
提醒:考虑到后续我们需要将相邻的连续8个16位AD7606数据(I16)通过并转串依次转移到PCIe通道2里面往上位机传输,所以这里需要创建8个I16类型的AD7606临时缓冲区FIFO,分别命名为:FIFO_AD7606 I16 Ch1……FIFO_AD7606 I16 Ch8。
图63-30:新建PCIe FIFO资源
(3)接着会自动弹出FIFO属性设置对话框,如图63-31所示。下面我们逐项解释一下每个选项的含义。
首先是“名称”,这里可以随便输入一个能够体现这个FIFO功能的名称,比如我们要把FPGA从AD7606芯片里面采集到的第1个通道的有符号16位数据(I16)存放到这个FIFO,那么可以重命名为“FIFO_AD7606 I16 Ch1”。
其次是“类型”,这个下拉列表里面默认只有一个选项“终端范围”,意思就是我们创建的这个FIFO只能在这个FPGA芯片内部使用,不能用于其他终端,如果用户使用的是NI的RT+FPGA终端设备,那么这个地方会有更多的选择,这里不再赘述。
接着是“请求元素数量”,默认是1023,这个数值就是FIFO的深度,最大可以缓冲的数据长度,数值越大,编译消耗的FPGA资源就越多。很多客户以为这个数值设置越大越好,这样就不容易溢出了,实则不然,如果多线程之间的FIFO读写速度差别很大的话,再大的FIFO对于FPGA这种高速运行的器件来说,也是瞬间就会溢出或者覆盖丢点的。所以问题的关键不在于FIFO深度,而是要设计一个合理的读写匹配机制。这里我们可以将FIFO深度设置为128.
图63-31:FIFO属性对话框:名称、类型与深度
然后是“实现”方式,里面有3个选项“触发器”、“查找表”和“存储器块”,如图63-32所示。前两个就是FPGA最为宝贵的逻辑门资源了,一般不轻易使用,除非是用户已经将FPGA内部的存储器全部用完了,否则优先使用内部存储器块,这个存储器不占用FPGA内部的逻辑资源,不用也是浪费。
图63-32:FIFO属性对话框:实现方式
最后是“控制逻辑”,这个特别需要注意,默认选择的是“终端优化”,如图63-33所示。最下方有个提示框,里面有段文字“根据FIFO使用的时钟域和终端类型,应用程序将会通过逻辑片架构或内置控制逻辑实现FIFO,因此当使用“终端优化”控制逻辑时,元素的实际数量可能有所不同。”也就是说LabVIEW在生成VHDL代码的时候,真实生成的FIFO深度并没有达到我们前面实际设置的“请求元素长度”。如果用户忽略这个,可能会导致程序在运行的时候,出现一些跟预期目标不一致的情况。
图63-33:FIFO属性对话框:控制逻辑:终端优化
所以建议用户选择“逻辑片架构”,如图63-34所示。这时下面会提醒用户“元素数量已强制转换为FPGA可高效实现的值。”也就是说LabVIEW生成的FIFO深度会强制为我们申请的长度。
图63-34:FIFO属性对话框:控制逻辑:逻辑片架构
(4)接下来需要设置FIFO缓冲区单元的数据类型,在左侧的“类别”列表里面选择“数据类型”,然后在右侧的下拉列表里面选择16位有符号整形数据,如图63-35所示。因为我们从AD7606芯片里面采集回来的数据位宽是16位。
图63-35:FIFO属性对话框:数据类型:I16
(5)最后点击左侧的“接口”,切换到仲裁选项,如图63-36所示。将读取和写入的仲裁全部设置为“从不仲裁”,这样编译出来的代码执行速度也就是时钟约束可以提高不少,同时代码的健壮性和确定性也比仲裁来的稳。
图63-36:FIFO属性对话框:接口:从不仲裁
(6)全部设置完成后,点击“确定”按钮,即可创建出一个用来存放AD7606数据的缓冲区“FIFO_AD7606 I16 Ch1”,然后再以同样的方式再创建7个接收缓冲区“FIFO_AD7606 I16 Ch1”……“FIFO_AD7606 I16 Ch8”,如图63-37所示。
图63-37:创建8个AD7606临时FIFO缓冲区
(7)因为我们给用户封装的LabVIEW FPGA PCIe Socket CLIP里面,支持64位、32位、16位和8位数据通道,用户可以根据实际情况进行选择。比如,本节实验,我们需要将最大200KS/s采样率的AD7606原始数据无损的传输到上位机PC中,那么可以选择16位位宽的ch2或者ch3(默认10MB/s);这里我们选择ch2作为上行通道,这个在前面分析过。
因此,为了将AD7606的数据通过PCIe上行通道传输到上位机PC端,我们还需要创建一个16位位宽的FIFO缓冲区作为不同线程里面的过渡。用户可以参考前面FIFO的创建步骤单独新建一个,也可以直接利用我们前面创建好的“FIFO_PCIe_Write_U16”这个FIFO,位于虚拟文件夹“FIFO_PCIe”里面,如图63-38所示。
图63-38:创建1个U16类型的PCIe上行通道FIFO缓冲区(实验54里面已经新建过了)
提醒:虽然用户可以根据实际情况选择U16或者I16,本节实验我们的AD7606输出的是有符号I16,但是在利用PCIe Socket CLIP上行通道往PC端传输的时候,必须要用无符号数据类型才行,所以这里需要将PCIe上行通道的FIFO数据类型设置为U16,上位机接收到U8字节数组后利用强制类型转换成I16,再在波形显示控件中显示出来即可。
(8)最后,我们还需要新建一个可以接收上位机PC下发指令和参数的接收FIFO缓冲区。需要注意的是:因为上位机下发的指令和参数一般速度不会太快,所以我们可以用低速的Channel4~Channel7作为下行数据通道;另外,上位机发送的数据一般会经过“强制类型转换”VI变成字符串或者字节数组,这两种类型的数据表示法默认都是U8,所以,这里我们创建的FPGA接收PC下发的读取FIFO的数据类型也要设置为U8,因此,“FIFO_PCIe_Read_U8”的数据类型要设置为U8,如图63-39所示。其实,这个FIFO我们在前面实验54里面也已经创建过了。如果用户没有新建,那么参考前面的FIFO创建步骤自己创建一个就可以了。
图63-39:创建1个U8类型的PCIe下行通道FIFO缓冲区(实验54里面已经新建过了)
(9)开始编写程序之前,我们先通过创建虚拟文件夹的方式,将接下来新建的主VI等文件分类进行管理,便于维护和调试。这里用到的方法建议用户在开发自己的项目时,优先分类管理。首先右击虚拟文件夹“实验63-PCIe DMA+16位ADC(模拟数据采集卡)”,新建1个子虚拟文件夹(Main),如图63-40所示。这个虚拟文件夹用来存放主VI文件。
图63-40:创建1个虚拟文件夹分别用来存放1类文件-便于管理
3.3、下位机FPGA PCIe DAQ主VI编写过程(基于PCIe DMA CLIP + AD7606)
(1)右击“实验63-PCIe DMA+16位ADC(模拟数据采集卡)”里面的子虚拟文件夹“Main”,选择“新建>>VI”,如图63-41所示。然后,新建一个VI,另存为“实验63-PCIe_DMA_16位数据采集卡(AD7606)-FPGA.vi”,如图63-42所示。
图63-41:右击虚拟文件夹Main新建一个主VI
图63-42:新建一个下位机FPGA PCIe 16位数据采集卡程序VI
3.3.1、编写16位高速ADC数据采集程序(AD7606用户线程)
(1)因为AD7606芯片的最大采样率是200KSps,这里我们选择50MSps比较合适,正好可以被5整除,50MHz也是一般的FPGA开发板上的晶振时钟。所以我们需要提前创建1个50MHz的衍生时钟。
(2)右击项目浏览器里面的板载时钟“200 MHz Onboard Clock”,如图63-43所示。在弹出的衍生时钟属性配置页面里面,将所需衍生频率设置为50MHz,如图63-44所示。点击“确定”按钮后,展开板载时钟“200 MHz Onboard Clock”可以看到这个新建的时钟源,如图63-45所示。实际上,这个50M的衍生时钟我们在前面的实验里面创建过了。
图63-43:新建FPGA衍生时钟
图63-44:新建50MHz衍生时钟
图63-45:本节实验需要的50MHz衍生时钟
(3)打开主VI“实验63-PCIe_DMA_16位数据采集卡(AD7606)-FPGA.vi”,切换到程序框图,放置一个定时循环结构,然后双击打开这个定时循环的时钟源配置页面,展开“200 MHz Onboard Clock” 选择里面的50MHz作为时钟源,如图63-46所示。
图63-46:为定时循环配置时钟源
(4)接下来,我们直接把本书前面第16个实验里面的这个FPGA VI“实验16.4-16位8通道高精度数据采集-并行读取-千兆以太网-通道不会错乱.vi”里面的AD7606数据采集线程定时循环拷贝到本节程序里面,如图63-47所示。然后将里面的8个FIFO换成本节实验我们创建的8个AD7606临时FIFO缓冲区,名称只差一个CH和Ch大小写;再把10s计数延时代码删掉,只保留开关控制AD7606采集使能,将“Send”按钮名称改成“开始/停止”,更加形象一些,这个按钮可以由上位机PC通过PCIe下发指令控制AD7606是否采集数据;最后再把里面的LED4指示灯删掉,因为这个LED4后面我们会用在并转串线程里面,用来观察并转串线程是否发生了数据丢失。
图63-47:拷贝AD7606数据采集线程(替换FIFO、删除延时和LED灯代码)
(5)由于AD7606芯片本身可以实现7个档位的采样率控制,因此,我们不需要像前面两个实验那样,人为加计数分频代码,比如,我们直接控制“sample_rate”就可以实现不同的采样率;当然了,如果用户觉得AD7606芯片自身的几个固定档位的采样率不方便自己使用,用户可以自己在FPGA下位机里面加入计数分频或者在上位机进行抽点分频都行,这里,不再赘述了。
(6)为了能够调节AD7606的采样率,我们在下位机FPGA程序前面板上放置了一个“sample_rate”,如图63-48所示,虽然看起来是枚举控件,本质上是一个U8数值控件。假设,sample_rate设置为0,那么实际的采样率就是200KS/s,等效于的吞吐率是8×200KS×2=3.2MSps,如图63-48所示;如果设置为1,那么采样率就是100KS/s,一共7个档位;以此类推,用户可以提前在前面板上设置好sample_rate,但是需要保存为默认值之后再编译,或者由上位机PC通过PCIe下发参数来控制这个sample_rate参数,然后观察上位机PC端的波形图控件里面的信号周期点数是否发生符合预期的变化。
注意1:这种方式相对来说比较简单,不会影响其他时序,特别是有些芯片需要严格时序逻辑时,不得不采用定时循环,但是同时又希望自己能够掌控实际采样率,那么可以采用这种设计模式,两全其美。
图63-48:FPGA下的16位高速AD7606采集程序前面板
注意2:直接复制过来的AD7606采集线程提示有错误,LabVIEW运行箭头是断开的,这是因为里面有IP Block调用网表的节点存在,拷贝意味着挪动,所以我们需要双击这个IP节点,重新走一遍配置过程就可以了,如图63-49所示。
图63-49:将拷贝到本节实验里面的AD7606 IP Block节点重新配置一遍
3.3.2、编写并转串程序(8个I16依次转移到U16缓冲里面)
接下来,我们需要将AD7606读出来的8个通道FIFO里面的有符号I16数据先转换成无符号U16,再按照小端格式,转移到16位位宽的PCIe上行通信ch2发送FIFO里面。AD7606最大采样时钟频率是200KHz,那么为了能够快速实现8个通道的数据转移,并转串线程的时钟只要≥200KHz×8=1.6MHz,就不会发生数据溢出或者覆盖,这里我们选择跟AD7606采集线程同时钟域的50M即可。
下面,我们单独开辟一个并转串线程,首先将AD7606通道FIFO里面的数据转换成无符号整形数据,然后再利用“交换字节”函数将U16高低字节进行翻转,因为Xillybus PCIe IP核底层的数据格式属于小端格式,所以我们再发送AD7606数据之前,需要先转换一下,如图63-50~63-57所示。当PCIe写入FIFO没有溢出的情况下,“输入就绪”会拉高,将这个信号按照四线握手接到8个AD7606原始数据FIFO的“输出就绪”端口上,然后再根据“输出有效”高低来决定是否跳转到下一个case条件分支,这样就实现了将8个AD7606 FIFO里面的按照顺序转移到PCIe Write FIFO里面去了,上位机只需要把原始的U8字节数组转成I16数组后,再利用“数组选板”里面的“抽取一维数组”函数直接提取就可以了。
图63-50:并转串+取消符号特性+小端格式转换(第1帧)
图63-51:并转串+取消符号特性+小端格式转换(第2帧)
图63-52:并转串+取消符号特性+小端格式转换(第3帧)
图63-53:并转串+取消符号特性+小端格式转换(第4帧)
图63-54:并转串+取消符号特性+小端格式转换(第5帧)
图63-55:并转串+取消符号特性+小端格式转换(第6帧)
图63-56:并转串+取消符号特性+小端格式转换(第7帧)
图63-57:并转串+取消符号特性+小端格式转换(第8帧)
提醒:并转串这个架构在前面的多个实验都讲到了,不熟悉的用户需要加深一下理解,多动手自己练习一下就会了。
注意:由于我们需要将8个I16依次上传到上位机进行拼接解析,如果中间错了一个字节或者丢失一个I16,那么会导致上位机解析的所有数据都会出错。因为负责把FPGA采集的数据发送到上位机的是16位位宽的通道2,如果传输的时候出现非8个I16,顺序计数不对的话,就完蛋了,因为上位机下发给FPGA的停止条件是随机的,虽然在后续的PCIe发送线程里面已经无条件将ch2上行通道里面的I16进行读取清空了,但是AD7606并行读出来的8个I16双字节的FIFO并没有清空,所以,为了保险起见,万无一失,我们可以将图63-50~63-57里面的逻辑代码框起来,然后在条件为“假”的分支里面对8个临时FIFO进行读取清空,同时对状态机的计数器进行清零操作,如下图63-58所示。这样就能搞定因通道排序错点等问题造成的上位机解析问题。这个思想在后面的实验64里还会用到。
图63-58:将状态机逻辑代码框起来并进行读取清空操作
3.3.3、编写下位机FPGA PCIe Send程序框图(FPGA–>PC):上行
(1)为了降低FPGA PCIe程序开发的难度,我们提前将Xillybus公司提供的Verilog版本的PCIe DMA IP核,利用NI公司的Socket CLIP技术,将其封装到FPGA终端下面,只把需要进行握手的FIFO端口预留出来,如图63-59所示。这样做的好处在于,用户即使完全不懂PCIe总线,也能在FPGA里面调用CLIP进行PCIe代码编写。关于这部分内容,前面7.4节已经做过了详细的讲解,不记得的用户一定要把前面7.4节温习一遍。
图63-59:利用NI Socket CLIP技术把Xillybus PCIe DMA IP核封装到LabVIEW里面
(2)打开主VI“实验63-PCIe_DMA_16位数据采集卡(AD7606)-FPGA.vi”,切换到程序框图,放置一个定时循环结构,然后双击打开这个定时循环的时钟源配置页面,选择100MHz的PCIe CLIP时钟作为时钟源,如图63-60所示。这个100MHz同步时钟驱动的定时循环,是专门用来处理用户创建的FIFO跟PCIe IP核内部的FIFO之间进行数据交互的,支持标准四线握手串联。
图63-60:为定时循环配置PCIe 100MHz同步时钟源
(3)接下来,我们可以将FPGA终端下的“pcie_lv_clip_ip_to_gpio_led”节点拖拽到刚刚创建的定时循环里面,如图63-61所示。然后利用“数值至布尔数组”转换VI将FXP<+4,4>定点数转换成布尔数组,再利用数组索引将前3个状态赋给AX7103开发板上的前3个LED,如图63-62所示。这样我们就可以通过LED_1(心跳1Hz闪烁)来判断FPGA里面的PCIe IP核是否正常工作;LED_2状态来判断FPGA是否发送了上行数据;LED_3状态来判断FPGA是否接收到了PC下发的下行数据。最后一个LED_4指示灯留给并转串线程里面的16位发送FIFO是否溢出用的。
图63-61:将FPGA终端下的PCIe CLIP里面的LED灯状态信号拖拽到定时循环里面
图63-62:将PCIe CLIP里面的前3个状态输出信号拉出来驱动板子上的LED灯
(FPGA PCIe IP核工作状态显示程序框图)
(4)接下来,我们需要编写一下FPGA传递数据给PCIe IP核然后发送到上位机PC的代码。首先,我们把FPGA终端下的PCIE Data里面的通道2(16位位宽)的3个信号端口“pcie_lv_clip_fpga2host_in_16_tdata_ch2”、“pcie_lv_clip_fpga2host_out_16_tready_ch2”、“pcie_lv_clip_fpga2host_in_16_tvalid_ch2”,拖拽到程序框图里面来,如图63-63所示。这3个信号端口其实符合标准的四线握手。
图63-63:将FPGA终端下的PCIe CLIP上行通道2的3个信号端口拖拽到定时循环里面
(5)然后把我们前面创建好的16位位宽的用户FIFO(FIFO_PCIe_Write_U16)也拖到程序框图里面,然后根据四线握手的方式,将PCIe CLIP通道2的3个握手信号接到FIFO上面去,完整的PCIe数据上行发送程序框图,如图63-64所示。
图63-64:将FPGA终端下的PCIe CLIP上行通道2的3个握手信号连接到用户FIFO上(FPGA–>PC上行数据发送程序框图)
下面我们来讲解一下上面红色框框里面的程序框图含义:当PCIe IP核内部的FIFO没有满时,这个“pcie_lv_clip_fpga2host_out_16_tready_ch2”信号会拉高,也就是通知FPGA,可以将采集的数据发送给PCIe IP核内部的FIFO(这个FIFO默认是2K大小)。我们可以将这个信号接到FPGA里面用户创建的FIFO(FIFO_PCIe_Write_U16)的“输出就绪”端口,然后把“元素”和“输出有效”两个端口分别接到PCIe IP核的“pcie_lv_clip_fpga2host_in_16_tdata_ch2”和“pcie_lv_clip_fpga2host_in_16_tvalid_ch2”两个信号端口上;当用户FIFO里面有数据输出时,就会直接通过PCIe总线传输到上位机PC端了。
提醒:为了让上位机PC能够自由动态的控制下位机FPGA使能采集,我们加了一个布尔型控件“开始/停止”按钮,这个控件可以由上位机下发指令和参数进行控制,通过取反之后跟准备就绪“pcie_lv_clip_fpga2host_out_16_tready_ch2”信号“相或”之后接到用户FIFO上面,同时还直接接到case结构上面。这样设计的目的是:是为了让下位机FPGA能够自动把用户FIFO(FIFO_PCIe_Write_U16)残留剩余的数据读走等效于清空掉,比如上位机此时下发了一个停止采集指令,然后将布尔控件“开始/停止”设置为“假”,取反之后为“真”,接到了用户FIFO上面,如果此时用户FIFO里面依然残留了一些数据,直接就读出来,通过后面的case结构“假”分支过滤掉了;如果此时上位机PC再下发一个开始采集指令,那么用户FIFO里面一定是最新的数据,通过PCIe发送到上位机的数据也就是最新的了。利用这种巧妙的方式可以模拟一个真实的DAQ数据采集设备。
当然,我们还需要在AD7606采集线程里面加上这个“开始/停止”按钮来控制AD7606原始FIFO数据是否写入,如图63-65所示。只有当这个按钮为“真”时,才会启动采集。
图63-65:将“开始/停止”按钮添加到AD7606采集线程里面
3.3.4、编写下位机FPGA PCIe Receive程序框图(PC–>FPGA):下行
(1)接下来,我们还需要编写一个下行(PC–>FPGA)数据接收的程序框图。由于下行发送的数据一般都是经过“强制类型转换”函数转成了字节数组或者字符串形式,本质上数据类型属于U8,所以这里,我们可以使用FPGA终端下PCIe Data这个CLP里面的后4个8位位宽(ch4~ch7)的host2fpga通道来传输PC下发的字节数组。比如,本节实验我们使用下行的ch4通道来接收PC下发的指令和参数。
(2)首先,用户可以将FPGA终端下行通道4的3个握手信号(pcie_lv_clip_host2fpga_out_8_tdata_ch4、pcie_lv_clip_host2fpga_in_8_tready_ch4、pcie_lv_clip_host2fpga_out_8_tvalid_ch4)拖拽到FPGA程序框图里面,如图63-66所示。
图63-66:将FPGA终端下的PCIe CLIP下行通道4的3个信号端口拖拽到定时循环里面
(3)然后根据四线握手的方式,将PCIe CLIP下行通道4的3个握手信号接到用户创建的Read FIFO(FIFO_PCIe_Read_U8)上面去,完整的FPGA PCIe数据下行接收程序框图,如图63-67所示。
图63-67:将FPGA终端下的PCIe CLIP下行通道4的3个握手信号连接到Read FIFO上(PC --> FPGA下行数据接收程序框图)
至此,关于FPGA PCIe四线握手的通信线程代码就编写完成了,这个线程也是最为核心的一个程序,今后,用户可以直接拷贝这个线程到自己的程序里面,而无需从头编写。
3.3.5、编写下位机FPGA数据解析程序框图:下行数据解析线程
(1)为了能够真实的模拟上位机PC和下位机FPGA之间交互运行,我们还需要编写一个数据解析线程,将PC下发的指令和参数正确的解析出来,然后赋值给FPGA前面板上相应的控件。关于这个数据解析线程的框架,其实我们在本书前面的串口通信和千兆以太网通信实验里面都有提到,不熟悉的用户可以回顾一下前面的相关内容。
(2)数据解析线程的整体框架,其实很简单:就是一个普通while循环,首先通过FIFO“获取待读取元素数量”方法节点,轮询PCIe Read FIFO里面是否接收到指定长度的数据(比如,做定长解析),如果达到了指定长度的数据,则利用for循环一次性将这些字节数组读取出来,然后进行拼接合并转换,变成实际意义的参数或者指令。
(3)比如本节实验里面,上位机PC每次下发的指令和参数一共是9个字节数据,那么我们在下位机FPGA程序里面,就可以将case结构的条件设置为9…,也就是说,只要下位机FPGA接到了至少有9个字节的数据,就会进入case里面,然后将for循环的长度也设置为9,这样就能一次性把这9个字节数据全部读取出来变成一个字节数组,然后再对这个一维数组进行索引拼接合并,甚至于进行加减乘除计算,完整的数据解析线程框图,如图63-68所示。通过程序框图可以看出,我们将前面4个字节进行拼接之后赋值给了前面的用户线程(AD7606数据采集)里面的“sample_rate”这个控件,用来改变AD7606芯片的采样率;第5个字节跟0进行比较,如果大于0就启动采集,小于等于0则停止采集,都是通过给布尔控件进行赋值实现的;最后4个字节是预留的,用户可以根据实际情况下发更多的参数或者指令给FPGA,只要改下对应的数据长度就行了,这里我们就不再赘述了。
图63-68:完整的下位机FPGA里面的数据解析线程
注意:有些细心的用户可能会问了:为啥上位机PC下发给FPGA的PCIe数据不需要像上行通道那样进行大小端格式转换呢?
这是因为我们在上位机已经提前将所有不同位宽的数据通过“强制类型”转换成字节数组了(单个字节是不存在大小端的),然后通过8位宽的ch4/5/6/7发送给FPGA的;如果直接利用ch0/1/2/3(虽然位宽超过了8位,但是这些函数的输入形参是U8[]或者Strings)这些超过8位位宽的通道来下发这些指令和参数,虽然LabVIEW里面的字节数组是大端格式,但是下位机FPGA接收到上位机PC下发的U64/32/16却是小端格式的,下位机LabVIEW FPGA读取出来之后,是不能直接参与运算的,我们需要把这些小端格式转成大端格式解析之后才能得到正确的指令和参数。
(4)将前面的AD7606数据采集线程、并转串线程、FPGA PCIe DMA通信线程(上行+下行)、下发的数据解析线程等4个线程(循环)放在一起,就形成了一个完整的基于LabVIEW PCIe Socket CLIP实现的FPGA PCIe下位机数据采集DAQ程序,如图63-69所示。
图63-69:完整的PCIe数据采集卡FPGA下位机程序框图
4、LabVIEW FPGA VI仿真
本节实验涉及的是PCIe总线通信,无法在计算机上进行模拟,所以这里的功能性仿真可以跳过,直接将LabVIEW FPGA主VI程序编译下载到FPGA芯片里面运行,然后重启电脑(热启动),再利用上位机通过下发指令和参数来测试插到电脑主板上的黑金AX7103开发板上的PCIe总线能否正常发送和接收数据包,并在上位机前面板上把采集到的波形数据显示出来。
5、LabVIEW FPGA VI编译下载
(1)直接点击FPGA主VI“实验63-PCIe_DMA_16位数据采集卡(AD7606)-FPGA.vi”上方工具栏里面的“运行”箭头按钮,保存VI之后,弹出Xilinx 编译服务器选择对话框,如图63-70所示。选择本地编译器,然后单击“取消”按钮,此时,在FPGA项目下的程序生成规范里面就会多出来一个与PCIe通信VI同名的程序生成规范(实验63-PCIe_DMA_16位数据采集卡(AD7606)-FPGA),如图63-71所示。双击打开这个程序生成规范属性配置页面,勾选“加载至FPGA时运行”复选框,最后点击“生成”或者“确定”按钮,如图63-72所示。如果点击的是“确定”按钮,那么需要重新运行一下VI即可再次启动Xilinx Vivado 2014.4编译器,进入编译状态。
图63-70:选择本地编译器进行编译
图63-71:自动生成的FPGA VI程序生成规范
图63-72:勾选加载至FPGA时运行
(2)因为7系列FPGA暂时不支持LabVIEW在线前面板调试,所以编译出来的bit文件需要在程序生成规范里面勾选“加载至FPGA时运行”,否则下载之后会无法启动运行。接下来,我们需要获取Vivado编译器对FPGA VI进行编译出来的原始bit位文件,而不是NI的lvbitx文件。运行我们为大家提供的“License-ID-Bitfile-ARTIX7.vi”这个软件即可导出原始bit文件,如图63-73所示。这个软件会跟Vivado编译器进行通信,实时监控Vivado编译过程,如图63-74所示。
注意:图63-73里面另存为出来的bit文件,路径尽量的短,因为后续使用Vivado工具下载bit文件时,路径不能太深,而且最好是英文路径,不要掺杂中文,否则会失败;另外,如果是win10系统,尽量选择非系统盘保存,因为win10系统默认情况下需要管理员权限才能对C盘进行复制保存。
图63-73:运行获取FPGA VI编译出来的原始bit文件
图63-74: Xilinx Vivado 2014.4编译器正在编译
(3)编译完成后,可以看到这个VI所占用的FPGA硬件资源和时钟约束情况,然后生成一个与之对应的lvbitx位文件,如图63-75所示。注意:这个lvbitx文件并非FPGA原始的bit位文件,而是NI在bit位文件的基础上又封装了一层,因此,不能通过其他软件或者工具直接下载lvbitx文件。同时,我们E盘里面多了一个原始的FPGA bit位文件,要比lvbitx文件小很多,如图63-76所示。这个bit文件正是后续我们需要用到的FPGA目标可执行文件。点击“关闭”按钮,退出ARTIX7原始bit文件获取软件。
图63-75:编译完成后的FPGA资源和时钟约束情况
图63-76:编译出来的16位PCIe数据采集FPGA VI对应的原始bit位文件
(4)编译结束后,如果出现编译失败的提示是“部分编译步骤未被执行”这个的话,如图63-77所示。这个并不影响最终的FPGA bit文件生成,这是因为NI原有的系统里面本身就没有ARTIX7这个家族芯片,我们自己在LabVIEW里面添加了这个芯片,所以编译会出现这样的提示,但是不影响整体的开发流程。用户可以直接忽略这个错误,因为我们已经获得了原始的FPGA bit文件。
图63-77:提示编译有未被执行的步骤(不影响,直接忽略)
6、PCIe DMA通信上位机PC端程序编写
下面我们需要编写一个PCIe DMA上位机应用程序来测试下位机FPGA PCIe程序是否满足设计需求,相当于开发一个PCIe采集卡上位机程序。这里用户可以调用我们提前给大家封装好的PCIe DMA DLL(使用C\C++\C#\Python)或者PCIe DMA lvlib库VI(使用LabVIEW直接调用),关于我们封装的lvlib库里面的驱动VI,在前面的7.3.3节里面已经做过详细的介绍了,不记得的用户,需要复习一下前面的内容,这里不再赘述。本节上位机程序框图具体的编写过程可以参考本节实验配套的视频教程。
(1)右击项目浏览器里面的“我的电脑”,选择新建一个虚拟文件夹,如图63-78所示。
图63-78:在“我的电脑”下面新建一个上位机虚拟文件夹
(2)然后对新建出来的虚拟文件夹重命名为“实验63-PCIe DMA+16位ADC(模拟数据采集卡)-上位机”,如图63-79所示。
图63-79:将新建的虚拟文件夹进行重命名
(3)然后右击上面的虚拟文件夹,选择新建一个VI,将这个VI保存为“实验63-PCIe_DMA_16位数据采集卡(AD7606)-PC.vi”,如图63-80所示。
图63-80:新建一个PCIe上位机通信程序
(4)为了加快讲解速度,这里我们直接给出PCIe上位机通信程序框图,如图63-81所示。程序框图由两个相互独立的线程组成,也就是两个while循环,分别是PCIe写线程(PC下发数据给FPGA)和PCIe读线程(PC读取FPGA上传的数据)。对应的前面板控件布局,我们也先贴出来,如图63-82所示。
图63-81:PCIe上位机通信程序框图
图63-82:PCIe上位机通信前面板
下面我们来分别介绍一下这两个线程对应的程序框图编写过程及其注意事项。
(5)首先,程序启动的时候,一般都会有一个初始化过程,这里也不例外。由于前面板上有一些按键和指示灯等控件,为了让程序每次运行的时候,都能恢复到一个默认状态,这里利用顺序结构,对需要设置的控件进行赋值操作,如图63-83所示。
图63-83:PCIe通信上位机控件初始化代码
(6)接下来,我们看看上位机PCIe写线程代码是怎么实现的。首先需要利用位于lvlib库里面的子虚拟文件夹“PC_FIFO_Drirect_RW_Raw_Func”里面的PCIe Write Pipe Init函数(FPGA_FIFO_Write_Pipe_Init_DLW30.vi)对PCIe下行通道进行初始化,如图63-84所示。然后在“Pipe_Write_Name”下拉列表里面选择下位机FPGA里面用到的通道,比如,本节实验里面充当下行数据的是8位位宽的写通道ch4,如图63-85所示。
图63-84:利用PCIe Write Pipe Init函数对下行通道进行初始化
图63-85:根据下位机FPGA里面实际用到的下行数据通道来选择对应的pipe
(7)如果初始化成功,则这个VI会返回一个非零的句柄出来,也就是“fd_write”,用户可以通过这个引用实现数据的下发。为了人为控制下发的指令和参数,可以利用case条件结构将下行数据的发送代码框起来,如图63-86所示。
图63-86:下行数据的发送代码
这个下行数据发送,也就是Pipe管道写VI位于lvlib库里面,如图63-87所示。如果今后用户要自己单独编写,可以直接参考例程也可以从lvlib库文件里面拖拽。
图63-87:下行数据发送管道VI(FPGA_FIFO_Write_Pipe_Send_DLW30.vi)所在路径
为了让上位机PC能够控制下位机FPGA里面的采样率和信号采集的启动和停止,我们将上位机前面板上的U32类型的“sample_rate”控件通过“强制类型转换”函数变成字节数组;再把前面板上的布尔型“开始/停止采集”按钮转成U8类型的0或者1;然后再把预留的一个参数“size_read_U32”控件通过“强制类型转换”变成字节数组;最后将这3个转换后的数据进行数组拼接(实际上就是字节数组),赋给“FPGA_FIFO_Write_Pipe_Send_DLW30.vi”写VI,如图63-88所示。这样就可以将上位机数据通过PCIe总线下发给FPGA。
图63-88:将上位机的指令和参数转换成字节数组后下发给FPGA
(8)下面,我们再来看看上位机PC端是如何把FPGA采集上传的数据从DMA FIFO里面读取出来的。同样,需要先对PCIe DMA FIFO进行初始化,用户可以利用lvlib库里面的多态函数“PC_FIFO_DMA_Poly_Init_DLW30.vi”先打开指定的DMA上行数据通道,比如本节实验里面的16位位宽的ch2,同时根据实际情况向计算机申请开辟一段DMA缓冲区,如图63-89所示。这里我们申请了500MBytes的空间来高速缓冲下位机FPGA上传的波形数据。当然,真实应用中,如果用户同时开启了多个DMA上行通道,那么可以根据计算机可用内存容量和吞吐率来合理的分配每个DMA FIFO的缓冲区大小。
当PCIe DMA FIFO初始化成功后,为了让前面板上的“内存池”最大值显示范围随着用户申请的深度自适应,我们将这个申请的FIFO深度值赋给了“内存池”控件的“范围最大值”,再利用“PC_FIFO_DMA_Poly_Start_DLW30.vi”函数来开启下位机FPGA的数据上传功能(实际就是以最快的速度接收FPGA发送给PC端的数据)。通过这两个函数,我们就完成了对PCIe读线程的初始化工作。
图63-89:对PCIe DMA上行通道进行初始化并启动传输功能
(9)当PCIe DMA缓冲区初始化完成并且启动上传数据接收功能后,程序就会进入PCIe读线程的while循环里面,通过调用“PC_FIFO_Poly_Buffer_Length_DLW30.vi”函数获取当前DMA FIFO里面存在的数据长度,单位是字节,这个VI实现的功能有点类似于我们平时用的比较多的一个NI-VISA属性节点“串口缓冲区字节数”。如图63-90所示。
图63-90:获取指定PCIe通道的DMA FIFO缓冲区里面的字节数量
(10)为了提高读取效率或者为了方便定长数据的解析,我们可以给定一个长度阈值,只有当上位机PC端的PCIe DMA FIFO里面接收到了一定长度的字节数之后,再把这些字节数组一次性读取出来。因为PCIe传输的数据都是以字节为单位,所以如果用户希望读取的数据位宽是字节的倍数,本节实验,下位机FPGA上传的AD7606数据是I16类型的信号,正好就是2个字节,下位机FPGA里面发送的一组数据包含8个通道,所以在判断之前,我们需要将U16点数×2×8,再做比较判断,这样读取出来的数据解析之后的点数才是正好的,如图63-91所示。
图63-91:轮询判断上位机PC端的DMA FIFO缓冲区里面的当前字节数是否达到指定长度
(11)一般情况下,如果下位机发送的数据吞吐率很高,那么上位机读取的频率和每次读取的数量长度要满足一定的范围才能保证所有的数据不会丢失,如果每次读取的数据长度很短,那么需要读取的速度就要很快,这样不仅会极大的消耗CPU的资源,也有可能会造成数据溢出丢失;关于这个数值取多少合适,用户可以根据实际情况做实验决定。当DMA FIFO缓冲区里面的数据达到指定长度后,用户可以利用“PC_FIFO_DMA_Poly_Read_DLW30.vi”函数从DMA FIFO里面将指定长度的字节数组读取到LabVIEW应用程序当中来,程序如图63-92所示。
图63-92:读取上位机PCIe DMA FIFO里面的数据到LabVIEW应用程序里面来
(12)读取出来的数据单位是字节数组,如果从FPGA里面接收到的数据物理意义并不是字节类型,那我们还需要借助“强制类型转换”函数将原始的字节数组转换真实的数据,本节实验里面,AD7606采集的数据不是U8,所以我们需要将上传回来的U8字节数组进行解析才能得到正确的8通道16位数据,程序如图63-93所示。
图63-93:如果下位机FPGA采集的不是U8数据,则需要强制类型转换
首先,将读取出来的原始字节数组利用“强制类型转换”函数变成有符号16位也就是I16数组,然后再利用“抽取一维数组”函数把AD7606里面8个通道的数据依次解析提取出来,如图63-94所示。其实,这个方法早在本书前面实验16里面也讲解过了,不记得的用户可以回顾一下前面的知识。
图63-94:利用“抽取一维数组”函数将下位机交织的通道数据提取解析出来
(13)当AD7606信号读到之后,我们可以在前面板上放置8个波形图显示控件,将FPGA发送上来的数据以波形的方式呈现出来,但是考虑到实际应用中,如果波形图控件里面每次显示的点数过大的话,会导致电脑CPU、内存和显卡压力过大,CPU运行速度会降低,这样不利于快速读取数据,因此,我们在PCIe-读线程里面加了两个控制按钮,可以人为控制读取出来的波形是否进行拼接以及是否开启波形显示功能,如图63-95所示。关于这几个功能的演示,我们在后续的实验演示环节里面再给用户做详细的介绍。
图63-95:是否开启波形显示和波形首尾拼接功能
(14)当用户需要将这个程序停止运行的时候,需要先把“PCIe-读线程”停下来,然后利用“PC_FIFO_DMA_Poly_Stop_DLW30.vi”、“PC_FIFO_DMA_Poly_Exit_Wait_DLW30.vi”和“PC_FIFO_DMA_Poly_Destroy_DLW30.vi”这3个VI将PCIe DMA读取通道销毁掉,如图63-96所示。
图63-96:停止程序后关闭、退出、销毁PCIe DMA读取通道
用户编程的时候,不能先销毁PCIe写通道,这是因为底层的PCIe DMA读通道内部调用的是非阻塞的文件读取函数,如果下位机FPGA停止上传数据,那么这个DMA Read函数就会阻塞,LabVIEW程序就会无法响应跟死机一般, 所以在销毁PCIe DMA Read之前,下位机FPGA必须要一直发送数据,中间不能停,发送快慢不影响,但是一定要发送数据才行。当PCIe DMA读取通道销毁之后,就可以下发停止指令给下位机FPGA,将FPGA里面的数据采集发送给停下来,最后再把PCIe Write写通道引用关闭掉,如图63-97所示。
图63-97:发送完停止FPGA采集指令后销毁PCIe Write写通道引用
注意:细心的用户发现了我们的“PCIe-读线程”while循环里面有一个禁用结构,里面是ms倍数延时,如图63-98。这个延时的作用是什么呢?这个ms倍数延时函数主要是用来控制我们的while循环读取频率,可以释放CPU资源。假设,当FPGA采样率很低的时候,也就意味着上位机PC端接收到的数据吞吐率低,那么我们就没有必要让“PCIe-读线程”跑的太快,这样会极大消耗CPU资源,如果主程序里面还有其他的事情要处理,那么CPU会显示吃力;如果下位机FPGA的采样率很高的时候,为了避免DMA FIFO缓冲区溢出,我们需要以最快的速度将里面的数据全部读取出来,这是我们可以给ms倍数延时函数赋值0或者直接将这延时函数禁用掉,这样“PCIe-读线程”这个while循环就会全速运行。
图63-98:控制PCIe读取速度的ms倍数延时函数
(15)最后,再来介绍一下这个PC端的上位机PCIe通信程序前面板上的控件有哪些功能和注意事项,完整的前面板如图63-99所示。
图63-99:PCIe上位机通信程序前面板
程序运行之后,不出意外的话,“申请开辟计算机内存成功?”布尔指示灯会点亮;然后用户可以通过“sample_rate”控件里面的数值参数控制下位机FPGA里面的采样率,比如当我们设置为0,实际传递下去控制的是FPGA前面板上的枚举控件里面的200KS/s,8通道16位的AD7606换算成字节为单位的采样率的话,也就是3.2MByte/s,这个采样率其实并不高;所以我们可以将前面板上的“开始/停止采集”和“显示波形?”两个按钮全部点亮,前者是用来通知FPGA启动采集发送数据,后者是把上位机接收到的波形数据在波形图控件中显示出来;然后可以在“size_read_U32”控件里面输入一个点数,比如200000,也就是3.2M个U8(因为本节实验下位机FPGA里面的数据是8通道16位的AD7606)点;最后,当我们点击一下“Send”按钮之后,上位机PC就会把指令和参数通过PCIe Write通道4下发给FPGA,然后就能看到“内存池”里面的水在不断升高,同时“Buffer_Valid_Length_U8[]”控件里面的数值也在不断变大,当内存池里面的数据量达到3.2MBytes时(200K×8×2),8个波形图里面都会出现一个长度为200K个点的信号,同时内存池里面的数据清空之后又会从0开始慢慢增加。这个过程可以很好地反应FPGA采集数据发送到PC端的整个动态展示,我们在后续的实验演示环节再给用户讲解。
7、实验现象
7.1、准备工作
(1)接好硬件设备。先把台式电脑或者工控机关机,拔掉电源;然后将黑金带PCIe接口的AX7103开发板插到电脑或者工控机主板的PCIe插槽里面,再把Xilinx JTAG下载器(当然,也可以用Digilent JTAG或者其他家的FPGA JTAG下载器),JTAG这头插到黑金AX7103开发板上的JTAG下载口,USB那头接到笔记本电脑上(注意:因为笔记本上面安装了LabVIEW FPGA开发环境和Vivado编译器,台式电脑没有安装Vivado软件,所以LabVIEW FPGA下位机程序我们是在笔记本里面开发的,由于笔记本没有PCIe插槽,所以上位机PCIe通信程序实际是运行在台式电脑或者工控机里面的;此时,笔记本的设备管理器里面识别出来一个Xilinx JTAG下载器设备,如图63-100所示。关于Xilinx下载器驱动安装方法在前面,我们已经给用户介绍过了,这里不再赘述。
图63-100:识别出来的Xilinx JTAG下载器(DCL9或者DCL10)
工控机主板未上电之前,实际接好的AX7103 PCIe FPGA开发板跟电脑之间的接线实物图,可参考图63-101~63-103所示。
图63-101:将AN706模块(AD7606)插到黑金AX7103开发板上的J11拓展口上面,然后再插到工控机主板上(注意:电脑千万不要上电、不要开机,PCIe不支持热插拔!!!)
图63-102:AX7103开发板与Xilinx JTAG下载器接线实物图
图63-103:AX7103与AD7606模块、信号发生器、JTAG下载器和笔记本之间的接线图
接下来,打开台式电脑或者工控机的电源开关,开机(必须),过一会,如果AX7103开发板上的红色电源指示灯点亮,说明主板给PCIe板卡的供电是正常的,如图63-104所示。注意:当主板上电后,主板会通过PCIe插槽自动提供电源给到黑金AX7103 FPGA开发板,不需要外接电源适配器。
图63-104:开机后,主板会通过PCIe插槽提供电源给到黑金AX7103 FPGA开发板(不需要接外部电源适配器)
7.2、下载bit文件
(1)找到NIFPGA安装目录下的Vivado.bat批处理文件,如果LabVIEW FPGA工具包安装在C盘,那么默认的路径就是:C:\NIFPGA\programs\Vivado2014_4\bin,如图63-105所示。双击运行这个bat文件,就可以启动Vivado工具,启动成功后的Vivado软件如图63-106所示。
图63-105:找到启动Vivado工具的批处理文件
图63-106:启动成功后的Vivado工具界面
(2)由于Vivado下载工具引入了所谓的硬件服务器的概念,具体什么东西,感兴趣的用户可以百度一下。这个服务hw_server经常由于系统防火墙或者杀毒软件或者win10实时防护等原因导致未开启,用户可以打开任务管理器,如果里面没有hw_server这个进程,表明这个服务没有启动。那么我们可以直接使用TCL Console命令手动重启一下这个hw_server服务。在Vivado软件界面最下方Tcl Console里面输入hw_server -s TCP::3121 -d,如图63-107所示。然后按下回车键,就能看到hw_server application启动成功的提示,如图63-108所示。
图63-107:通过TCL命令手动重启hw_server服务器
图63-108:hw_server application服务启动成功
(3)接着,单击首页面里面的“Open Hardware Manager”按钮启动下载页面,打开FPGA硬件管理器,然后点击“Open target”,选择下拉列表里面的“Open New Target”,如图63-109所示。在弹出来的硬件选择对话框里面,点击“Next”,如图63-110所示。
图63-109:启动Vivado FPGA硬件管理器
图63-110:进入FPGA硬件目标配置页面
(4)进入硬件服务器设置页面之后,可以看到有两个服务器供用户选择,一是Local server本地服务器和Remote server远程服务器。一般情况下,默认选择Local server,然后点击Next,如图63-111所示。此时会弹出一个进度条,提示正在Connect连接服务器,如图63-112所示。如果连接成功则继续,如果出现图63-113所示的结果,说明本地服务器无法连接成功,此时我们需要采取另外一种方式,也就是Remote server。
图63-111:FPGA硬件服务器选择页面
图63-112:硬件服务器连接中
图63-113:本地服务器连接失败
(5)既然本地服务连接不了,那我们就选择Remote server远程服务器,如图63-114所示。其中Host name需要填与FPGA相连的计算ID名称,用户可以单击开始菜单,右击“计算机”选择属性,如图63-115所示,然后在弹出来的属性对话框页面的最下方,找到计算机ID名称,如图63-116所示。将其复制到图63-114里面来,端口号保持默认的3121不变。
图63-114:填写远程服务器主机名称和端口号
图63-115:右击计算机选择属性
图63-116:在属性页面里找到计算机名称
(6)点击Next下一步之后,可以看到立刻发现了Xilinx JTAG下载器和Aritx7主芯片,如图63-117所示。为了提高下载速度,我们可以将JTAG Clock Frequency时钟频率拉到最大值12MHz,然后点击Next下一步,进入汇总页面,如图63-118示。
图63-117:识别出来的Xilinx JTAG下载器和ARTIX7芯片
图63-118:FPGA硬件目标汇总页面
(7)接下来,在Hardware窗口里面,右击ARTIX7芯片里面的FPGA部分,也就是XC7A100T,选择“Program Device…”,如图63-119所示。然后在弹出来的位流文件路径选择对话框里面,单击右侧的浏览按钮,如图63-120所示。找到E盘下面编译成功的PCIe数据采集FPGA bit文件(ARTIX7_XC7A100T_PCIe_X4_8Chs_B+AD7606.bit),如图63-121所示。点击OK按钮,然后回到路径选择页面,再点击“Program”按钮即可将这个bit文件烧写到FPGA芯片里面运行,如图63-122所示。烧写过程非常快,原因是因为前面我们把Xilinx JTAG下载器的时钟频率设置成了最大的12MHz,所以一瞬间就下载完成了,如图63-123所示。
图63-119:右击FPGA芯片选择Program Device
图63-120:点击浏览按钮选择bit文件
图63-121:找到先前编译成功的LabVIEW FPGA原始bit文件
图63-122:单击Program按钮下载bit文件
图63-123:FPGA bit文件下载过程
(8)当含有PCIe IP核的FPGA bit文件下载到FPGA芯片里面运行之后,AX7103开发板上的4个LED灯全部熄灭了,如图63-124所示。这是因为,虽然FPGA bit下载进去了,但是上位机主机端(台式电脑或者工控机)并没有主动去识别加载PCIe驱动,所以下位机FPGA里面的PCIe底层通信代码并没有进入正常运行模式,故4个LED灯都是熄灭的。
图63-124:FPGA bit文件下载成功,开发板上的4个LED灯全部熄灭状态
7.3、重启台式电脑或者工控机(热启动,加载PCIe驱动)
当PCIe FPGA bit文件下载到FPGA芯片里面之后,如果开发板掉电又上电的话,那么FPGA芯片里面的代码会自动消失的,所以有时候为了避免反复下载bit文件,我们可以把编译出来的bit文件先转成mcs或者斌文件,然后固化到开发板上的Flash里面,这样即使开发板掉电程序依然存在。但是固化bin文件时间会比较长,为此,这里我们推荐用户采用热启动的方式来重启电脑,FPGA开发板不掉电的情况下,重启电脑自动识别加载PCIe驱动。
(1)当我们把含有PCIe通信IP核的FPGA bit文件下载到FPGA芯片里面,电脑并不会主动去识别这个PCIe设备,我们需要重启电脑才可以让主机去主动识别加载PCIe驱动。由于bit文件运行在FPGA芯片里面的,所以不能直接关机重启,因为电脑关机之后,主板就没有电了;所以我们需要右击电脑开始菜单,选择“重启”,如图63-125所示。这就是所谓的热重启。
图63-125:点击开始菜单,选择重新启动(热启动)
(2)当电脑重启成功后,打开设备管理器,如果出现未知PCI设备黄色感叹号,我们可以右击更新驱动程序,然后选择加载一下Xillybus官方提供的inf驱动文件即可,如图63-126所示。正常情况下,只要加载一次驱动文件就可以了,以后重启的时候,系统会自动去识别所有底层使用了Xillybus提供的PCIe IP核的FPGA硬件。
图63-126:第一次识别加载PCIe硬件时,需要选择更新Xillybus提供的PCIe inf驱动文件
(3)PCIe驱动加载成功后,设备管理器里面就会出现名为Xillybus的设备,如图63-127所示。
图63-127:主机识别成功后的FPGA PCIe硬件设备默认名称
(4)接下来,还需要检验一下上位机主机系统是否正常加载了FPGA下位机里面添加的PCIe DMA通道号。比如我们在下位机FPGA里面封装了8上8下的PCIe DMA CLIP节点,那么上位机PC端必须要识别出来所有的通道号名称和具体路径,这样后续主机端才能跟下位机FPGA进行PCIe通信。
具体怎么操作呢?用户可以到微软官方网站上,下载一个小工具“WinObj.exe”,直接双击运行,它可以将我们电脑上所有正常安装的硬件,统统识别出来,比如我们平时常用的串口设备,如图63-128所示。而我们刚刚加载安装成功的Xillybus PCIe设备端口读写通道号也被枚举出来了,如图63-129所示。
图63-128:通过WinObj工具可以查看计算机所有的串口设备号
图63-129:通过WinObj工具查看计算机枚举出来的Xillybus PCIe端口通道号(A版本)
这些枚举出来的端口通道名称就是上位机应用程序需要访问的端口名称,比如A版本里面的“xillybus_write_16_ch2”,意为下行(Host–>FPGA)的写端口,位宽是16位,通道号是0;B版本则是“xillybus_write_8_ch4”,意为下行(Host–>FPGA)的写端口,位宽是8位,通道号是4;本节实验我们使用的就是B版本的读取通道ch2和写入通道ch4。
关于Xillybus驱动加载和通道号查看及变更等更多的信息,不熟悉的用户可以回顾一下本章前面7.3.2节的相关内容,这里不再赘述。
(5)当前面的步骤走完之后,以后重启电脑就不需要了,因为系统已经记住了这些操作和驱动。此时,可以看到插到主板上的FPGA PCIe开发板上的LED1指示灯以1Hz的频率开始闪烁起来了,如图63-130所示。说明FPGA芯片里面的PCIe通信代码正常启动运行了,LED1属于心跳信号,Xillybus提供的PCIe IP核就是通过这个状态来告诉用户,PCIe底层代码是否正常运行。
图63-130:电脑重启之后,FPGA芯片里的PCIe IP核工作正常后,LED1闪烁(心跳)
到这里,我们的FPGA下位机PCIe通信程序运行起来了,并且上位机的PCIe驱动也已安装成功了!
(6)将外部函数信号发生器的波形设置为Sine正弦,频率设置为:1KHz,峰峰值设置为±2.5V,如图63-131所示。
图63-131:将函数信号发生器输出信号设置为正弦(频率=1KHz;峰峰值=±2.5V)
7.4、观察现象(运行上位机PCIe通信测试程序)
(1)打开运行上位机PCIe通信测试程序,位于项目浏览器“我的电脑”下面的“实验63-PCIe_DMA_16位数据采集卡(AD7606)-PC.vi”,如图63-132所示。
图63-132:运行上位机PCIe总线通信测试程序
(2)运行上位机程序之后,在输入控件“sample_rate”里面输入2,相当于等效的采样率是50KS/s<=8Chs=>800KB/s(AD7606位宽是16位);按下“波形显示?”按钮,这样后面就能在波形图里面看到每次读取的Sine信号了;再在输入控件“size_read_U32”里面输入每次从PC端的DMA FIFO缓冲区里面需要读取的数据长度,由于前面我们设置的采样率不高,所以这里输入的读取长度相对自由一些,比如我们设置200000,也就是每个通道都准备读取200K个点,就是每次读取3.2M个Byte;最后再按下点亮“开始/停止采集”这个按钮。
当一切设置就绪后,点击一下前面板上的“Send”发送按钮(这是一个触发型的控件),每点击一次,上位机都会把前面板上设置的这些参数转换成字节数组通过PCIe写通道ch4发送给下位机FPGA,当FPGA接收完成并解析出来指令和参数后,会立刻把采集到的Sine信号通过PCIe DMA上行通道ch2源源不断的发送给上位机PC内存里面。
此时,插到主板上的FPGA PCIe开发板上的LED2和LED4都是熄灭状态,LED3常亮,LED1心跳灯正常闪烁。LED3常亮是因为我们下发的数据量和吞吐率都很低;LED2反应的是下位机FPGA发送数据到上位机PC端的状态,如果数据传输吞吐率很高,且不闪烁的话,说明在Xillybus PCIe IP核传输这一层没有发生数据丢失(因为B版本里面ch2通道我们默认设置的传输带宽是10MB/s);LED4是我们人为加在AD7606采集并转串线程里面的16位发送FIFO端的超时状态指示灯,如果LED4点亮或者闪烁,那就说明AD7606采集线程里面的FIFO数据溢出了,采集的原始Sine信号还没来得及给到Xillybus PCIe IP核之前就丢点了。关于这4个指示灯的实际运行状态,用户必须要自己做实验才能看到。
因此,通过上面LED2和LED4指示灯的状态分析,可以看出:下位机FPGA模拟采集到的所有Sine数据没有发生溢出或者丢失,上位机全部接收到了,如图63-133所示。
图63-133:开启波形显示功能,禁用波形首尾拼接(采样率:50KS/s<=8Chs=>800KB/s)
上位机前面板上的“内存池”液罐显示控件里面的高度(程序默认开辟的是500MB),在不断升高,直到涨到3.2MB时,一下子又清空了,这是因为我们的上位机LabVIEW应用程序把这3.2MB数据全部读取出来了,经过强制类型转换之后在波形图上显示出来了;所以,实际观察到的情况是:每隔4s,内存池会清空一次,同时,波形图里面的曲线会刷新一次,这是因为下位机FPGA的采样率是800KB/s(50KS×8×2),上位机LabVIEW程序每次批量读取3.2MB数据,所以刷新周期就是4s。
我们将波形图里面的曲线进行放大,比如,将横坐标设置为500个点进行显示,可以看到,正好有10个周期的正弦曲线在里面,因为外部Sine信号频率是1KHz,AD7606采样率设置的是50KS/s,所以每个周期的采集量化点数就是50个,因此,500个点就是10个周期信号。再来看看幅度对不对,外部信号峰峰值是±2.5V,而AD7606模块的采集范围是正负5V,采集到上位机的I16数据范围是-32768~32767,其中-32768对应的是-5V,32767对应的是5V,0对应的是0V;所以-2.5V应该就是-16384,+2.5就是16384;这个分析结果正好与上面图63-132完全匹配上了,说明我们编写的下位机FPGA程序和上位机程序都是完全正确的。
(3)接下来,我们验证一下FPGA发送上来的数据是否是连续的,是否存在丢点。首先,按下点亮前面板上的“拼接?”按钮,其他控件参数保持不变;然后点击一下前面板上的“Send”发送按钮(这是一个触发型的控件),每点击一次,上位机会立刻把前面板上设置的这些参数转换成字节数组通过PCIe写通道ch4发送给下位机FPGA,当FPGA接收完成并解析出来指令和参数后,会立即把采集到的Sine信号通过PCIe DMA上行通道ch2源源不断的发送给上位机PC内存里面。
因为我们没有改变FPGA里面的控制参数,所以插到主板上的FPGA PCIe开发板上的4个LED灯现象应该与上面是一致的:即LED2和LED4都是熄灭状态,LED3常亮,LED1心跳灯正常闪烁。LED3常亮是因为我们下发的数据量和吞吐率都很低;LED2反应的是下位机FPGA发送数据到上位机PC端的状态,如果数据传输吞吐率很高,且不闪烁的话,说明在Xillybus PCIe IP核传输这一层没有发生数据丢失(因为B版本里面ch2通道我们默认设置的传输带宽是10MB/s);LED4是我们人为加在FPGA用户线程里面的发送FIFO端的超时状态指示灯,如果LED4点亮或者闪烁,那就说明AD7606并转串线程里面的FIFO数据溢出了,采集的原始信号还没来得及给到Xillybus PCIe IP核之前就丢点了。关于这4个指示灯的实际运行状态,用户必须要自己做实验才能看到。
因此,通过上面LED2和LED4指示灯的状态分析,可以判断出:下位机FPGA模拟采集到的所有Sine数据没有发生溢出或者丢失,上位机全部接收到了,如图63-134所示。
图63-134:开启波形显示功能,开启波形首尾拼接(采样率:50KS/s<=8Chs=>800KB/s)
上位机前面板上的“内存池”液罐显示控件里面的高度(程序默认开辟的是500MB),在不断升高,直到涨到3.2MB时,一下子又清空了,这是因为我们的上位机LabVIEW应用程序把这3.2MB数据全部读取出来了,经过强制类型转换之后在波形图上显示出来了;实际观察到的情况是:每隔4s,内存池会清空一次;同时,波形图里面的Sine曲线会以每隔4s的速度增加变长,而且中间都是完全无缝衔接连续的;之所以是4s刷新一次,是因为下位机FPGA的采样率是800KB/s(50KS×8×2),上位机LabVIEW程序每次批量读取3.2MB数据,所以刷新周期就是4s。
用户可以将200000整数倍处进行放大观察,看看采集上来的信号是否是连续的,图63-134里面显示的是连续的,也间接说明采集过程中没有发生数据溢出或丢失。
注意:如果让这个LabVIEW上位机程序一直运行着,过一会会弹出一个错误提示:系统内存不足,请释放内存空间。这是因为我们在程序框图里面开启了波形首尾拼接功能,随着时间的延长,程序框图里面的数组长度和波形里面的斜线越来越大,一旦超越了LabVIEW软件本身的承受能力,就会导致LabVIEW报警甚至卡死崩溃。所以,一般情况下,我们都是人为控制一下程序框图里面的数组长度以及波形控件里的数据点数,不能太大,否则不仅会造成计算机卡顿甚至造成软件崩溃。
(4)下面我们来提高一下FPGA采样率,看看会有什么情况发生:首先,将输入控件“sample_rate”里面的2改成1,相当于等效的采样率提高了2倍: 100KS/s<=8Chs=>1.6MB/s(AD7606位宽是16位);按下“波形显示?”按钮,这样后面就能在波形图里面看到每次读取的Sine信号了,熄灭“拼接?”按钮;其他参数保存不变,比如,输入控件“size_read_U32”里面还是输入200000,也就是每个通道都准备读取200K个点,换算成字节的话,就是每次读取3.2M个Byte(点数×8×2);“开始/停止采集”这个按钮要一直点亮。
设置完成后,再点击一下前面板上的“Send”发送按钮(这是一个触发型的控件),每点击一次,上位机都会把前面板上设置的这些参数转换成字节数组通过PCIe写通道ch4发送给下位机FPGA,当FPGA接收完成并解析出来指令和参数后,会立刻把采集到的Sine信号通过PCIe DMA上行通道ch2源源不断的发送给上位机PC内存里面。
此时,插到主板上的FPGA PCIe开发板上的4个LED灯状态跟前面还是一样的:即LED2和LED4都是熄灭状态,LED3常亮,LED1心跳灯正常闪烁。LED3常亮是因为我们下发的数据量和吞吐率都很低;LED2反应的是下位机FPGA发送数据到上位机PC端的状态,如果数据传输吞吐率很高,且不闪烁的话,说明在Xillybus PCIe IP核传输这一层没有发生数据丢失(因为B版本里面ch2通道我们默认设置的传输带宽是10MB/s);LED4是我们人为加在FPGA用户线程里面的写入FIFO端的超时状态指示灯,如果LED4点亮或者闪烁,那就说明并转串线程里面的16位FIFO数据溢出了,采集的原始信号还没来得及给到Xillybus PCIe IP核之前就丢点了。关于这4个指示灯的实际运行状态,用户必须要自己做实验才能看到。
因此,通过上面LED2和LED4指示灯的状态分析,即使此时下位机FPGA里面的采样率提高到了1.6MB/s,下位机FPGA采集到的所有Sine数据依然没有发生溢出或者丢失,上位机全部接收到了,如图63-135所示。
图63-135:开启波形显示功能,禁用波形首尾拼接(采样率:100KS/s<=8Chs=>1.6MB/s)
上位机前面板上的“内存池”液罐显示控件里面的高度(程序默认开辟的是500MB),在不断升高,经过2s左右就涨到了3.2MB时,然后又清空了,这是因为下位机FPGA的采样率是1.6MB/s,上位机LabVIEW程序每次批量读取3.2MB数据,所以刷新周期就是2s,相当于0.5Hz的刷新率;波形图里面的Sine信号基本上处于不变状态,这是因为AD7606采样率跟外部信号的频率整除关系,用户可以尝试改变一下外部Sine信号的频率或者幅度就能看到上位机Sine曲线明显变化了。
(5)下面我们进一步提高FPGA采样率,看看会有什么情况发生:先将输入控件“sample_rate”里面的1改成0,相当于等效的采样率提高了2倍:200KS/s<=8Chs=>3.2MB/s(AD7606位宽是16位I16);其他保持不变,比如,“波形显示?”按钮处于点亮状态,这样后面就能在波形图里面看到每次读取的正弦信号了;“拼接?”按钮依然设置为熄灭状态,防止内存溢出;输入控件“size_read_U32”里面的200000,也就是3.2M个点保持不变,换算成字节的话,就是每次读取3.2M个Byte(点数×8×2);“开始/停止采集”这个按钮要一直点亮。
设置完成后,再点击一下前面板上的“Send”发送按钮(这是一个触发型的控件),每点击一次,上位机都会把前面板上设置的这些参数转换成字节数组通过PCIe写通道ch4发送给下位机FPGA,当FPGA接收完成并解析出来指令和参数后,会立刻把采集到的Sine信号通过PCIe DMA上行通道ch2源源不断的发送给上位机PC内存里面。
此时,插到主板上的FPGA PCIe开发板上的4个LED灯状态跟前面还是保持一样,其中,LED4依然保持常灭状态,所以,通过LED4指示灯的状态分析可知,即使下位机FPGA里面的AD7606采样率提高到了最大的200KS/s,FPGA采集的信号数据没有溢出和丢失,同时Xillybus PCIe DMA传输层也没有出现拥堵现象;上位机读到的Sine信号是连续的,如图63-136所示。这是因为我们选择的是ch2通道进行传输,这个通道的带宽是10MB/s,加上上位机没有什么复杂的解析和算法,能及时读走DMA缓冲区里面的数据,所以就没有产生数据溢出或者丢失。
图63-136:LED2和LED4依然熄灭,没有丢点(采样率:200KS/s<=>3.2MB/s)
我们将波形图里面的曲线进行放大,比如,将横坐标设置为800个点进行显示,可以看到,正好有4个周期的正弦曲线在里面,因为外部Sine信号频率是1KHz,AD7606采样率设置的是200KS/s,所以每个周期的采集量化点数就是200个,因此,800个点就是4个周期信号。幅度没有变化,峰峰值依然是-16384~16383,对应的就是±2.5V。
(6)当然,感兴趣的用户,还可以将下位机FPGA采集线程里面的AD7606并行读取数据改成AD7606 SPI串行读取,如图63-137所示。这个需要提前将AD7606的工作模式改成串行才可以,具体方法可以参考AD7606官方手册或者联系本书作者:DLW30@126.com(拓展实验,并行读取逻辑简单,但是耗费引脚多;串行读取节约大量FPGA引脚)
图63-137:AD7606串行采集数据(节约引脚资源)
7.5、实验分析(至关重要)
(1)本节实验因为下位机FPGA的采样率不高,远远小于上行通道ch2的极限带宽,加上上位机没有什么复杂的算法,所以即使把采样率提到最大,依然不会溢出或者丢点。
(2)重要结论:只要上位机PCIe DMA读线程足够快,就不会发生下位机FPGA FIFO溢出,LED2和LED4就不会闪烁,数据就不会丢失;所有采集的数据都可以传输到计算机内存里面来;实际测试发现:当FPGA采样率设置为0(200KS/s),也就是3.2MB/s,PCIe DMA传输都是正常的,因为我们封装的B版本里面的PCIe IP核底层里面的通道ch2吞吐率最大可以到10MB/s,在其他通道没有使用的情况下,因为所有的通道共享了800MB/s带宽。
(3)有些细心的用户发现了,如果上位机每次读取长度设置为2048000或者1024之类的,随着时间的推移,波形里面的曲线会周期性的出现截断现象,这是什么原因呢?其实,早在前面给大家讲解上位机PCIe DMA函数的时候,就重点提到过了,如下:
注意:初始化函数的缓冲区深度设置,跟后面的(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。
看到这里,估计用户清楚了,我们可以将读取长度改成200000,而不是204800,效果就完全不一样了!
8、FPGA VI程序固化(Bit文件)
前面通过LabVIEW将编译成功后的FPGA VI下载到FPGA芯片里面运行,一旦掉电或者按下复位键之后,FPGA里面的程序就没有了,无法实现脱机独立运行。因此我们需要将FPGA编译出来的bit文件通过下载器下载到Xilinx官方PROM或者第三方的Flash或者SD卡里面,这个过程我们称之为“固化”。如果是传统的6系列FPGA,我们可以将bit文件转成mcs文件然后再利用IMPACT.exe下载到Flash里面,但是对于7系列的FPGA来说,Vivado编译工具无法直接生成mcs文件,虽然可以勾选生成bin文件,但是如果没有完整的Vivado工程的话,这个bin文件还是生成不了。
我们把问题简化一下,前面已经获得了原始的按键捕捉bit文件,那么我们就需要想办法抛开Vivado,将bit文件直接转化成bin,mcs或者hex,然后下载固化到Flash里面。类似于把bit文件转成edf网表文件,思路是一样的。
(1)好在Xilinx提供了TCL脚本命令,可以使用write_cfgmem这个命令来强制转换。参考前面的步骤打开Vivado软件,然后在最下方的TCL Console控制台里面输入下面的命令,然后回车,如图63-138所示。其中,SPIx1指的是Flash芯片的通信线数,板载的N25Q128支持X1、X2和X4,X4读写速度最快,默认情况下,LabVIEW FPGA编译出来的bit文件格式里面是x1,所以在写转换指令的时候,必须要选择SPIx1;16是指Flash的大小,单位是MByte,因为黑金AX7103开发板上的Flash容量是128Mbit,相当于16MByte;红色部分是原始的bit文件路径,蓝色的是转换出来的bin文件路径。
注意1:命令里面有个-force,这个是为了强制重写,可以直接覆盖已有的bin文件。由于这串命令很长,用户可以直接复制,然后根据实际情况修改紫色、红色和蓝色部分的字符串就可以了。下面的命令就是把E盘下的原始FPGA bit文件,转换成bin文件,还是存放在E盘下面。
write_cfgmem -force -format bin -interface SPIx1 -size 16 -loadbit “up 0x0 E:/ARTIX7_XC7A100T_PCIe_X4_8Chs_B+AD7606.bit” E:/ARTIX7_XC7A100T_PCIe_X4_8Chs_B+AD7606.bin
图63-138:在Vivado TCL控制台里面输入write_cfgmem强制转换命令
注意2:如果是Digilent的Basys3开发板,假设我们生成了一个流水灯的bit之后,同样可以用下面的指令来生成bin文件,需要注意的是,Basys3上面的Flash型号是s25fl032,容量是32Mbit,也就是4MByte,所以指令如下:
write_cfgmem -force -format bin -interface SPIx1 -size 4 -loadbit “up 0x0 E:/Basy3_LED.bit” E:/ Basy3_LED.bin
(2)按下回车之后,可以看到控制台打印显示窗口里面的输出结果,如图63-139所示。包括从哪里加载bit文件,转换出来的bin文件路径等基本信息。
图63-139:Vivado执行完TCL命令之后的输出结果
(3)接下来,我们把bin文件通过Vivado工具下载到AX7103开发板上的Flash里面,这样FPGA上电就会自动从Flash里面加载程序了。这颗Flash芯片容量是128Mbit,相当于16MByte,具体型号是Numonyx公司生产的N25Q128。右击XC7A100T主芯片,选择“Add Configuration Memory Device”,如图63-140所示。然后在弹出来的Flash器件选型页面里面,制造商Manufacture选择Micron美光,容量选择128Mbit,宽度选择x1_x2_x4兼容模式。然后在下拉列表里面选择3.3V电平的n25q128芯片就可以了,如图63-141所示。因为黑金AX7103开发板上的n25q128芯片接的是3.3V电压。图63-142显示的是Digilent Basys3开发板上的Flash芯片固化选型。
图63-140:添加用于存放FPGA配置文件的Memory器件
图63-141:筛选出指定的Flash芯片(n25q128)-黑金AX7103开发板
图63-142:筛选出指定的Flash芯片(s25fl032)-Digilent Basys3开发板
(4)点击OK之后,会弹出提示框,提醒用户是否现在就对这个Flash或者PROM进行编程,也就是烧写bin/mcs/hex文件,如图63-143所示。点击OK之后,弹出添加配置文件窗口,点击旁边的浏览按钮,如图63-144所示,然后选择前面转换出来的bin文件,如图63-145所示。最后点击OK之后,开始下载烧写bin文件,经过擦除、编程、验证3个步骤之后,会弹出提示框,提醒用户程序固化成功了,如图63-146所示。此时,AX7103开发板上的PCIe DMA通信程序并没有加载运行,用户需要重启一下电源,然后FPGA会自动从Flash里面读取刚刚下载的bin文件加载到FPGA芯片内部运行。
图63-143:是否现在对Memory器件进行编程
图63-144:点击浏览按钮找到前面转换出来的bin文件
图63-145:选择一个符合n25q128芯片的bin文件
图63-146:bin文件烧写过程与成功后的提醒
(5)此时,Vivado里面的FPGA芯片下面多了一个n25q128配置芯片,如果用户想要再次烧写bin文件进去,可以右击这个芯片,然后选择“Program Configuration Memory Device”即可重新下载固化FPGA VI对应的bin文件,如图63-147所示。
图63-147:右击Vivado里面的Flash器件可以再次烧写程序
结论:因为Vivado软件本身要比ISE操作起来麻烦一些,很多指令都变成了TCL脚本,考虑到开发过程中的稳定性和易用性,我们建议用户对本书里面讲到的Vivado软件基本操作熟悉一下,除了下载bit和bin文件需要用到Vivado外,其余所有的开发都是在LabVIEW里面进行的,相对来说还是比较简单的。
9、总结
本节实验内容写的非常细致,难度也非常大,因为涉及到很多下位机FPGA PCIe和上位机PC端通信的知识点和内容,用户需要细心学习研究。在学习研究本节实验之前,有些用户是跳着看的,建议大家最好先把本章前面的PCIe基础内容看一遍,否则有些概念都不清楚的话,是很难真正掌握LabVIEW FPGA下的PCIe总线开发的。
本节实验重点需要理解和掌握PCIe总线通信过程、原理;学会我们封装的下位机FPGA PCIe Socket CLIP里面的读写通道的调用及其注意事项;复习前面我们学过的16位ADC芯片AD7606采集线程的编写;掌握控制AD7606采样率的编程方法;学会接收并解析上位机下发的指令和参数;熟练使用FIFO四线握手制编程方式;最后就是学习和掌握我们封装的上位机PCIe DMA通信lvlib库里面的所有函数(VI)的功能和注意事项,做到熟练使用。