4. DMA传输粒度与突发传输:单次传输、突发传输(Burst)、传输宽度(8/16/32/64位)配置

DMA控制器的核心任务是在无需CPU干预的情况下,高效地在内存与外围设备之间搬运数据。理解传输粒度(单次 vs 突发)与传输宽度(8/16/32/64位)是配置DMA引擎、优化总线带宽利用率的关键前提。本章将深入剖析这两种配置的硬件行为、驱动编程接口以及实际选型策略。

4.1 传输宽度(Data Width)

传输宽度定义了DMA控制器在一个传输节拍内从源地址读取或向目标地址写入的数据位数。常见的硬件支持宽度包括8位(字节)、16位(半字)、32位(字)和64位(双字)。

关键概念:传输宽度必须与源/目标设备的数据总线宽度以及地址对齐要求匹配。例如,访问一个32位寄存器时,若配置为8位宽度,则需4个节拍才能完成一次逻辑上的32位传输,且可能引发非对齐访问异常。

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);
常见错误:当源和目标宽度不一致时(如从32位内存搬运到8位UART FIFO),DMA控制器会自动进行数据打包/解包。但部分硬件不支持宽度自动调整,此时驱动必须手动将传输拆分为多个描述符,或使用循环DMA模式处理。

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次节拍。

性能优势:突发传输能显著降低总线仲裁开销,提高有效数据传输带宽。例如,传输64字节数据:单次传输需要64次总线授权+64次传输;而16次突发(每次4字节)仅需4次总线授权+64次传输,总线授权开销减少93.75%。

4.2.3 突发传输的硬件行为示意图

下图展示了单次传输与突发传输在总线占用上的核心差异:

单次传输 vs 突发传输 总线占用对比 单次传输(Burst=1) D1 A D2 A D3 A D4 D: 数据传输节拍 A: 总线仲裁/授权开销 突发传输(Burst=4) A D1 D2 D3 D4 A D5 D6 D7 D8 突发传输将多次总线仲裁合并为一次,大幅减少总线授权开销,提升有效带宽

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频繁响应。突发传输模式下,中断通常在整个突发完成或描述符完成时触发,有效降低中断负载。

下图展示了不同传输粒度下的中断频率对比:

传输粒度与中断频率关系 单次传输(Burst=1) 每传输一个数据节拍产生一次中断(8次中断) 突发传输(Burst=4) 每完成一个突发(4个节拍)产生一次中断(仅2次中断)

4.6 实际配置决策流程

在驱动开发中,建议按照以下决策树选择传输粒度和宽度:

  1. 步骤1:确定外设数据宽度——读取外设数据手册,获取FIFO/寄存器的物理宽度。
  2. 步骤2:检查地址对齐——确保源和目标缓冲区地址满足宽度对齐要求;若不满足,降级为8位宽度或使用对齐缓冲区。
  3. 步骤3:评估FIFO深度——突发长度 ≤ FIFO深度 / 2(防止溢出/下溢)。
  4. 步骤4:考虑系统实时性——若系统有严格中断延迟要求(如音频播放),优先使用较小的突发长度(4-8)。
  5. 步骤5:性能测试调优——在实际硬件上测试不同突发长度组合,使用perf或DMA控制器提供的性能计数器选择最优配置。
硬件兼容性警告:部分DMA控制器(如某些ARM PrimeCell DMA)要求源和目标突发长度必须相等,或突发长度必须是传输宽度的整数倍。务必查阅具体IP核的参考手册,避免配置无效导致传输挂起。

通过合理配置传输宽度与突发长度,驱动开发者可以在总线带宽、CPU中断负载和外设实时性之间取得最佳平衡,充分发挥DMA控制器的硬件加速能力。