DMA控制器架构:从寄存器到状态机
聊DMA控制器架构,我习惯先从它的“骨架”说起。说白了,一个DMA控制器就是一块专门处理数据传输的微引擎。它不需要CPU插手,自己就能把数据从A搬到B。
我在项目中遇到过不少工程师,总觉得DMA控制器不过是一堆寄存器加一个状态机。其实没那么简单。内部每个模块都有各自的分工,配合不好就会出各种奇怪的问题。
内部组成模块
先来看DMA控制器的“五脏六腑”。一个典型的DMA控制器,至少包含以下几个核心部件:
- 系统总线接口:负责与系统总线握手,发起读写请求。这是DMA与外界通信的唯一通道。
- DMA引擎:整个控制器的“大脑”,包含地址发生器、字节计数器和传输逻辑。它决定下一步该做什么。
- FIFO缓冲:临时存放数据。当源和目的速度不匹配时,FIFO能缓解压力。我见过一次因为FIFO深度不够,导致频繁欠载或溢出,最后传输效率还不如CPU直接搬。
- 控制/状态寄存器组:软件与硬件交互的窗口。后面会单独讲。
- 状态机:驱动整个传输流程的小型有限状态机。它会根据当前状态和外部信号,决定下一跳。
重点:DMA引擎里的地址发生器不是简单累加器。它支持递增、递减、固定模式,还能按二维/三维地址跳转。这是实现“分散-聚集”传输的基础。
寄存器组详解
寄存器是DMA控制器的“控制面板”。每个DMA通道至少有一组独立的寄存器。我通常把它们分为四类:
| 寄存器类型 | 典型名称 | 作用 |
|---|---|---|
| 源地址寄存器 | SAR | 存放数据源的起始地址。可配置为内存地址或外设地址。 |
| 目的地址寄存器 | DAR | 存放数据目的地的起始地址。同样支持多种地址空间。 |
| 字节计数器 | BCR / NDTR | 记录剩余待传输的字节数。每次传输后递减,到零触发完成信号。 |
| 控制/状态寄存器 | CR / SR | 控制位包括:传输方向、数据宽度、增量模式、中断使能等。状态位包括:忙、完成、错误等。 |
避坑指南:我曾经在一个项目中,因为没仔细看寄存器说明,把源地址和目的地址的增量模式配反了。结果数据全被写成同一个地址,找了好久才发现。建议新项目第一次使用DMA时,先验证一遍所有寄存器配置。
状态机:传输的“心脏”
状态机是DMA控制器工作的核心。大多数DMA控制器的状态机都类似,下面是我常用的简化模型:
- IDLE(空闲):等待启动信号。此时所有寄存器可以配置。
- PREP(准备):检查源和目的地址有效性,锁存参数。这个状态很短,但很关键。我在调试时发现,如果这个阶段地址校验失败,状态机会直接进入ERROR状态。
- XFER(传输):实际产生总线请求,传输数据。根据传输模式(单次、突发、行),这个状态会反复进入。
- DONE(完成):字节计数器归零,产生中断(如果使能)。然后自动回到IDLE。
注意:有些DMA控制器在传输过程中会因总线错误进入ABORT(终止)状态。该状态下寄存器内容可能被清零或锁定,需要软件重新初始化。建议在中断服务程序里增加超时检测,避免死锁。
为什么会需要这么复杂的状态机?想想看,CPU随时可能改变DMA的配置,外设也可能产生暂停信号。没有状态机保证事务原子性,传输逻辑就会乱掉。
工作原理:一条完整的数据流
我们来走一遍完整的传输过程。假设你要把外设FIFO里的数据搬进内存缓冲区:
- CPU配置DMA通道:SAR指向外设FIFO地址,DAR指向内存缓冲区,BCR设为1024字节,传输方向设为“外设→内存”,并使能完成中断。
- CPU写控制寄存器启动位。
- DMA控制器进入PREP状态:检查地址是否对齐(我的经验:很多系统要求源地址对齐数据宽度,否则浪费时间做拆分)。
- 进入XFER状态:DMA引擎向总线发出读请求,外设返回数据,存入内部FIFO。
- 如果FIFO有数据,DMA继续发写请求,将数据写入内存地址,然后递增目的地址(或保持,取决于配置)。
- 每完成一次读写,BCR递减。当BCR减到0,状态机进入DONE。
- DMA产生中断,CPU处理完成事件。
这个过程中,DMA引擎不需要CPU参与任何一次总线事务。你想想看,如果这个传输由CPU完成,每次搬一个字都需要取指、解码、访问内存、写回,开销至少是DMA的5倍以上。
内部结构示意图
下面这张图展示了DMA控制器的内部模块和状态机流转关系:
实际项目中的体会
讲几个我踩过的坑。
- FIFO深度不足:某次做高速ADC采集,DMA FIFO只有4个字,而总线延迟较大,导致频繁欠载。后来把DMA配置改为“突发传输长度 = FIFO深度的一半”,问题解决。
- 状态机未考虑总线错误:有一次外设掉电,DMA还在不停请求总线,导致系统挂死。后来在状态机里增加了超时复位机制,并在中断中检测ABORT状态。
- 寄存器访问时序:某些DMA控制器的控制寄存器在传输过程中写入无效。我习惯在空闲状态才修改配置,或者先停掉通道再更新。
一点建议:如果你在调试DMA传输时发现数据错位或丢失,优先检查地址对齐和FIFO深度匹配。这两个点出问题的概率最高。
一个设计良好的DMA控制器,能让你的系统在CPU零干预的情况下,以总线带宽的90%以上搬运数据。但所有前提是——你真正理解了它的内部架构。接下来,你会看到这些模块如何协同实现各种高级传输模式。