PCIe接口在FPGA上的实现

引言

  1. PCI Express Base Specification Revision 3.0
  2. PCI Local Bus Specification Revision 3.0
  3. 书籍:PCI Express System Architecture,对应那本紫色的《PCI Express 体系结构标准教材》

  上面的两个Specification的文档虽然不是从官网找的,但是可信度还是有保证的。我们学校图书馆有那本中文的书,基本上跟规范里的内容是一致的,而且应该更好理解一点。第一个规范里主要看第七章“Software Initialization and Configuration”,里面介绍了PCIe配置空间的大部分寄存器;第二个规范介绍的是PCI协议,PCIe很多都跟PCI兼容,所以这个文档也很重要,这里主要看第六章“Configuration Space”,MSI相关的寄存器只有在这个文档里才有。在开发过程中,Xilinx的IP文档里没有对这些配置寄存器做具体的说明,所以需要查看这两个规范文档。
  目前我的需求是实现FPGA和DSP之间利用PCIe链路进行通信,所以准备先从FPGA这边的实现开始介绍,然后再写DSP相关的配置。我采用的方式是DSP作为RC(Root Complex)端,FPGA作为EP(End Point)端。我觉得作为嵌入式开发者,对于PCIe的协议其实不用理解特别深入,能够实现初始化配置、数据传输和中断就可以了。

PCIe协议简介

PCIe的配置空间

  首先最重要的就是PCIe的配置空间,当PCIe板卡插到主机上时,host可以通过这一块配置空间获取PCIe设备的信息,同时可以对其进行配置。
在这里插入图片描述
  这块空间的前64个字节(0x00~0x3F)作为这块空间的头部,有两种类型,Type 0 和Type 1 。Type 0 是作为端点设备(EP)的设置,Type 1 是作为跟复合体(RC)或者交换结构(Switch)的设置。也就是说,主机和从机,这块配置空间头部是不同的。

BAR空间

  BAR,Base Address Register,就是基址寄存器。什么的基址呢?PCIe的数据空间的基址。PCIe的EP有一块自己的数据空间,上游设备可以通过发包的形式直接对指定的数据空间进行读写。
  一般只在EP上实现BAR,很少有在RC上实现BAR。因为一般都是主机来方位PCIe设备的数据空间。
  如果EP上是32位的BAR,它会先用BAR中低位0的个数来表明BAR对应的数据空间的大小。比如BAR的初始值是“0xFFFF0000”,它的低位有16个0,这几个bit都是硬件上固定的,RC无法改写。这个BAR就表明了它的大小是64kB。RC先读这个BAR,根据低位0 的数量确定了BAR对应的数据空间大小之后,再对高位的“F”进行改写,改成需要的PCIe地址空间的地址。比如改写成“0x68000000”,那么从0x68000000开始的64kB的数据就是这个BAR对应的数据空间。
  如果EP是64位的BAR,情况会有点不一样,但是大同小异。

MSI中断

  MSI,Message Signaled Interrupts,EP可以用这个给RC发中断。MSI中断实际上是EP向RC发送双字数据包,这个双字的地址和内容由RC给定。就是在系统初始化的时候,需要由RC的系统软件往EP的MSI Capability Structure里面进行配置。不难理解,这里的一组寄存器只有RC能够对它改写,因为这是RC告诉EP可以往哪里写数据就表示发中断;如果EP随便往一个地址写数据,搞不好会出大问题。MSI Capability Structure有下面这几种结构:
在这里插入图片描述
  这几种结构的区别在于是32位地址还是64位地址,是否支持Per-vector Masking。
  地址的区别很好理解,如果是64位地址,就会多一个Message Upper Address的寄存器。
  Per-vector Masking是一个可选的功能。支持这一功能的话就会多一个Mask Bits的寄存和一个Pending Bits的寄存器。Mask Bits中每一个bit对应一个MSI vector,MSI总共支持32个中断向量。如果把相应的bit置一,EP想要发送这个中断向量的中断的话,就发不出去,而对应的Pending Bits中的bit会置一。如果把mask撤销,这个被挂起的中断就会被发送。
  Message Control寄存器中的有相应的只读的bit来表明目前这个设备是哪种结构的MSI Capability Structure。Xilinx的PCIe的IP好像是支持64bit地址,但是不支持Masking。

