天天看点

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

前面7.4节我们给用户讲解了PCIe下的FIFO DMA高速传输通道的用法,FIFO顾名思义就是在保证数据不丢失的情况下,高速传输,特别适合那些数据采集、图像采集、波形回放等应用;本节我们给用户介绍另外一种相对慢速的通道,只要不断电可以一直保存数据的存储器,也称之为Memory。

这里我们说的Memory并不是LabVIEW FPGA芯片里面的存储器,而是Xillybus官方PCIe IP核里面封装的Memory通道,上位机可以直接通过PCIe总线把上位机要进行读写的地址和数据通过Memory通道下发给FPGA,非常适合用来传输同时具备地址和数据信息的内容。

相对于只有一个数据属性的FIFO而言,Memory通道具备两个属性参数,一个是地址概念,一个是数据部分。简而言之,就是下位机FPGA里面如果我们模拟一个一维数组或者一个Memory,上位机可以直接调用我们封装的好的DLL驱动,读写下位机的数组或者Memory里面指定位置的元素,地址就相当于索引,这样做的好处是,用户可以提前将下位机里面用到的参数放在数组或者Memory里面进行自动更新维护,上位机可以随时对下位机的数组或者Memory进行指定位置的读写。

举1个常见的例子:比如上位机要通过PCIe控制FPGA板子上的LED灯、继电器、数字IO输出引脚,还想着读取FPGA板子外接的按键、开关、输入信号这些状态信号。一般有两个实现方式。

第一种方式是,将下位机所有的数据拼接之后通过PCIe DMA FIFO发送到上位机,或者把上位机拼接后的数据通过FIFO下发给FPGA再逆向解析出来,这种思路很常见,对应前面我们讲解的串口、网口、USB里面其实都有所提到。优点是框架统一,不局限于何种通信接口,缺点是用户需要对数据包里面的数据组织方式有所了解。

第二种方式是,利用本节我们所要讲解的Memory通道实现地址和数据的下发。比如我们可以先把下位机FPGA里面要控制的LED灯、继电器、DO或者AO这些引脚或者参数统一按照先后顺序放到一个数组或者Memory里面(元素类型可以是U8、U16、U32),这个数组或者Memory我们称之为“下行数组”,意为上位机可以直接通过PCIe总线对这个数组或者Memory里面的元素进行写操作;然后把按键、开关、DI或者AI等信息按照先后顺序放到另外一个数组或者Memory里面,这个数组称之为“上行数组”,意为上位机可以直接通过PCIe总线读取这个数组或者Memory里面指定位置的元素。优点是用户不需要像前面FIFO那样对接收的所有数据包进行解析了,直接将各个变量当成元素存放到数组或者Memory里面去,上位机想读写控制下位机FPGA时,直接通过下发地址就可以实现对指定位置的元素进行读取或者更新了;缺点是Memory的传输速度没有FIFO快,但是对于一般的指令或者参数等状态信息是完全足够的,另外就是下位机FPGA里面要始终维护对数组或者Memory里面的数据更新。

上面描述的这个过程,我们在后续实验69里面再详细的给用户讲解怎么编程实现。

衍生:通过上面的讲解,用户是不是有一种感觉:那就是可以直接将下位机FPGA前面板上的控件以及感兴趣的局部或者全局变量按照顺序放到一维数组或者Memory里面去,然后上位机只要知道每个控件或者参数所在数组或者Memory的索引位置,就可以通过我们封装的PCIe Memory通道Socket CLIP下发地址来读取或者更新下位机FPGA的控件数值了。等于是变相的实现了类似NI FPGA那样的在线前面板仿真、调试、运行、观察。

本节重点给用户讲解的是我们封装好的LabVIEW FPGA下的PCIe Memory CLIP组件功能和用法。这个PCIe Memory CLIP也是我们My FPGA软件工具包的核心组件之一,我们花费了近1个月的时间,将Xillybus官方提供的Memory代码进行移植、修改和适配,并进行了大量的测试验证,最终才把底层的精髓部分PCIe Memory IP封装到LabVIEW FPGA CLIP里面来,并且给用户提供了极其简单的四线握手Memory接口,所有关于PCIe Memory协议本身的通信都封装到底层去了,用户不需要了解什么是PCIe Memory,可以直接使用LabVIEW FPGA来调用这个PCIe Memory来读写下位机FPGA里面的控件或者IO端口。

下面我们重点给用户介绍一下我们封装好的2上2下的FPGA PCIe Memory Socket CLIP,首先在LabVIEW项目浏览器下的“我的电脑”上右击,选择新建“终端和设备”,如图7-58所示。注意:这个后缀为8CMs的FPGA终端是完全包含后缀为8Chs的FPGA终端项目的。因此,用户将8Chs终端项目下的所有例程拖拽到8CMs下面重新编译下载都是可以的。

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-58:右击“我的电脑”选择新建FPGA终端和设备

