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 桥接、数字信号处理流水线 | 源外设请求 | 源/目的均为外设地址;需同步两端外设的时钟域与数据宽度 |
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 图
3.3 内存到外设 (M2P) 传输详解
M2P 模式是数据输出类外设的核心传输方式。DMA 控制器从内存中读取准备好的数据,写入外设的发送 FIFO 或数据寄存器。典型代表:LCD 控制器从帧缓冲取数据、I2S 从内存取音频数据。
3.3.1 流控与握手协议
M2P 传输必须响应外设的 DMA 请求信号 (DMA_REQ)。当外设发送 FIFO 为空或低于阈值时,外设拉高 DMA_REQ,DMA 控制器仲裁后执行一次读内存→写外设的操作。
3.3.2 M2P 传输硬件交互 SVG 图
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 图
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 控制器硬件必须支持双请求接口。