2. DMA控制器硬件架构:DMA引擎、通道、请求线与握手协议
DMA(Direct Memory Access)控制器是嵌入式系统中负责在内存与外设之间、或内存与内存之间直接搬运数据的硬件引擎。理解其内部架构是进行设备树配置、驱动开发和性能调优的基础。本章将深入剖析DMA控制器的四大核心组件:DMA引擎、通道(Channel)、请求线(Request Line)以及握手协议(Handshake)。
学习提示: 在阅读本节时,请将DMA控制器想象成一个“数据搬运工”团队。引擎是团队的管理者,通道是具体的搬运工,请求线是外设发出的“呼叫铃”,而握手协议则是搬运工与呼叫者之间约定的“交接暗号”。
2.1 DMA引擎:核心调度与数据通路
DMA引擎是整个控制器的核心大脑,负责解析传输描述符、仲裁通道优先级、生成地址并控制数据总线。它通常包含以下关键模块:
- 控制逻辑单元: 解析来自CPU或链表的传输指令(源地址、目的地址、传输宽度、突发长度等)。
- 地址生成器: 支持递增、递减或固定地址模式,用于不同场景(如内存到外设的环形缓冲区)。
- 传输计数器: 跟踪剩余待传输的数据量,当计数器归零时触发传输完成中断。
- 仲裁器: 当多个通道同时请求时,根据优先级策略(固定优先级或轮询)决定哪个通道获得总线控制权。
- 总线主控接口: 连接系统总线(如AXI、AHB),作为总线主设备发起读写事务。
2.2 DMA通道:独立的传输上下文
DMA通道是执行一次完整数据传输的硬件上下文。每个通道都独立拥有自己的寄存器组,用于保存传输参数。多通道设计允许DMA控制器同时处理多个外设或内存区域的传输请求。
| 通道属性 | 说明 | 典型配置值 |
|---|---|---|
| 源地址寄存器 | 数据读取的起始地址 | 外设数据寄存器地址 / 内存缓冲区地址 |
| 目的地址寄存器 | 数据写入的起始地址 | 内存缓冲区地址 / 外设数据寄存器地址 |
| 传输宽度 | 单次总线传输的数据位宽 | 8-bit / 16-bit / 32-bit / 64-bit |
| 突发长度 | 一次总线仲裁后连续传输的次数 | 1 / 4 / 8 / 16 (取决于总线协议) |
| 传输方向 | 内存到外设、外设到内存、内存到内存 | M2P / P2M / M2M |
| 循环模式 | 传输完成后自动重载,用于连续数据流 | 启用 / 禁用 |
性能调优要点: 通道的突发长度和传输宽度直接影响总线利用率。对于高速外设(如MMC、以太网),建议使用最大支持的突发长度和32-bit或64-bit传输宽度,以减少总线仲裁开销。但需注意,过大的突发长度可能阻塞其他总线主设备(如CPU)的访问。
2.3 请求线:外设与DMA的通信桥梁
请求线是外设向DMA控制器发出数据传输请求的硬件信号线。每个DMA通道可以连接一条或多条请求线,外设通过拉高或拉低请求线来通知DMA控制器“我有数据要发送”或“我需要数据”。
- 硬件请求(Hardware Request): 由外设硬件直接驱动,例如UART接收FIFO非空时触发RX DMA请求。
- 软件请求(Software Request): 由CPU写寄存器触发,用于内存到内存的拷贝或测试模式。
- 请求映射: 在设备树中,通过
dma-requests属性或dmas中的请求ID来指定外设使用的请求线编号。
2.4 握手协议:同步与流控机制
握手协议定义了DMA通道与外设之间如何通过请求线和应答信号进行同步,以确保数据传输的可靠性和效率。常见的握手协议包括:
| 协议类型 | 信号线 | 工作方式 | 适用场景 |
|---|---|---|---|
| 单次请求(Single Request) | DREQ (DMA Request) | 外设每次准备好一个数据单元时,拉高DREQ一个周期;DMA传输一个单元后等待下一次请求。 | 低速外设(UART、I2C) |
| 突发请求(Burst Request) | DREQ + DACK (DMA Acknowledge) | 外设拉高DREQ并保持,DMA连续传输多个数据单元(突发长度),直到DREQ被撤销或传输完成。 | 高速外设(MMC、以太网) |
| 流控模式(Flow Control) | DREQ + DACK + 外设流控信号 | 外设通过额外的流控信号(如FIFO水位线)控制DMA的启停,防止缓冲区溢出或下溢。 | 实时数据流(音频、视频) |
常见陷阱: 在设备树中配置
dma-requests 时,必须确保请求线编号与硬件手册中的外设请求映射一致。错误的请求线编号会导致DMA无法响应外设请求,表现为数据传输超时或数据丢失。例如,某SoC的UART2使用DMA请求线5,但设备树中误配为请求线3,则DMA控制器将永远收不到UART2的传输请求。
2.5 握手时序与性能影响
握手协议的时序直接决定了DMA传输的延迟和带宽。以下是一个典型的单次请求握手时序图:
从时序图可以看出,每次数据传输都包含请求、应答和数据传输三个阶段。握手协议的延迟(从DREQ拉高到DACK拉低)直接影响DMA的吞吐量。在突发请求模式下,一次握手可以触发多次数据传输,从而显著降低握手开销。
性能调优实战: 在调试DMA传输性能时,可以通过示波器或逻辑分析仪抓取DREQ和DACK信号,测量握手延迟。如果发现DREQ保持高电平的时间远大于DMA响应时间,说明外设FIFO深度不足或DMA通道优先级过低。此时,可以尝试增加外设FIFO深度(如果硬件支持)或提高对应DMA通道的优先级。
2.6 设备树中的DMA控制器描述
在设备树中,DMA控制器及其通道、请求线通过以下方式描述:
// DMA控制器节点
dma: dma-controller@ff200000 {
compatible = "snps,axi-dma-1.01a";
reg = <0xff200000 0x1000>;
interrupts = <0 42 4>;
#dma-cells = <1>; // 每个DMA通道需要一个cell(通道ID)
dma-channels = <8>; // 硬件支持8个通道
dma-requests = <16>; // 硬件支持16条请求线
clocks = <&clkc 3>;
clock-names = "axi_dmac_clk";
};
// 外设节点使用DMA
uart0: serial@ff000000 {
compatible = "snps,dw-apb-uart";
reg = <0xff000000 0x1000>;
interrupts = <0 33 4>;
clocks = <&clkc 2>;
dmas = <&dma 0>, <&dma 1>; // 使用通道0(TX)和通道1(RX)
dma-names = "tx", "rx";
};
关键属性说明:
#dma-cells:定义DMA控制器需要多少个cell来描述一个DMA通道。通常为1(通道ID)或2(通道ID + 请求线ID)。dma-channels:声明硬件支持的通道数量,驱动据此分配资源。dma-requests:声明硬件支持的请求线数量,用于请求线映射验证。dmas:外设节点中引用DMA控制器,指定使用的通道和请求线。
设备树调试要点: 如果DMA传输不工作,首先检查
dma-channels 和 dma-requests 是否与硬件手册一致。其次,确认外设的 dmas 属性中引用的DMA控制器节点路径正确,且通道ID在有效范围内。使用 of_dma_request_slave_channel() 函数时,驱动会解析这些属性并申请对应的硬件通道。