尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

PCIe BAR配置与AXI Memory Mapped IP核实战:从地址映射到性能优化

PCIe BAR配置与AXI Memory Mapped IP核实战:从地址映射到性能优化 做PCIe设备开发这几年我有个特别深的感受很多人一开始把精力全放在PCIe协议细节、LTSSM状态机、TLP报文格式上结果真上了板子最先卡住你的往往是BAR配置。用Xilinx的AXI Memory Mapped IP核做PCIe Endpoint时BAR配错了主机枚举出来一片空白BAR配对了但地址映射不对齐读写一上去就超时。这篇文章我把BAR的分配逻辑、AXI Memory Mapped IP核在Vivado里的配置路径、以及性能优化几个关键点从头到尾串一遍结合我实际调试中的踩坑记录帮你少走几步弯路。1. BAR到底在干什么先搞懂PCIe地址空间的分配逻辑1.1 主机视角下的BAR设备在PCIe总线上的门牌号BAR的全称是Base Address Register翻译过来就是基地址寄存器。它在PCIe协议里的作用说直白一点给外部设备在CPU的物理地址空间里占一个坑。PCIe枚举的时候Root ComplexRC会扫描整个总线树找到所有连接着的Endpoint设备然后给每个设备分配一段独立的物理地址区间。这段区间怎么分配就是通过读写配置空间里的BAR寄存器完成的。枚举软件先向BAR写入全10xFFFFFFFF再读回来通过读回来数值中哪些位是0就能算出这个设备需要多大的地址空间以及这个空间的对齐要求。比如写入全1之后读回来0xFFFF0000取反加1得到0x00010000也就是64KB。这种机制我第一次接触的时候觉得很巧妙——设备不用主动声明自己需要多大空间主机通过一次写读操作就能自动探测出来。这个逻辑放在AXI Memory Mapped IP核上同样适用。你在Vivado里把IP核的BAR大小配置好之后IP核的配置空间硬件逻辑会自动响应枚举期间对BAR寄存器的写读操作。主机侧看到的是一段连续的物理内存映射区域而EP侧内部实际上是把这段区域映射到了AXI总线上。这里要提醒一个特别容易踩的坑BAR空间的大小必须是2的幂次方而且最小不能小于128字节这是PCIe规范强制要求的。Vivado的IP配置界面里虽然给了很多档位选择但你选的BAR大小会直接决定硬件里地址译码逻辑的复杂度。选得太大浪费地址空间选得太小后面驱动和应用程序都不够用。1.2 四种BAR属性怎么选Memory还是IO32位还是64位BAR不只是简单的大小配置它还带了一组属性位。低4位里有几位专门用来描述这个BAR的类型。我在调板子的过程中见过不少人在这一步随手选完就不管了结果后面各种问题。先看Memory和IO的差别。IO BAR在PCIe时代基本属于“遗老遗少”PCIe规范里虽然还保留着对IO空间的支持但绝大多数新设计都不再使用。IO空间大小有限、访问开销高、还有各种兼容性问题。如果做新项目第一反应就应该选Memory BAR。然后是32位和64位。32位BAR只能把窗口映射到4GB以下的物理地址空间64位BAR则分成两个相邻的BAR寄存器BARn和BARn1组合成一个完整的64位地址窗口可以映射到更高的物理地址区域。从系统架构的角度看64位Prefetchable BAR是性能最好的选择因为系统软件可以把它们映射到64位地址空间的高位区域避免和低位内存产生冲突。还有一个容易被忽略的属性叫Prefetchable可预取。这个名字听起来有点抽象实际意思是这段地址空间的数据不会因为读取操作而产生副作用所以CPU和系统逻辑可以放心地提前预取、合并读写。对于设备端的寄存器空间有些寄存器读一次之后状态就变了这种就不能标记为Prefetchable。但是DMA缓冲区、大块数据存储区都建议用64位Prefetchable Memory BAR。属性优点缺点适用场景IO BAR兼容老驱动地址空间有限、性能差尽量不选32位Memory BAR配置简单只能映射到4GB以下小型寄存器窗口64位Memory BAR映射位置灵活占用两个BAR槽位大容量DMA缓冲区Prefetchable支持合并访问寄存器读副作用场景不能用数据区、缓冲池有一种情况需要特别小心寄存器区域如果误设成Prefetchable驱动读状态寄存器的时候可能会拿到旧数据。因为预取逻辑可能提前读了一次并缓存了结果你的读请求返回的是缓存值而不是设备当前状态。我遇到过不止一次这种“寄存器读出来不对”的问题最后定位到就是Prefetchable配错了。2. AXI Memory Mapped IP核的BAR配置Vivado里的每一步2.1 配置面板逐项解读从BAR0到BAR5在Vivado里例化Xilinx的PCIe IP核不管是最常用的7 Series Integrated Block for PCI Express、UltraScale Integrated Block还是带DMA能力的XDMA只要接口类型选择了AXI Memory Mapped都会有一个BAR配置页面。这个页面看起来选项不多实际每一栏都值得仔细推敲。先看BAR使能。IP核一般提供最多6个BAR槽位BAR0到BAR5但AXI Memory Mapped模式下并不是每个BAR都能独立映射到AXI地址空间。以UltraScale Integrated Block为例有效映射到AXI总线的通常只有前几个BAR。如果你把数据放在BAR4上结果发现AXI那边根本没有对应的地址译码输出那就白配了。我一般建议从BAR0开始用按顺序使用不要跳槽位。再看类型和大小。类型就是上面说的Memory还是IO大小档位从128字节一直到几十GB都有得选。这里有个我自己总结的默认配置寄存器控制区用BAR064位Non-Prefetchable大小给4KB就够用了数据缓冲区用BAR264位Prefetchable大小按实际DMA缓冲池需求来。为什么分开因为驱动访问寄存器时走的是Non-Prefetchable每次都直接访问设备保证读到的是最新状态数据区走Prefetchable允许系统做合并和预取吞吐更好。还有个选项叫“Class Code”或者设备类型这个和BAR没有直接关系但会影响系统对设备的识别。如果你做的是存储控制器就填Mass Storage Controller做网卡的填Network Controller。类代码填错了操作系统会加载错误的通用驱动导致后续BAR空间访问逻辑变得诡异。最后是高地址还是低地址。Vivado里一般会问你BAR是否支持64位以及是否支持Prefetchable。选择64位支持之后软件枚举阶段会自动把高32位BAR寄存器配合使用分配一个连续的64位地址段。这里强烈建议所有BAR都打开64位支持哪怕你实际只用了低32位。因为系统在分配地址时64位BAR的选择范围更大不容易和其他设备地址冲突。2.2 从PCIe地址到AXI地址映射关系怎么设计才不会翻车配置完BAR大小和属性真正的工作才刚开始——PCIe地址空间到AXI地址空间之间的映射关系必须明确设计。Xilinx的PCIe IP核在AXI Memory Mapped模式下BAR空间和AXI地址空间之间默认存在对应关系。不同IP版本实现不太一样有些是直接拿去掉BAR基地址后的偏移量作为AXI地址有些则允许你配置地址偏移寄存器。比如XDMA IP核它的AXI地址映射寄存器就允许DMA引擎把PCIe地址空间里的某个区间映射到用户逻辑的AXI地址空间里的任意位置这种灵活性在驱动做IOMMU映射时非常有用。我在设计阶段做了一件让后续调试轻松很多的事把所有BAR空间手动划分成几个清晰的region。比如BAR0基地址0x0000到0x00FF是控制寄存器0x0100到0x01FF是状态寄存器0x1000以后是门铃寄存器BAR2整个就是数据缓冲区。软件访问每个区域都遵循相同的地址偏移规则硬件里地址译码逻辑也一目了然。常见的一个错误是端到端地址不匹配驱动在主机侧访问BAR地址偏移0x0000期望操作设备里的寄存器A但硬件里AXI译码逻辑把偏移0x0000翻译成了寄存器B两边定义不一致读出来全是乱码、写进去没反应。所以强烈建议在项目初期就把“PCIe偏移地址—AXI地址—寄存器名称”的对应关系以表格形式写下来作为硬件和驱动两边共同遵守的接口契约。还有一个很多人忽视的点如果EP内部有多个AXI从端口BAR和从端口之间需要做交叉翻译。有的设计里DMA引擎的AXI主端口要访问主机内存同时BAR的AXI从端口接收主机访问两个方向可能共用同一段AXI地址空间这时候必须仔细设计优先级和仲裁策略避免写冲突。3. 性能优化让AXI MM接口真正跑满PCIe链路3.1 先分清MPS和MRRS这俩参数决定你能跑多快性能优化和BAR配置看起来是两件事实际上它们共享了同一组寄存器里的几个位。Max Payload SizeMPS和Max Read Request SizeMRRS是PCIe设备性能的两个核心参数都配置在Device Control寄存器里。MPS决定了一个TLP数据包最多能携带多少有效载荷有128B、256B、512B等几个档位。这个值是整个链路协商出来的取的是RC、Switch、Endpoint里所有设备支持的最小值。也就是说即使你的IP核支持512B MPSRC只支持256B实际生效的就是256B。MRRS决定了一次读请求最大能请求多少数据它不要求小于等于MPS但读请求会被拆成多个Completion包返回。这两参数对性能的影响差异体现在读写方向上。写性能主要由MPS决定MPS越大写TLP的有效载荷比例越高TLP头部的开销占比越小。读性能则主要由MRRS决定MRRS太小意味着CPU发一次大块读设备要拆成一堆小的读请求去处理往返延迟立刻放大。我实测过一个场景MRRS从128B提高到512B读吞吐提升了差不多25%到30%。这个差距在长距离传输或者经过PCIe Switch转发时更明显。在Vivado IP配置界面里你通常能看到Target MPS或者Max Payload Size的设置项但MRRS更多时候是由驱动侧在Device Control寄存器控制寄存器里写的。做驱动的时候建议初始化阶段显式把MRRS写入合理的值比如512B或更大不要依赖BIOS的默认值。还有一个容易忽略的坑某些老版本BIOS会把MRRS限制在128B驱动不主动改的话你链路速率再高也没用。调试的时候可以先用setpci命令直接读寄存器的当前值确认实际协商出来的MPS和MRRS。3.2 AXI数据位宽、时钟频率与链路带宽的匹配AXI Memory Mapped接口的吞吐能力由数据位宽和时钟频率共同决定。如果这个数值低于PCIe链路能提供的带宽瓶颈就不在PCIe上而在你自己设计的AXI总线上。算一下常见场景。PCIe Gen3 x4链路物理层速率8GT/s编码方案128b/130b单向理论有效带宽是8Gbps乘以4除以130再乘以128大约31.5Gbps也就是3.94GB/s。如果你的AXI接口是64位跑250MHz理论带宽只有16Gbps约2GB/s这时候AXI就成了瓶颈。如果换成128位跑250MHz理论带宽32Gbps刚好能接近PCIe链路极限。我一般建议AXI侧理论带宽至少留出20%余量因为还有读写混合、仲裁开销、B响应通道占用等损耗。下表是我实际项目中常用的搭配参考PCIe链路链路有效带宽单向AXI位宽AXI时钟匹配度Gen3 x2约1.97GB/s64-bit250MHz合适Gen3 x4约3.94GB/s128-bit250MHz合适Gen3 x4约3.94GB/s64-bit250MHz不足Gen3 x8约7.88GB/s128-bit500MHz勉强Gen4 x4约7.88GB/s256-bit250MHz合适前面说的是理论带宽实际上你还要考虑时钟域交叉的问题。AXI接口的时钟可以和PCIe IP核内部的user clock同源也可以由用户逻辑提供独立时钟。如果两个时钟频率不同之间需要异步FIFO做缓冲缓冲深度不够会在高负载下出现反压吞吐直接掉一大截。我建议在有条件的情况下尽量让AXI时钟频率等于或者略高于PCIe内部时钟频率并且预留足够的写入缓冲。提一个测量经验写吞吐比读吞吐更容易跑满链路。因为写操作是单向的只要数据链路不拥堵TLP发出去就完事读操作需要请求—响应来回Completion返回的延迟会限制最大吞吐。这就是为什么很多DMA测试里write速率能到3.6GB/sread速率只有2.8GB/s。想逼近极限需要配合下面3.3节说的outstanding机制。3.3 Outstanding与流水线深度让延迟不再卡吞吐AR和AW通道上的outstanding能力是AXI Memory Mapped接口高性能的关键点。所谓outstanding就是允许在等待上一次传输完成之前继续发送新的请求。本质上是把串行的“请求-等待-完成”变成了流水线操作让总线上始终有请求在飞行。在PCIe IP核里读请求发出后要等待Completer返回数据。如果没有足够的outstanding能力每次读请求都要等上一次完全结束才能发下一次吞吐就完全被往返延迟限制死了。对于内存访问这种高延迟操作outstanding不足的性能损失非常明显。Xilinx的AXI Memory Mapped IP核配置里一般会有Max Outstanding Read Requests或者说Tag数量的设置。内部结构上IP核需要为每个在读请求保留一个Tag用来匹配对应的Completion报文。这个数量越多能同时处理的读请求就越多。增加outstanding数量的代价是内部资源消耗变大主要是存储Completion数据的RAM和跟踪逻辑。对UltraScale器件来说这部分资源不是问题可以尽量放开。实际动手建议初始配置想办法限制Tag数量以简化调试之后再做性能测试时再逐步加大。因为outstanding大了之后如果驱动侧处理Completion的顺序逻辑写得不严谨可能引发乱序处理问题。虽然PCIe规范允许乱序完成但驱动里的匹配逻辑要支持才行。MSI-X中断对性能的影响也在这里体现。如果你的设备每处理完一小批数据就发一次传统MSI中断CPU会被打断到无法继续提交新的DMA描述符吞吐同样上不去。换成MSI-X后可以配合多队列把中断分散到多个CPU核心处理链路利用率会有可感知的提升。4. 常见问题与排查技巧实录4.1 枚举阶段看不到BAR或者空间为零板卡插入插槽开机系统启动后在主机侧看到的设备BAR空间为零。这种问题多见于第一次上板的情况原因通常不是IP配置错了而是链路训练还停留在Configuration阶段。PCIe枚举是分步走的先训练链路L0后就进入Configuration阶段RC开始查找设备、分配BAR空间。如果BAR全是0先检查链路状态确认EP和RC之间完成链路训练。在Linux下可以用lspci -vvv查看设备状态重点看LinkCap和LinkSta两行里面的速度和宽度是否正常。如果是链路已经正常但BAR为0检查配置空间里的Command寄存器。该寄存器的bit1是Memory Space Enable软件访问非Prefetchable窗口之前必须置1。很多裸驱动容易漏掉这一步直接在BAR地址上读写结果总线返回全F。注意这里工具看到的内存窗口使能位和BAR空间本身是两回事即使Command寄存器没使能BAR寄存器里的基地址可能还是有的但不使能就不能正常访问。还有一个常见现象是BAR读到0xFFFFFFFF不是0。这种情况说明枚举软件在探测BAR大小时设备没有正确响应或者是BAR的低属性位被错误当成了地址位。可以对照DATASHEET确认BAR寄存器位宽的预期值同时用逻辑分析仪抓配置空间读写时序。4.2 写入无响应、读取卡死的时序类问题BAR分配没问题驱动也加载了但一访问就卡住或者返回错误数据。这种问题多半不在PCIe协议层而在AXI接口的握手时序上。AXI Memory Mapped IP核从PCIe侧接收读写请求后会通过AW、W、AR、R、B这些AXI通道与用户逻辑交互。如果用户逻辑的从设备没有持续拉高READY信号IP核会一直等在那里方法表现为读写卡死。我排查过类似问题最后发现是用户逻辑里的状态机在等待一个不会到来的中断。排查这种问题最直接的手段是抓ILA。把AXI接口的AWREADY、WREADY、ARREADY、RVALID、BVALID信号全部拉出来看看请求到了哪一步停住了。如果发现BREADY和BVALID永远握手不上问题大概率在IP核内部的写响应路径上需要检查IP配置里关于Write Response的处理选项。不少Xilinx IP配置界面会有一个“Enable Always Ready”或者类似的选项。打开之后IP核在AXI侧会无条件接受写请求不关心用户逻辑是否准备好——这个选项适合对性能要求高、且用户逻辑处理写入很快的场景。如果用户逻辑本身就是慢速外设建议关闭让反压机制正常工作避免数据丢失。4.3 性能实测远低于理论值从哪几个方向下手链路速率正常BAR访问正常DMA也在跑但性能就是上不去。这类问题调试起来比较费劲因为瓶颈可能藏在好几个层面。先看协商参数。链路速率、MPS、MRRS这三个数值用setpci或驱动里读出来看一眼往往能直接定位问题。很多卡性能的板卡MPS只有128B、MRRS只有128B这种情况下大块读写跑不满是必然的。驱动里主动设置MRRS到512B或更高是性价比最高的调优手段。再看方向。如果写吞吐正常、读吞吐很低怀疑outstanding/Tag不足。如果两个方向都低那可能是链路本身没跑到预计的速率比如Gen3的卡插到Gen1的槽位上或者PCIe金手指有脏污导致降速。这里提一下热词里有人搜“ubuntu查看显卡pcie速率”说的就是lspci -vvv里的LnkSta字段这个思路对自研板卡一样适用。最后看数据路径上有没有意外的“串行点”。比如AXI侧某个FIFO深度只有16而IP核配置的outstanding数量是64那多余的数据就被FIFO挡在后面了。带宽测试时可以在DMA描述符里做大块、连续的传输避免大量小粒度随机访问那样测出来的一定是延迟而不是带宽。4.4 一张自检清单照着填就能少折腾两天表格里是我每次调PCIe AXI Memory Mapped设备时固定走一遍的流程写在这里当速查表。阶段检查项关键命令或位置上电后链路是否L0、速率宽度lspci -vvvLnkSta枚举后BAR基地址和大小是否符合预期lspci -vvvMemory行驱动加载前Command寄存器Memory Space Enablesetpci -s 02:00.0 COMMAND驱动初始化MPS和MRRS实际协商值setpci -s 02:00.0 DEVICE_CONTROL寄存器访问先写后读回比对数据一致devmem 2/dev/null / 自定义驱动日志DMA传输先做小包64KB测试再逐步加大带宽测试工具或自定义计数器持续运行长时间打流观察是否有ECC错误或TLP错误lspci -vvv错误统计段这套流程跑下来绝大多数问题都会在最开始几步暴露出来不至于在性能优化阶段才忽然发现链路协商就有问题。说一个我自己的习惯吧在硬件寄存器头文件里把BAR大小、地址偏移、MRRS期望值都定义成宏然后驱动初始化时打印出来和Vivado IP配置页逐项比对。一次打印能省掉后面无数次对着代码和原理图猜谜的时间。PCIe调试就是在这种“慢就是快”的节奏里进行的配置期较真是之后所有性能测试的底气。
返回列表