4. DMA描述符链(Descriptor Chain)

DMA描述符链是高性能嵌入式系统中实现批量、连续、零CPU干预数据传输的核心机制。它将多个传输任务组织成链表,由DMA控制器硬件自动遍历并执行,彻底解放处理器。

4.1 描述符结构定义

每个DMA描述符(Descriptor)是一个固定格式的内存结构,通常包含以下关键字段:

字段 宽度(典型) 描述
Next Descriptor Pointer 32/64 bit 指向下一个描述符的物理地址;链尾通常为0或回环指针
Source Address 32/64 bit 本次传输的源地址(内存或外设FIFO)
Destination Address 32/64 bit 本次传输的目的地址
Transfer Length 16/32 bit 本次传输的字节数(或数据项个数)
Control / Status 16/32 bit 控制标志(中断使能、传输类型)和完成状态位
设计要点:描述符必须对齐到缓存行边界(通常32或64字节),且驻留在非缓存(Non-cacheable)或一致性(Coherent)内存区域,避免CPU与DMA之间的缓存一致性问题。

以下是一个典型的描述符结构体定义(以ARM PL080为例):

/* DMA描述符结构体 */
struct dma_descriptor {
    uint32_t    next;           /* 下一个描述符物理地址 */
    uint32_t    src_addr;       /* 源地址 */
    uint32_t    dst_addr;       /* 目的地址 */
    uint32_t    length;         /* 传输长度(字节) */
    uint32_t    ctrl;           /* 控制字:中断使能、传输宽度等 */
    uint32_t    status;         /* 状态字:完成标志、错误标志 */
    uint32_t    reserved[2];    /* 保留,对齐到32字节 */
} __attribute__((aligned(32)));

4.2 链表组织方式

描述符链采用单向链表结构,每个描述符的next字段指向下一个描述符的物理地址。DMA控制器内部维护一个当前描述符指针寄存器(Current Descriptor Pointer, CDP),初始指向链首。

链表组织规则:
  • 线性链:最后一个描述符的next置为0或NULL,传输完成后DMA停止。
  • 环形链:最后一个描述符的next指向链首,实现无限循环传输(常用于音频、视频流)。
  • 中断插入:在任意描述符的控制字中设置中断标志,DMA完成该段后触发中断,允许CPU动态修改后续链表。
DMA描述符链组织结构 DMA描述符链组织结构 描述符 0 src: 0x8000_0000 len: 1024 描述符 1 src: 0x8000_0400 len: 2048 描述符 2 src: 0x8000_0C00 len: 512 next = 0 (链尾) DMA控制器 CDP → 描述符 0 线性链:描述符0 → 描述符1 → 描述符2 → NULL 环形链:描述符2的next指向描述符0,形成循环

4.3 硬件自动加载机制

DMA控制器通过硬件状态机自动遍历描述符链,整个过程无需CPU参与:

  1. 初始化:CPU将链首描述符的物理地址写入DMA的CDP寄存器,并启动DMA。
  2. 加载描述符:DMA控制器通过总线主控(Bus Master)从内存读取当前描述符到内部寄存器。
  3. 执行传输:根据描述符中的源地址、目的地址和长度,启动一次DMA传输。
  4. 更新状态:传输完成后,DMA硬件将status字段置为完成标志(通常写回内存)。
  5. 加载下一个:读取当前描述符的next字段,若非0则跳转到步骤2;否则停止或触发中断。
关键时序约束:硬件加载描述符需要占用总线带宽。在高速传输场景(如PCIe、GigE)中,描述符加载时间可能成为瓶颈。建议将描述符链放在高速SRAM或紧耦合内存(TCM)中,并尽量使用突发读取(Burst Read)来减少加载延迟。
DMA硬件自动加载描述符链流程 DMA硬件自动加载描述符链流程 ① CPU设置CDP并启动 写入链首物理地址 ② 硬件加载描述符 总线主控读取内存 ③ 执行DMA传输 源→目的 数据搬运 ④ 写回状态标志 status = 完成 next? 非0 → 跳转步骤② 0 → 停止/中断

4.4 链式传输的优势

相比单次DMA传输,描述符链在性能、灵活性和CPU利用率方面具有显著优势:

对比维度 单次DMA传输 描述符链传输
CPU干预次数 每次传输都需要CPU配置和启动 一次配置,硬件自动遍历整个链表
传输连续性 段间存在CPU处理延迟 硬件无缝衔接,零延迟切换
内存碎片处理 需要CPU合并分散缓冲区 每个描述符可指向不同内存区域
中断频率 每段传输都产生中断 仅在链尾或指定节点产生中断
适用场景 简单、低频、小数据量 高速流、大数据块、实时系统
典型应用场景:
  • 网络数据包处理:将多个不连续的Socket缓冲区(SKB)组织成描述符链,实现零拷贝收发包。
  • 音频/视频流:使用环形描述符链实现双缓冲或多缓冲,消除画面撕裂和音频卡顿。
  • 存储控制器:将文件系统的多个分散块通过描述符链一次性提交给存储设备,提升IOPS。
  • FPGA加速器:通过描述符链向FPGA发送批量任务描述符,实现流水线处理。

描述符链的另一个关键优势是动态可扩展性。CPU可以在DMA运行过程中,通过修改链尾描述符的next指针来追加新的传输任务,实现运行时链表扩展。这种机制在不确定数据量的流式处理中尤为重要。

链式传输 vs 单次传输对比 链式传输 vs 单次传输:CPU干预对比 单次传输(高CPU负载) CPU配置段1 DMA传输段1 CPU配置段2 DMA传输段2 链式传输(低CPU负载) CPU一次配置整个链表 DMA自动传输段1 DMA自动传输段2 DMA自动传输段3 链式传输:CPU只在初始化时介入一次,后续所有段由硬件自动完成,CPU可处理其他任务