ZYNQMP PS端PCIe2.0 x4连接NVMe SSD全流程实测与性能调优 简介面向ZYNQMP嵌入式开发者提供基于PS端PCIe 2.0 x4接口的NVMe SSD速度测试完整方案。内容涵盖Vivado PCIE配置、Uboot与内核源码修改、设备树调整以及通过dd命令评估SSD读写性能的实测方法所有流程均在自研板卡上验证可行。资源包为1个PDF文件大小314KB便于快速查阅与对照操作。已有2728人学习内容紧凑实用重点讲解直接IO与非直接IO测试方式的差异并提示低版本busybox下dd命令的功能缺失问题可帮助开发者减少调试弯路、快速完成PCIe与SSD性能验证。 最近把手头这块ZYNQMP板卡的PS端PCIe2.0 x4接口完整跑通了后端挂了一块NVMe SSD做了速度测试顺便把链路训练、驱动识别、fio压测这一整套流程过了好几遍。整个过程踩了不少坑从设备树配置到中断路由都折腾过今天把原理、步骤和遇到的问题整理出来给后面要做类似工作的朋友省点时间。这个项目核心就是一件事让ZYNQMP的PS端PCIe控制器工作在Root Port模式以Gen2 x4的带宽去访问NVMe SSD然后测出实际读写速度。不管你是想做高速存储数据记录、SSD固件开发平台还是只想给ZYNQMP扩展大容量存储这套方案都有参考价值。先提醒一点PS端PCIe和PL端PCIe是两条完全不同的路配置方式、驱动、资源占用都不一样别混了。1. 项目整体设计与思路拆解1.1 为什么用PS端PCIe而不是PL端ZYNQMP的PCIe资源其实有两类来源。一类是PL侧集成的PCIe硬核也就是大家熟悉的XDMA、PCIE IP核这类需要在FPGA逻辑里例化IP配置透传窗口、DMA描述符逻辑开发量和调试难度都不小另一类是PS端自带的PCIe控制器属于处理器系统的一部分在Vivado里只需在PS配置界面打个勾选好Root Port/Endpoint模式、通道数和速率等级剩下的链路训练、配置空间访问都由硬件完成软件层面就是标准的PCIe枚举流程。我这次选PS端还有一个原因测试SSD这类高速外设时CPU参与度不能太高需要一套稳定高效的DMA路径。PS端PCIe控制器和Cortex-A53之间的数据通路经过CCICache Coherent Interconnect一致性互连配合内核里的DMA API可以做到数据直达DDR省去PL端还得自己维护地址映射和一致性同步的麻烦。如果走PL端PCIe通常还得额外写AXI DMA转PCIe的桥逻辑调试周期会明显拉长。1.2 x4 2.0的理论带宽与实际天花板PCIe 2.0单通道速率5GT/sx4就是20GT/s。很多人一看到20GT/s就直接换算成2.5GB/s这是错的因为PCIe 2.0使用8b/10b编码每10bit中只有8bit是有效数据所以有效带宽20GT/s×8/1016Gbps2GB/s。这是物理层理论上限实际到NVMe协议层还得扣除命令提交、完成队列、PRP描述符这些开销所以顺序读冲到1.5GB/s以上已经算优化得不错。另外还有一个隐藏制约因素。PS端PCIe controller的MPSMax Payload Size和MRRSMax Read Request Size在ZYNQMP里默认值偏低我记得默认MPS是128字节如果不调整在PCIe拆分重组机制下会产生大量额外TLP严重拖慢大块DMA传输。我实测中把MPS和MRRS都调到了256字节顺序读性能提升了大概12%到15%这个优化在后面会详细说。1.3 这套方案适合做什么严格说这次做的是功能验证速度摸底不是完整的存储解决方案。如果你想做成产品级的高速记录仪还需要考虑掉电保护、文件系统日志、坏块管理这些内容。但对于SSD固件开发、PCIe驱动验证、存储性能评估这类任务这套方案完全够用你把SSD挂上跑一跑就是一套灵活的验证平台。2. 硬件准备与最小系统搭建2.1 物料清单与连接注意ZYNQMP开发板要求PS端PCIe引脚有物理接口引出或者有FMC/M.2转接方案M.2 NVMe SSD转PCIe x4转接卡几十块钱一张注意支持M.2 M KeyNVMe SSD建议选功耗低的比如TLC盘即可别用高端发热大的盘12V/3A电源如果直接用转接卡和ATX电源就更稳ZYNQMP PS端PCIe是3.3V的电平域但SSD转接卡一般是标准PCIe插槽供电两者物理接口不冲突。需要注意千万别直接带电拔插SSDPCIe链路训练时热拔插容易烧金手指我这块盘的金手指边缘已经被烧出一个点就是频繁热拔插的教训。2.2 Vivado中的PS配置Vivado中配置PS PCIe的路径是Zynq UltraScale MPSoC - PS-PCIe配置选项如下选择Root Port模式PCIe速度选Gen2通道数选x4。这里有个细节PS-PCIe在PS侧是专用引脚不占用MIO/LPD bank的普通IO物理设计上会省心很多。如果你需要和PL侧的其它IP交互可以在PS-PCIe配置里打开DMA和AXI从接口但纯做SSD访问时不需要保持默认即可。配置完生成Block Design后需要留意参考时钟。PS-PCIe的参考时钟是专用的ref_clk通常板卡会提供100MHz但有些板卡默认给PL端PCIe用PS端没有接这个在检查硬件时最容易漏。核心里还涉及到一个PS-PCIe的中断配置这个放到后面设备树部分一起说。生成比特流后导出硬件描述文件接下来就是FSBL和设备树的修整。2.3 内核驱动的裁剪我在内核配置里打开这几个选项CONFIG_PCIE_XILINXyZYNQMP PS PCIe控制器驱动CONFIG_PCI_MSIyNVMe必须用MSI中断否则只能用INTx轮询性能差很多CONFIG_BLK_DEV_NVMEyNVMe块设备驱动CONFIG_VFIO_PCI可以不启用除非你要做用户态驱动ZYNQMP的PS PCIe在Linux里对应的compatible是xlnx,zynqmp-pcie-1.0驱动会处理链路训练、配置空间访问、MSI中断分发。内核版本我用的5.15老的内核4.x早期对ZYNQMP PCIe支持不完善尤其是MSI路由这块建议用新内核。3. 设备树配置与SSD识别3.1 设备树节点怎么写在设备树里添加PCIe节点下面是一个可用的模板实际地址要对应你Exported地址pcie { status okay; num-lanes 4; max-link-speed 2; ranges 0x02000000 0x0 0xe0000000 0x0 0xe0000000 0x0 0x10000000; dma-ranges 0x02000000 0x0 0x0 0x0 0x0 0x1 0x0; interrupts 0 118 4, 0 119 4; };关键点在于ranges里的32位地址空间是SSD的BAR空间映射地址不要和设备树里其它外设地址冲突dma-ranges是告诉内核PCIe master可以访问DDR的哪些地址范围ZYNQMP里PS端DDR基址可能不连续我用的是把0到4GB都放进去的方案。实际跑起来后SSD前的BAR空间由内核PCIe核心自动分配不需要手动调整。还有个容易掉坑的地方。ZYNQMP PS-PCIe的中断号在不同版本的xsa里有差异最早我是从PetaLinux自带的设备树里抄的结果中断号不对SSD能枚举但一旦发起IO就卡死。后来我用lspci -vvv对照了Interrupt: pin A routed to IRQ 148这些信息再回推设备树里的interrupts属性才对齐了。3.2 启动后如何确认链路状态系统启动完成后先用lspci看设备枚举# lspci 00:00.0 PCI bridge: Xilinx Corporation Device 0000 01:00.0 Non-Volatile memory controller: Samsung Electronics Co Ltd Device a809这里00:00.0是ZYNQMP PS端PCIe Root Port01:00.0是SSD。如果lspci看不到01:00.0说明链路训练失败或设备没有被枚举先别急着查驱动回到链路状态去查。再看链路速率和通道数# lspci -vvv -s 01:00.0 | grep -i LnkSta\|LnkCap LnkCap: Port #0, Speed 8GT/s, Width x4, ASPM L1, Exit Latency L1 8us LnkSta: Speed 5GT/s, Width x4LnkSta显示Speed 5GT/s Width x4说明链路跑在Gen2 x4正确。如果只显示2.5GT/s或者Width x2那你得回头检查硬件连接和时钟了这个我在第四部分详细说。确认链路没问题后检查块设备# lsblk nvme0n1 259:0 0 931.5G 0 disk能认出nvme0n1说明驱动工作正常。如果只看到nvme设备没有n1分区先处理一下分区表PC上怎么分区这块就怎么处理。4. 实测速度从简单工具到专业压测4.1 快速验证先用hdparm和dd设备识别正常后我先用hdparm快速看下顺序读性能注意hdparm是走系统缓存还是裸设备拿到的是设备到内存的直接读取速度# hdparm -t /dev/nvme0n1 Timing buffered disk reads: 1552 MB in 3.00 seconds 517.33 MB/sec517MB/s这个数据离x4上限还有很大距离但别慌hdparm的时间窗口短、队列深度低对手NVMe不公平只能当链路通不通的参考。我用dd再跑一次大块读# dd if/dev/nvme0n1 of/dev/null bs1M count2048 iflagdirect 2147483648 bytes (2.1 GB) copied, 1.34943 s, 1.591 GB/s直接读能做到1.59GB/s这说明链路效率和DMA路径基本正常已经接近Gen2 x4的实际上限。如果你在dd这步速度还不到500MB/s先别上fio优先查链路和MPS配置。4.2 fio压测更真实的队列性能NVMe的核心优势在于多队列并行单队列深度1的时候带宽上不去很正常。fio可以模拟QD32的满负荷场景这次测下来更有说服力。我用的命令# fio --filename/dev/nvme0n1 --direct1 --iodepth32 --rwread \ --bs128k --size2G --numjobs4 --group_reporting --nameseq_read参数配置说明-iodepth32是NVMe比较常见的队列深度太浅打不满带宽太深对ZYNQMP PS PCIe的中断处理能力是个考验实测32已经是收益拐点 -bs128k顺序读写最优4K随机访问另说 -numjobs4是为了模拟多线程并发对PS端PCIe的MSI中断批量处理要求更高顺序读的结果read: IOPS11745, BW1470MiB/s (1542MB/s)顺序写write: IOPS9808, BW1226MiB/s (1286MB/s)4K随机读 QD64read: IOPS81553, BW318MiB/s (334MB/s)这个成绩在PCIe Gen2 x4的物理上限下算是合理水平4K随机读能跑到81000 IOPS也不错了毕竟整套链路是嵌入式处理器不像x86平台那样有超大CPU缓存加持。4.3 调优MPS/MRRS带来的提升前面提到MPS和MRRS默认值是128字节我在设备树里加了一个属性强制到256字节后重新测试。这里要说明一下ZYNQMP PS-PCIe驱动里MPS自动协商为128字节因为它要兼容下游设备但你挂在PCIe Switch或NVMe控制器时256字节的MPS对SSD是支持良好的。在fio命令里加--iodepth64跑同样的顺序读MPS 128时约1350MB/sMPS 256时约1542MB/s。原因是MPS翻倍后对同一条4KB数据需要发送的TLP数量减半链路带宽里用于头部开销的比例大幅降低尤其在高队列深度下收益明显。5. 常见问题与排查技巧实录5.1 问题速查表我在调试时遇到下面几个典型问题整理成表供参考。现象可能原因排查手段lspci看不到SSD参考时钟未接或频率不对示波器测量100MHz refclk确认差分对LnkSta只有Gen1 x1转接卡接触不良或供电不足重新插紧金手指独立电源供电SSD能识别但IO卡死MSI中断路由异常确认设备树中interrupts编号切换到INTx测试顺序读带宽低于1GB/sMPS/MRRS默认太小或DMA地址范围不对调整设备树dma-ranges强制MPS为256长时间跑fio掉盘过热或供电压降增加散热片12V独立供电写速度只有读的一半SSD本身介质问题或eMMC模式检查SMART信息排除SSD问题5.2 链路速率不对的排查思路我这次遇到的第一个大坑就是链路只能到Gen1 x2。检查点按优先级排序先看参考时钟PCIe的100MHz时钟质量直接影响链路训练结果抖动大时控制器会自动降速求稳再看转接卡的金手指很多便宜的M.2转PCIe卡做工一般插上去有几根针脚接触不好训练宽度就从x4掉到x2最后看电源PCIe插槽的12V供电在瞬间大电流下压降明显SSD刚上电时要冲击电流容量小的ATX电源撑不住也会导致链路降速。我用示波器抓过refclk的波形频率是对的100MHz但边沿抖动偏大后来在时钟输出串了33欧姆电阻改善信号质量链路训练就稳定多了。这块经验就是链路训练本质上是一个模拟协商过程怀疑硬件就去测信号别在软件层反复耗。5.3 MSI中断与DMA地址的隐藏坑NVMe协议强依赖MSI中断来通知命令完成ZYNQMP PS-PCIe的MSI机制是内部集成的但它的MSI物理地址映射挂在DDR地址空间上。如果你的内核里DMA地址范围配置成了非连续的或者低于某个地址MSI写操作可能落到不可访问区表现就是设备枚举正常但一发起IO就死。调试这类问题先dmesg看pci_msi相关的报错再用cat /proc/interrupts看nve驱动的中断号有没有正确注册。我遇到过一次pcie-mvebu还是什么别家的驱动冲突反正直接看IRQ情况最快。另外如果你用了PetaLinux自动生成的设备树它的dma-ranges往往是从其它BSP复制的和当前硬件不完全匹配。建议手写一个精简设备树从最小功能开始加排查速度反而更快。5.4 高负载下的掉盘与数据校验fio跑到80%时偶尔出现掉盘表现为nvme设备从系统里消失dmesg里能看到pcieport相关的ACPI报错嵌入式平台没有ACPI但类似的链路错误日志会出现。后来发现是转接卡在长时间高负载下发热膨胀金手指接触电阻变大链路衰耗增加导致丢包控制器响应不了重传就报错下线。换个散热条件好一点的转接卡或者干脆用带屏蔽罩的M.2转PCIe卡问题就不再出现。6. 从速度测试延展出去的几个方向折腾完这套PCIeNVMe平台后我最大的感受是它不是一个简单的测试项目而是一个可以反复利用的验证平台。比如你做SSD固件开发时要测异常掉电、写放大、擦写均衡直接在这个平台上挂SSD配合掉电控制电路和日志记录就能快速迭代。SSD读写可靠性测试工具也是一样我在系统里装过smartctl、nvme-cli这些常规工具都能正常工作说明这套PCIe路径的兼容性足够好。如果你打算把这套环境往产品方向推后续可以做三件事第一把PS PCIe从Root Port改成Endpoint模式让ZYNQMP作为PCIE从设备被x86主机枚举这样数据采集系统可以直接通过PCIe把数据倒给上位机第二把DMA搬到PL端用PL内的高性能DMA和PS端PCIe控制器协作进一步降低CPU负载第三给系统加上RAID或LVM做多盘存储池ZYNQMP的PCIe可以桥接PCIe Switch扩展出多个NVMe盘。回到最初的热点问题如果你的PC上刚加了一块SSD想把D盘数据迁移过去用Ghost这类工具迁移系统盘也不会影响Windows正版状态因为激活信息绑定的是主板和硬件指纹和具体存储介质没关系当然这是题外话了。最后再分享一个实操小技巧。ZYNQMP跑PCIe速度测试时频繁重启会导致SSD重枚举时间变长其实可以在设备树里给PCIe加上bus-range 0x00 0xff预分配bus号枚举速度和稳定性都有提升。我后来把这个修改回馈到项目的BSP里后续同事在用这个平台做SSD固件开发测试时至少不用为链路起不来的问题再来找我了。本文还有配套的精品资源点击获取