3. DMA控制器硬件原理
DMA(Direct Memory Access)控制器是系统中负责在外设与内存、内存与内存之间直接传输数据的硬件引擎。理解其内部组成与寄存器模型,是进行驱动开发和性能调优的基础。本章将深入剖析DMA控制器的核心硬件结构。
3.1 DMA控制器内部组成
一个典型的DMA控制器由以下功能单元构成,它们协同工作以完成一次完整的传输事务。
核心组成单元:
- 控制逻辑单元(Control Logic): 解析CPU写入的控制寄存器,生成状态机,协调各模块工作。
- 地址生成单元(Address Generation Unit, AGU): 管理源地址寄存器和目的地址寄存器,支持递增、递减或固定模式。
- 传输计数单元(Transfer Counter): 维护传输计数寄存器,每次传输后递减,计数归零时触发中断或链式传输。
- 数据缓冲单元(Data Buffer / FIFO): 暂存数据以匹配不同总线宽度,支持突发传输(Burst Transfer)。
- 总线接口单元(Bus Interface Unit, BIU): 负责与系统总线(AHB/AXI/OCP)握手,发起读/写事务。
- 通道仲裁器(Channel Arbiter): 多通道DMA中,根据优先级策略选择下一个服务的通道。
3.2 源/目的地址寄存器
地址寄存器决定了DMA传输的数据从何处读取、写入何处。根据硬件设计,地址寄存器支持多种寻址模式。
| 寄存器 | 功能 | 典型位宽 | 寻址模式 |
|---|---|---|---|
| 源地址寄存器(SAR) | 存储数据源地址(内存或外设FIFO地址) | 32位 / 64位 | 递增、递减、固定 |
| 目的地址寄存器(DAR) | 存储数据目的地址(内存或外设FIFO地址) | 32位 / 64位 | 递增、递减、固定 |
实战要点: 外设到内存传输时,源地址通常固定(指向外设数据寄存器),目的地址递增(写入内存缓冲区)。内存到外设传输则相反。地址对齐(Alignment)对性能影响显著,建议源和目的地址均按传输宽度对齐。
3.3 传输计数寄存器
传输计数寄存器(Transfer Count Register, TCR)记录待传输的数据单元数量。每次传输完成后,硬件自动递减该寄存器值。
- 初始值设置: 由驱动程序在启动DMA前写入,表示本次传输的总数据量(以字节、半字或字为单位)。
- 递减机制: 每完成一次数据单元传输,TCR减1。当TCR减至0时,触发传输完成中断或启动链式描述符中的下一段传输。
- 链式传输支持: 高级DMA控制器支持多个描述符链表,每个描述符包含独立的TCR,实现连续大块数据的分段传输。
3.4 控制寄存器
控制寄存器(Control Register, CR)是DMA引擎的“大脑”,驱动通过写入该寄存器来配置传输模式、触发启动、查询状态。
| 位域 | 名称 | 功能描述 |
|---|---|---|
| [0] | ENABLE | 通道使能位,置1启动传输,传输完成后硬件自动清零 |
| [1] | INT_EN | 中断使能,置1允许传输完成或错误时触发中断 |
| [3:2] | TRANSFER_TYPE | 00: 内存到内存;01: 内存到外设;10: 外设到内存;11: 外设到外设 |
| [5:4] | BURST_SIZE | 突发传输大小:00: 1字节;01: 4字节;10: 8字节;11: 16字节 |
| [7:6] | ADDR_MODE | 地址模式:00: 递增;01: 递减;10: 固定;11: 保留 |
| [8] | CHAIN_EN | 链式描述符使能,置1启用链表传输模式 |
| [31:16] | STATUS | 状态位(只读):传输完成、总线错误、描述符错误等 |
常见陷阱: 在修改控制寄存器前,必须先确保ENABLE位为0(通道已停止)。在传输进行中写入控制寄存器可能导致状态机紊乱。正确的操作序列为:读状态 → 确认空闲 → 写配置 → 写地址/计数 → 置位ENABLE。
3.5 寄存器编程示例
以下是一个简化的DMA初始化代码片段,展示如何配置寄存器以完成一次外设到内存的传输。
/* 伪代码:DMA控制器寄存器编程示例 */
#define DMA_BASE_ADDR 0x1C000000
#define DMA_SAR (DMA_BASE_ADDR + 0x00) /* 源地址寄存器 */
#define DMA_DAR (DMA_BASE_ADDR + 0x04) /* 目的地址寄存器 */
#define DMA_TCR (DMA_BASE_ADDR + 0x08) /* 传输计数寄存器 */
#define DMA_CR (DMA_BASE_ADDR + 0x0C) /* 控制寄存器 */
void dma_start_periph_to_mem(uint32_t periph_addr, uint32_t mem_addr, uint32_t count)
{
/* 1. 确保DMA通道已停止 */
while (readl(DMA_CR) & 0x1);
/* 2. 配置源地址(外设地址,固定模式) */
writel(periph_addr, DMA_SAR);
/* 3. 配置目的地址(内存地址,递增模式) */
writel(mem_addr, DMA_DAR);
/* 4. 设置传输计数(以字为单位) */
writel(count, DMA_TCR);
/* 5. 配置控制寄存器:
* - 传输类型: 外设到内存 (0x04)
* - 突发大小: 4字节 (0x10)
* - 地址模式: 源固定 + 目的递增 (0x00)
* - 中断使能: 开启 (0x02)
* - 通道使能: 开启 (0x01)
*/
uint32_t cr = (0x04 << 2) | (0x01 << 4) | (0x00 << 6) | (0x01 << 1) | 0x01;
writel(cr, DMA_CR);
}
性能提示: 突发传输大小(BURST_SIZE)的选择直接影响总线利用率。对于高速外设(如以太网MAC、USB控制器),建议使用8字节或16字节突发以降低总线仲裁开销。但突发过大可能阻塞其他低延迟外设的访问,需根据系统负载权衡。
3.6 寄存器配置与性能关系
寄存器的配置不仅决定传输的正确性,更直接影响DMA的吞吐量和延迟。下表总结了关键配置项对性能的影响。
| 配置项 | 推荐值 | 性能影响 |
|---|---|---|
| 突发大小 | 8 ~ 16字节 | 增大突发可减少总线事务开销,提升吞吐量 |
| 地址对齐 | 按传输宽度对齐 | 未对齐地址会导致硬件拆分事务,性能下降30%~50% |
| 中断使能 | 批量传输时关闭 | 频繁中断增加CPU负载,建议使用轮询或链式传输 |
| 传输计数 | 尽量大(不超过硬件限制) | 单次传输数据量越大,DMA启动/停止开销占比越低 |