2. DMA控制器硬件架构

DMA(Direct Memory Access)控制器的硬件架构是理解驱动配置与性能调优的基础。本章深入剖析DMA引擎的内部结构、通道与请求线的映射关系,以及地址与计数寄存器的运作机制,为后续描述符链表和循环缓冲的实现奠定硬件认知。

2.1 DMA引擎内部结构

一个典型的DMA控制器由多个功能单元组成,这些单元协同工作以实现高效的数据搬运。下图展示了DMA引擎的核心内部结构:

DMA引擎内部结构框图 DMA主控单元 状态机 / 仲裁器 系统总线接口 AHB / AXI / 本地总线 通道阵列 通道 0 通道 1 通道 2 通道 3 (可扩展至 N 通道) 描述符取指单元 链表遍历 / 预取 数据缓冲 FIFO 源 / 目的暂存 中断控制单元 完成 / 错误中断 关键说明: • 主控单元负责通道仲裁与状态切换,决定哪个通道获得总线控制权。 • 描述符取指单元从内存中预取下一个传输描述符,实现链表连续传输。 • 数据缓冲 FIFO 用于源与目的地址之间的数据暂存,支持宽度转换。
架构要点: 现代DMA控制器(如ARM PL080、Xilinx AXI DMA)通常采用多通道 + 描述符链表架构。通道之间通过硬件仲裁器共享系统总线,描述符取指单元独立于数据传输路径,支持流水线操作。

2.2 通道与请求线

DMA通道是独立的硬件数据搬运路径,每个通道包含一组专用的寄存器集(源地址、目的地址、计数、控制)。请求线(DMA Request Line)是外设向DMA控制器发起传输请求的物理信号线。

2.2.1 通道属性对比

属性 硬件通道 虚拟通道 说明
寄存器集 独立物理寄存器 共享物理寄存器,软件分时复用 硬件通道延迟更低
并发能力 支持真正并行传输 仅支持分时复用 硬件通道适合高带宽外设
资源消耗 高(门电路多) 低(共享逻辑) 嵌入式SoC常用虚拟通道
典型应用 高速ADC、PCIe、以太网 UART、SPI、I2C 根据外设带宽需求选择

2.2.2 请求线映射机制

外设通过请求线向DMA控制器发送传输请求。请求线映射通常由硬件设计固定,或通过软件配置的请求选择寄存器(DMA Request Select)动态分配。

DMA请求线映射与通道分配 外设组 UART0 (Req 0) SPI1 (Req 1) I2C2 (Req 2) ADC (Req 3) 请求选择器 ReqSel 寄存器 通道0 ← Req 2 通道1 ← Req 0 通道2 ← Req 3 通道3 ← Req 1 DMA引擎 通道 0 (I2C2) 通道 1 (UART0) 通道 2 (ADC) 通道 3 (SPI1)
驱动配置要点: 在Linux DMA引擎驱动中,通过 dma_request_channel() 申请通道,并使用 dmaengine_slave_config() 设置请求线映射(如 slave_id 字段)。请求线编号通常对应SoC数据手册中的DMA Request ID。

2.3 地址与计数寄存器

每个DMA通道的核心寄存器组包括源地址寄存器(SAR)、目的地址寄存器(DAR)和传输计数寄存器(CTL/CCR)。这些寄存器直接控制数据搬运的源、目的和长度。

2.3.1 寄存器功能详解

寄存器 缩写 位宽 功能描述
源地址寄存器 SAR 32/64位 存储当前传输的源内存地址或外设地址
目的地址寄存器 DAR 32/64位 存储当前传输的目的内存地址或外设地址
传输计数寄存器 CTL / CCR 16/32位 记录剩余待传输的数据单元数量(通常以字节或字为单位)
控制寄存器 CR 32位 配置传输方向、地址增减模式、数据宽度、中断使能等
状态寄存器 SR 32位 指示通道是否忙、传输完成、错误标志等

2.3.2 地址增减模式