然后在弹出来的终端设备新建对话框里面,找到ARTIX7-100T对应的X4模式下包含2通道Memory和8通道FIFO的B版本FPGA终端,如图7-59所示。

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-59:在弹出的列表框中找到X4模式下2通道Memory与8通道FIFO的B版本FPGA终端

点击“确定”按钮后,同时包含PCIe Memory和DMA FIFO通信的Socket CLIP组件会自动添加到FPGA终端里面来,如图7-60所示。

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-60:新建出来的Aritx7包含B版本的PCIe FPGA Socket CLIP终端

展开这个CLIP之后,用户可以看到一共有67个EIO节点,主要分为两个部分:

前面50个是8对DMA FIFO通道的,也就是16个通道,每个通道都是一个标准的四线握手制FIFO,相当于3根线,所以,所有通道的握手信号就有16×3=48个,再加上1个100MHz同步时钟和反应PCIe IP工作状态的的4个LED灯,一共就是48+1+1=50个EIO节点。这部分我们在前面的7.4节已经做过详细的讲解了。

后面17个EIO节点是我们封装的2对PCIe Memory读写通道(位宽分别是16位和32位),也就是4个通道,每个通道都有4跟信号线,2根是地址线,2根是数据线,因此,所有Memory的握手信号就是:4×4=16个,再加上1个125MHz同步时钟,一共就是16+1=17个EIO节点。如图7-61所示。

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-61:展开PCIe Memory Socket CLIP组件里面的EIO节点

默认情况下,我们给这2上2下共计4个PCIe Memory通道分配的驱动名称和带宽,如下面的图7-62所示。这个表里面每个Memory通道的传输带宽也可以自由分配,并不局限于我们的设置那样,今后,如果用户需要修改这些参数可以联系我们神电测控。

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-62:我们封装的两通道PCIe Memory对应的主机端(PC)通道名称和传输带宽

下面我们给大家逐一讲解一下B版本的LabVIEW FPGA PCIe Memory CLIP里面每个信号端口的含义和注意事项。通过虚线将17个CLIP端口划分成3类:分别Memory端口同步125M时钟、16位和32位的上行(FPGA–>Host)Memory读取通道以及下行(Host–>FPGA)写入通道。其中,Host可以直观的理解成上位机PC。

1)Clock PCIE_LV_MEM_Clock_125M
           
2)pcie_lv_clip_from_host_16_rdaddr_update_mem0
 3)pcie_lv_clip_from_host_16_rdaddr_mem0
 4)pcie_lv_clip_from_host_16_rden_mem0
 5)pcie_lv_clip_to_host_16_rddata_mem0

 6)pcie_lv_clip_from_host_16_wraddr_update_mem0
 7)pcie_lv_clip_from_host_16_wraddr_mem0
 8)pcie_lv_clip_from_host_16_wren_mem0
 9)pcie_lv_clip_from_host_16_wrdata_mem0
           
10)pcie_lv_clip_from_host_32_rdaddr_update_mem0
 11)pcie_lv_clip_from_host_32_rdaddr_mem0
 12)pcie_lv_clip_from_host_32_rden_mem0
 13)pcie_lv_clip_to_host_32_rddata_mem0

 14)pcie_lv_clip_from_host_32_wraddr_update_mem0
 15)pcie_lv_clip_from_host_32_wraddr_mem0
 16)pcie_lv_clip_from_host_32_wren_mem0
 17)pcie_lv_clip_from_host_32_wrdata_mem0
           

备注:29与1019对应的信号端口含义都是一样的,只是端口的位宽不一样,用法完全一样,前8个是16位位宽的Memory读写通道,后面8个是32位位宽的Memory读写通道。用户可以根据实际应用中所需要的控件类型选择合适位宽的Memory通道。下面我们给用户详细讲解一下Memory每个端口的含义。

1)Clock PCIE_LV_MEM_Clock_125M

FPGA PCIe Memory通道读写时钟,默认是125MHz,这个时钟是路由出来给用户自己写下位机FPGA程序用的Memory同步读写时钟。众所周知,Xilinx官方给的PCIe IP核配置页面里面,时钟一般是125MHz或者250MHz,对于Memory而言,用的就是这个125MHz时钟,对于前面7.4节里面的DMA FIFO,用的是带隔离的100MHz时钟,切记,不要弄混了。也就是说,如果下位机FPGA想要把上位机PC下发给FPGA的地址和数据读出来或者将FPGA数据通过Memory通道返回给上位机PC,必须要使用这个125MHz时钟源,才能保持Memory同步握手。为此,我们将这个125MHz时钟直接封装到我们的LabVIEW FPGA PCIe Socket CLIP里面来了,时钟信号名为“Clock PCIE_LV_MEM_Clock_125M”。

