4. DMA 控制器硬件特性:SG-DMA、循环 DMA、内存到内存、外设到内存模式
DMA(Direct Memory Access)控制器的硬件能力直接决定了数据传输的吞吐量、CPU 卸载效率以及系统实时性。本章深入剖析四种核心 DMA 传输模式:SG-DMA(Scatter-Gather DMA)、循环 DMA(Cyclic DMA)、内存到内存(Mem-to-Mem) 和 外设到内存(Peripheral-to-Mem)。理解这些模式的硬件特性,是后续进行驱动性能优化和 CPU 负载均衡的基础。
4.1 四种 DMA 传输模式概览
下表从触发方式、数据流向、典型应用场景和 CPU 干预程度四个维度,对四种模式进行横向对比:
| 模式 | 触发方式 | 数据流向 | 典型应用 | CPU 干预程度 |
|---|---|---|---|---|
| SG-DMA | 软件请求 / 外设事件 | 内存 ↔ 内存 / 外设 | 网络包收发、文件系统 I/O | 低(仅配置描述符链表) |
| 循环 DMA | 硬件定时 / 外设连续请求 | 外设 → 内存 / 内存 → 外设 | 音频流、ADC 连续采样 | 极低(仅初始化) |
| 内存到内存 | 软件显式启动 | 内存 → 内存 | 帧缓冲拷贝、数据搬移 | 中等(需等待完成) |
| 外设到内存 | 外设硬件请求(DREQ) | 外设 → 内存 | UART 接收、SPI 数据采集 | 低(仅配置后等待中断) |
4.2 SG-DMA(Scatter-Gather DMA)
SG-DMA 是高性能 DMA 控制器的标志性特性。它允许 DMA 引擎通过一个描述符链表(Descriptor Chain)来管理多个不连续的物理内存缓冲区,实现一次请求完成多次传输。
4.2.1 核心硬件结构
SG-DMA 的核心是硬件描述符。每个描述符包含:源地址、目的地址、传输长度、控制标志(如中断使能、链尾标记)以及指向下一个描述符的指针。DMA 控制器自动从链表头部开始,依次处理每个描述符,直到遇到链尾标记。
- 描述符基址寄存器(DESC_BASE):指向链表首地址。
- 当前描述符指针(CUR_DESC):硬件内部维护,指向正在处理的描述符。
- 描述符完成中断:每个描述符或整个链表完成时可触发中断。
4.2.2 SG-DMA 传输流程 SVG 图
下图展示了 SG-DMA 从配置描述符链表到传输完成的完整硬件流程:
4.2.3 驱动开发关键点
- 描述符对齐:硬件通常要求描述符按 8 字节或 16 字节对齐,否则可能触发总线错误。
- Cache 一致性:描述符链表所在内存必须使用
dma_alloc_coherent()分配,或显式执行dma_sync_single_for_device()。 - 中断负载:建议仅在链表完成时触发中断,避免每个描述符都中断,减少 CPU 打扰。
/* SG-DMA 描述符结构体示例(以某 SoC 为例) */
struct sg_dma_desc {
u32 src_addr; /* 源物理地址 */
u32 dst_addr; /* 目的物理地址 */
u32 len; /* 传输长度 */
u32 ctrl; /* 控制位:中断使能、链尾标记等 */
u32 next_desc; /* 下一个描述符物理地址 */
u32 status; /* 硬件更新:传输状态 */
} __attribute__((aligned(16)));
4.3 循环 DMA(Cyclic DMA)
循环 DMA 专为连续、周期性的数据流设计。它使用一个环形缓冲区(Ring Buffer),DMA 控制器在传输完最后一个缓冲区后自动回绕到第一个,形成无限循环,无需 CPU 重新配置。
4.3.1 硬件特性
- 自动回绕:硬件内部维护当前缓冲区索引,到达末尾后自动重置为 0。
- 双缓冲 / 多缓冲:通常配合 2 个或 N 个缓冲区,一个被 DMA 填充时,CPU 处理另一个。
- 半满 / 全满中断:硬件支持在缓冲区半满或全满时触发中断,实现流式处理。
4.3.2 循环 DMA 环形缓冲区结构 SVG 图
4.3.3 驱动开发关键点
- 缓冲区大小选择:必须大于外设最大传输单元,避免溢出。音频场景通常选择 2 的幂次大小以优化地址计算。
- 中断频率:半满中断可降低延迟,但增加 CPU 负载;全满中断吞吐量高,但延迟增大。需根据实时性要求权衡。
- 停止与恢复:循环 DMA 停止后,重新启动时需清空 FIFO 并重置指针,防止数据错位。
4.4 内存到内存模式(Mem-to-Mem)
内存到内存模式用于在系统内存的两个区域之间高速拷贝数据,常用于帧缓冲更新、加密引擎输入准备等场景。与 CPU 使用 memcpy() 相比,DMA 可以释放 CPU 核心用于其他计算任务。
4.4.1 硬件特性
- 双通道传输:DMA 控制器同时从源地址读取和向目的地址写入,总线利用率高。
- 突发传输(Burst Transfer):支持 4、8、16 字节突发,显著提高内存带宽利用率。
- 无外设同步:传输完全由软件触发,无需等待外设请求信号。
4.4.2 性能对比:DMA vs CPU memcpy
| 指标 | DMA Mem-to-Mem | CPU memcpy() |
|---|---|---|
| CPU 占用率 | 仅启动和完成中断时占用 | 传输期间 100% 占用 |
| 小数据量延迟(< 64 字节) | 较高(DMA 启动开销大) | 极低 |
| 大数据量吞吐(> 1 MB) | 高(利用突发传输) | 中等(受 Cache 影响) |
| Cache 污染 | 无(直接访问内存) | 严重(污染 L1/L2 Cache) |
4.5 外设到内存模式(Peripheral-to-Mem)
这是最常用的 DMA 模式,用于将外设(如 UART、SPI、I2S、ADC)的数据直接传输到内存,无需 CPU 逐字节读取。外设通过硬件请求线(DREQ)向 DMA 控制器发起传输请求。
4.5.1 硬件握手流程
- 外设数据就绪:外设接收 FIFO 达到预设阈值(如半满),或单字节就绪。
- 断言 DREQ:外设拉高 DMA 请求信号。
- DMA 仲裁:DMA 控制器仲裁优先级,获得总线控制权。
- 单次 / 突发传输:DMA 从外设数据寄存器读取数据,写入内存缓冲区。
- 撤销 DREQ:外设检测到数据被读取,撤销请求信号。
- 重复:直到传输计数归零或收到停止指令。
4.5.2 外设到内存模式硬件握手 SVG 图
4.5.3 驱动开发关键点
- FIFO 阈值匹配:外设 FIFO 阈值应与 DMA 突发大小匹配。例如,SPI FIFO 深度 16 字节,设置阈值为 8 字节,DMA 使用 8 字节突发。
- DREQ 极性:部分外设支持高电平有效或低电平有效,需在 DMA 控制器中正确配置极性。
- 超时处理:外设长时间未产生数据时,DMA 可能挂起。驱动应实现超时定时器,强制复位 DMA 通道。
/* 外设到内存 DMA 配置示例(伪代码) */
struct dma_slave_config config = {
.direction = DMA_DEV_TO_MEM,
.src_addr = (phys_addr_t)&spi_regs->dr, /* 外设数据寄存器地址 */
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.src_maxburst = 4, /* 每次突发 4 字节 */
.dst_addr = (phys_addr_t)buf_phys,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.dst_maxburst = 4,
};
dmaengine_slave_config(chan, &config);