1. 课程导入与系统总览

在工业相机、医疗影像、5G 小基站、机器视觉采集卡以及多路数据采集设备中,经常需要把 FPGA 采集或预处理后的高速数据送到主机 CPU 或 GPU。传统方案常用 USB 3.0、千兆以太网或并行总线,但当单通道数据率超过 5 Gb/s、且要求低延迟、低 CPU 占用、支持 DMA 直接搬移时,PCI Express(PCIe)几乎是唯一现实的选择。本课程讨论的,就是如何用 Lattice FPGA(典型如 ECP5、CertusPro-NX、CrossLink-NX 等带 SerDes 或硬核 PCIe 的器件)构建一条从板级链路到主机驱动的完整 PCIe 高速互连系统。

需要先明确一个基本判断:Lattice FPGA 的 PCIe 方案与 Xilinx/Altera 的“整包 IP + 参考设计”路线不同。Lattice 多数中小容量器件并不内置完整 PCIe 硬核,常见做法是用 SerDes 加软核(Soft IP)实现 PCIe 端点,或者使用带硬核 PCIe 的特定型号。这意味着开发者必须更清楚地理解物理层、事务层、配置空间和 DMA 之间的分工,而不能只靠图形化向导点几下就完成。本课程的目标,正是把这条链路从“信号怎么进来”到“数据怎么被主机读到”完整讲透。

典型应用场景与系统边界

先看几个真实场景,帮助建立系统边界感。第一类是高速数据采集:FPGA 通过 LVDS 或 JESD204B 接收 ADC 数据,缓存到 DDR,再经 PCIe DMA 写入主机内存,主机端用驱动和上位机读取。第二类是图像采集卡:Camera Link 或 MIPI 图像进入 FPGA,做去马赛克、缩放、格式转换后,以 PCIe 传输到主机显示或存储。第三类是嵌入式加速卡:FPGA 作为协处理器,主机通过 PCIe 下发任务描述符,FPGA 计算完成后用中断通知主机取回结果。这三类场景的共同点是:数据方向明确、带宽要求高、需要 DMA 而非 CPU 逐字节搬运。

系统边界可以这样划分:FPGA 侧包括 SerDes 收发器、PCIe 软核/硬核、DMA 引擎、用户逻辑和 DDR 控制器;链路侧是差分对、参考时钟、复位和电源;主机侧是 Root Complex、PCIe 交换机(可选)、内存和驱动程序。课程后续章节会分别深入这些部分,而本章先建立整体地图。

整体架构:从差分对到用户逻辑

下面这张图给出 Lattice FPGA PCIe 系统的分层结构。自下而上依次是:物理链路层(差分对、参考时钟、复位)、SerDes/PHY 层、PCIe 数据链路层与事务层、配置空间、DMA 引擎、用户逻辑与 DDR 缓冲,最后到主机侧的 Root Complex 和驱动。图中同时标出了数据流方向和控制流方向,便于理解“谁发起、谁响应”。

Lattice FPGA PCIe 高速互连系统分层架构 主机侧(Root Complex) CPU / 内存 PCIe 驱动 / 上位机 配置空间枚举 · BAR 映射 · MSI/MSI-X 中断 PCIe 物理链路 差分对 TX/RX · 100 MHz 参考时钟 · PERST# 复位 · 12V/3.3V 电源 Lattice FPGA 内部 SerDes / PHY(PCS + PMA) PCIe 数据链路层 + 事务层(软核/硬核) 配置空间 / BAR DMA 引擎 用户逻辑 + DDR 缓冲 数据流(DMA 写) 用户逻辑 → DDR 缓冲 → DMA 引擎 → 事务层 → 链路层 → SerDes → 差分对 → 主机内存 TLP: Mem Write / Completion 控制流(枚举与中断) 主机枚举 → 配置读/写 → BAR 分配 驱动加载 → 下发描述符 → DMA 启动 FPGA 完成 → MSI/MSI-X 中断 → 主机取数 关键参数与观察点 • 链路速率:Gen1 2.5 GT/s / Gen2 5 GT/s • 链路宽度:x1 / x2 / x4 • 参考时钟:100 MHz ±300 ppm • 复位:PERST# 低有效,需满足时序 • 观察点:LTSSM 状态、TLP 计数、DMA 完成中断

