2. DMA 基础概念:DMA 控制器工作原理、传输类型与通道

在嵌入式系统(尤其是 QNX BSP 移植场景)中,DMA(Direct Memory Access,直接存储器访问)是提升数据吞吐量的核心技术。它允许外设或内存块之间直接交换数据,而无需 CPU 逐字节干预。本章节将深入剖析 DMA 控制器的工作原理、三大传输类型以及通道与请求线的映射关系,为后续 DMA 映射故障定位打下坚实基础。

2.1 DMA 控制器工作原理

DMA 控制器是一个独立的总线主控设备,能够接管系统总线,在源地址与目标地址之间搬运数据。其核心工作流程可概括为以下四个阶段:

  1. 请求阶段:外设(如 UART、SPI、ADC)或软件通过 DMA 请求线向 DMA 控制器发出传输请求。
  2. 仲裁阶段:DMA 控制器向 CPU 发出总线请求(HOLD/BREQ),CPU 在完成当前总线周期后释放总线控制权(HLDA/BGNT)。
  3. 传输阶段:DMA 控制器直接控制地址总线和数据总线,执行源到目标的搬运,每次传输后自动更新地址和计数器。
  4. 完成阶段:当传输计数器归零时,DMA 控制器向 CPU 发送中断信号,通知传输完成。
QNX BSP 关键点: 在 QNX 系统中,DMA 控制器通常被抽象为 io-dma 资源管理器。BSP 移植时需要正确初始化 DMA 控制器的基地址、时钟使能以及中断路由,否则会导致系统启动时 DMA 资源枚举失败。
DMA 控制器工作流程(四阶段) ① 请求阶段 外设/软件发起 DMA 请求 ② 仲裁阶段 CPU 释放总线控制权 ③ 传输阶段 DMA 直接搬运数据 ④ 完成阶段 触发中断通知 CPU 总线控制权转移说明 • 请求阶段:外设通过 DMA_REQ 线发起请求,DMA 控制器内部锁存请求。 • 仲裁阶段:DMA 控制器向 CPU 发送 HOLD 信号,CPU 在总线空闲时回复 HLDA。 • 传输阶段:DMA 控制器成为总线主控,每个时钟周期搬运一个数据单元。

2.2 DMA 传输类型

根据数据源和目标的不同,DMA 传输分为三种基本类型。在 QNX BSP 中,每种类型对应不同的描述符配置和地址空间属性。

传输类型 源地址 目标地址 典型场景 QNX 注意事项
内存到内存 DDR/SRAM DDR/SRAM 数据拷贝、缓冲区移动、图像处理 需确保源和目标缓冲区均被缓存一致性管理;使用 CacheFlush() 或 mmap() 带 MAP_UNCACHED 标志
外设到内存 外设 FIFO/寄存器 DDR/SRAM UART 接收、SPI 读取、ADC 采样 外设地址空间需映射为 MAP_PHYS;注意外设 FIFO 的 burst 大小对齐
内存到外设 DDR/SRAM 外设 FIFO/寄存器 UART 发送、SPI 写入、DAC 输出 外设目标地址通常要求 4 字节对齐;避免在传输过程中修改源缓冲区
BSP 移植提示: 在 QNX 中,外设到内存的 DMA 是最容易出问题的类型。常见故障包括:外设地址未正确映射到系统页表、DMA 描述符中的源地址未使用物理地址、以及外设 FIFO 深度与 DMA burst 大小不匹配导致的溢出。
三种 DMA 传输类型对比 内存 → 内存 源缓冲区 目标缓冲区 CPU 零参与 带宽最高 外设 → 内存 外设 FIFO 内存缓冲区 需处理缓存一致性 外设地址需物理映射 内存 → 外设 内存缓冲区 外设 FIFO 传输前需 Flush Cache 外设对齐要求严格

2.3 DMA 通道与请求线

DMA 通道是 DMA 控制器内部独立的硬件上下文,每个通道包含一组寄存器(源地址、目标地址、传输计数、控制配置)。请求线则是外设与 DMA 通道之间的物理或逻辑连接。

2.3.1 通道与请求线的映射关系

在典型 SoC 中,DMA 控制器提供多个通道(如 8/16/32 路),每个通道可以独立配置。请求线映射通常有两种模式:

  • 固定映射:每个外设的 DMA 请求线固定连接到特定通道号。例如,UART0 的 RX 请求固定映射到 DMA 通道 0。这种模式简单,但灵活性差。
  • 可编程映射:通过 DMA 控制器内部的交叉开关(Crossbar)或路由寄存器,将任意外设请求线连接到任意空闲通道。QNX 的 io-dma 资源管理器通常支持这种模式,通过 dma_channel_alloc() 动态分配。
特性 固定映射 可编程映射
配置复杂度 低(硬件固定) 高(需软件初始化路由表)
通道利用率 低(外设独占通道) 高(通道可复用)
BSP 移植工作量 少(只需确认硬件连接) 多(需实现路由配置函数)
典型 SoC 示例 STM32F4、NXP i.MX6UL TI AM335x、Xilinx Zynq
常见故障模式: 在 QNX BSP 移植中,DMA 请求线映射错误是导致外设 DMA 传输无响应的首要原因。典型症状包括:DMA 描述符已提交但传输计数器不递减、外设状态寄存器显示请求已发出但 DMA 未响应。排查时应首先检查 /proc/bus/dma 下的通道分配状态,确认请求线号与通道号是否匹配。
DMA 通道与请求线映射架构 外设请求线 UART0_TX_REQ UART0_RX_REQ SPI1_TX_REQ ADC0_DONE 路由矩阵 可编程交叉开关 (Crossbar) 请求线 → 通道 映射寄存器 支持动态重映射 DMA 通道 通道 0 通道 1 通道 2 通道 3 映射示例 UART0_TX → 通道 2 UART0_RX → 通道 3 SPI1_TX → 通道 0

2.4 DMA 描述符与链表

在复杂 DMA 传输中(如 scatter-gather),DMA 控制器使用描述符链表来管理多个不连续的缓冲区。每个描述符包含:

  • 源地址:数据源物理地址。
  • 目标地址:数据目标物理地址。
  • 传输计数:本次传输的数据单元数(字节/半字/字)。
  • 控制标志:中断使能、传输类型、地址递增模式等。
  • 下一个描述符指针:指向链表中下一个描述符的物理地址。
QNX 描述符管理: 在 QNX 中,DMA 描述符通常通过 dma_desc_create() 创建,并通过 dma_chain_submit() 提交到硬件队列。BSP 移植时需要确保描述符内存位于非缓存区域,或使用 CacheFlush() 保证一致性。描述符地址必须是物理地址,且对齐到 8 字节边界。
/* QNX DMA 描述符结构体示例(基于 TI AM335x EDMA) */
typedef struct {
    uint32_t    src_addr;       /* 源物理地址 */
    uint32_t    dst_addr;       /* 目标物理地址 */
    uint16_t    count;          /* 传输计数(字节数) */
    uint16_t    flags;          /* 控制标志:中断使能、同步类型等 */
    uint32_t    next_desc;      /* 下一个描述符物理地址(0 表示链表结束) */
} dma_desc_t;

/* 初始化一个内存到内存传输描述符 */
void dma_desc_init_m2m(dma_desc_t *desc, uint32_t src, uint32_t dst, uint16_t len) {
    desc->src_addr  = src;
    desc->dst_addr  = dst;
    desc->count     = len;
    desc->flags     = DMA_FLAG_INTERRUPT | DMA_FLAG_SRC_INC | DMA_FLAG_DST_INC;
    desc->next_desc = 0;
}