2. DMA 传输原理:控制器架构、描述符链与地址映射

DMA(Direct Memory Access)是 PCIe 系统中实现高吞吐、低延迟数据传输的核心机制。本章从控制器架构、描述符链、地址映射与传输粒度四个维度,系统阐述 DMA 传输的底层原理与硬件设计要点。

2.1 DMA 控制器架构

典型的 PCIe DMA 控制器由以下功能模块构成:

模块 功能描述 关键设计点
描述符引擎 从系统内存中获取描述符,解析传输参数 支持链式预取、缓存描述符
地址翻译单元 完成源/目的地址的 PCIe 地址空间映射 支持 AXI ↔ PCIe 地址转换
数据搬运引擎 执行实际的数据读写操作 支持多通道、多方向并发
完成状态管理 记录传输完成状态,触发中断或写回描述符 支持中断聚合、状态缓存
PCIe 接口层 封装 TLP 包,处理链路层协议 支持 Max Payload Size 配置
设计要点: 高性能 DMA 控制器通常采用 描述符预取 + 乒乓缓冲 架构,在硬件层面隐藏描述符获取延迟,实现连续传输。
提示: 在 FPGA 实现中,描述符引擎与数据搬运引擎应使用独立的时钟域与 FIFO,避免描述符处理阻塞数据通路。
DMA 控制器内部架构 DMA 控制器内部架构 系统内存 描述符 / 数据缓冲区 描述符引擎 预取 · 解析 · 缓存 地址翻译单元 AXI ↔ PCIe 映射 数据搬运引擎 多通道 · 多方向 PCIe 接口层 TLP 封装 · 链路协议 完成状态管理 中断 · 状态写回

2.2 描述符链(Descriptor Chain)

描述符链是 DMA 控制器与软件驱动之间的核心通信契约。每个描述符定义一次传输的全部参数,多个描述符通过指针链接形成链表,实现批量传输调度。

2.2.1 描述符结构

// 典型 32 字节描述符结构
typedef struct {
    uint64_t    src_addr;       // 源地址(系统内存或设备地址)
    uint64_t    dst_addr;       // 目的地址
    uint32_t    length;         // 传输字节数
    uint16_t    flags;          // 控制标志(中断使能、方向、链结束等)
    uint16_t    status;         // 完成状态(硬件写回)
    uint64_t    next_desc;      // 下一个描述符指针(物理地址)
    uint32_t    reserved;       // 保留字段
} dma_descriptor_t;

2.2.2 链式工作流程

  1. 软件准备: 驱动在系统内存中分配描述符缓冲区,填充传输参数,形成链表。
  2. 硬件获取: DMA 控制器通过 PCIe 读取描述符头节点,解析传输参数。
  3. 传输执行: 数据搬运引擎根据描述符中的源/目的地址与长度执行读写。
  4. 状态写回: 传输完成后,硬件将状态写入描述符的 status 字段。
  5. 链推进: 若 next_desc 非空,控制器自动预取下一个描述符,继续传输。
注意: 描述符链的最后一个描述符必须设置 链结束标志(EOL),否则控制器会持续读取无效地址,导致总线挂死。
描述符链工作流程 描述符链工作流程 描述符 1 src=0x1000 dst=0xA000 len=4096 next → 描述符 2 描述符 2 src=0x2000 dst=0xB000 len=8192 next → 描述符 3 描述符 3 (EOL) src=0x3000 dst=0xC000 len=2048 next=0x0 (EOL) 链式推进机制 硬件自动读取 next_desc → 预取下一个描述符 → 流水线执行

2.3 源地址与目的地址映射

在 PCIe DMA 系统中,地址映射涉及三个地址空间:系统物理地址、PCIe 总线地址 和 设备本地地址(如 FPGA 的 AXI 地址空间)。

2.3.1 地址映射关系

