4. PCIe DMA传输模型:RC与EP角色、TLP包格式、Memory Read/Write事务
PCIe DMA传输的核心在于理解总线上的角色分工、数据封装的格式以及读写事务的完整生命周期。本章从RC(Root Complex)与EP(Endpoint)的拓扑角色出发,深入TLP(Transaction Layer Packet)的字节布局,最后拆解Memory Read/Write事务的握手流程,为后续驱动中的DMA描述符编程打下坚实基础。
4.1 RC与EP:谁发起、谁响应
在PCIe拓扑中,RC(Root Complex) 通常集成在CPU主桥内,负责连接CPU、内存和PCIe总线;EP(Endpoint) 则是功能设备,如NVMe SSD、FPGA加速卡或网卡。DMA传输中两者的角色并非固定:
| 角色 | 典型位置 | DMA中的行为 | 是否可发起Memory请求 |
|---|---|---|---|
| RC | CPU主桥 / 北桥 | 配置EP的DMA引擎,响应EP的读写请求 | 是(通过CPU核心或内置DMA控制器) |
| EP | PCIe功能设备 | 主动发起DMA Read/Write TLP,搬运数据 | 是(作为Requester) |
| Switch | RC与EP之间 | 透明转发TLP,不修改事务内容 | 否(仅桥接) |
实战要点: 在驱动开发中,EP侧的DMA引擎通常通过BAR空间暴露控制寄存器;RC侧驱动只需写EP的DMA描述符地址,EP便会自动发起Memory Read(从系统内存取数据)或Memory Write(向系统内存写数据)。理解谁发起TLP,决定了中断处理与缓存一致性维护的代码位置。
4.2 TLP包格式:事务层的数据信封
TLP是PCIe事务层的核心数据单元,所有Memory Read/Write、I/O、Configuration和Message事务都封装在TLP中。一个完整的TLP由以下字段组成:
+--------+--------+--------+--------+--------+--------+--------+--------+
| Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 |
+--------+--------+--------+--------+--------+--------+--------+--------+
| Fmt | Type | TC | Attr | TH | TD | EP | Attr | Length |
| (2b) | (5b) | (3b)| (2b) | (1b)| (1b)| (1b)| (2b) | (10b) |
+--------+--------+--------+--------+--------+--------+--------+--------+
| Requester ID (16b) | Tag (8b) | Last DW BE | 1st DW BE |
+--------------------------------+----------------+------------+-----------+
| Address[63:32] 或 Address[31:2] + 0b00 (32-bit) |
+-----------------------------------------------------------------------+
| Data Payload (1~1024 DW, 4字节对齐) |
+-----------------------------------------------------------------------+
| CRC (32-bit, 可选) |
+-----------------------------------------------------------------------+
关键字段解读:
- Fmt + Type: 决定事务类型。例如
000 00000为Memory Read Request(32位地址),001 00000为Memory Read Request(64位地址)。 - Length: 以DW(4字节)为单位的有效数据长度,最大1024 DW(4 KB)。
- Requester ID: 由Bus/Device/Function(BDF)组成,标识发起者。
- Tag: 用于区分同一Requester发出的多个未完成事务,驱动必须管理Tag池。
- Address: 目标内存地址,32位或64位,必须4字节对齐。
4.3 Memory Read事务:EP从系统内存取数据
Memory Read事务用于EP从RC侧的系统内存读取数据,典型场景是FPGA从DDR中抓取待处理的数据块。事务流程如下:
- EP发起Memory Read Request TLP: 包含目标地址、Length、Requester ID和Tag。
- RC接收请求: 通过地址译码找到对应的内存控制器,发起系统内存读取。
- RC返回Completion with Data TLP: 携带读取的数据、相同的Tag以及Completion Status(成功/失败)。
- EP接收Completion: 根据Tag匹配未完成的请求,将数据写入EP内部FIFO或寄存器。
性能陷阱: Memory Read事务需要等待Completion返回,属于“Split Transaction”。如果EP连续发出大量Read请求而RC无法及时响应,会导致Tag耗尽或EP侧FIFO溢出。驱动中应合理设置Max Payload Size(MPS)和Max Read Request Size(MRRS),避免小粒度读取。
4.4 Memory Write事务:EP向系统内存写数据
Memory Write事务用于EP将采集或计算后的数据写入系统内存,是DMA传输中最常用的操作。与Read不同,Write事务不需要Completion:
- EP发起Memory Write Request TLP: 包含目标地址、Length、Data Payload。
- RC接收请求: 将数据写入系统内存对应地址。
- RC返回(可选)Completion: 对于Posted事务(Memory Write属于Posted),RC不强制返回Completion;只有错误发生时才会返回错误消息。
为什么Write不需要Completion? PCIe协议将Memory Write定义为Posted事务,即发送后不需要确认。这大幅降低了写延迟,但也意味着驱动必须通过其他机制(如Doorbell寄存器或中断)确认数据已到达内存。在FPGA DMA设计中,通常由EP在写完最后一个数据后触发MSI中断通知RC。
4.5 事务类型对比与选择策略
| 特性 | Memory Read | Memory Write |
|---|---|---|
| 事务分类 | Non-Posted(需要Completion) | Posted(无需Completion) |
| 延迟 | 较高(等待往返) | 较低(单向发送) |
| 数据方向 | RC → EP | EP → RC |
| 典型DMA场景 | EP从系统内存读取描述符或输入数据 | EP向系统内存写入输出数据或状态 |
| Tag管理 | 必须管理Tag池,避免溢出 | 不需要Tag(Posted事务) |
| 错误处理 | 通过Completion Status返回错误 | 通过错误消息(ERR_COR/ERR_NONFATAL)上报 |
4.6 完整DMA传输数据流(内联SVG)
4.7 驱动开发中的TLP字段编程要点
在编写DMA驱动时,虽然不需要手动构造TLP(由PCIe控制器硬件完成),但必须理解以下字段对驱动配置的影响:
- Max Payload Size (MPS): 决定单个TLP能携带的最大数据量。FPGA EP通常支持128/256/512字节,需与RC协商一致。MPS过小会导致TLP数量激增,降低有效带宽。
- Max Read Request Size (MRRS): EP一次Memory Read请求能读取的最大长度。驱动通过配置寄存器设置,建议与MPS保持一致。
- Tag数量: EP必须支持至少32个Tag(PCIe 3.0要求)。驱动在提交多个DMA Read请求时,需确保Tag不重复且不超出硬件容量。
- 地址对齐: TLP中的Address字段必须4字节对齐。如果DMA描述符中的源地址未对齐,驱动需进行拆分或使用字节使能(Byte Enable)字段处理。
实战检查清单:
- 通过lspci -vvv确认EP的MPS和MRRS值。
- 在FPGA侧验证TLP的Requester ID是否与BDF一致。
- 使用PCIe分析仪抓取TLP,检查Length字段是否与描述符配置匹配。
- 确认Memory Write事务的地址落在系统内存的DMA区域(非缓存区或IO空间)。