3. DMA控制器硬件原理
DMA(Direct Memory Access)控制器是系统中负责在内存与外设之间、或内存与内存之间直接搬运数据的硬件引擎。理解其内部组成与传输流程,是驱动开发者正确配置和使用DMA引擎的基础。
3.1 DMA控制器内部组成
一个典型的DMA控制器由以下核心模块构成:
核心模块一览:
- 通道(Channel):独立的DMA传输上下文,每个通道可独立配置源地址、目的地址、传输宽度和传输计数。
- 请求线(Request Line):外设向DMA控制器发起传输请求的硬件信号线,通常与硬件握手信号(DREQ/DACK)关联。
- 地址发生器(Address Generator):根据配置的地址模式(递增、递减、固定)自动生成源和目的地址,无需CPU干预。
- 控制/状态寄存器组:存放通道配置、传输状态、中断标志等控制信息。
- 仲裁器(Arbiter):当多个通道同时请求时,按优先级策略分配总线使用权。
下图展示了DMA控制器的内部结构及各模块之间的连接关系:
3.2 通道(Channel)
通道是DMA控制器的基本执行单元。每个通道包含一组寄存器,用于描述一次DMA传输的全部参数:
| 寄存器 | 宽度 | 描述 |
|---|---|---|
| 源地址寄存器 (SAR) | 32/64位 | 数据源起始地址,支持递增、递减或固定模式 |
| 目的地址寄存器 (DAR) | 32/64位 | 数据目的起始地址,支持递增、递减或固定模式 |
| 传输计数寄存器 (TCR) | 16/32位 | 待传输的数据单元数量,每完成一次传输递减 |
| 控制寄存器 (CR) | 32位 | 配置传输方向、宽度、突发大小、中断使能等 |
| 状态寄存器 (SR) | 32位 | 指示通道是否忙、传输完成、错误等状态 |
通道数量与系统设计: 嵌入式SoC中DMA通道数通常为4~32个。驱动开发时需注意通道的独占性——同一通道不能被两个外设同时使用,否则会导致地址和计数冲突。
3.3 请求线(Request Line)
请求线是外设与DMA控制器之间的硬件握手信号线。典型的请求线包括:
- DREQ (DMA Request):外设向DMA控制器发出的传输请求信号,表示外设已准备好发送或接收数据。
- DACK (DMA Acknowledge):DMA控制器向外设发出的应答信号,表示当前总线周期已被分配给该外设。
- EOP (End of Process):可选信号,用于指示传输结束或外部终止。
请求线的触发方式分为电平触发和边沿触发两种:
| 触发方式 | 特点 | 适用场景 |
|---|---|---|
| 电平触发 | DREQ保持有效电平直到DACK响应,适合连续数据流 | 音频I2S、高速ADC |
| 边沿触发 | DREQ上升沿或下降沿触发一次传输,适合单次事件 | GPIO事件、按键中断 |
3.4 地址发生器(Address Generator)
地址发生器是DMA控制器的“计算核心”,负责在每次传输后自动更新源地址和目的地址。常见的地址更新模式包括:
- 递增模式(Increment):地址每次增加传输宽度(如4字节),用于连续内存块传输。
- 递减模式(Decrement):地址每次减少传输宽度,用于反向扫描或栈操作。
- 固定模式(Fixed):地址保持不变,用于访问固定寄存器或FIFO。
- 循环模式(Circular):地址到达边界后回绕到起始地址,用于环形缓冲区。
地址对齐要求: 大多数DMA控制器要求源地址和目的地址与传输宽度对齐。例如,32位传输时地址必须4字节对齐。未对齐的地址可能导致总线错误或性能下降,驱动中需通过
dma_set_addr_alignment()或手动对齐处理。
3.5 DMA传输流程
一次完整的DMA传输经历四个阶段:请求 → 响应 → 传输 → 完成。下图展示了各阶段的时序关系:
3.6 传输流程详细说明
3.6.1 请求阶段
外设(如UART、SPI、ADC)在准备好数据或需要数据时,通过硬件请求线DREQ向DMA控制器发出传输请求。请求信号可以是电平或边沿触发,具体由外设接口协议决定。
3.6.2 响应阶段
DMA控制器内部的仲裁器检测到DREQ有效后,根据通道优先级和当前总线状态决定是否响应。若总线空闲且该通道优先级最高,DMA控制器执行以下操作:
- 置位DACK信号,通知外设请求已被接受。
- 获取系统总线控制权(通过HOLD/HLDA握手或直接总线主控)。
- 锁定当前通道,防止其他通道干扰。
3.6.3 传输阶段
这是DMA的核心执行阶段。地址发生器根据SAR和DAR的配置模式生成当前总线地址,数据在源与目的之间直接搬移。每次传输完成后:
- 地址寄存器按配置模式更新(递增/递减/固定)。
- 传输计数寄存器(TCR)递减。
- 若配置为突发模式,连续传输多个数据单元后才释放总线。
突发传输与总线延迟: 突发传输(Burst Transfer)允许DMA连续占用总线传输多个数据单元,减少总线切换开销。但过长的突发会阻塞CPU和其他外设的总线访问。驱动中需根据系统实时性要求合理设置突发大小(通常为4、8、16个数据单元)。
3.6.4 完成阶段
当传输计数寄存器(TCR)递减到零时,DMA控制器认为本次传输完成。此时:
- 清除通道的忙标志位。
- 若控制寄存器中使能了传输完成中断,则向中断控制器发送中断请求。
- 释放总线控制权,返回空闲状态。
驱动中的中断服务程序(ISR)读取状态寄存器确认传输完成,然后调用回调函数处理数据或启动下一次传输。
3.7 传输模式对比
| 传输模式 | 描述 | 总线占用 | 典型应用 |
|---|---|---|---|
| 单次传输(Single) | 每次请求只传输一个数据单元 | 低 | 低速外设、GPIO |
| 突发传输(Burst) | 一次请求连续传输多个数据单元 | 高 | 高速ADC、音频流 |
| 链表传输(Scatter-Gather) | 通过描述符链表管理多个不连续内存块 | 中 | 网络包、存储设备 |
| 循环传输(Circular) | 地址回绕,连续循环传输 | 持续 | 环形缓冲区、实时采样 |
驱动开发提示: 在Linux内核中,DMA引擎驱动通过
dmaengine_prep_slave_single()、dmaengine_prep_dma_cyclic()等API配置传输模式。理解硬件控制器的通道和请求线映射关系,是正确调用这些API的前提。