3. DMA控制器硬件原理:内部结构、通道、请求线与传输粒度
DMA(Direct Memory Access)控制器是系统中负责在内存与外设之间、或内存与内存之间直接搬运数据的硬件引擎。理解其内部结构、通道分配、请求线机制以及传输粒度,是编写高效DMA驱动的理论基础。本章从硬件微架构出发,逐一拆解这些核心概念。
3.1 DMA控制器内部结构
一个典型的DMA控制器内部由多个功能模块组成,它们协同完成一次完整的DMA传输。下图展示了DMA控制器的核心架构:
各模块功能说明:
| 模块 | 功能描述 | 驱动关注点 |
|---|---|---|
| 主控状态机 (FSM) | 控制DMA传输的完整生命周期:空闲→请求→读→写→计数更新→完成/中断 | 状态寄存器读取、超时处理 |
| 地址生成单元 (AGU) | 根据源/目的地址、增量模式、地址宽度,自动生成下一次访问地址 | 地址对齐、增量/固定模式配置 |
| 传输计数单元 (TCU) | 记录剩余待传输字节数,传输完成后触发中断或链式传输 | 计数寄存器初始化和读取 |
| 数据缓冲FIFO | 暂存从源端读取的数据,等待写入目的端;深度决定突发能力 | FIFO阈值、水线配置 |
| 通道寄存器组 | 每个通道独立保存源地址、目的地址、传输计数、控制配置 | 通道描述符的编程接口 |
| 通道仲裁器 | 多通道同时请求时,按固定优先级或轮询策略选择下一个服务通道 | 优先级分配、避免低优先级饿死 |
| 系统总线接口 | 与系统总线(AHB/AXI/OCP)握手,发起读/写传输事务 | 总线协议适配、突发类型设置 |
驱动提示: 在初始化DMA控制器时,务必先复位状态机,再配置通道寄存器。部分控制器在状态机处于忙状态时,写通道寄存器会导致未定义行为。
3.2 DMA通道:独立的数据搬运路径
DMA通道(Channel)是控制器内部一条独立的、可编程的数据搬运路径。每个通道拥有自己的一组寄存器,可以独立配置源地址、目的地址、传输方向和触发方式。多通道的存在使得DMA控制器能够同时服务多个外设或内存区域。
通道的典型生命周期如下:
- 空闲(Idle):通道未配置或已完成传输,等待软件或硬件触发。
- 配置(Configured):软件写入通道描述符,设置源/目的地址、传输计数、控制标志。
- 请求(Requested):外设发出DMA请求,或软件手动启动通道。
- 传输中(Active):控制器执行读→写循环,AGU和TCU持续更新。
- 完成(Completed):TCU计数归零,触发中断或链式跳转到下一个描述符。
通道数量与系统设计: 嵌入式SoC中DMA通道数通常为4~32个。驱动开发时需注意:
• 通道是稀缺资源,使用前需申请,使用后及时释放。
• 部分控制器支持通道复用(多个外设共享一个通道),需通过请求线映射配置。
• 通道是稀缺资源,使用前需申请,使用后及时释放。
• 部分控制器支持通道复用(多个外设共享一个通道),需通过请求线映射配置。
3.3 DMA请求线:外设与DMA的握手信号
DMA请求线(DMA Request Line)是外设向DMA控制器发起数据传输请求的硬件信号线。每个外设(如UART、SPI、I2S、ADC)在准备好发送或接收数据时,会通过专用的请求线通知DMA控制器。
请求线的典型工作流程:
- 硬件请求(Hardware Request):外设的RX FIFO非空或TX FIFO空时,拉高DMA请求信号。
- DMA响应:DMA控制器检测到请求后,通过仲裁器选择通道,发起总线读/写事务。
- 请求清除:DMA完成一次传输后,外设自动清除请求信号(或由DMA控制器回读清除)。
- 软件请求(Software Request):驱动通过写寄存器手动触发DMA传输,用于内存到内存的拷贝。
| 请求类型 | 触发源 | 典型应用 | 驱动配置 |
|---|---|---|---|
| 硬件请求 | 外设硬件信号 | UART RX/TX、SPI、I2S、ADC | 设置通道与外设请求线映射 |
| 软件请求 | CPU写寄存器 | 内存拷贝、数据搬移 | 设置通道为软件触发模式 |
| 外部中断触发 | GPIO中断 | 低速传感器数据采集 | 中断服务程序中启动DMA |
| 定时器触发 | 定时器PWM/比较输出 | 周期性数据采样 | 定时器输出连接DMA请求线 |
常见陷阱: 外设的DMA请求信号可能是电平触发或边沿触发。电平触发模式下,若DMA响应不及时,外设可能覆盖数据;边沿触发则需确保请求脉冲宽度满足DMA采样时序。驱动开发时务必查阅外设和DMA控制器的数据手册,确认请求信号类型。
3.4 传输粒度:字节、字、突发与对齐
传输粒度(Transfer Granularity)定义了DMA每次总线事务搬运的数据量。粒度的选择直接影响总线带宽利用率和外设FIFO的匹配程度。
常见的传输粒度包括:
- 字节(8-bit):每次传输1字节,适用于串行通信外设(如UART)。
- 半字(16-bit):每次传输2字节,适用于16位外设(如I2S)。
- 字(32-bit):每次传输4字节,适用于32位外设和内存。
- 双字(64-bit):每次传输8字节,适用于64位总线系统。
- 突发(Burst):连续传输多个数据单元(如4字、8字、16字突发),最大化总线利用率。
| 粒度 | 数据宽度 | 总线事务数 | 适用场景 | 对齐要求 |
|---|---|---|---|---|
| 字节 | 8-bit | N次 | UART、低速传感器 | 无 |
| 半字 | 16-bit | N/2次 | I2S、16位ADC | 2字节对齐 |
| 字 | 32-bit | N/4次 | SPI、内存拷贝 | 4字节对齐 |
| 双字 | 64-bit | N/8次 | DDR、高速数据流 | 8字节对齐 |
| 突发4 | 4×32-bit | N/16次 | AXI总线、图像处理 | 16字节对齐 |
| 突发8 | 8×32-bit | N/32次 | 高性能DMA | 32字节对齐 |
驱动开发最佳实践:
• 优先使用与外设FIFO深度匹配的突发粒度,减少总线占用。
• 内存到内存的DMA传输,使用最大支持的突发长度以获得最高吞吐。
• 如果源和目的地址对齐要求不同,以更严格的一方为准。
• 在设备树或平台数据中声明DMA的粒度能力,避免运行时动态计算。
• 优先使用与外设FIFO深度匹配的突发粒度,减少总线占用。
• 内存到内存的DMA传输,使用最大支持的突发长度以获得最高吞吐。
• 如果源和目的地址对齐要求不同,以更严格的一方为准。
• 在设备树或平台数据中声明DMA的粒度能力,避免运行时动态计算。
3.5 关键寄存器与编程模型
DMA控制器的编程接口通常包含以下几类寄存器,驱动通过读写这些寄存器来配置和控制DMA传输:
| 寄存器类别 | 典型寄存器 | 位宽 | 驱动操作 |
|---|---|---|---|
| 控制寄存器 | DMA_CR | 32-bit | 使能/复位控制器、设置优先级策略 |
| 状态寄存器 | DMA_SR | 32-bit | 读取通道忙状态、错误标志 |
| 中断状态寄存器 | DMA_ISR | 32-bit | 判断哪个通道传输完成或出错 |
| 中断清除寄存器 | DMA_ICR | 32-bit | 写1清除对应中断标志 |
| 通道源地址寄存器 | CHx_SAR | 32/64-bit | 设置源起始地址 |
| 通道目的地址寄存器 | CHx_DAR | 32/64-bit | 设置目的起始地址 |
| 通道传输计数寄存器 | CHx_TCNT | 16/32-bit | 设置待传输字节数 |
| 通道控制寄存器 | CHx_CR | 32-bit | 设置粒度、方向、增量模式、中断使能 |
| 通道请求映射寄存器 | CHx_REQMAP | 8/16-bit | 将外设请求线映射到本通道 |
一个典型的DMA通道初始化代码片段(伪代码):
/* 假设DMA控制器基地址为 DMA_BASE,通道0寄存器偏移已知 */
#define DMA_CH0_SAR (DMA_BASE + 0x100)
#define DMA_CH0_DAR (DMA_BASE + 0x104)
#define DMA_CH0_TCNT (DMA_BASE + 0x108)
#define DMA_CH0_CR (DMA_BASE + 0x10C)
/* 配置通道0:从外设数据寄存器到内存缓冲区 */
void dma_channel0_init(uint32_t src_periph, uint32_t dst_mem, uint32_t size)
{
/* 1. 确保通道处于空闲状态 */
while (readl(DMA_CH0_CR) & DMA_CR_CH_ENABLE)
writel(DMA_CH0_CR, readl(DMA_CH0_CR) & ~DMA_CR_CH_ENABLE);
/* 2. 设置源地址(外设寄存器地址,固定不变) */
writel(DMA_CH0_SAR, src_periph);
/* 3. 设置目的地址(内存缓冲区,每次传输后递增) */
writel(DMA_CH0_DAR, dst_mem);
/* 4. 设置传输字节数 */
writel(DMA_CH0_TCNT, size);
/* 5. 配置控制寄存器:
- 源地址固定(外设FIFO)
- 目的地址递增
- 字粒度(32-bit)
- 硬件请求触发
- 传输完成使能中断 */
uint32_t cr = DMA_CR_SRC_FIXED | DMA_CR_DST_INC |
DMA_CR_WIDTH_WORD | DMA_CR_REQ_HW |
DMA_CR_IE_COMPLETE;
writel(DMA_CH0_CR, cr);
/* 6. 使能通道 */
writel(DMA_CH0_CR, readl(DMA_CH0_CR) | DMA_CR_CH_ENABLE);
}
注意: 在使能通道之前,必须确保所有配置寄存器已写入正确的值。部分DMA控制器在使能后锁定通道寄存器,此时修改无效。另外,传输计数寄存器通常以字节为单位,但有些控制器以传输次数为单位(需除以粒度宽度),务必查阅数据手册。
3.6 本章小结
DMA控制器的硬件原理是驱动开发的基础。本章涵盖了:
- 内部结构:FSM、AGU、TCU、FIFO、通道寄存器组、仲裁器、总线接口的协同工作。
- 通道机制:每个通道独立的状态机生命周期,从空闲到完成的完整路径。
- 请求线:外设与DMA之间的硬件握手信号,包括硬件请求和软件触发两种模式。
- 传输粒度