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 和驱动。图中同时标出了数据流方向和控制流方向,便于理解“谁发起、谁响应”。
图中右侧把数据流、控制流和关键参数分开标注,是因为初学者最容易混淆“配置空间读写”和“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 是否单描述符串行。