基于Agilex的PCIe接口DMA传输与驱动调试 - 第2节
2.1 PCIe 拓扑结构:从树形到交换式互联
PCI Express(PCIe)采用点对点串行互联的拓扑结构,彻底取代了传统 PCI 的共享并行总线。在 Agilex FPGA 系统中,理解拓扑是配置 DMA 引擎和进行地址映射的前提。
2.1.1 核心组件与层级关系
一个标准的 PCIe 拓扑由以下四类组件构成:
- 根复合体(Root Complex, RC):CPU 与 PCIe 域之间的桥梁,负责生成配置请求、管理中断和地址转换。在 x86 平台中,RC 集成在 CPU 内部;在 Agilex 系统中,RC 通常由 HPS(硬核处理器系统)或 FPGA 逻辑中的硬核 PCIe IP 实现。
- 交换器(Switch):由上行端口(Upstream Port)和多个下行端口(Downstream Port)组成,负责在多个端点之间路由事务包。交换器内部包含一个虚拟 PCI 桥,用于管理总线编号和内存域。
- 端点(Endpoint, EP):功能设备,如 Agilex FPGA 作为 EP 时,通过 PCIe 接口与 RC 交换数据。DMA 控制器通常位于 EP 内部。
- 桥(Bridge):用于连接 PCIe 与其它总线(如 PCI、PCI-X),在纯 PCIe 系统中较少见。
lspci -t 命令查看拓扑时,Agilex EP 会出现在 CPU 根端口(Root Port)下的某个总线分支中。
2.1.2 拓扑结构示意图
下图展示了一个典型的 PCIe 树形拓扑,包含 RC、Switch 和多个 EP。Agilex FPGA 作为 EP 之一,通过 x4 或 x8 链路连接到 Switch 的下行端口。
2.1.3 总线枚举与设备发现
系统上电后,RC 通过深度优先遍历算法扫描 PCIe 总线,为每个设备分配总线号(Bus Number)、设备号(Device Number)和功能号(Function Number)(BDF)。在 Linux 中,可通过 lspci 查看:
# 查看 Agilex EP 的 BDF 信息
$ lspci -s 03:00.0 -vv
03:00.0 Memory controller: Intel Corporation Device 0000 (rev 01)
Subsystem: Intel Corporation Device 0000
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Interrupt: pin A routed to IRQ 47
Region 0: Memory at 91000000 (64-bit, non-prefetchable) [size=16M]
Capabilities: [40] Power Management version 3
Capabilities: [50] MSI: Enable+ Count=1/1 Maskable- 64bit+
Capabilities: [70] MSI-X: Enable- Count=8 Masked-
Capabilities: [a0] Express (v2) Endpoint, MSI 00
Capabilities: [100] Advanced Error Reporting
Capabilities: [148] Device Serial Number 00-00-00-00-00-00-00-00
Capabilities: [158] Power Budgeting <?>
Capabilities: [168] Secondary PCI Express
Kernel driver in use: pcie-dma-drv
lspci 无法看到 Agilex 设备,请检查:1) FPGA 配置是否完成(DONE 灯是否亮起);2) PCIe 参考时钟是否稳定;3) 链路训练状态机(LTSSM)是否进入 L0 状态。可使用 setpci 直接读取配置空间寄存器进行诊断。
2.2 事务层(TLP):PCIe 通信的核心协议
事务层是 PCIe 分层架构中最上层,负责将来自软件或 DMA 引擎的读写请求封装成事务层包(Transaction Layer Packet, TLP)。TLP 是 PCIe 通信的基本单元,所有数据交换(包括配置、内存、I/O 和消息)都通过 TLP 完成。
2.2.1 TLP 格式与字段
一个标准的 TLP 由头部(Header)、数据负载(Data Payload)和可选的ECRC(端到端 CRC)组成。头部长度根据地址类型分为 3DW(双字,12 字节)或 4DW(16 字节)。
| 字段 | 位宽 | 描述 |
|---|---|---|
| Fmt | 2 bits | 格式:00=无数据 3DW, 01=有数据 3DW, 10=无数据 4DW, 11=有数据 4DW |
| Type | 5 bits | 事务类型:MRd(内存读)、MWr(内存写)、Cpl(完成)、CplD(带数据完成)等 |
| TC | 3 bits | 流量类别(Traffic Class),用于 QoS 管理 |
| TD | 1 bit | TLP Digest 存在标志(ECRC) |
| EP | 1 bit | 中毒位(Poisoned),指示数据负载损坏 |
| Attr | 2 bits | 属性:顺序模型、宽松排序、无侦听等 |
| Length | 10 bits | 数据负载长度,单位是 DW(双字),最大 1024 DW |
| Requester ID | 16 bits | 请求者 BDF(Bus:Device:Function) |
| Tag | 8 bits | 标签,用于匹配请求与完成 |
| Address / 1st DW BE | 32/64 bits | 目标地址(内存/配置)或第一个双字的字节使能 |
2.2.2 内存读写事务流程
在 DMA 传输中,最常用的是内存写(MWr)和内存读(MRd)事务。以 Agilex EP 向 RC 内存写数据为例:
- EP 发起 MWr TLP:头部包含目标地址(RC 侧内存地址)、数据负载(DMA 数据)、请求者 ID(EP 的 BDF)和 Tag。
- Switch 路由:根据地址范围将 TLP 转发到 RC 的上行端口。
- RC 接收:将数据写入系统内存,并可选地返回完成(Cpl) TLP(对于 Posted 写,无需完成;对于 Non-Posted 写,需要完成)。
Posted 事务(如 MWr):发送后不需要接收方返回完成包,吞吐量高,但无错误反馈。
Non-Posted 事务(如 MRd、配置读写):必须等待完成包(Cpl/CplD)返回,延迟较大但可靠性高。
DMA 写通常使用 Posted 事务以获得更高性能;DMA 读必须使用 Non-Posted 事务。
2.2.3 TLP 路由方式
PCIe 支持三种 TLP 路由机制,由头部中的路由字段决定:
- 地址路由(Address Routing):用于内存和 I/O 事务,根据目标地址进行路由。这是 DMA 传输中最常用的方式。
- ID 路由(ID Routing):用于配置事务,根据 BDF(总线:设备:功能)进行路由。配置读写必须使用 ID 路由。
- 隐式路由(Implicit Routing):用于消息事务(如 INTx 中断、电源管理事件),由消息类型隐式决定目标。
2.3 数据链路层与物理层:可靠传输与电气连接
数据链路层和物理层位于事务层之下,共同保证 TLP 在物理链路上的可靠传输。理解这两层有助于诊断链路训练失败、比特错误和链路带宽问题。
2.3.1 数据链路层(Data Link Layer)
数据链路层的主要职责包括:
- TLP 序列化与重传:为每个 TLP 分配序列号(Sequence Number),并附加 LCRC(链路 CRC)。接收方通过 ACK/NAK 协议确认或请求重传。
- 数据链路层包(DLLP):用于链路管理,包括 ACK/NAK、电源管理、流控制(Flow Control)等。DLLP 不暴露给事务层。
- 流控制(Flow Control):通过信用(Credit)机制防止接收缓冲区溢出。每个 VC(虚拟通道)独立管理信用。
| DLLP 类型 | 功能 | 方向 |
|---|---|---|
| ACK | 确认已正确接收 TLP,释放重放缓冲区 | 双向 |
| NAK | 请求重传损坏或丢失的 TLP | 双向 |
| InitFC1/InitFC2 | 初始化流控制信用值 | 双向 |
| UpdateFC | 更新可用信用值,允许发送方继续发送 | 双向 |
| PM_* | 电源管理状态转换(如进入 L1 低功耗状态) | 双向 |
2.3.2 物理层(Physical Layer)
物理层分为逻辑子层和电气子层,负责:
- 8b/10b 或 128b/130b 编码:Gen1/Gen2 使用 8b/10b 编码,Gen3/Gen4 使用 128b/130b 编码以提高效率。
- 链路训练状态机(LTSSM):管理链路状态转换,从 Detect → Polling → Configuration → L0(正常工作)→ Recovery(错误恢复)等。
- 差分信号对(Lane):每个 Lane 由一对发送差分对(TX+/-)和一对接收差分对(RX+/-)组成。Agilex 支持 x1/x2/x4/x8 链路宽度。
在 Linux 中,可通过
lspci -vvv -s <BDF> 查看链路状态:LnkSta: Speed 8GT/s (Gen3), Width x4, TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-如果显示
Speed 2.5GT/s (Gen1) 或 Width x1,说明链路训练未达到最佳状态,需检查 PCB 走线或参考时钟。
2.3.3 分层架构与数据流
下图展示了从软件到物理链路的完整数据流,以及各层之间的交互关系。
2.4 PCIe 配置空间:设备身份与资源映射
每个 PCIe 设备(包括 Agilex EP)必须实现配置空间(Configuration Space),用于系统软件识别设备类型、分配资源(内存、I/O、中断)和配置功能。配置空间通过配置读写 TLP(Type 0 或 Type 1)访问。
2.4.1 配置空间布局
PCIe 配置空间大小为 4KB(传统 PCI 为 256 字节)。前 256 字节为配置头(Configuration Header),后 3840 字节为能力结构(Capability Structures)。
| 偏移地址 | 寄存器名称 | 位宽 | 描述 |
|---|---|---|---|
| 0x00 | Vendor ID | 16 bits | 设备制造商 ID(Intel = 0x8086,Altera = 0x1172) |
| 0x02 | Device ID | 16 bits | 设备型号 ID(Agilex 系列 ID 需查阅手册) |
| 0x04 | Command | 16 bits | 控制 I/O 空间、内存空间、总线主控、奇偶校验等 |
| 0x06 | Status | 16 bits | 中断状态、能力列表、错误状态等 |
| 0x08 | Revision ID | 8 bits | 版本号 |
| 0x09 | Class Code | 24 bits | 设备类别(如 0x058000 表示内存控制器) |
| 0x10-0x24 | Base Address Registers (BAR0-BAR5) | 32/64 bits each | 设备内存/I/O 空间基地址,由 BIOS 或操作系统分配 |
| 0x34 | Capabilities Pointer | 8 bits | 指向第一个能力结构的偏移地址 |
| 0x3C | Interrupt Line | 8 bits | 中断向量号(由操作系统分配) |
| 0x3D | Interrupt Pin | 8 bits | 中断引脚(INTA# = 1, INTB# = 2, INTC# = 3, INTD# = 4) |
2.4.2 能力结构(Capability Structures)
PCIe 设备通过能力结构实现高级功能,每个能力结构以能力 ID 和下一能力指针链接成链表。Agilex DMA 驱动需要关注以下能力:
- PCI Express Capability (Cap ID = 0x10):包含链路状态(LnkSta)、设备控制(DevCtl)等寄存器,用于查询链路速度和宽度。
- MSI Capability (Cap ID = 0x05):配置 MSI 中断,包括消息地址、消息数据和中断屏蔽。
- MSI-X Capability (Cap ID = 0x11):配置 MSI-X 中断,支持更多中断向量(最多 2048 个),每个向量独立配置。
- Power Management Capability (Cap ID = 0x01):管理设备电源状态(D0-D3hot)。
- Advanced Error Reporting (AER) Capability (Cap ID = 0x01, Extended):增强错误报告,用于诊断链路错误。
2.4.3 BAR 空间与 DMA 地址映射
BAR(基地址寄存器)是配置空间中最关键的资源。Agilex EP 通过 BAR 向 RC 暴露内部寄存器或内存区域。DMA 驱动需要:
- 读取 BAR 大小:向 BAR 写入全 1,再读回,通过低比特位的 0 判断地址空间大小。
- 映射 BAR 到虚拟地址:使用
ioremap()将物理地址映射到内核虚拟地址空间。 - 配置 DMA 描述符:通过 BAR 映射的寄存器设置源地址、目标地址、传输长度和方向。
- BAR0:通常映射为 64-bit 可预取内存空间,用于 DMA 描述符环形缓冲区。
- BAR2:映射为 32-bit 非可预取空间,用于控制/状态寄存器。
- 在 Quartus 的 PCIe IP 参数化界面中,可以设置 BAR 类型、大小和地址对齐方式。
- 使用
lspci -vvv -s