ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FPGA PCIE卡示意图别乱画:吃透AXI握手、DMA与PCIE枚举是关键

FPGA PCIE卡示意图别乱画:吃透AXI握手、DMA与PCIE枚举是关键 别人把需求丢过来的时候通常就一句话“帮我把这块FPGA PCIE卡的示意图画一下。”等你打开画图软件准备拖几个方框才发现真正难的不是连线而是你根本不知道这个框里该有几层东西、哪些信号该画、哪些答案问出来能兜住。基于AXI PCIE IP的FPGA PCIE卡图面看起来很简单无非是金手指、IP核、用户逻辑三个大块但实际画出来能不能指导后续开发、能不能顺手预估带宽和排查问题完全取决于你是不是真把AXI握手、DMA路径和PCIE枚举过程想明白了。这篇文章我按自己画过几张PCIE卡的经验把一张示意图背后必须吃透的内容拆开讲。准备硬件的、搞FPGA的、甚至只负责和驱动讨价还价的都能在这套思路里找到自己能用的部分。1. 画PCIE卡框图前先搞懂这三层数据流1.1 一张能指导开发的示意图信息量要大于“方块箭头”很多人复盘示意图的时候画的是一张“架构概念图”左边PCIE金手指中间一个IP方块右边用户逻辑箭头是双向的完事。这套东西拿去开会对齐方向没问题但拿去做设计、写RTL、和驱动同事讨论地址映射基本等于什么都没说。一张能拿出手的FPGA PCIE卡示意图至少要包含这几类信息物理链路PCIE x1/x2/x4/x8、参考时钟来源、复位关系以及金手指处需要的电源供电结构。IP内部边界用的是Xilinx AXI PCIE IP还是带DMA引擎的XDMAIP自己的配置空间、BAR空间、中断逻辑在哪里。总线接口形态AXI Memory Mapped还是AXI Stream位宽是多少工作在什么时钟频率下。用户侧数据路径DMA读写请求往哪里发DDR控制器在整个路径中处于什么位置图像/采样数据从哪个接口进来。控制与状态路径寄存器表通过哪条AXI-Lite拿到中断是MSI还是MSI-X由谁触发、往哪送。把这些信息标上去图就不再是“给别人看的概念图”而是帮你发现自己设计漏洞的检查清单。我见过一张PCIE卡的框图上只画了一个IP、一根DMA箭头、没有DDR结果后面驱动同事问地址映射整个团队在图前站了十分钟没人说清楚数据从哪落盘。1.2 三路数据流配置流、DMA流、控制流PCIE EP卡的逻辑流看着复杂归纳起来就三路画图时最好用三种颜色区分第一路是配置流。主机侧CPU通过Root Complex枚举时往PCIE配置空间里读写配置请求。在AXI PCIE IP内部这类配置请求通常不会直接打到你的用户逻辑而是由IP自己消化后生成AXI事务。换句话说主机访问BAR空间时本质上是在读写在FPGA里映射出来的一段AXI地址。第二路是DMA数据流。这是吞吐量的大头无论是FPGA往主机搬运数据还是主机下发数据到FPGA数据都是通过DMA描述符告诉硬件“源地址、目的地址、长度”再由DMA引擎实际发起PCIE Memory Read/Write TLP。画图时这条路径要画得特别粗因为它决定性能。第三路是控制流。包括寄存器表、中断、状态信号。控制流的带宽很小但时序要求很高中断延迟直接影响驱动效率。画图时把它单独画成细线不要和DMA数据流混在一起否则后面做时序约束和驱动联调你会被自己画的图坑到。把这三路数据流在图上搞清楚你才算是真正“看见”了这张卡。接下来要聊的就是这三流里最容易被忽略又最容易翻车的那个环节。2. PCIE枚举那点事IP在你不知道的时候做了什么2.1 枚举过程到底是“谁问谁答”很多FPGA工程师写用户逻辑时不会关注PCIE枚举觉得那是驱动和BIOS的事。可真到了调试阶段Vendor ID读不出来、BAR地址不对、设备在系统中不出现最后都得回到枚举这个过程里找原因。PCIE枚举的主线是这样板卡上电后链路先做训练也就是LTSSM状态机从Detect一路走到L0物理层有了基础链接。Root Complex随后发起配置请求枚举时向总线上每个设备分配Bus/Device/Function编号读每个设备的Vendor ID、Device ID、Class Code、Revision ID等配置信息。如果这些ID和驱动预期的对不上驱动可能拒绝加载。接下来RC会读取设备的BAR大小和类型。注意这里的顺序RC先向BAR寄存器写全1再读回以此判断BAR需要多大的地址空间以及能否预取。AXI PCIE IP内部已经帮你实现了这些逻辑但你必须在IP配置界面里设置正确的BAR数量、大小和类型。这块的坑在于BAR大小决定了主机给FPGA映射的存储窗口如果FPGA侧内部寄存器只做了4KB但你BAR0配了8KB那主机窗口中超出4KB的部分访问下去是没意义的。反过来BAR配小了驱动一访问就越界。所以画图时应该把每个BAR对应的FPGA侧地址区间标出来比如BAR0窗口4KB用于用户寄存器BAR2窗口1MB用于DMA描述符或数据缓冲区访问。2.2 BAR空间与AXI地址映射的关系理解BAR映射核心是搞懂“PCIE地址”和“AXI地址”不是同一个概念但有固定关系。主机CPU访问一段物理地址Root Complex把该访问转换成PCIE TLPTLP里的地址是BAR空间对应的地址。AXI PCIE IP收到TLP后丢掉PCIE路由信息把TLP转换成AXI读或AXI写事务地址变成AXI地址。也就是说IP核内部有一个固定地址转换规则主机访问BAR0的偏移0x100到FPGA侧大概率就是AXI地址0x100如果不开偏移和地址重映射的话。如果你的卡上还要再经过AXI Interconnect做地址译码那最终落到寄存器上的地址就要看互连接线的映射配置。我自己调试时踩过一个例子驱动往BAR00x100写控制字逻辑里却检测不到写信号查了很久发现是AXI Interconnect配置时把从机地址段设置成了0x1000起始等于驱动写的是0x100而互连寻址的是0x1100对不上。示意图上如果早写清楚“BAR0 → AXI从机地址段0x0000-0x0FFF”这种问题五分钟就能定位。另外PCIE枚举过程中还会配置中断机制。如果IP支持MSI/MSI-XRC会分配中断号并把MSI地址和数据写入配置空间。IP把这些信息送给DMA引擎或AXI中断控制器用户逻辑在需要时向该地址发起写操作就完成了一次中断请求。示意图里中断路径千万别省MSI-X Table、PBA都在IP内部外部连接很简单但你不画出来调试时驱动说“没收到中断”根本无从下手。3. AXI握手中的背压逻辑这才是不丢数据的命根子3.1 valid/ready不是简单的“拉高拉低”不管是看XDMA手册还是自己写DMA状态机AXI握手语言就两条规则但很多人连这两条都没彻底理解就往上冲valid信号表示发送方准备好发送数据valid一旦拉高必须保持到本次握手完成不能中途撤销。ready信号表示接收方准备好接收数据ready可以因内部FIFO状态随时拉高拉低。当valid和ready同时为高时传输才真正发生。如果一本正经地描述很多新手的第一反应是“那我用valid通知接收方接收方用ready应答不就行了”。其实差别很大。Acknowledge式握手是“我先跟你说你回我收到”而valid/ready是“我保证我的数据一直有效直到你告诉我你收到了”。这个机制的最关键意图是让接收方拥有反压能力你让我等多久我都等着直到等来你的ready。实际写代码时最容易出错的是valid依赖ready的情况。比如发送方写状态机判断“如果上一次ready拉高了这次才拉valid”这就违反了valid不能等待ready的规则很可能会造成死锁。正确做法是valid只由自己的发送请求和内部FIFO空满决定ready归接收方管。3.2 stall与背压为什么读/写通道都要拍一拍AXI-Stream里最常听到的一个词叫stall其实就是接收端拉低ready让发送端停下来。这个“停”不是停掉时钟而是通过保证valid持续有效暂停一次传输让数据留在总线上。所以stall本质是背压backpressure的一种实现。为什么PCIE场景下背压这么重要因为PCIE IP的接收FIFO是有限深度的。假设FPGA侧做图像采集DMA引擎不停往主机写数据但是主机内存带宽瞬时不足、驱动又没及时取走描述符PCIE IP接收侧FIFO就会满。这时候IP会通过链路层的流控机制向主机发送背压信号要求主机端暂缓发送数据。主机侧的PCIE控制器也会对CPU访问做重试或等待处理。反过来FPGA到主机方向的写请求如果遇到PCIE链路拥塞IP把写请求挂起用户侧的AXI接口就会出现写响应延迟很长的情况。如果用户逻辑的写数据FIFO没有做好ready反压就会来到“数据已经present但对方不收”的状态最后要么数据被覆盖要么逻辑状态机跑飞。所以当你在示意图上只画一根粗箭头写个“DMA Data”的时候其实漏掉了最关键的保障机制这根粗箭头上必须同时画出valid/ready两个方向的信号并且注明它们连接到哪个FIFO的空满标志上。没有背压设计的数据通路只能叫草图不能叫设计。3.3 在示意图上怎么画握手信号才专业画图时不需要把AXI的每一根信号都画出来但要画到“通道和方向”这个粒度。比如AXI-MM有5个通道写地址AW、写数据W、写响应B、读地址AR、读数据R。每个通道都有valid/ready画图时至少要在箭头两端标注出“M - S”还是“S - M”。AXI-Stream则通常是一组单向数据流master向slave发TVALID、TDATA、TKEEP、TLASTslave向master回TREADY。如果还有TID/TDEST这种边带信号也可以标上。很多人画图不画TLAST结果后面做包传输时接收端不知道包边界在哪只能在数据里自己定义帧头帧尾白白增加逻辑开销。我的习惯是在示意图的接口名称一栏写“AXI-Stream (512bit, tvalid/tready/tdata/tlast)”旁边再用一个小箭头标出backpressure方向。这样拿到图的人一眼就知道这里会不会stall带宽大概是多少。4. 从AXI MM到AXI Stream示意图里总线该怎么画4.1 AXI Memory Mapped带地址的快递AXI Memory Mapped简称AXI MM是一种带地址的总线接口。每次传输主机都必须提供目标地址接收方把数据放到对应地址上。它适合随机访问、寄存器配置、或需要按地址搬数据的情景。Xilinx AXI PCIE IP对上电后暴露的通常是一组AXI MM接口支持master和slave两个方向。Master接口用于FPGA主动发起主机内存读写slave接口用于主机访问FPGA内部寄存器。XDMA的SGDMA引擎也是通过AXI MM master去访问主机内存里的描述符然后搬运真正的数据。如果示意图上画的是“AXI MM master到DDR控制器”意思就是DMA引擎可以把数据写到板载DDR里也可以从DDR读回来再发给主机。这种结构在高速采集卡、图像加速卡里非常常见因为DDR充当缓冲能够吸收主机带宽抖动。4.2 AXI Stream没地址的传送带AXI Stream和AXI MM完全不是一个思路。AXI Stream没有地址通道数据一发接收方按顺序收核心信号就是tdata、tvalid、tready、tkcep、tlast。它像一条传送带只管把东西一个个送过去不关心东西要放哪个货架。所以数据流比较固定、本身就按“包”或“行”组织的场景最适合用AXI Stream。图像传感器经过MIPI转出来的数据、以太网MAC送出来的网络帧、ADC采集的连续采样流都是AXI Stream的天然用户。在PCIE卡里很多时候会做一次格式转换XDMA或自研DMA从主机侧以AXI MM的形式读写内存但FPGA内部图像处理链路用AXI Stream。两种总线之间通过AXI DataWidth Converter、AXI Stream FIFO或手动状态机做桥接。示意图上应该明确标出“这里存在AXI MM → AXI Stream的转换点”因为转换点的两侧时钟、位宽、背压策略可能完全不同。4.3 图中需要标注的关键信号组我不建议把底层信号全部画出来但以下信息必须体现在图中总线位宽比如AXI MM是256bit还是128bitAXI Stream是512bit还是64bit。这个直接决定理论带宽。时钟域PCIE user clock是250MHz、125MHz还是500MHzDDR用户接口时钟又是多少。跨时钟域点要画成异步FIFO。主从方向AXI MM接口用箭头“Master/Slave”或者“M/S”标注AXI Stream则画单向箭头另一条线表示tready。FIFO深度关键的异步FIFO要在图上标出深度比如“1K x 512bit”。深度不够是背压丢包的常见元凶。有一类示意图画得很漂亮但别人问“这个FIFO是几深的”“这个AXI总线是AXI4还是AXI4-Lite”答不上来那这张图基本就是摆设。PCIE卡设计和纯软件架构图最大的区别就是参数即约束位宽、深度、频率任何一个参数不对做出来的硬件都不工作。5. XDMA还是自研DMA我两种都试过给你说点实话5.1 XDMA的框图与分工XDMA的全称是DMA/Bridge Subsystem for PCI Express。它把PCIE硬核、DMA引擎、中断控制和AXI接口集成在一起对大多数应用场景来说这是从零开始做PCIE卡最省力的方案。XDMA内部大致分这几块PCIE接口包含PCIE物理层、数据链路层、事务层负责和主机交互TLP。SGDMA引擎通过读取主机内存中的描述符环形队列获得传输源地址、目的地址、长度和控制位。AXI主接口XDMA作为主机通过AXI MM master访问FPGA侧DDR或者用户逻辑搬运数据。AXI从接口通常以AXI4-Lite或AXI MM的slave形式把XDMA的控制寄存器、状态寄存器开放给主机。中断控制器把用户逻辑产生的中断打包成MSI/MSI-X中断发送给主机。画XDMA示意图时很多人只画出“XDMA和DDR连接”但忘了画描述符这条链路。其实XDMA每次真正搬数据前都会先去主机内存里读描述符然后按描述符更新状态。描述符的读写走的是AXI MM master和用户数据共用同一个接口只是时间和地址不同。这张图如果想指导驱动开发必须把“HOST Memory: Descriptor Ring”这个节点画出来。5.2 自研DMA的适用场景但不是所有场景都值得上XDMA。Xilinx还有一种更精简的选择就是只使用AXI PCIE IP不集成DMA引擎。这时候FPGA自己要写DMA逻辑。自研DMA的典型做法是PCIE IP提供一个AXI MM master接口自己实现一个状态机负责从主机得到一个描述或者按照寄存器里写好的源地址/目的地址发起传输。因为没有硬件描述符队列所以在简单场景下更可控。比如固定长度的采集卡只需要主机往寄存器里写一个内存地址和一个上传长度FPGA就开始往那个地址发写请求。这种设计不依赖XDMA内部状态机反而容易做出低延迟。自研DMA的缺点也很明显你要自己对TLP的地址对齐、payload大小、标签管理负责。PCIE IP会帮你做TLP组包和拆包但burst的大小、outstanding请求的数量直接决定能不能把链路带宽吃满。没有充足经验的话自研DMA很容易做到500MB/s就上不去。我的经验是如果应用是以数据搬运为主、主机驱动也不想写太复杂直接选XDMA如果数据流里面有大量私有协议、更愿意自己控制所有时序就选AXI PCIE IP加自研DMA但一定做好打硬仗的准备。5.3 描述符和中断这是最容易画错的地方无论是XDMA还是自研DMA描述符和中断是绕不开的两个点。示意图中最常见的错误是把描述符队列画在FPGA里。实际上对于真正的SGDMA描述符队列应该放在主机内存里因为驱动要能动态地提交和回收描述符放在FPGA里只能做小规模固定操作。XDMA使用环形描述符在驱动初始化时分配一块连续的物理内存把描述符地址和数量告诉XDMA寄存器。XDMA搬完一段数据后写回状态并产生中断通知驱动回收。驱动在中断处理里更新tailXDMA读取head和tail决定下一个处理哪个描述符。画图时这段环形链路要画成“Host RAM - XDMA”的虚线箭头不是一根数据箭头。中断方向也常画错。XDMA的中断可以由DMA传输完成触发也可以由用户逻辑置位一个中断寄存器触发。用户逻辑通常通过AXI-Stream或者AXI-Lite接口向XDMA发送中断请求然后XDMA产生MSI/MSI-X。示意图里如果只在靠近金手指的位置画一个“Interrupt”方块不标明是MSI还是MSI-X也不说明谁触发那驱动调试时还是不知道去哪查。6. 带宽估算与调试画完图之后的硬仗6.1 从PCIe Gen3 x4说起链路带宽不等于有效带宽画PCIE卡带宽估算是基本功。以最常见的Gen3 x4为例每通道速率是8GT/s利用128b/130b编码每个通道的有效数据率大概是7.877Gbps通道数4理论最高约3.94GB/s。但这不是用户能拿到的带宽。PCIE是包交换网络每个TLP前面有header数据链路层还会加序列号和CRC物理层还会插入一些控制字符。实际有效payload率通常要打7到8折。加上DMA描述符读取、中断响应、内存访问对齐等因素能做到3GB/s稳定吞吐已经是很不错的结果。示意图上不要只写“PCIE Gen3 x4”最好把理论带宽、预期有效带宽都标在对应链路上。比如写“PCIe Gen3 x4, 理论3.94GB/s, 应用中约2.5-3GB/s”。这样一来后续做DDR带宽选型时就能直接算数。6.2 实测中的性能瓶颈在哪里我实际测试过的PCIE卡性能瓶颈往往不是PCIE链路本身而出现在这么几个地方第一个是DDR访问效率。DDR控制器顺序读效率高随机读效率低。如果你的DMA引擎专职做256B小包传输DDR效率可能只有50%。示意图里的“DDR Controller”节点务必要标出用户位宽和时钟比如“DDR4 64bit 2400MT/s, 理论19.2GB/s, 实际流式读写约60-70%”。第二个是AXI接口频率和位宽。AXI MM如果是128bit 250MHz理论上4GB/s刚好贴着Gen3 x4的有效带宽上限一加协议开销就满了。想跑满PCIE建议至少256bit接口、250MHz或更高。第三个是outstanding能力。AXI总线允许master在未收到写响应前继续发下一笔写这个“未完成事务数”就是outstanding transaction的能力。Xdma的master通常配置了比较高的outstanding能力自研DMA如果只做一收一发链路空窗期非常大吞吐上不去。第四个是中断频率。每传一个4KB包就发一次MSICPU会被中断淹没。示意图上中断路径后面最好加一个“中断合并”逻辑比如传输多个描述符后再收一次中断能明显提高带宽。6.3 调试工具和手段PCIE卡调试时我自己的顺序是先物理层、再事务层、最后应用层。物理层方面Xilinx的IBERT可以在不写用户逻辑的情况下通过GT wizard生成测试图案验证PCIE每个lane的误码率。如果BER很高先查电源、参考时钟和耦合电容。事务层方面在主机侧用lspci看设备信息确认枚举出来的Vendor ID、Device ID、BAR大小和类型在FPGA内部用ILA观察AXI接口的valid/ready看看有没有持续的stall。尤其是DMA读写的时候可以使用计数器统计拍数一拍数据如果validready有效出现多少次除以总周期数就能算出实际带宽。应用层方面跑一下简单的DMA回环测试把一段数据从主机写到FPGA再从FPGA读回主机对数据内容做校验。如果数据错位或者丢数多半是地址对齐或背压逻辑问题。此时示意图像检查清单一样发挥作用对着数据路径一步步看数据是否在预期的地方停留。最后分享一个小技巧。画PCIE卡示意图永远不要把“Host内存”和“FPGA内部缓冲”画成同一个地方。表格对比一下不同设计阶段的要点设计阶段示意图重点常见坑方案阶段三路数据流、BAR空间分配只画数据箭头不画配置流RTL实现阶段AXI接口位宽、时钟域、FIFO深度不标背压方向死锁隐患驱动联调阶段描述符环形队列、中断类型中断路径不清晰性能优化阶段带宽标注、DDR效率把理论带宽当实际带宽如果你也要画这样一张卡我的建议是先别急着打开画图软件拿出一张白纸把文章开头说的三路数据流画出来然后一项项补上BAR空间、AXI接口细节、DMA描述符路径、中断信号。等这张图经得起“驱动工程师看一眼就知道寄存器写哪”的考验时这张示意图才真正为后续工作省了时间。
返回列表