ARTICLE DETAIL

资讯详情

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

ZYNQMP PS端PCIe 2.0 x4挂载NVMe SSD:从硬件时序到fio测速的实战指南

ZYNQMP PS端PCIe 2.0 x4挂载NVMe SSD:从硬件时序到fio测速的实战指南 简介基于ZYNQMP平台PS端PCIE2.0x4接口功能验证及NVMe SSD读写速度测试的完整实操笔记适合嵌入式工程师、FPGA开发者和Linux底层驱动调试人员聚焦Zynq UltraScale MPSoC上PCIe Root Port调通与存储性能评估的常见问题。资源为单个PDF技术文档共314KB内容涵盖Vivado中PCIe IP的引脚与高级配置、Uboot设备树使能检查、Kernel源码配置修改以及通过dd命令分别以直接IO和非直接IO方式对NVMe SSD进行读写测速的完整操作过程并附有内核打印、lspci识别信息和测速结果输出便于读者核对自身执行效果。所有步骤均基于Xilinx官方源码在自设计板卡上实际验证还针对复位引脚遗漏、低版本busybox dd功能缺失等硬件与软件细节给出处理建议对于相似硬件平台有直接复用价值可直接作为PCIe调试的参考模板。已有2728人学习浏览尤其适合正在调试ZYNQMP PCIe外设或评估NVMe硬盘读写性能的开发者快速上手。 把一块NVMe SSD接到ZYNQMP的PS端PCIe接口上听起来就是找个M.2转接卡的事真正做起来才发现从REFCLK到设备树再到fio参数每一环都有讲究。这次项目的目标是验证PS端PCIe 2.0 x4接口作为Root Complex能不能稳定挂载SSD并测出这条链路上的实际读写带宽。文章把这次项目里最关键的选择、测试方法和踩坑点整理出来适合正在评估ZYNQMP PCIe方案、或者已经能启动系统但SSD不识别/速度不理想的朋友参考。1. ZYNQMP的PCIe路线盘清楚再动手1.1 PS端PCIe控制器和PL端Integrated Block的分工很多朋友看到Zynq UltraScale就默认PCIe要用PL里的Integrated Block这其实是个误区。ZU的PS内部集成了一路DesignWare PCIe控制器既可以做Root Complex也可以做Endpoint最大支持PCIe 2.0 x4PL里的Integrated Block则能跑到Gen3 x4/x8甚至更宽的配置但需要烧bit、需要自己处理AXI/DMA控制通路。这个项目选PS端是因为目标就是验证PS自带的PCIe接口在存储场景下的稳定性启动即用不需要PL参与省掉FPGA逻辑开发也能在Linux下用标准驱动直接测速。选PS端还有个容易被低估的好处它挂在PS总线上CPU通过普通内存映射就能发起配置事务和DMA调试时用lspci、dmesg这些标准工具就能看到状态不用额外写PL侧的调试逻辑。做选型之前建议把UG1085里PS-PCIe和PL-PCIe的对比表翻出来弄清楚两者在带宽、lane数、时钟要求、中断支持上的区别。如果只是接一块NVMe系统盘或者做数据采集卡的低速扩展PS端Gen2 x4一般够了如果你后面要跑万兆网卡、GPU这类需要大带宽的设备就得老老实实上PL Gen3。这一步想清楚后面能少折腾很多。1.2 PCIe2.0 x4的带宽账理论值和实际天花板ZynqMP PS端PCIe是Gen2 x4每条lane速率5GT/s。链路总原始速率是20GT/s但因为PCIe 2.0采用8b/10b编码有效数据速率是16Gbps换算过来单向理论最大2GB/s而且是全双工的上行下行各2GB/s。了解这个很重要否则测速的时候容易产生“我是不是没调好”的错觉。实际上SSD顺序读能跑到1.6~1.8GB/s已经很不错因为NVMe协议、TLP头、DMA描述符、中断处理都会吃掉一部分带宽。如果你拿一个顺序读标称3.5GB/s的NVMe盘来测在PS端PCIe 2.0 x4下也会被链路卡在2GB/s以内。这个先有个预期后面看到速度才不会慌。另外注意测试结果也和你选的SSD本身有关一些入门级SSD的顺序读本来就只有1.2GB/s左右那速度瓶颈就在盘而不在PCIe链路判读时不能把所有差异都归到接口上。我习惯整理一个简单对照表按应用场景选接口应用场景接口方式单向带宽上限系统盘/日志盘PS PCIe Gen2 x4约2GB/s大容量阵列/高带宽采集PL PCIe Gen3 x8约7.9GB/s万兆网卡/GPU扩展PL PCIe Gen3 x8/x16取决于PL资源和封装顺带提醒一句PS-PCIe速度测试必须用真正的Gen2设备有些老的PCIe网卡只支持Gen1 x1插上去会协商到2.5GT/s x1带宽只有250MB/s左右这是正常的别误判为接口坏。2. 硬件侧先把Link训练搞可靠2.1 参考时钟、PERST和供电三个最容易被忽略的点PS端PCIe要正常工作REFCLK、PERST和供电三件事缺一不可而且这三样恰恰是“从市场上买一块转接卡插上”时最容易踩的坑。先说时钟。ZYNQMP PS的PCIe需要一个100MHz差分参考时钟作为控制器和PHY的基准。做RC时不能只给ZynqMP送时钟因为SSD这个Endpoint自己不会产生100MHz它要由主机侧提供。合理的做法是用一颗时钟buffer把同一个100MHz时钟源分别送到ZynqMP的REF_CLK引脚和M.2插槽的REFCLK引脚保证两边同源同频。我调试时见过最典型的现象M.2转接卡自带的100MHz晶振和PS的参考时钟不同源链路虽然能training但Gen2协商失败一直掉回Gen1速率直接减半。后来改成同源时钟buffer问题立刻消失。所以layout和选型阶段就要把“同源”两个字写在设计约束里差分线阻抗按PCIe要求的85Ω控制lane组内等长也要尽量匹配。然后是复位。M.2卡槽的PERST#信号要连接到系统复位控制逻辑并且必须在3.3V电源稳定和REFCLK稳定之后释放。PCIe规范对复位释放到配置访问有明确时序要求实际项目中如果复位释放太早SSD主控还没准备好RC枚举时就会读不到Vendor ID或者读到一个全F导致设备直接被判为不存在。如果复位释放太晚系统启动流程又被拖慢。通常我会在板子上把PERST#用一颗GPIO控制软件上先保证供电再延时拉高避免硬件时序不确定。最后是供电。NVMe SSD瞬时电流很容易超过2A高性能盘在满负载写入时峰值冲到2.5A以上也不罕见。如果你用的是从USB取电或底板供电能力不足的M.2转接卡轻则读写时3.3V跌落导致掉盘重则Link反复training。测试时我建议至少用万用表或示波器盯一下3.3V在跑fio写测试瞬间的电压跌落幅度如果低于3.2V就要认真考虑供电余量了。2.2 用lspci确认Speed和WidthLink训练结果怎么看硬件调通后第一步不是直接挂盘而是在Linux下确认链路的实际协商状态。执行lspci -vvv找到NVMe设备那一节看LnkCap和LnkSta两行。正常情况下LnkSta应该显示Speed 5GT/s、Width x4这代表链路已经稳定训练到Gen2 x4。如果显示Speed 2.5GT/s、Width x1或x2说明虽然Link up了但没有达到预期。这时先查REFCLK是否干净、PERST时序、lane布线特别是差分对有没有接反、有没有串了不必要的AC耦合电容。PCIe链路本身会在训练阶段反复降速协商硬件问题不解决软件无论怎么调都上不去。如果LnkSta连不上dmesg里驱动日志一般会报link down这时优先查时钟和供电。还有一个容易被忽视的参数M.2卡槽的Key类型。NVMe SSD通常走M Key支持x4B Key只有x2如果转接板或底板把信号连到B Key的lane上本身就只有x2能力lspci看到Width x2是正常的别误判成硬件故障。确认卡槽type之后再结合板级原理图核对lane映射才能判断协商结果是否合理。这条排查顺序要养成习惯否则很容易在“明明插了盘却不识别”上浪费好几天。3. 从枚举到块设备系统侧调试链路3.1 内核配置与设备树节点ZYNQMP跑LinuxPCIe要能正常工作内核配置项得对得上。我以Xilinx近几年的内核为例至少需要打开CONFIG_PCI、CONFIG_PCI_MSI、CONFIG_PCIE_XILINX_DWC_PLATFORM对应PS端DWC控制器RC驱动以及CONFIG_BLK_DEV_NVME。如果板级设备树里pcie节点没使能系统启动时根本不会注册host bridgedmesg里连“PCI host bridge”都看不到。常见的ZynqMP设备树节点长这样pcie { status okay; num-lanes 4; /* 与板级实际连接的lane数一致 */ max-link-speed 2; /* 2表示Gen2设成1会锁死在2.5GT/s */ };有些BSP版本会把compatible写成xlnx,zynqmp-pcie-2.0reg地址在0xFD0E0000附近这些细节以你用的内核和device tree源码为准。这里重点提醒两件事第一num-lanes不是想写几就写几必须和硬件实际连接的lane数一致第二max-link-speed如果设成1链路会被强制锁在2.5GT/s速度只有Gen2的一半我遇到过有人为了调试方便加了这个参数之后忘记改回来测速一直上不去。确认dts后重新编译boot.bin或image.ub启动后执行ls /sys/bus/pci/devices/能看到0000:00:00.0这样的root port节点说明host bridge已经注册成功。如果在这里发现枚举阶段资源分配报错比如BAR空间不足或bus number不够可以临时在内核命令行加pcirealloc试试它会强制重新分配BAR。不过治本的办法还是在设备树里把ranges属性的地址窗口配够尤其是映射到PS DDR的地址窗口给PCIe设备预留足够的outbound空间。ZynqMP上PCIe访问DDR一般通过地址转换窗口没配好会出现“设备能枚举但DMA传输失败”的诡异现象。3.2 枚举失败时的排查顺序最常见的情况是系统起来了lspci就是看不到设备。我的排查顺序是固定的先看dmesg里有没有pcie相关报错再看LnkSta有没有up最后再怀疑驱动和软件。如果LnkSta也没有回到第2章的硬件三要素如果LnkSta是up的但lspci看不到多半是枚举阶段的配置事务没有正确返回设备ID。执行echo 1 /sys/bus/pci/rescan可以重新触发一次扫描排除启动前设备未就绪的问题。如果rescan之后出现了设备但设备名显示“PCI bridge”或者Vendor ID/Device ID全F那就是配置空间读取异常优先复位PERST后再rescan同时确认时钟没有中途丢失。按这个顺序走大多数不识别问题都能定位到硬件时序上而不是内核配置。真正需要改驱动源码的场景很少别一开始就钻进驱动里。对于NVMe盘lspci里能看到Non-Volatile memory controller之后还得确认内核有没有加载nvme驱动lspci -k看Kernel driver in use是否为nvme。如果显示 检查CONFIG_BLK_DEV_NVME是否编译成模块且被自动加载。正常加载后/dev/nvme0n1就会出现配合fdisk -l能看到盘容量这一步完成才说明PS端PCIe作为RC的功能是通的。3.3 功能验证读写一致性先于性能测试速度测试之前我强烈建议先做一轮读写一致性验证因为接口“通”和“稳”是两回事。最简单的办法挂载文件系统后用dd写一个4GB文件读回来比对校验值。mkdir -p /mnt/nvme mount /dev/nvme0n1p1 /mnt/nvme dd if/dev/urandom of/mnt/nvme/test.dat bs1M count4096 statusprogress md5sum /mnt/nvme/test.dat dd if/mnt/nvme/test.dat of/dev/null bs1M count4096 statusprogress md5sum /mnt/nvme/test.dat前后两次md5一致说明数据链路基本可信。如果校验值不一致说明DMA路径或缓存有问题这种状态下测出来的速度没有任何参考意义必须先把数据正确性搞定。这一步在FPGAPCIe项目里特别重要因为DMA描述符、地址对齐、Cache一致性都可能引起数据错误而这些错误不会立刻让系统崩溃只会在某些数据模式下悄悄坏掉。ZynqMP的PS侧DMA与DDR之间有硬件一致性保障但软件侧仍需确保分配的DMA缓冲地址对齐和长度合理。做完整验证后再进入速度测试会省掉很多误判。4. 速度测试怎么做才可信4.1 测试工具与方法fio参数怎么给测SSD速度最忌讳随手拿dd一测就下结论。dd读/dev/nvme0n1到/dev/null数据会经过page cache如果文件/块设备内容比内存小读出来的是内存缓存里的数据带宽高得没有意义如果是从一个刚写完还没落盘的文件里读也是一样。要得到可信数据要么保证读的数据量远大于系统内存要么用fio这种支持O_DIRECT的测试工具。推荐用fio先挂载文件系统然后按顺序执行以下几组命令fio --nameseq-read --rwread --bs1M --size8G --iodepth32 --numjobs1 --direct1 --ioenginelibaio --filename/mnt/nvme/fio-test --group_reporting fio --nameseq-write --rwwrite --bs1M --size8G --iodepth32 --numjobs1 --direct1 --ioenginelibaio --filename/mnt/nvme/fio-test --group_reporting fio --namerand-read --rwrandread --bs4k --size2G --iodepth32 --numjobs1 --direct1 --ioenginelibaio --filename/mnt/nvme/fio-test --group_reporting关键参数拆开说direct1绕过page cacheioenginelibaio用异步IO模拟真实高并发iodepth32让盘端的队列深度跑起来NVMe盘队列深度不足时4K随机性能会非常难看size设大是为了让测试时间足够长避免SLC cache带来的虚高。写测试会覆盖文件内容fio-test这个文件最后可以删掉千万别在放着重要数据的文件系统上随便找个路径跑写测试。如果想测raw设备把filename指向/dev/nvme0n1也可以但一定要确认盘上没有你要的数据。除了fiohdparm -t /dev/nvme0n1可以快速看个大致读带宽适合冒烟测试但它读的是块设备测试模式比较单一不能作为最终结论。我一般把hdparm当作“链路通不通、速度数量级对不对”的快速判断把fio当作正式测试数据来源。4.2 测试结果的判读与瓶颈定位在PCIe 2.0 x4这个路线上我习惯用下面这张表来判断结果是否正常测试项期望范围参考如果明显偏低先查什么顺序读1.5~1.8GB/sLnkSta是否Gen2 x4SSD本身顺序读规格顺序写1.2~1.6GB/s盘是否SLC cache耗尽供电是否跌落4K随机读300~600MB/s视盘而定iodepth是否够NVMe队列是否生效4K随机写100~400MB/s视盘而定盘主控与Flash型号是否在缓存窗口外注意这只是参考范围具体数值和你的SSD型号、环境温度、转接卡质量都有关系核心是看数量级而不是盯着某一位数字。如果顺序读只有300MB/s先查lspci里的LnkSta我遇到过“明明之前在别的板子上是x4换了一块转接卡变成x2”的情况大概率是卡槽或者转接板把信号漏掉了。如果LnkSta正常但速度仍低再查供电和温度NVMe过热会主动限速。还要确认是不是ASPM把链路降到了低功耗状态高延迟中断会导致吞吐上不去。可以在内核命令行加pcie_aspmoff或关闭CONFIG_PCIEASPM来排除这个因素。写速度偏低也不一定是链路问题很多消费级SSD有SLC cache测试一开始写入速度像2GB/s写到缓存耗尽后直接掉到几百MB/s这是盘的特性不是接口问题。跑写测试时时间要拉长size给到8G甚至16G才能看到稳态速度。总的来说速度测试的价值不只是拿到一个数字而是通过数字反推链路、电源、盘本身的状态。PS端PCIe 2.0 x4的边界很明确遇到“达不到预期”时先回到硬件三要素和Link状态大部分问题都能解释得通。最后再分享一点个人体会。ZYNQMP PS端PCIe做NVMe SSD扩展功能验证和速度测试的关键不在于把fio参数背得多熟而在于把硬件时序和枚举原理搞清楚REFCLK同源、PERST释放时序、供电余量、LnkSta状态这四个点决定了90%的成败。我在这条路上踩过的坑几乎都是“看起来软件问题实际全是硬件时序问题”。如果你也想在自己的板子上复现这套流程建议先按硬件检查步骤走一遍再开内核调试否则即使测出一个漂亮的速度数字也不代表接口真的靠谱。希望这些经验能帮你少走点弯路。本文还有配套的精品资源点击获取
返回列表