Xilinx提供的IP

  Xilinx 提供了三个和PCIe相关的IP,就是下面这三个。文档都可以在DocNav里很容易找到。

  1. Integrated Block for PCI Express
  2. AXI Memory Mapped to PCI Express
  3. DMA/Bridge Subsystem for PCI Express

  我感觉第一个IP的接口是最复杂的,有四个Stream通道,然后又有很多物理层的配置接口。这个应该是PCIe最底层的IP,Virtex 7系列的FPGA的IP文档还是独立的,但都是“Integrated Block for PCI Express”。这个文档里有一部分内容很重要,就是第二章的PCI Configuration Space,这里介绍了PCIe配置空间的寄存器分布。即使我们实际用的是另外两个IP,也需要通过这个文档查寄存器的位置。而这每个寄存器的功能就要查之前说的PCIe的协议规范了。
  第二个IP应该是用AXI接口封了一层,这个AXI接口直接访问PCIe的BAR寄存器对应的数据空间。这个就很方便了,相当于我可以直接从PCIe的数据空间读写数据。这个数据空间既可以是存储器,也可以是外设,总之非常方便灵活。然后这个IP还支持传统的PCI中断,也支持MSI中断。当我们拿它作EP时,可以给RC发中断,这个在之后的具体设计中应该也会经常用到。
  第三个IP的功能最强大,它不仅实现了第二个IP的功能,而且实现了数据上下行的DMA通道,这个IP也被称为XDMA。RC可以给它发送DMA传输的描述符,然后它自己就能搬数据。它也有DMA Bypass 的通道,就是RC也可以不用XDMA,而是像第二个IP一样直接对一块数据空间进行读写。
  第二个IP和第三个IP我觉得各有侧重点。第三个IP虽然既能DMA传输,又能DMA Bypass地直接访问,但是它的Bypass空间只能有一个BAR寄存器对应,而第二个IP可以灵活地设置多个BAR。而且因为第三个IP实现了XDMA,它把原有的最多32个MSI中断中的16个固定作为了DMA传输中断,剩余16个留给用户;而第二个IP则是32个MSI中断都可以由用户自己使用。
  不管是用哪个IP都可以根据实际需求来选择,在每个IP的文档中的Product Specification里面介绍了相关的寄存器。然后要看清楚有些寄存器写着“Only applicable to Root Port cores”,那就是它只能在用作RC的时候使用,要分清楚哪些寄存器是作为RC用的,哪些是作为EP用的。

电路设计

  • UG476 - 7 Series FPGAs GTX/GTH Transceivers
  • 对应器件的 Integrated Block for PCI Express(PG023、PG054、PG156、PG213)

  我用的FPGA是国产的的Virtex 7 系列的XC7VX690T,其实跟Xilinx的是一样的用法。FPGA的PCIe接口是在BANK115上做的,在设计电路的时候要查看上面两篇文档来选择BANK。
在这里插入图片描述
  先看Integrated Block for PCI Express,第四章Design Flow Steps里的Constraining the Core里的Transceiver Placemenet里面介绍了每条链路应该放在哪个Block里。要找准型号和封装,就比如我用的是XC7VX690T,就找到下面这张表,然后找到FFG1927的封装,准备用下面红框中的这几个Block来做一个x4的链路。
在这里插入图片描述
  找到Block之后就需要去另一个文档——UG476里面找对应的引脚位置。在附录A里面根据Tranceiver的类型和封装可以找到下面这张图。我们在上面已经知道了LANE0是在X1Y23的Block,就像下面红框中标注的一样,我们用红框内的引脚作为LANE0的引脚。所以我们就依次可以确定LANE0~LANE4的发送和接收的差分对应该接哪几个引脚。