如何使用外部CLIP路由进来的时钟,跟前面我们封装的千兆以太网、USB时钟一样,用户可以直接在FPGA定时循环左边创建一个时钟源常量,然后在下拉列表里面选择,如图7-63所示;或者直接双击FPGA定时循环,在弹出来的时钟配置页面里面选择“PCIE Data\ Clock PCIE_LV_MEM_Clock_125M”,如图7-64所示。

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-63:为FPGA定时循环创建一个PCIe Memory读写时钟源常量

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-64:直接在FPGA定时循环时钟配置页面里面选择PCIe Memory同步时钟

2)pcie_lv_clip_from_host_16_rdaddr_update_mem0
 3)pcie_lv_clip_from_host_16_rdaddr_mem0
 4)pcie_lv_clip_from_host_16_rden_mem0
 5)pcie_lv_clip_to_host_16_rddata_mem0	
           

上面这4个是16位位宽的PCIe Memory读取通道的四线握手信号,其中,地址信号和数据信号各占用2个。为了让用户更加形象的理解和掌握这些握手信号的用法,我们先给出一个FPGA PCIe Memory读取程序框图,如图7-65所示。毕竟LabVIEW框图看起来显而易见,一图胜千言!

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-65:FPGA接收上位机下发的地址后再把本地的数组或者Memory里面地址对应的数据索引出来发送给上位机PC端(上行读Memory)

如果“pcie_lv_clip_from_host_16_rdaddr_update_mem0”信号拉高,说明上位机下发的地址也就是数组或者Memory的索引值已经到达了FPGA,此时,可以将这个地址值“pcie_lv_clip_from_host_16_rdaddr_mem0”读出来经过条件判断后放到移位寄存器里面,如图7-66所示,相当于将地址进行锁存保持,这部分代码其实也可以用带条件的反馈节点来实现,如图7-67所示。

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-66:利用条件判断和移位寄存器实现地址锁存(上行读Memory)

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-67:利用带启用条件的反馈节点实现地址锁存(上行读Memory)

当FPGA接收到上位机下发的地址后,用户就可以将这个地址当成数组或者Memory的索引值了,等到“pcie_lv_clip_from_host_16_rden_mem0”信号有效时,将FPGA里面的数组或者Memory元素按照地址索引出来赋给“pcie_lv_clip_to_host_16_rddata_mem0”这个端口,如图7-68所示。此时,上位机就收到了一个FPGA发上来的数据,这样就实现了上位机访问读取下位机FPGA里面的指定位置的数组或者Memory元素了。

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-68:将地址索引出来的Memory数据通过to host通道发送给PC(上行读Memory)

要想真正理解Memory读取的工作机理,需要同时理解上位机和下位机FPGA的Memory运行机制:其实,上位机对下位机里面的Memory进行读取的时候,上位机首先会调用lseek函数改变读的文件指针位置,当lseek函数执行完成后,下位机FPGA的Memory address两根信号(pcie_lv_clip_from_host_16_rdaddr_update_mem0、pcie_lv_clip_from_host_16_rdaddr_mem0)线就有反应了;然后上位机再调用read函数读取文件里面的数据,此时,下位机FPGA发送回来的Memory元素就被上位机读取出来了。这部分涉及上位机驱动程序,我们在接下来的7.7节里面再给用户做详细的讲解。

下面我们再看看FPGA PCIe Memory写入通道都有哪些信号端口以及是如何工作的。

6)pcie_lv_clip_from_host_16_wraddr_update_mem0
 7)pcie_lv_clip_from_host_16_wraddr_mem0
 8)pcie_lv_clip_from_host_16_wren_mem0
 9)pcie_lv_clip_from_host_16_wrdata_mem0
           

跟读通道一样,写通道也是由4个四线握手信号构成,其中,地址信号和数据信号各占用2个。同理,为了方便表述和理解,我们先看一个FPGA PCIe Memory写入程序框图,如图7-69所示。毕竟LabVIEW框图看起来显而易见,一图胜千言!

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-69:FPGA接收上位机PC下发的地址后再把接收到的数据更新写入到本地的数组或者Memory地址对应的位置里面去(下行写Memory)

