3. DMA 传输类型:内存到内存 (M2M)、内存到外设 (M2P)、外设到内存 (P2M)、外设到外设 (P2P)
DMA 控制器作为系统总线上的主设备,其核心价值在于无需 CPU 干预即可完成数据搬运。根据数据源(Source)与目的地(Destination)的物理位置,DMA 传输被划分为四种基本类型。理解这四种类型的硬件路径、触发方式及适用场景,是进行设备树配置和驱动热路径优化的前提。
核心概念: 传输类型的本质差异在于 源地址 与 目的地址 是否属于同一内存域,以及是否需要硬件事件(如 FIFO 半满、定时器溢出)来触发传输。
3.1 四种传输类型对比总览
下表从硬件路径、触发源、典型应用及设备树关键属性四个维度,对四种传输类型进行横向对比:
| 传输类型 | 源地址域 | 目的地址域 | 触发源 | 典型应用 | 设备树关键属性 |
|---|---|---|---|---|---|
| M2M | DDR / SRAM | DDR / SRAM | 软件请求(立即触发) | 内存拷贝、帧缓冲搬运、CRC 校验 | dma-no-event 或 dma-channel 无外设句柄 |
| M2P | DDR / SRAM | 外设 FIFO / 寄存器 | 外设请求(DMA_REQ 信号) | LCD 显示、音频播放、SPI 发送 | dma-requests 指定外设请求线 |
| P2M | 外设 FIFO / 寄存器 | DDR / SRAM | 外设请求(DMA_REQ 信号) | 摄像头采集、音频录音、UART 接收 | dma-requests 指定外设请求线 |
| P2P | 外设 A FIFO | 外设 B FIFO | 外设 A 或 B 请求 | 数字信号处理、高速数据重定向 | dma-crossbar 或专用通道映射 |
实战提示: 在嵌入式 Linux 中,M2M 传输通常使用
dma_alloc_coherent 分配缓冲区,而 M2P/P2M 必须使用 dma_map_single 或 dma_map_sg 进行缓存一致性维护。P2P 传输在主流 SoC(如 i.MX8、TI AM65x)中支持有限,通常需要硬件交叉开关(Crossbar)支持。
3.2 内存到内存 (M2M) —— 纯软件触发的数据搬运
M2M 是最简单的 DMA 传输模式,源和目的都在系统内存(DDR 或片内 SRAM)中。传输由 CPU 通过写 DMA 控制器的软件触发寄存器启动,无需外设硬件事件参与。
3.2.1 硬件路径与触发流程
- CPU 配置: 设置源地址、目的地址、传输宽度(8/16/32 位)、突发长度(Burst Length)。
- 软件触发: 写 DMA 通道的使能位或触发寄存器。
- 总线仲裁: DMA 控制器向系统总线发出读请求,从源地址读取数据。
- 数据写入: DMA 控制器将读取的数据直接写入目的地址。
- 传输完成: 当传输计数归零,DMA 控制器产生中断或轮询标志位。
性能陷阱: M2M 传输会占用系统总线带宽。如果 CPU 同时访问 DDR,可能造成总线竞争。在实时性要求高的场景(如音频处理),建议使用专用 SRAM 或设置 DMA 通道优先级。
3.2.2 设备树配置示例
// 设备树中描述一个支持 M2M 的 DMA 通道
dma_controller: dma-controller@1c00000 {
compatible = "vendor,axi-dma-1.0";
reg = <0x1c00000 0x1000>;
#dma-cells = <2>; // 第一个 cell: 通道号, 第二个 cell: 传输类型
dma-channels = <8>;
};
// 使用 M2M 传输的虚拟设备
m2m_device {
compatible = "vendor,m2m-accel";
dmas = <&dma_controller 0 0>; // 通道 0, 类型 0 (M2M)
dma-names = "m2m";
};
3.3 内存到外设 (M2P) —— 从内存向外设“推”数据
M2P 传输将内存中的数据搬运到外设的 FIFO 或寄存器。传输由外设发出的 DMA 请求信号(DMA_REQ)触发,典型场景是 LCD 控制器从帧缓冲取数据、音频 I2S 接口从内存取 PCM 数据。
3.3.1 关键硬件特性
- 请求信号: 外设的 FIFO 空/半空状态会拉高 DMA_REQ 线,DMA 控制器检测到后启动一次突发传输。
- 流控制: 外设作为流控方(Flow Controller),决定何时需要数据。
- 地址递增: 源地址(内存)通常递增,目的地址(外设 FIFO)固定不变。
3.3.2 设备树配置示例
// SPI 控制器作为 M2P 传输的外设端
spi_controller: spi@1d00000 {
compatible = "vendor,spi-v2";
reg = <0x1d00000 0x100>;
dmas = <&dma_controller 1 1>; // 通道 1, 类型 1 (M2P)
dma-names = "tx";
dma-requests = <16>; // 外设请求线编号
};
3.4 外设到内存 (P2M) —— 从外设向内存“拉”数据
P2M 是 M2P 的逆过程,数据从外设 FIFO 流向内存。外设产生数据(如 ADC 采样、摄像头像素)时,通过 DMA_REQ 信号通知 DMA 控制器读取。
3.4.1 与 M2P 的对称性
| 特性 | M2P | P2M |
|---|---|---|
| 源地址 | 内存(递增) | 外设 FIFO(固定) |
| 目的地址 | 外设 FIFO(固定) | 内存(递增) |
| 触发条件 | 外设 FIFO 空 | 外设 FIFO 满 |
| 缓存一致性 | 写外设前需 clean 缓存 | 读外设后需 invalidate 缓存 |
3.5 外设到外设 (P2P) —— 零内存中转的数据直通
P2P 传输在两个外设之间直接搬运数据,不经过系统内存。这种模式延迟最低,但硬件支持最复杂,通常需要 DMA 控制器具备多通道映射能力和交叉开关(Crossbar)。
3.5.1 典型应用场景
- 数字麦克风到 I2S 输出: PDM 数据直接转换为 PCM 输出。
- 硬件加密引擎到网络控制器: 加密后的数据直接送入 TX FIFO。
- 高速数据采集到 DSP 加速器: 避免内存带宽瓶颈。
驱动开发注意: P2P 传输在 Linux DMA Engine 框架中支持有限。主流实现依赖
dma_request_chan 获取两个通道句柄,然后通过 dmaengine_prep_dma_cyclic 或 dmaengine_prep_slave_sg 配置。设备树中通常需要 dma-crossbar 节点描述外设之间的物理连接。
3.6 四种传输类型的硬件架构对比图
下图展示了四种 DMA 传输类型在 SoC 内部的数据流路径:
3.7 驱动热路径中的传输类型选择
在性能调优视角下,传输类型的选择直接影响延迟和吞吐量:
- M2M 适合批量处理: 利用 DMA 的突发传输特性,一次搬运大量数据(如 4KB 以上),减少 CPU 拷贝开销。
- M2P/P2M 适合流式处理: 使用循环缓冲区(Cyclic Buffer)模式,配合双缓冲(Double Buffering)消除乒乓延迟。
- P2P 适合低延迟链路: 延迟可低至几个总线时钟周期,但需要硬件支持且驱动复杂度高。
热路径优化原则: 在中断处理函数或高优先级任务中,优先使用 P2M 或 M2P 的
dma_async_issue_pending 异步提交,避免同步等待。对于 M2M,使用 dmaengine_prep_dma_memcpy 并设置 DMA_PREP_INTERRUPT 标志,在回调中处理后续数据。