2. DMA控制器硬件架构:DMA引擎、通道、请求线与握手协议

DMA(Direct Memory Access)控制器是嵌入式系统中负责在内存与外设之间、或内存与内存之间直接搬运数据的硬件引擎。理解其内部架构是进行设备树配置、驱动开发和性能调优的基础。本章将深入剖析DMA控制器的四大核心组件:DMA引擎、通道(Channel)、请求线(Request Line)以及握手协议(Handshake)。

学习提示: 在阅读本节时,请将DMA控制器想象成一个“数据搬运工”团队。引擎是团队的管理者,通道是具体的搬运工,请求线是外设发出的“呼叫铃”,而握手协议则是搬运工与呼叫者之间约定的“交接暗号”。

2.1 DMA引擎:核心调度与数据通路

DMA引擎是整个控制器的核心大脑,负责解析传输描述符、仲裁通道优先级、生成地址并控制数据总线。它通常包含以下关键模块:

  • 控制逻辑单元: 解析来自CPU或链表的传输指令(源地址、目的地址、传输宽度、突发长度等)。
  • 地址生成器: 支持递增、递减或固定地址模式,用于不同场景(如内存到外设的环形缓冲区)。
  • 传输计数器: 跟踪剩余待传输的数据量,当计数器归零时触发传输完成中断。
  • 仲裁器: 当多个通道同时请求时,根据优先级策略(固定优先级或轮询)决定哪个通道获得总线控制权。
  • 总线主控接口: 连接系统总线(如AXI、AHB),作为总线主设备发起读写事务。
DMA引擎内部架构图 DMA 引擎核心 控制逻辑单元 地址生成器 传输计数器 仲裁器 总线主控接口 (AXI/AHB) 系统总线 CPU 配置/描述符

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来指定外设使用的请求线编号。
DMA请求线连接示意图 UART 外设 (数据就绪) SPI 外设 (缓冲区空) 请求线 0 请求线 1 DMA 控制器 通道 0 通道 1 请求线映射表 请求线映射规则 • 每个外设占用一条请求线 • 请求线通过映射表连接到通道 • 设备树中通过dmas属性指定 • 支持多外设共享通道(需仲裁)

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传输的延迟和带宽。以下是一个典型的单次请求握手时序图:

单次请求握手时序图 CLK DREQ 外设请求数据 DACK DMA应答并读取数据 DATA 数据 延迟 = 请求到应答的时钟周期数

从时序图可以看出,每次数据传输都包含请求、应答和数据传输三个阶段。握手协议的延迟(从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() 函数时,驱动会解析这些属性并申请对应的硬件通道。