资讯中心

RK3588与FPGA高速PCIe通信实战:从链路建立到3GB/s带宽调优

📅 2026/11/13 23:43:32
RK3588与FPGA高速PCIe通信实战:从链路建立到3GB/s带宽调优
RK3588 这颗芯片这两年热度一直没降过8 核 CPU 加 6TOPS NPU 的配置让它成了边缘计算和视觉项目里的常客。但真正把它用在工业现场或者高端视觉设备里的人都知道单靠 SoC 本身是不够的——高速数据采集、多路图像预处理、确定性时序控制这些活还是得交给 FPGA 来干。于是问题就来了RK3588 和 FPGA 之间怎么连答案基本只有一个PCIe。可 PCIe 这东西理论带宽写得漂亮实际跑起来能到多少、怎么跑、坑在哪才是真正决定项目成败的地方。这篇内容就是围绕 RK3588 与 FPGA 之间的高速 PCIe 通信展开从链路建立、带宽测算、DMA 传输到实战调优把整个流程拆开讲透。不管你是刚接触 PCIe 的嵌入式新手还是已经调过几轮但总觉得带宽上不去的老手下面这些内容应该都能对上你的需求。1. 先搞清楚 RK3588 的 PCIe 家底和 FPGA 端的角色定位1.1 RK3588 到底有几个 PCIe 控制器各自能干什么RK3588 的 PCIe 资源在同类 SoC 里算是相当阔气的。它内部集成了多个 PCIe 控制器具体来说通常可用的配置是一个 PCIe 3.0 x4 控制器、一个 PCIe 3.0 x2 控制器以及一个 PCIe 2.0 x1 控制器。注意这些控制器的 lane 分配并不是完全独立的很多时候需要和 SATA、USB 3.0 等高速接口做复用取舍。这一点在硬件设计阶段就必须确认清楚否则板子打回来才发现 lane 被占了那就只能改板。从实际项目经验看大多数 RK3588 加 FPGA 的架构会选择 PCIe 3.0 x4 这条链路。原因很直接x4 的 lane 宽度在 PCIe 3.0 下能提供接近 4GB/s 的理论单向带宽扣掉协议开销后实际有效带宽也能到 3GB/s 以上足够应付多路高清视频流或者高速 AD 采样数据的实时搬运。而 PCIe 2.0 x1 那条链路理论带宽只有 500MB/s 左右通常留给低速外设或者做调试通道用。这里有个容易被忽略的点RK3588 的 PCIe 控制器在 RC 模式和 EP 模式之间是可以切换的。绝大多数场景下RK3588 作为 RCFPGA 作为 EP这是最自然的搭配。但在某些特殊架构里比如 RK3588 需要被另一台主机管理时也可以把 RK3588 配成 EP。不过这种用法比较少见下面讨论默认都是 RK3588 做 RC、FPGA 做 EP 的模式。1.2 FPGA 端 PCIe 硬核的选型与配置要点FPGA 这边Xilinx 和 Altera 两家的 PCIe 硬核都很成熟。Xilinx 7 系列及以上通常用 Integrated Block for PCIeUltraScale 系列则是 PCIe Gen3/Gen4 的硬核。Altera 这边对应的是 Hard IP for PCI Express。选型时最核心的参数就三个支持的 PCIe 代际、lane 数量、以及是否支持 DMA 相关的描述符旁路。以 Xilinx 的 XDMA 为例它其实是一个官方提供的 DMA 子系统 IP封装了 PCIe 硬核和 DMA 引擎对外提供 AXI 接口。用 XDMA 的好处是驱动成熟、Linux 内核里 xdma 驱动已经主线化省去了自己写 DMA 引擎和驱动的巨大工作量。但 XDMA 也有它的局限比如描述符环的深度、中断聚合策略等在高吞吐场景下需要仔细调。如果你用的是 Altera 的 PCIe Hard IP通常需要自己搭配一个 DMA 控制器比如用 Verilog 写一个简单的描述符搬运引擎或者用 Altera 的 Modular Scatter-Gather DMA。这条路更灵活但开发周期明显更长。提示FPGA 端 PCIe 硬核的参考时钟要求非常严格通常是 100MHz 的差分时钟抖动要控制在很低的范围内。如果板上时钟源质量不够链路训练可能反复失败或者训练成功后误码率偏高。这一点在硬件设计阶段就要和时钟树一起考虑。1.3 链路宽度与代际的协商逻辑PCIe 链路训练是个自动协商的过程。RC 和 EP 各自上报自己支持的最大链路宽度和最高代际最终取双方都支持的最小值。比如 RK3588 的控制器支持 Gen3 x4FPGA 硬核配置成了 Gen3 x4那链路就会以 Gen3 x4 建立。但如果 FPGA 那边因为 PCB 走线或者时钟问题只能稳定跑 Gen2那链路就会降到 Gen2 x4。这里有个实战中经常遇到的情况链路明明协商到了 Gen3 x4但实际吞吐量却远低于预期。这时候不要急着怀疑 DMA先用 lspci 看一下链路状态。在 RK3588 的 Linux 系统里执行lspci -vvv找到对应的 FPGA 设备看 LnkSta 那一行。如果显示的是Speed 8GT/s, Width x4说明链路本身没问题如果显示Speed 5GT/s或者Width x2那就是协商阶段就掉下来了得回头查硬件。2. 理论带宽和实际带宽之间到底差了多少2.1 PCIe 3.0 x4 的带宽账本PCIe 3.0 每 lane 的原始速率是 8GT/s用的是 128b/130b 编码所以每 lane 的有效数据速率是 8GT/s 乘以 128/130约等于 7.877Gbps。四条 lane 加起来就是 31.5Gbps换算成字节是大约 3.94GB/s。这是单向的理论峰值。但实际能跑到的带宽要打几个折扣。首先是 TLP 包头开销每个 TLP 包都有一定比例的头部字节有效载荷比例取决于包大小。如果 DMA 传输的包大小是 256 字节那开销大概在 5% 到 8% 之间。其次是流控和确认机制带来的额外开销。再算上 RC 和 EP 两端 AXI 接口的位宽和频率限制实际有效带宽通常在理论值的 70% 到 85% 之间。我实测过的一组数据是RK3588 通过 PCIe 3.0 x4 连接 Xilinx Kintex-7 FPGA用 XDMA 做 H2C 传输包大小 256KB最终稳定在 2.8GB/s 到 3.1GB/s 之间。这个数字已经相当不错了基本能满足 4K 60fps 多路视频的实时搬运需求。2.2 为什么你的带宽只有理论值的一半带宽上不去原因通常集中在几个地方。第一个是 DMA 描述符环太浅。XDMA 默认的描述符环深度可能只有几十个如果传输的数据块很大描述符很快用完DMA 引擎就会空转等描述符带宽自然上不去。解决办法是增大描述符环深度或者用更大的传输块减少描述符消耗。第二个是中断聚合没配好。每次 DMA 传输完成都产生一个中断CPU 频繁进出中断上下文开销很大。XDMA 支持中断聚合可以设置每 N 个描述符完成才产生一次中断或者按时间窗口聚合。这个参数调好了CPU 占用率能降一大截带宽也更稳定。第三个是 AXI 接口的位宽和频率不匹配。FPGA 端 XDMA 的 AXI 接口通常是 256 位或者 512 位如果用户逻辑这边的 AXI 位宽只有 64 位那就需要做位宽转换转换逻辑本身可能成为瓶颈。理想情况下用户逻辑的 AXI 位宽应该和 XDMA 的 AXI 位宽一致频率也要匹配。2.3 用 iperf 和自定义工具做带宽基线测试在调优之前先建立一个带宽基线。Linux 下最方便的工具是lspci加dd但更专业的做法是用 XDMA 自带的测试工具或者自己写一个简单的 DMA 测试程序。一个常用的方法是在 FPGA 端开辟一块 BRAM 或者 DDR 空间通过 XDMA 的 H2C 通道往里面写数据然后用 C2H 通道读回来比较写入和读出的带宽。测试程序可以用xdma_test或者自己用mmap映射 BAR 空间后直接读写。# 查看 PCIe 设备链路状态 lspci -vvv | grep -A 20 Xilinx # 用 dd 做简单的 BAR 空间读写测试需要先映射 # 实际项目中更推荐用 XDMA 提供的字符设备接口注意做带宽测试时一定要确保 CPU 没有跑在其他高负载任务上否则测出来的数字波动很大。最好把测试进程绑到某个隔离的 CPU 核上用taskset指定核心。3. DMA 传输的实战配置与调优细节3.1 XDMA 的 H2C 和 C2H 通道怎么用才高效XDMA 提供两组通道H2C 和 C2H。H2C 是 Host to Card也就是 RK3588 往 FPGA 写数据C2H 是 Card to HostFPGA 往 RK3588 读数据。每组通道有多个引擎可以并行工作。高效使用的关键是让 H2C 和 C2H 同时跑起来。很多项目只用了单向传输比如只做采集那 C2H 是主力但如果同时还有配置下发或者反馈数据H2C 也不能闲着。XDMA 的 H2C 和 C2H 是独立的通道理论上可以同时达到接近单向峰值的带宽。另一个关键是传输块大小的选择。太小的块会导致描述符开销占比过高太大的块又可能受限于描述符环深度和内存连续性。经验值是 64KB 到 1MB 之间具体要看 FPGA 端缓冲区和 RK3588 端内存的实际情况。3.2 描述符环深度和中断聚合的参数计算描述符环深度怎么定一个简单的估算方法是假设你要达到 3GB/s 的带宽传输块大小是 256KB那每秒需要传输大约 12000 个块。如果描述符环深度是 128那 DMA 引擎每处理完 128 个描述符就需要 CPU 补充一次也就是每秒需要补充大约 94 次。这个频率对 CPU 来说不算高但如果描述符环深度只有 32那每秒就要补充 375 次CPU 负担明显加重。中断聚合的参数通常有两个中断计数阈值和中断时间阈值。计数阈值是指累积多少个描述符完成才产生中断时间阈值是指最长多久必须产生一次中断。这两个参数配合使用既能保证吞吐量又能控制延迟。对于高吞吐场景计数阈值可以设大一些比如 64 或 128对于低延迟场景时间阈值要设小比如 10 微秒。3.3 内存对齐和缓存一致性那些事DMA 传输对内存对齐非常敏感。XDMA 通常要求传输的源地址和目的地址按 4KB 或者更大粒度对齐。如果地址没对齐DMA 引擎可能无法使用最优的传输模式带宽会明显下降。在 RK3588 的 Linux 系统里分配 DMA 缓冲区最好用dma_alloc_coherent或者kmalloc加dma_map_single。用户态程序如果要做 DMA通常需要通过mmap映射内核分配的缓冲区或者用udmabuf这类框架。直接 malloc 出来的内存做 DMA 会有缓存一致性问题数据可能不对。缓存一致性是另一个大坑。RK3588 的 CPU 有缓存FPGA 通过 PCIe 写入的数据如果直接进了内存CPU 读的时候可能读到缓存里的旧数据。解决办法是在 DMA 传输前后做缓存刷新或者失效操作。Linux 内核的 DMA API 会自动处理这些但用户态程序需要自己注意。4. 从链路训练失败到带宽跑满的排查链路4.1 链路训练失败的常见原因和定位方法链路训练失败是 PCIe 调试里最让人头疼的问题之一。现象通常是lspci根本看不到 FPGA 设备或者看到了但 BAR 空间映射失败。排查的第一步是看 FPGA 端的 PCIe 硬核有没有报错。Xilinx 的硬核通常有 LTSSM 状态机输出可以通过 ILA 抓取。如果 LTSSM 一直停在 Detect 或者 Polling 状态说明物理层就没通。这时候要查参考时钟、复位信号、以及 PCB 走线。如果 LTSSM 到了 Configuration 状态但没进 L0那可能是链路宽度或者代际协商有问题。可以尝试强制 FPGA 端只支持 Gen1 x1看能不能先建立链路再逐步往上调。这个方法在调试初期非常有用能快速定位是物理层问题还是配置问题。4.2 链路建立但 BAR 映射失败的排查链路建立了lspci也能看到设备但 BAR 映射失败通常是因为 BAR 空间大小和 RK3588 的地址窗口不匹配。RK3588 的 PCIe 控制器对 BAR 空间有地址范围限制如果 FPGA 端申请的 BAR 空间太大或者地址不对齐就会映射失败。解决办法是检查 FPGA 端 PCIe 硬核的 BAR 配置。XDMA 通常有 BAR0 用于配置寄存器BAR1 或 BAR2 用于 DMA 通道。确保这些 BAR 的大小和 RK3588 的窗口匹配。另外RK3588 的设备树里 PCIe 节点的ranges属性也要和实际地址对应。4.3 带宽只有几百 MB 时的逐层排查带宽异常低的时候按下面的顺序逐层排查排查层级检查内容常用工具物理层链路速率和宽度lspci -vvv配置层BAR 空间映射lspci -vvv, /proc/iomemDMA 层描述符环状态XDMA 驱动 debugfs内存层缓冲区对齐和缓存dma_alloc_coherent 返回值应用层传输块大小和并发度自定义测试程序先从物理层开始确认链路是 Gen3 x4。然后看 DMA 层XDMA 驱动通常在/sys/kernel/debug/xdma/下有统计信息能看到描述符的处理速率和错误计数。如果描述符处理速率远低于预期那就是描述符环或者中断聚合的问题。4.4 中断风暴和 CPU 占用率过高的处理中断风暴是另一个常见问题。现象是 CPU 占用率飙升系统响应变慢但带宽并没有提高。原因通常是中断聚合没配好或者 DMA 传输块太小导致中断频率过高。处理方法是先看/proc/interrupts里 XDMA 相关中断的计数如果每秒中断次数超过几万次那肯定有问题。然后调整 XDMA 的中断聚合参数增大计数阈值。如果还是不行考虑用轮询模式替代中断模式虽然会占用一个 CPU 核但在高吞吐场景下反而更稳定。5. 几个容易被忽略但影响巨大的实战细节5.1 RK3588 设备树里 PCIe 节点的配置陷阱RK3588 的设备树里PCIe 节点的配置有几个容易踩的坑。第一个是num-lanes属性必须和实际硬件一致。如果板子上只走了 x2 的线设备树里写了 x4链路训练会失败或者降级。第二个是max-link-speed属性。RK3588 的 PCIe 3.0 控制器可以配成 Gen1、Gen2 或 Gen3。如果硬件信号完整性不够好强制 Gen3 可能导致链路不稳定。这时候可以降到 Gen2 先跑起来等硬件改版再上 Gen3。第三个是reset-gpios属性。FPGA 端的 PCIe 复位信号通常由 RK3588 的 GPIO 控制设备树里要正确配置这个 GPIO。如果复位时序不对FPGA 可能还没准备好RK3588 就开始链路训练了结果就是训练失败。5.2 FPGA 端参考时钟和复位时序的硬件配合FPGA 端 PCIe 硬核的参考时钟质量直接决定链路稳定性。100MHz 差分时钟的抖动要控制在 1ps 以内相位噪声也要足够低。如果板上时钟源不满足要求可以考虑用专用的时钟发生器芯片比如 Si5332 或者 LMK 系列。复位时序也很关键。PCIe 规范要求 PERST# 信号在参考时钟稳定后至少保持 100ms 的低电平。如果 RK3588 的 GPIO 复位信号释放太早FPGA 的 PCIe 硬核可能还没完成初始化链路训练就会失败。在设备树里可以通过reset-gpios和reset-delay-us来控制这个时序。5.3 大块数据传输时的内存碎片和连续性问题大块 DMA 传输需要物理连续的内存。如果系统跑了一段时间内存碎片化严重可能分配不到足够大的连续缓冲区。解决办法是在系统启动早期就预留一大块 CMA 或者用dma_alloc_coherent分配。RK3588 的 Linux 内核里CMA 的大小可以在设备树里配置。对于 PCIe DMA 场景建议预留至少 256MB 的 CMA。如果传输块特别大比如几 MB那 CMA 还要更大。另外用udmabuf框架可以在用户态直接使用预留的 DMA 缓冲区省去了内核态和用户态之间的拷贝。5.4 温度对 PCIe 链路稳定性的影响高速 PCIe 链路对温度比较敏感。RK3588 满载时温度可以到 80 度以上如果散热不好PCIe 控制器的性能可能下降甚至链路降速。FPGA 那边也是高速收发器在高温下抖动会增加误码率上升。实测中遇到过这样的情况常温下链路跑 Gen3 x4 很稳带宽 3GB/s但机箱内温度升到 70 度后带宽掉到 2GB/s 以下lspci看链路还是 Gen3 x4但误码率明显上升。后来加了散热片和风扇问题解决。所以做高吞吐 PCIe 项目时散热设计一定要提前考虑。6. 把带宽从 1GB/s 推到 3GB/s 的调优记录6.1 初始状态和瓶颈定位项目初始状态是RK3588 加 Kintex-7 FPGAPCIe 3.0 x4 链路XDMA 传输实测 H2C 带宽只有 1.1GB/sC2H 带宽 0.9GB/s。这个数字连理论值的三分之一都不到肯定有问题。先用lspci -vvv确认链路状态显示 Gen3 x4物理层没问题。然后看 XDMA 的 debugfs 统计发现描述符处理速率很低每秒只有几千个。再看中断计数每秒中断次数超过 5 万次明显是中断太频繁了。6.2 第一轮优化中断聚合和描述符环深度第一轮优化针对中断聚合和描述符环深度。把 XDMA 的中断计数阈值从默认的 1 调到 64时间阈值从 0 调到 100 微秒。同时把描述符环深度从 64 增加到 512。改完之后重新测试H2C 带宽提升到 1.8GB/sC2H 提升到 1.6GB/s。中断次数降到每秒 8000 次左右CPU 占用率从 40% 降到 15%。效果明显但离 3GB/s 还有距离。6.3 第二轮优化传输块大小和内存对齐第二轮优化针对传输块大小和内存对齐。原来传输块大小是 32KB改成 256KB。同时检查 DMA 缓冲区的对齐发现原来用的kmalloc分配的内存只有 8 字节对齐改成dma_alloc_coherent后保证 4KB 对齐。这一轮改完H2C 带宽到了 2.5GB/sC2H 到了 2.3GB/s。继续加大传输块到 1MB带宽提升不明显但延迟增加了所以最终定在 256KB。6.4 第三轮优化AXI 位宽匹配和并发通道第三轮优化针对 FPGA 端的 AXI 接口。原来用户逻辑的 AXI 位宽是 128 位XDMA 的 AXI 位宽是 256 位中间做了位宽转换。把用户逻辑改成 256 位后带宽又提升了一截。同时启用 XDMA 的多个 DMA 引擎让 H2C 和 C2H 各用两个引擎并行传输。最终 H2C 稳定在 3.0GB/sC2H 稳定在 2.8GB/s。这个数字已经接近理论值的 80%对于实际项目来说完全够用了。6.5 最终配置参数汇总参数项初始值优化值中断计数阈值164中断时间阈值0100us描述符环深度64512传输块大小32KB256KB内存对齐8 字节4KBAXI 位宽128 位256 位DMA 引擎数12这套参数在多个项目中复用效果都比较稳定。当然具体数值还要根据 FPGA 型号和实际负载微调但大方向是一致的先解决中断和描述符的瓶颈再优化内存和位宽最后考虑并发。7. 一些延伸场景和后续可扩展的方向7.1 多 FPGA 级联时的 PCIe Switch 使用有些项目需要多个 FPGA 同时工作比如多路高速采集或者大规模图像处理。这时候 RK3588 的 PCIe 控制器 lane 数可能不够用就需要 PCIe Switch 来扩展。常用的 Switch 芯片有 Microchip 的 PM8532 或者 Broadcom 的 PEX 系列。用 Switch 之后RK3588 看到的是一个 PCIe 树下面挂多个 FPGA。每个 FPGA 的 BAR 空间和 DMA 通道需要独立配置带宽也是共享的。这时候要特别注意 Switch 的上行端口带宽不能成为瓶颈通常上行端口要配成 x4 或者 x8。7.2 结合 NPU 做端侧推理的数据通路设计RK3588 的 NPU 是它的一大卖点。如果 FPGA 采集的数据需要做 AI 推理数据通路可以设计成FPGA 通过 PCIe DMA 把数据搬到 RK3588 的内存CPU 做预处理后送给 NPU 推理推理结果再通过 PCIe 下发给 FPGA 做后续处理。这个通路里PCIe 带宽主要消耗在数据搬运上。如果数据量很大比如多路 4K 视频那 PCIe 带宽就是关键瓶颈。可以考虑在 FPGA 端做一部分预处理比如缩放、格式转换减少通过 PCIe 传输的数据量。7.3 实时性要求下的延迟优化思路有些应用对延迟非常敏感比如工业控制或者高频交易。PCIe 的延迟主要来自几个方面TLP 传输延迟、DMA 描述符处理延迟、中断响应延迟。要降低延迟可以用更小的传输块、更浅的描述符环、更短的中断时间阈值。但低延迟和高带宽往往是矛盾的。小传输块意味着更多的描述符和中断带宽会下降。所以实际项目中要根据需求做权衡。如果延迟要求是微秒级那可能需要考虑用 FPGA 端的寄存器直接映射到 BAR 空间用 MMIO 读写替代 DMA虽然带宽低但延迟可以做到几百纳秒。7.4 从 PCIe 3.0 到 4.0 的升级考量RK3588 只支持到 PCIe 3.0所以在这颗芯片上讨论 4.0 意义不大。但如果后续平台升级到支持 PCIe 4.0 的芯片那 FPGA 端也要跟着升级。PCIe 4.0 的每 lane 速率是 16GT/sx4 的理论带宽翻倍到接近 8GB/s。但 4.0 对信号完整性的要求高得多PCB 板材、走线长度、连接器都要重新设计。从项目经验看如果当前 PCIe 3.0 x4 的带宽已经够用没必要急着上 4.0。4.0 带来的硬件成本和调试难度都是成倍增加的。等真正有带宽需求时再升级也不迟。调完这几轮之后我最大的体会是PCIe 带宽优化不是一锤子买卖而是一个逐层排查、逐步逼近的过程。物理层、配置层、DMA 层、内存层、应用层每一层都可能成为瓶颈。先用lspci和 debugfs 把链路状态和 DMA 统计看清楚然后从最影响吞吐量的参数开始调每改一轮就测一次记录数据。这样即使遇到问题也能快速定位是哪一层出了状况。另外散热和硬件设计这些看起来跟软件调优无关的事情在实际项目里往往才是决定性的因素。

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案