3. DMA 传输类型:四种传输模式的深度解析

DMA 传输的核心价值在于绕过 CPU 直接搬运数据,但不同的硬件场景对搬运的源端、目的端以及数据流方向有着截然不同的要求。根据源地址与目的地址的物理属性,DMA 传输可划分为四种基本模式:内存到内存 (M2M)、内存到外设 (M2P)、外设到内存 (P2M) 以及 外设到外设 (P2P)。理解这四种模式的硬件拓扑、触发机制与驱动适配要点,是构建高效 DMA 引擎的基石。

3.1 四种传输模式的核心差异

下表从数据流向、典型场景、硬件触发源、描述符配置要点四个维度,对四种模式进行横向对比:

传输模式 数据流向 典型场景 硬件触发源 描述符关键配置
内存到内存 (M2M) DDR → DDR 帧缓冲拷贝、数据加密预处理、内存镜像 软件触发 (SW Trigger) 源/目的地址均为内存地址;无需硬件握手信号
内存到外设 (M2P) DDR → 外设 FIFO LCD 显示刷新、音频播放、网络数据发送 外设请求 (DMA Req) 目的地址为外设寄存器/FIFO 地址;需配置外设请求信号极性
外设到内存 (P2M) 外设 FIFO → DDR 摄像头数据采集、ADC 采样、网络数据接收 外设请求 (DMA Req) 源地址为外设寄存器/FIFO 地址;需配置突发长度 (Burst Size)
外设到外设 (P2P) 外设 FIFO → 外设 FIFO SPI 到 UART 桥接、数字信号处理流水线 源外设请求 源/目的均为外设地址;需同步两端外设的时钟域与数据宽度
驱动开发提示: M2M 模式通常用于系统内部的数据整理,不涉及外设时序,调试相对简单。而 M2P/P2M 模式必须处理外设的 流控信号 (DMA Request / Acknowledge),驱动中需要正确配置 DMA 控制器的握手协议(如单次请求传输一次 vs. 突发传输)。

3.2 内存到内存 (M2M) 传输详解

M2M 是最纯粹的 DMA 传输形式,其核心特征是源端和目的端都在系统内存 (DDR/SRAM) 中,不涉及任何外设接口。CPU 只需写入一个描述符,DMA 控制器便自动完成数据搬迁。

3.2.1 硬件拓扑与触发机制

M2M 传输的 DMA 控制器通常由软件写寄存器直接启动,无需等待外部硬件信号。其内部数据流路径为:
内存源地址 → DMA 读端口 → 内部 FIFO → DMA 写端口 → 内存目的地址

关键特征:
  • 无流控:不依赖外设请求信号,传输速率完全由 DMA 控制器主频和总线带宽决定。
  • 地址递增:源地址和目的地址通常都按传输宽度自动递增(支持固定地址模式用于特殊场景)。
  • 典型应用:Linux 内核中的 dmaengine_prep_dma_memcpy() 接口即用于构造 M2M 传输描述符。

3.2.2 M2M 传输流程 SVG 图

内存到内存 (M2M) DMA 传输流程 M2M DMA 传输流程 源内存 (DDR) 数据块 A 地址: 0x8000_0000 目的内存 (DDR) 数据块 A' 地址: 0x9000_0000 DMA 控制器 内部 FIFO 读总线 写总线 软件写启动寄存器 传输完成 → 触发中断

3.3 内存到外设 (M2P) 传输详解

M2P 模式是数据输出类外设的核心传输方式。DMA 控制器从内存中读取准备好的数据,写入外设的发送 FIFO 或数据寄存器。典型代表:LCD 控制器从帧缓冲取数据、I2S 从内存取音频数据。

3.3.1 流控与握手协议

M2P 传输必须响应外设的 DMA 请求信号 (DMA_REQ)。当外设发送 FIFO 为空或低于阈值时,外设拉高 DMA_REQ,DMA 控制器仲裁后执行一次读内存→写外设的操作。

驱动陷阱: 如果外设的 DMA_REQ 信号是电平触发,DMA 控制器必须配置为“请求保持”模式,否则可能丢失请求。如果是边沿触发,则需注意请求脉冲的最小宽度,确保 DMA 控制器在时钟域同步时不会漏采。

3.3.2 M2P 传输硬件交互 SVG 图

内存到外设 (M2P) DMA 传输硬件交互 M2P DMA 传输:内存 → 外设 系统内存 发送数据缓冲区 DMA 控制器 读端口 → FIFO → 写端口 握手状态机 外设 (TX FIFO) 发送移位寄存器 读数据 写数据 DMA_REQ (外设请求) DMA_ACK (控制器应答)

