2. DMA 控制器架构
DMA(Direct Memory Access)控制器是系统中负责在外设与内存之间、或内存与内存之间直接传输数据的硬件引擎。理解其内部结构、通道与请求线机制以及传输模式,是掌握 DMA 缓冲区管理的基础。本章从硬件架构视角拆解 DMA 控制器的核心组件与工作流程。
2.1 DMA 控制器内部结构
一个典型的 DMA 控制器由多个功能单元组成,各单元协同完成数据传输的调度与执行。下图展示了 DMA 控制器的内部逻辑架构:
各内部单元的功能如下表所示:
| 单元名称 | 功能描述 | 关键寄存器/参数 |
|---|---|---|
| 控制与状态寄存器 | 配置传输方向(读/写)、传输模式、中断使能、启动/停止控制 | DMA_CR, DMA_SR |
| 地址生成器 | 管理源地址与目的地址的初始值、递增/递减模式、地址对齐 | DMA_SAR, DMA_DAR |
| 字节计数器 | 记录剩余待传输字节数,传输完成后触发中断或链式跳转 | DMA_BCR |
| 通道仲裁器 | 多通道请求时按固定优先级或轮询策略分配总线访问权 | 优先级寄存器 |
| 数据缓冲 FIFO | 暂存数据以匹配外设与内存之间的速率差异,支持突发传输 | FIFO 深度(通常 4~16 字) |
| 总线接口单元 | 负责与系统总线(AHB/AXI/PCIe)握手,驱动地址/数据/控制信号 | 总线协议状态机 |
dma_device 结构体抽象,每个通道对应一个 dma_chan 实例。驱动通过 dma_request_channel() 获取通道,并通过 dmaengine_slave_config() 配置地址与传输参数。
2.2 通道与请求线
DMA 通道是独立的硬件数据通路,每个通道可服务于一个外设或内存区域。请求线(DMA Request Line)是外设向 DMA 控制器发起传输请求的物理信号线。通道与请求线的映射关系决定了系统的并发传输能力。
通道与请求线的关键特性:
- 固定映射: 每个外设请求线直接连接到指定通道,硬件布线简单,但灵活性差。常见于低功耗 MCU 中的 DMA。
- 可编程映射: 通过 DMA 控制器内部的映射寄存器,将任意请求线路由到任意通道。例如 STM32 的 DMA 控制器支持 8 个通道与 16 个请求线之间的灵活映射。
- 逻辑通道: 一个物理通道可通过软件分时复用服务多个外设,适用于外设数量多于通道数量的场景。
- 请求线类型: 包括硬件请求(由外设硬件信号触发)和软件请求(由 CPU 写寄存器触发)。
dmas 属性用于描述外设与 DMA 通道的绑定关系,例如:
uart0: serial@fdd0c000 {
compatible = "qcom,msm-uart";
reg = <0xfdd0c000 0x1000>;
interrupts = <0 112 0>;
dmas = <&dma0 0 2>; /* 使用 dma0 的通道 2 */
dma-names = "tx";
};
2.3 DMA 传输模式
DMA 控制器支持多种传输模式,以适应不同场景下的数据吞吐量、实时性和 CPU 干预需求。三种核心模式为:单次传输、块传输和链式传输。
2.3.1 单次传输
单次传输是最基本的模式。CPU 配置一次源地址、目的地址和传输长度后启动 DMA,完成指定数量的数据搬运后产生中断。每次传输都需要 CPU 重新配置,适用于外设数据量小且不频繁的场景(如按键扫描、传感器单次读取)。
2.3.2 块传输
块传输模式下,DMA 控制器在一次启动后连续传输整个数据块(通常为 64 字节、256 字节或更大),期间不需要 CPU 干预。块传输利用 FIFO 和突发传输(Burst Transfer)提高总线利用率,是音频帧、摄像头行缓冲等中等吞吐量场景的首选。
2.3.3 链式传输
链式传输(也称为描述符链或 scatter-gather)是最高效的模式。CPU 在内存中构建一个描述符链表,每个描述符包含一次传输所需的全部参数(源地址、目的地址、长度、控制标志)以及指向下一个描述符的指针。DMA 控制器启动后,自动从链表头取出描述符执行传输,完成后自动加载下一个描述符,直到链表结束。
链式传输的核心优势:
- 零 CPU 干预: 一次配置即可完成多个不连续内存区域的传输,适用于网络数据包、文件系统 I/O 等复杂场景。
- 描述符结构: 典型的描述符包含以下字段:
struct dma_descriptor {
uint32_t src_addr; // 源地址
uint32_t dst_addr; // 目的地址
uint32_t length; // 传输长度(字节)
uint32_t ctrl; // 控制标志(中断使能、传输方向等)
uint32_t next_desc; // 下一个描述符指针(物理地址)
uint32_t reserved; // 保留/状态
};
dmaengine 框架中的 dma_async_tx_descriptor 结构。驱动通过 dmaengine_prep_slave_sg() 或 dmaengine_prep_dma_cyclic() 创建描述符链,并通过 dmaengine_submit() 提交到硬件队列。Android 内核中,多媒体框架(如 Codec2、ION)广泛使用链式传输实现零拷贝内存映射。
2.4 传输模式选择策略
在实际系统设计中,选择哪种传输模式取决于以下因素:
| 场景 | 推荐模式 | 原因 |
|---|---|---|
| 低速外设(UART 单字节、GPIO) | 单次传输 | 传输量小,CPU 配置开销可接受 |
| 音频播放/录音(周期性缓冲) | 块传输 或 循环链式 | 固定块大小,需连续流式传输 |
| 摄像头帧捕获(大块连续数据) | 块传输 | 单帧数据量大,地址连续 |
| 网络数据包(分散/聚集 I/O) | 链式传输 | 数据分布在多个不连续缓冲区 |
| 文件系统读写(页缓存与设备间) | 链式传输 | 内存页分散,需高效聚合 |