在这里插入图片描述
  DSP的电路没有什么好说的,PCIe的模块引脚都是固定的。值得一提的是时钟的问题,PCIe通信的双方时钟可以是同步时钟,也可以使异步时钟。如果用异步时钟的话需要注意下面的问题。我用的是异步时钟,但是一般来说同步时钟用得比较多。
关于时钟

Block Design

  下面是我的整体的设计,主要是实现了让DSP通过PCIe接口访问FPGA上外接的DDR的功能。
在这里插入图片描述
  新建一个Block Design之后,加入“AXI Bridge for PCI Express”的IP,点击页面上方的“Run Block Automation”之后,可以做一下简单的配置(之后还要改),它就能够生成一个时钟输入的Buffer
在这里插入图片描述
  双击对其配置,这里的PCIe Block Location是和具体的引脚引脚分配有关的,已经在前面介绍了。如果这里的Block选得不对,后面的Implement就会报时序的问题。
在这里插入图片描述
  IP配置中的PCIe ID那一栏可以默认不管;MISC那一栏是中断相关的,根据需求配置即可;AXI:BARs是AXI地址到PCIe地址的转换,这个应该是用了AXI Slave接口之后才会用到,我这次的设计里没有用Slave接口所以这个也没配置;Debug Options也没啥东西。所以只剩下这个PCIe的BARs了,我分配了三个BAR空间,如下面的图所示。
在这里插入图片描述
  BAR0指向的是AXI PCIe的配置空间,它其实非常小,64kB已经完全能够访问到所有寄存器了;BAR1是指向的是我自己做的IP,用来从向RC发MSI中断;BAR2指向的是DDR内的128MB的数据空间。这里的PCIe to AXI Translation只要把AXI的基址填上去就行,要跟Address Editor里面的相对应。Address Editor里的地址我是自动分配的(很多可能BAR访问不到,但这不要紧)。用了XADC是因为DDR的MIG需要一个温度输入,如果MIG里面能直接Enable XADC那就很省事了,但我这不行所以在外面加了XADC。

在这里插入图片描述

一些经验

  Windows下的Vivado默认线程(thread)数是2个,Linux系统下的默认线程数是8个。可以用下面的tcl命令获取当前的最大线程数:

get_param general.maxthreads

  Windows下的支持的最大线程数是8个,可以用下面的tcl命令更改

set_param general.maxthreads 8

  每次启动Vivado,这个最大线程数都会回到默认的2个,可以用这个博主的方法来一劳永逸地更改Vivado的默认线程数:链接
在这里插入图片描述
  线程数量越多,实现每个job的速度越快。在Run Setting里面的Number of jobs是指可以同时运行的job个数,我们一般只有一个实现,所以即使把这个设置得很大,也不会有加速的效果。Number of jobs的设置在预先独立综合每个IP的时候很有用,每个IP可以各自综合,不同IP综合之间的并行会受到这个数量限制。
在这里插入图片描述
  我之前为了在一个工程里实现两个方案,所以新建了一个Run,同时把不用的源文件disable掉。但我看别人好像是用不同的约束文件来限制不同的Run,但是源文件是不用改的。我这么做的时候要改源文件,有时候会报一些奇怪的报错,但确实能在一个工程里做两个实现。
在这里插入图片描述
  最后是在读写DDR的时候,出现了这个Transaction Overflow的问题,但是数据都是没错的。DDR在读写的时候会有好几个Outstanding的Transaction,就是那种在读写,但是还没有响应完成的事务。我用的是加在Block Design里面的System ILA,只要在这个IP里面把Enable Transaction Tracing Counter的勾去掉或者把计数值加大就好了。
