4. DMA传输粒度与突发传输:单次传输、突发传输(Burst)、传输宽度(8/16/32/64位)配置
DMA控制器的核心任务是在无需CPU干预的情况下,高效地在内存与外围设备之间搬运数据。理解传输粒度(单次 vs 突发)与传输宽度(8/16/32/64位)是配置DMA引擎、优化总线带宽利用率的关键前提。本章将深入剖析这两种配置的硬件行为、驱动编程接口以及实际选型策略。
4.1 传输宽度(Data Width)
传输宽度定义了DMA控制器在一个传输节拍内从源地址读取或向目标地址写入的数据位数。常见的硬件支持宽度包括8位(字节)、16位(半字)、32位(字)和64位(双字)。
4.1.1 宽度配置与地址对齐规则
驱动开发者在配置描述符时,必须确保源地址、目标地址与传输宽度满足自然对齐。下表总结了常见宽度与对齐约束:
| 传输宽度 | 数据大小 | 地址对齐要求 | 典型应用场景 |
|---|---|---|---|
| 8位 | 1字节 | 1字节对齐(无约束) | 串口FIFO、单字节外设寄存器 |
| 16位 | 2字节 | 2字节对齐(地址最低位为0) | 音频Codec、16位并行总线 |
| 32位 | 4字节 | 4字节对齐(地址低2位为0) | 内存拷贝、LCD控制器、网络数据包 |
| 64位 | 8字节 | 8字节对齐(地址低3位为0) | 高速存储控制器、GPU数据流、PCIe |
4.1.2 宽度配置的驱动代码示例
在Linux DMA引擎驱动中,传输宽度通过dma_slave_config结构体的src_addr_width和dst_addr_width字段指定:
struct dma_slave_config config = {
.direction = DMA_MEM_TO_DEV,
.src_addr = (dma_addr_t)mem_buf_phys,
.dst_addr = (dma_addr_t)uart_tx_fifo_phys,
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, // 32位
.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, // 8位
.src_maxburst = 1,
.dst_maxburst = 1,
};
dmaengine_slave_config(dma_chan, &config);
4.2 单次传输与突发传输(Burst)
单次传输和突发传输描述了DMA控制器在获得总线控制权后连续传输的数据节拍数。这是影响总线利用率的关键参数。
4.2.1 单次传输(Single Transfer)
在单次传输模式下,DMA控制器每获得一次总线授权,只传输一个数据宽度单位(如一个32位字),然后立即释放总线。这种模式适用于:
- 低延迟要求:CPU或其他总线主控可以快速抢占总线。
- 外设数据速率不稳定:如UART接收,数据到达时间不可预测。
- 避免长时间占用总线:在共享总线系统中防止DMA“饿死”其他主控。
4.2.2 突发传输(Burst Transfer)
突发传输允许DMA控制器在一次总线授权后,连续传输多个数据宽度单位(称为突发长度或Burst Length),直到完成指定数量的节拍后才释放总线。突发长度通常可配置为4、8、16、32、64或128次节拍。
4.2.3 突发传输的硬件行为示意图
下图展示了单次传输与突发传输在总线占用上的核心差异:
4.3 突发长度(Burst Length)配置策略
突发长度并非越大越好。驱动开发者需要根据系统总线拓扑、外设FIFO深度和实时性要求综合权衡:
| 突发长度 | 总线利用率 | 总线延迟(对其他主控) | 适用场景 |
|---|---|---|---|
| 1(单次) | 低(约30-50%) | 最低 | UART、低速I2C、SPI |
| 4-8 | 中(约60-80%) | 较低 | 音频流、中等速率ADC |
| 16-32 | 高(约85-95%) | 中等 | LCD刷新、网络数据包搬运 |
| 64-128 | 极高(>95%) | 高(可能引发CPU饥饿) | 高速存储(eMMC、NVMe)、GPU纹理传输 |
4.4 传输粒度与描述符编程
在描述符接口中,传输粒度通过源突发长度(src_maxburst)和目标突发长度(dst_maxburst)分别控制。以下是一个典型的多描述符链配置示例:
struct dma_async_tx_descriptor *desc;
struct scatterlist sg[2];
// 配置第一个SG条目:32位宽度,突发16次
sg_dma_len(&sg[0]) = 64; // 64字节
sg_dma_address(&sg[0]) = src_phys;
// 配置第二个SG条目:16位宽度,突发8次
sg_dma_len(&sg[1]) = 32; // 32字节
sg_dma_address(&sg[1]) = src_phys + 64;
// 准备描述符链
desc = dmaengine_prep_slave_sg(dma_chan, sg, 2, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
dev_err(dev, "Failed to prepare slave sg descriptor\n");
return -ENOMEM;
}
// 提交并启动传输
dmaengine_submit(desc);
dma_async_issue_pending(dma_chan);
- 对于内存到内存(memcpy)传输,优先使用最大突发长度和最大传输宽度(64位),以获得最佳吞吐量。
- 对于外设到内存传输,突发长度不应超过外设FIFO深度的一半,防止FIFO溢出。
- 在多核系统中,过大的突发长度可能导致缓存一致性开销增加,需结合Cache Line大小(通常64字节)调整。
4.5 传输粒度与中断生成的关系
中断频率与传输粒度直接相关。单次传输模式下,每个节拍都可能触发中断(若使能),导致CPU频繁响应。突发传输模式下,中断通常在整个突发完成或描述符完成时触发,有效降低中断负载。
下图展示了不同传输粒度下的中断频率对比:
4.6 实际配置决策流程
在驱动开发中,建议按照以下决策树选择传输粒度和宽度:
- 步骤1:确定外设数据宽度——读取外设数据手册,获取FIFO/寄存器的物理宽度。
- 步骤2:检查地址对齐——确保源和目标缓冲区地址满足宽度对齐要求;若不满足,降级为8位宽度或使用对齐缓冲区。
- 步骤3:评估FIFO深度——突发长度 ≤ FIFO深度 / 2(防止溢出/下溢)。
- 步骤4:考虑系统实时性——若系统有严格中断延迟要求(如音频播放),优先使用较小的突发长度(4-8)。
- 步骤5:性能测试调优——在实际硬件上测试不同突发长度组合,使用
perf或DMA控制器提供的性能计数器选择最优配置。
通过合理配置传输宽度与突发长度,驱动开发者可以在总线带宽、CPU中断负载和外设实时性之间取得最佳平衡,充分发挥DMA控制器的硬件加速能力。