地址寄存器支持三种增减模式,通过控制寄存器的相应位域配置:

  • 固定模式(Fixed): 地址保持不变,适用于外设FIFO(如UART数据寄存器)的重复访问。
  • 递增模式(Increment): 每次传输后地址增加数据宽度,适用于内存缓冲区。
  • 递减模式(Decrement): 每次传输后地址减少数据宽度,用于特殊内存操作。
地址增减模式示意图 固定模式 地址: 0x1000 → 0x1000 适用于: 外设FIFO 每次传输后地址不变 连续读取同一外设寄存器 递增模式 地址: 0x1000 → 0x1004 适用于: 内存缓冲区 每次传输后地址 + 数据宽度 连续填充内存区域 递减模式 地址: 0x1000 → 0x0FFC 适用于: 栈/特殊场景 每次传输后地址 - 数据宽度 从高地址向低地址填充

2.3.3 传输计数与自动重载

传输计数寄存器(CTL)在每次数据传输后递减。当CTL减至0时,DMA控制器根据配置执行以下操作之一:

  • 单次传输完成: 触发完成中断,通道停止。
  • 自动重载(Auto-Reload): 从描述符链表或重载寄存器重新加载计数和地址,实现循环缓冲。
  • 链表跳转: 加载下一个描述符,继续传输。
性能陷阱: 在循环缓冲模式下,如果自动重载延迟过高(如中断响应不及时),可能导致FIFO溢出或数据丢失。建议使用双缓冲(Double Buffering)或描述符链表来消除重载间隙。

2.4 寄存器编程示例

以下代码展示了在裸机环境下配置DMA通道寄存器的典型流程:

/* DMA通道寄存器结构体定义 */
struct dma_channel_regs {
    volatile uint32_t sar;   /* 源地址寄存器 */
    volatile uint32_t dar;   /* 目的地址寄存器 */
    volatile uint32_t ctl;   /* 传输计数寄存器 */
    volatile uint32_t cr;    /* 控制寄存器 */
    volatile uint32_t sr;    /* 状态寄存器 */
};

/* 配置DMA通道进行内存到外设传输 */
void dma_channel_config(struct dma_channel_regs *ch,
                        uint32_t src_addr,
                        uint32_t dst_addr,
                        uint32_t length)
{
    /* 1. 设置源地址(内存缓冲区) */
    ch->sar = src_addr;

    /* 2. 设置目的地址(外设FIFO地址) */
    ch->dar = dst_addr;

    /* 3. 设置传输计数(以字节为单位) */
    ch->ctl = length;

    /* 4. 配置控制寄存器:
     *    - 位[1:0]: 数据宽度 00=8bit, 01=16bit, 10=32bit
     *    - 位[3:2]: 源地址模式 00=递增, 01=固定
     *    - 位[5:4]: 目的地址模式 00=递增, 01=固定
     *    - 位[7]:   中断使能
     *    - 位[8]:   通道使能
     */
    ch->cr = (2 << 0)  |   /* 32位数据宽度 */
             (0 << 2)  |   /* 源地址递增 */
             (1 << 4)  |   /* 目的地址固定 */
             (1 << 7)  |   /* 使能传输完成中断 */
             (1 << 8);     /* 使能通道 */
}

/* 启动传输 */
void dma_channel_start(struct dma_channel_regs *ch)
{
    ch->cr |= (1 << 8);   /* 设置使能位启动传输 */
}
驱动开发提示: 在Linux DMA引擎框架中,上述寄存器操作被封装在 device_prep_slave_sg() 和 device_issue_pending() 等API中。理解底层寄存器有助于调试硬件相关问题和优化传输性能。

2.5 本章小结

DMA控制器的硬件架构决定了驱动设计的边界:

  • 引擎内部结构 包含主控单元、通道阵列、描述符取指单元和数据缓冲,各单元协同实现高效传输。
  • 通道与请求线 的映射关系决定了外设与DMA通道的绑定方式,驱动需正确配置请求选择寄存器。
  • 地址与计数寄存器 是DMA传输的核心控制点,地址增减模式和自动重载机制直接影响循环缓冲的实现效率。