2. DMA 硬件架构:DMA 控制器内部结构
DMA 控制器是系统总线上一个独立的总线主控设备。理解其内部结构是驱动开发的基础,直接决定了描述符链的编排方式和带宽优化策略的选择。本节深入剖析 DMA 控制器的三大核心模块:通道、请求仲裁和地址发生器,并分析其与系统总线的连接拓扑。
2.1 DMA 控制器内部核心模块
一个典型的 DMA 控制器内部由多个并行工作的硬件模块组成。下图展示了其宏观架构:
2.2 通道 (Channel)
通道是 DMA 控制器中执行一次数据传输任务的基本硬件单元。每个通道独立维护一组配置寄存器,包括:
- 源地址寄存器 (SAR):存放数据源地址。
- 目的地址寄存器 (DAR):存放数据目的地址。
- 传输计数寄存器 (CTL/NDT):记录剩余待传输的数据量。
- 控制寄存器 (CCR):配置传输方向、数据宽度、突发大小、中断使能等。
- 描述符指针寄存器 (LLP):用于链式传输时指向下一个描述符。
驱动开发要点: 通道数量决定了系统能同时处理的 DMA 传输任务数。在驱动中,通常使用一个通道池来管理这些硬件资源,避免多个软件模块冲突。
2.3 请求仲裁 (Arbiter)
当多个通道同时请求总线访问时,仲裁器决定哪个通道获得总线控制权。常见的仲裁策略包括:
| 仲裁策略 | 工作原理 | 适用场景 | 带宽影响 |
|---|---|---|---|
| 固定优先级 | 通道编号越小,优先级越高(CH0 > CH1 > ...) | 实时性要求高的关键数据流 | 高优先级通道可能饿死低优先级通道 |
| 轮询 (Round-Robin) | 所有通道轮流获得总线使用权 | 多个通道带宽需求相近 | 带宽分配公平,但延迟可能增加 |
| 混合策略 | 高优先级通道使用固定优先级,其余使用轮询 | 既有实时流又有批量数据传输 | 兼顾实时性和公平性 |
避坑指南: 在驱动初始化时,务必根据业务场景配置仲裁策略。例如,音频 DMA 通道应设置为高优先级,而文件系统 DMA 通道可使用轮询。错误的仲裁配置会导致音频断流或存储性能下降。
2.4 地址发生器 (AGU)
地址发生器负责在每次数据传输后自动更新源地址和目的地址。其核心能力决定了 DMA 能否处理复杂的内存布局:
- 固定地址模式:地址不变,用于访问 FIFO 或外设数据寄存器。
- 增量模式:地址按数据宽度递增,用于连续内存块传输。
- 循环缓冲模式:地址在指定范围内循环,用于音频环形缓冲区。
- 二维/三维地址跳转:支持行/帧步进,用于图像数据块传输。
2.5 DMA 与系统总线的连接方式
DMA 控制器作为总线主控设备,通过系统总线直接访问内存和外设。常见的连接拓扑包括:
2.5.1 单总线架构
DMA 控制器、CPU 和所有外设共享一条系统总线(如 AHB)。DMA 传输时,CPU 必须等待总线释放。这种架构简单,但带宽受限,适用于低功耗嵌入式 MCU。
2.5.2 多总线架构 (Multi-Layer Bus Matrix)
DMA 控制器通过独立的 master 端口连接到总线矩阵,可以同时访问多个 slave 设备。例如,DMA 从 SRAM 读取数据的同时,CPU 可以访问 Flash。这种架构显著提升了系统并发性能。
2.5.3 专用 DMA 总线 (DMA Bus)
在高性能 SoC 中,DMA 控制器拥有专用的数据总线,不经过系统总线矩阵,直接连接到内存控制器和高带宽外设(如 GPU、网络控制器)。这种架构延迟最低,带宽最大。
| 连接方式 | 典型总线 | 并发能力 | 延迟 | 典型应用 |
|---|---|---|---|---|
| 单总线 | AHB / APB | 低 | 高 | STM32、低功耗 MCU |
| 多总线矩阵 | AXI / AHB-Lite | 中 | 中 | ARM Cortex-A 系列 SoC |
| 专用 DMA 总线 | AXI-Stream / PCIe | 高 | 低 | FPGA、GPU、高速网卡 |
2.6 驱动开发中的硬件抽象
在 Linux 内核中,DMA 引擎驱动通过 dma_device 结构体抽象硬件能力。驱动开发者需要填充以下关键字段:
struct dma_device {
unsigned int channels; // 硬件通道数量
enum dma_cap_mask_t cap_mask; // 能力位掩码
struct list_head channels; // 通道链表
// 通道操作函数
int (*device_alloc_chan_resources)(struct dma_chan *chan);
void (*device_free_chan_resources)(struct dma_chan *chan);
// 描述符操作
struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)(
struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
size_t len, unsigned long flags);
struct dma_async_tx_descriptor *(*device_prep_slave_sg)(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
unsigned long flags, void *context);
// 控制操作
int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
unsigned long arg);
enum dma_status (*device_tx_status)(struct dma_chan *chan,
dma_cookie_t cookie, struct dma_tx_state *txstate);
void (*device_issue_pending)(struct dma_chan *chan);
};
关键理解: 硬件架构决定了驱动代码的组织方式。通道数量对应驱动中的通道池大小;仲裁策略影响中断处理优先级;地址发生器能力决定了驱动是否支持
scatter-gather 和 cyclic 传输模式。在编写 DMA 驱动前,务必先阅读芯片参考手册中的 DMA 控制器章节,确认上述硬件参数。