地址类型 描述 典型范围
系统物理地址 (PA) CPU 视角的物理地址,用于描述符与数据缓冲区 0x0000_0000 ~ 0xFFFF_FFFF (32位) / 扩展至 64位
PCIe 总线地址 (BDF) PCIe 事务层使用的地址,通过 BAR 映射 由 BIOS/OS 分配,通常位于 64-bit 高位
设备本地地址 (LA) FPGA 内部 AXI 总线地址,用于访问 BRAM/DDR 0x0000_0000 ~ 0x3FFF_FFFF (取决于实现)

2.3.2 典型映射场景

  • H2C (Host to Card): 源地址 = 系统物理地址(主机内存),目的地址 = 设备本地地址(FPGA DDR)。
  • C2H (Card to Host): 源地址 = 设备本地地址(FPGA BRAM),目的地址 = 系统物理地址(主机内存)。
  • Peer-to-Peer: 源/目的地址均为 PCIe 总线地址,数据在两个设备间直接传输,不经过主机内存。
关键约束: 描述符中的地址必须是 物理地址,不能是虚拟地址。驱动需通过 dma_map_single() 或 dma_alloc_coherent() 获取 DMA 可用地址。
地址映射关系 地址映射关系 系统物理地址 CPU / 主机内存 PCIe 总线地址 BAR 映射空间 设备本地地址 FPGA AXI 空间 地址转换流程 PA → iommu/IOMMU 转换 → PCIe 总线地址 → BAR 解码 → 设备本地地址

2.4 传输粒度与对齐要求

PCIe DMA 传输的粒度与对齐直接影响总线利用率和传输效率。不满足对齐要求会导致性能下降甚至传输错误。

2.4.1 关键粒度参数

参数 典型值 对齐要求 影响
Max Payload Size (MPS) 128 / 256 / 512 字节 必须对齐到 MPS 边界 决定单次 TLP 最大数据量
Read Request Size (RRS) 64 / 128 / 256 字节 建议对齐到 64 字节 影响读请求合并效率
描述符地址对齐 16 / 32 字节 必须对齐到描述符大小 避免跨 Cache Line 访问
数据缓冲区对齐 4K / 64K 字节 建议 4K 对齐 避免跨页拆分

2.4.2 对齐规则与避坑

  • 地址对齐: 源地址与目的地址必须对齐到传输粒度的整数倍。例如,MPS=256 时,地址低 8 位必须为 0。
  • 长度对齐: 传输长度应为 MPS 的整数倍,否则最后一个 TLP 会使用 部分写入,降低效率。
  • 描述符对齐: 描述符基地址必须对齐到 32 字节(描述符大小),避免硬件读取时跨 Cache Line 拆分。
  • 跨 4K 边界: 单次传输不应跨越 4K 物理页边界,否则可能触发地址翻译异常。
常见错误: 驱动未对描述符地址做 32 字节对齐,导致 DMA 控制器读取描述符时产生 Unsupported Request (UR) 错误,传输无法启动。
传输对齐要求 传输对齐要求 ✅ 正确对齐 地址 0x1000 · 长度 4096 · MPS=256 ❌ 错误对齐 地址 0x1001 · 长度 4095 · MPS=256 对齐检查清单 1. 地址低 N 位 = 0 (N=log2(MPS)) 2. 长度 % MPS == 0 3. 描述符地址 % 32 == 0 4. 不跨 4K 物理页边界 5. 缓冲区起始地址 4K 对齐

2.5 本章小结

  • DMA 控制器由 描述符引擎、地址翻译单元、数据搬运引擎、完成状态管理、PCIe 接口层 五大模块组成。
  • 描述符链通过 链表结构 实现批量传输调度,硬件自动预取与推进。
  • 地址映射涉及 系统物理地址 → PCIe 总线地址 → 设备本地地址 三级转换。
  • 传输粒度与对齐是性能关键:MPS、RRS、描述符对齐、4K 边界 必须严格满足。