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中抓取待处理的数据块。事务流程如下:

  1. EP发起Memory Read Request TLP: 包含目标地址、Length、Requester ID和Tag。
  2. RC接收请求: 通过地址译码找到对应的内存控制器,发起系统内存读取。
  3. RC返回Completion with Data TLP: 携带读取的数据、相同的Tag以及Completion Status(成功/失败)。
  4. 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:

  1. EP发起Memory Write Request TLP: 包含目标地址、Length、Data Payload。
  2. RC接收请求: 将数据写入系统内存对应地址。
  3. 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)

PCIe DMA传输数据流:Memory Read/Write事务 系统内存 DDR4 / HBM RC (Root Complex) CPU主桥 + 内存控制器 PCIe Switch 透明转发TLP EP (Endpoint) FPGA / NVMe / 网卡 DMA引擎 描述符解析 + 数据搬运 内存控制器接口 TLP TLP 内部总线 Memory Read (Completion with Data) Memory Write (Posted, 无Completion) 说明: 红色虚线 = Memory Read事务路径(EP发起请求 → RC返回数据) 绿色虚线 = Memory Write事务路径(EP直接写入系统内存,无需返回确认)

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)字段处理。
实战检查清单:
  1. 通过lspci -vvv确认EP的MPS和MRRS值。
  2. 在FPGA侧验证TLP的Requester ID是否与BDF一致。
  3. 使用PCIe分析仪抓取TLP,检查Length字段是否与描述符配置匹配。
  4. 确认Memory Write事务的地址落在系统内存的DMA区域(非缓存区或IO空间)。