图中右侧把数据流、控制流和关键参数分开标注,是因为初学者最容易混淆“配置空间读写”和“DMA 数据传输”这两条路径。前者是主机在枚举阶段通过配置事务访问 FPGA 的 BAR 空间,数据量小、方向以主机发起为主;后者是 FPGA 主动发起 Mem Write TLP,把大批数据写入主机内存,方向以 FPGA 发起为主。两者共用同一条物理链路,但事务类型、发起方和用途完全不同。

学习路径:先链路、再事务、后 DMA

本课程按“由底向上、由静到动”的顺序组织。第一步是物理层与链路训练:理解 SerDes 的发送/接收、8b/10b 或 128b/130b 编码、LTSSM 状态机如何从 Detect 走到 L0。第二步是事务层与配置空间:理解 TLP 格式、配置读写、BAR 映射、MSI/MSI-X 中断。第三步是 DMA 引擎:理解描述符环、地址映射、完成通知和带宽估算。第四步是驱动与上位机联调:理解 Linux 下 PCIe 驱动的基本结构、mmap、中断处理和性能测试方法。第五步是调试与排障:用示波器、协议分析仪、Lattice Reveal 和 dmesg 定位链路不训练、DMA 不完成、带宽不达标等问题。

这条路径不是并列的,而是有依赖关系:链路不训练,后面全部无从谈起;配置空间不对,驱动无法绑定;DMA 描述符格式不对,数据会写错地址或根本不动。因此每一章都会给出可观察的判断依据,而不是只讲概念。

开发流程:从工程创建到上板验证

实际开发时,一个典型的 Lattice PCIe 工程流程如下。首先在 Lattice Diamond 或 Radiant 中创建工程,选择带 SerDes 的器件型号,例如 ECP5-85F 或 CertusPro-NX。然后例化 PCIe 软核 IP,配置链路宽度、最大速率、BAR 数量和大小、厂商 ID 与设备 ID。接着编写用户逻辑,把采集数据写入 DDR 缓冲,并实现 DMA 描述符解析。之后添加时序约束,重点是参考时钟、差分对和 PERST# 的时序。综合、布局布线后生成比特流,下载到 FPGA。最后在主机端加载驱动,用 lspci 确认设备枚举,用自定义测试程序发起 DMA 并检查数据一致性。

这里给出一个示例参数表,用于说明配置阶段需要确定的关键项。表中数值属于教学示例,实际项目需根据器件手册和主机平台调整。

配置项 示例值 说明
链路速率 Gen2 (5 GT/s) 需 SerDes 和主机同时支持
链路宽度 x4 占用 4 对 TX + 4 对 RX
BAR0 大小 64 KB 映射 DMA 寄存器和描述符环
BAR1 大小 1 MB 映射用户逻辑寄存器或 DDR 窗口
厂商 ID / 设备 ID 0x1204 / 0x0001 示例值,需与驱动匹配
中断类型 MSI-X,1 个向量 DMA 完成时触发
参考时钟 100 MHz 需满足 PCIe 抖动要求

结果判断与常见误区

上板后如何判断系统是否正常?第一步看枚举:在 Linux 下执行 lspci -vv,应能看到对应厂商 ID 和设备 ID,且 Link Status 显示 Speed 和 Width 符合预期。第二步看 BAR:lspci -vv 中 BAR 地址应被正确分配,且不与其它设备冲突。第三步看中断:加载驱动后,/proc/interrupts 中应出现对应 MSI-X 向量。第四步看 DMA:发起一次传输后,主机内存中的数据应与 FPGA 发送端一致,且中断计数增加。如果链路训练失败,通常先查参考时钟和 PERST# 时序;如果枚举成功但 DMA 不动,先查描述符地址和 BAR 映射;如果带宽远低于预期,先查链路宽度是否协商为 x1、DMA 是否单描述符串行。

建议在第一次上板时,先用 Lattice Reveal 观察 LTSSM 状态和 TLP 计数,再逐步加入 DMA。不要一开始就同时调链路、DMA 和驱动,否则问题会互相掩盖。