3.4 外设到内存 (P2M) 传输详解

P2M 是数据采集类外设的标准传输模式。外设产生数据后,通过 DMA 请求将数据搬运到内存缓冲区。典型场景:摄像头传感器输出图像数据到 DDR、ADC 连续采样结果存储。

3.4.1 关键参数:突发长度与 FIFO 阈值

P2M 传输的效率高度依赖于突发长度 (Burst Size) 和外设 FIFO 阈值的匹配。如果突发长度设置过大,外设 FIFO 可能溢出;设置过小,总线利用率下降。

驱动配置公式:
Burst_Size ≤ FIFO_Depth / 2 (确保 DMA 响应延迟不会导致 FIFO 溢出)
FIFO_Threshold = FIFO_Depth - Burst_Size (当 FIFO 中数据量达到阈值时触发 DMA 请求)

3.4.2 P2M 传输描述符链配置示例

/* P2M 描述符配置:外设到内存,循环模式用于连续采集 */
struct dma_async_tx_descriptor *desc;

desc = dmaengine_prep_slave_sg(
    chan,                    /* DMA 通道 */
    sgl,                     /* 内存 scatter-gather 列表 */
    sg_len,                  /* SG 条目数 */
    DMA_DEV_TO_MEM,          /* 方向:外设到内存 */
    DMA_PREP_INTERRUPT | DMA_CTRL_ACK);  /* 使能完成中断 */

/* 配置外设参数:源地址、数据宽度、突发长度 */
struct dma_slave_config config = {
    .src_addr = (dma_addr_t)SPI_RX_FIFO,  /* 外设接收 FIFO 地址 */
    .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,  /* 32位宽度 */
    .src_maxburst = 16,      /* 每次请求传输 16 个字 */
    .direction = DMA_DEV_TO_MEM,
};
dmaengine_slave_config(chan, &config);

3.5 外设到外设 (P2P) 传输详解

P2P 是最复杂的 DMA 传输模式,数据直接从源外设 FIFO 搬运到目的外设 FIFO,完全不经过内存。这种模式用于构建低延迟的硬件数据通路,例如:SPI 接收的数据直接送入 I2S 发送,实现协议转换。

3.5.1 P2P 传输的挑战

  • 时钟域同步: 源外设和目的外设可能工作在不同时钟域,DMA 控制器需要支持异步时钟处理。
  • 数据宽度匹配: 如果源外设是 8 位宽度,目的外设是 16 位宽度,DMA 控制器必须支持数据打包/解包。
  • 流控死锁: 如果源外设没有数据而目的外设持续请求,可能导致 DMA 通道挂起。驱动需要实现超时恢复机制。

3.5.2 P2P 传输拓扑 SVG 图

外设到外设 (P2P) DMA 传输拓扑 P2P DMA 传输:外设 → 外设(无内存参与) 源外设 SPI 从机 RX FIFO 8位数据宽度 DMA 控制器 数据宽度转换 时钟域同步 流控仲裁 目的外设 I2S 发送 FIFO 16位数据宽度 源数据 转换后数据 源 DMA_REQ 目的 DMA_REQ

3.6 四种模式的驱动适配要点总结

在 Linux DMA Engine 框架下,四种模式通过 dma_transfer_direction 枚举区分。驱动开发者在实现 device_prep_slave_sg 或 device_prep_dma_memcpy 回调时,必须根据传输模式做差异化处理:

  • M2M: 使用 DMA_MEM_TO_MEM 方向,无需配置 dma_slave_config,直接调用 dmaengine_prep_dma_memcpy()。
  • M2P: 使用 DMA_MEM_TO_DEV 方向,必须配置 dst_addr 和 dst_maxburst。
  • P2M: 使用 DMA_DEV_TO_MEM 方向,必须配置 src_addr 和 src_maxburst。
  • P2P: 使用 DMA_DEV_TO_DEV 方向,需要同时配置源端和目的端的 slave_config,且 DMA 控制器硬件必须支持双请求接口。
性能优化方向: 对于 M2P/P2M 模式,描述符预取 (Descriptor Prefetch) 和 乒乓缓冲 (Ping-Pong Buffer) 是消除传输间隙的关键技术。对于 P2P 模式,直接内存访问绕过 (DMA Bypass) 和 硬件环路 (Hardware Loop) 可以进一步降低延迟。