在这里插入图片描述

  • 39
    点赞
  • 414
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
### 回答1: FPGA是可编程逻辑器件,提供灵活的硬件编程能力。PCIe接口是一种高速数据传输标准,能够满足大规模数据传输的需求。将二者结合起来可以实现PCIE接口测试程序,方便测试和验证硬件设计的正确性和性能。 实现PCIE接口测试程序需要掌握FPGA硬件设计和PCIE协议的基本知识。首先需要编写FPGA的硬件描述语言程序,设计FPGA接口电路和寄存器等基本模块。 接着需要编写PCIE协议的相关控制器程序,进行端点或者根端点的控制和数据传输。需要了解PCIE中的传输协议和信号电平等相关信息,如TLP,DLLP,ACK等。 在实际测试中,可以通过使用PCIE调试工具或者PCIE模拟器等辅助工具来验证PCIE接口的数据传输和控制是否正确。可以采用连续读写数据、回环测试等方法逐步验证。 在测试过程中,需要注意FPGA与主机的连接方式和传输速率等参数的匹配,以保证测试结果的准确性和稳定性。同时还需要注意根据具体应用场景选择适当的测试工具和方法,如信号分析仪、差分探针等。 总之,FPGA实现PCIE接口测试程序是一项重要的硬件设计工作,需要深入理解相关协议和信号电平等知识,并具备较强的硬件开发能力。 ### 回答2: FPGA作为一种可编程逻辑器件,它具有高度的灵活性和可定制性,能够实现各种各样的应用场景。其中,实现PCI Express(PCIe接口测试程序就是一个典型的应用场景。 在FPGA实现PCIe接口测试程序,主要分为如下几个步骤: 第一步,设计FPGA的逻辑架构。这个逻辑架构应该基于PCIe接口的规范,包括端点(Endpoint)和根端点(Root Port)的架构,以及信号定义、时序、寄存器等方面的细节。 第二步,通过FPGA开发工具,编写PCIe接口测试程序的源代码,包括基于PCIe规范的收发数据接口以及基于测试用例的控制逻辑等部分。 第三步,将代码编译成位流文件,然后通过JTAG或其他方式烧录到FPGA芯片中,并进行原语级仿真和功能仿真以确保功能的正确性。 第四步,连接FPGA芯片和测试设备(如测试仪器、PC机等),按照事先定义好的测试用例进行测试,并记录测试结果。 第五步,根据测试结果进行调试和修正,直至测试结果符合预期,确保FPGA实现PCIe接口测试程序的准确性和稳定性。 总之,通过FPGA实现PCIe接口测试程序,不仅可以实现高效的数据交换和传输,还可以提升测试的灵活性和精度,同时也提供了便捷的平台进行调试和验证。 ### 回答3: FPGA实现PCIe接口测试程序的过程中,首先需要应用FPGA的硬件描述语言进行设计和开发。这个过程中需要针对PCIe接口标准进行深入的研究,理解其协议和数据传输格式。同时,还需要对FPGA的IO资源进行规划和分配,确定每个输入/输出信号的物理接口位置和引脚连接方式。 接着,我们需要使用FPGA开发工具,如Xilinx或Altera,来编写程序和进行验证。FPGA的编程语言可以是Verilog或VHDL,这些语言都具有高度的硬件描述能力,可以使我们在FPGA实现按照PCIe接口标准执行的逻辑。 在开发阶段中,我们需要进行逐步验证和测试,确保程序的正确性和有效性。这个过程中可以使用仿真工具进行模拟测试,还可以通过支持PCIe接口的开发板来进行硬件测试。 在项目开发完成后,我们就可以将FPGA板卡和PC连接起来,进行PCIe接口的测试和验证。在测试过程中,我们连接了PCIe接口的输入/输出信号,并通过测试程序来发送和接收数据。通过对测试结果进行分析和比对,我们可以评估FPGA实现PCIe接口性能和可靠性,确保其满足设计要求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小裘HUST

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值