如果“pcie_lv_clip_from_host_16_wraddr_update_mem0”信号拉高,说明上位机下发的地址也就是数组或者Memory的索引值已经到达了FPGA,此时,可以将这个地址值“pcie_lv_clip_from_host_16_wrdata_mem0”读出来经过条件判断后放到移位寄存器里面,如图7-70所示,相当于将地址进行锁存保持,这部分代码其实也可以用带条件的反馈节点来实现,如图7-71所示。

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-70:利用条件判断和移位寄存器实现地址锁存(下行写Memory)

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-71:利用带启用条件的反馈节点实现地址锁存(下行写Memory)

当FPGA接收到上位机下发的地址后,用户就可以将这个地址当成数组或者Memory的索引值了,等到“pcie_lv_clip_from_host_16_wren_mem0”信号有效时,就可以将端口“pcie_lv_clip_from_host_16_wrdata_mem0”里面的数据读出来然后赋给FPGA本地的数组或者Memory,按照给定的地址进行覆盖,如图7-72所示。此时,上位机就实现了对FPGA里面的数组或者Memory进行写入更新了。

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-72:将上位机PC下发的数据按照指定的地址写入到FPGA里面的数组或者Memory里面去(下行写Memory)

要想真正理解Memory写入的工作机理,需要同时结合上位机和下位机FPGA的Memory运行机制:上位机对下位机里面的Memory进行数据写入之前,上位机首先会调用lseek函数改变读的文件指针位置,当lseek函数执行完成后,下位机FPGA的Memory address两根信号(pcie_lv_clip_from_host_16_wraddr_update_mem0、pcie_lv_clip_from_host_16_wrdata_mem0)线就有反应了;然后上位机再调用write函数把数据写入到文件里面去,此时,下位机FPGA就接收到了下发下来的数据,最后用户利用锁存的地址和这个数据做自己想做的任何事,比如数组更新。这部分涉及上位机驱动程序,上位机我们在接下来的7.7节里面再给用户做详细的讲解。

10)pcie_lv_clip_from_host_32_rdaddr_update_mem0
 11)pcie_lv_clip_from_host_32_rdaddr_mem0
 12)pcie_lv_clip_from_host_32_rden_mem0
 13)pcie_lv_clip_to_host_32_rddata_mem0

 14)pcie_lv_clip_from_host_32_wraddr_update_mem0
 15)pcie_lv_clip_from_host_32_wraddr_mem0
 16)pcie_lv_clip_from_host_32_wren_mem0
 17)pcie_lv_clip_from_host_32_wrdata_mem0
           

余下的8个信号端口,跟前面16位位宽的Memory类似,只是封装的时候,我们将位宽设置成32位,方便用户针对不同的项目应用时,可以灵活应对。

注意:Xillybus提供的PCIe Memory IP核跟前面的DMA FIFO是一样的,内部都是小端格式,也就是说,低字节在前,高字节在后,所以,要么在下位机FPGA里面提前做翻转,要么在上位机做大小端变换!比如,如果用户在FPGA里面接收或者发送一个32位位宽的数据,那么需要借助“拆分数字”和“交换字节”两个函数来完成大小端格式的转换,如图7-73所示。这里,我们给出了3种不同数据类型的大小端格式转换,这个功能将在后续的实验部分会用到,大家可以留心一下。

提醒1:其实在学完后面的实验之后,很多用户发现,对于用到DMA FIFO相关的实验里面(实验54~实验67),我们都是在下位机FPGA里面做了大小端格式转换,这样做的好处是,上位机就不需要再转换了,因为DMA FIFO传输的数据量有时候非常大,上位机做大小端格式转换的话会增加不必要的额外时间,消耗更多的CPU资源,所以我们直接在下位机FPGA里面处理好,上位机就轻松了!

提醒2:而针对本节的Memory,我们在后续的实验68里面,采用的是截然相反的方法,也就是下位机FPGA里面对于16位或者32位位宽的Memory数据不做任何转换,而是在上位机提前将要下发的数据或者读取的数据进行大小端格式转换,这是因为Memory一次只传输一个数据(U16或者U32),并且传输速度慢,所以不会消耗上位机太多资源,反而节省了下位机FPGA的逻辑资源。

LabVIEW FPGA PCIe开发讲解-7.6节:LabVIEW FPGA PCIe 2通道Memory Socket CLIP 讲解(可以变相实现通过PCIe读写FPGA前面板控件等状态信息)

图7-73:FPGA里面不同数据类型的大小端格式转换代码

可以看出,虽然PCIe协议本身很复杂,但是对于用户来说,只要理解了FIFO和Memory的四线握手编程,就可以搞定所有线程之间的数据传输问题了,这也就是为什么我们在书中每个实验里面都反复强调四线握手和并转串以及串转并的重要性。

继续阅读