2. DMA 基础概念:DMA 控制器工作原理、传输类型与通道
在嵌入式系统(尤其是 QNX BSP 移植场景)中,DMA(Direct Memory Access,直接存储器访问)是提升数据吞吐量的核心技术。它允许外设或内存块之间直接交换数据,而无需 CPU 逐字节干预。本章节将深入剖析 DMA 控制器的工作原理、三大传输类型以及通道与请求线的映射关系,为后续 DMA 映射故障定位打下坚实基础。
2.1 DMA 控制器工作原理
DMA 控制器是一个独立的总线主控设备,能够接管系统总线,在源地址与目标地址之间搬运数据。其核心工作流程可概括为以下四个阶段:
- 请求阶段:外设(如 UART、SPI、ADC)或软件通过 DMA 请求线向 DMA 控制器发出传输请求。
- 仲裁阶段:DMA 控制器向 CPU 发出总线请求(HOLD/BREQ),CPU 在完成当前总线周期后释放总线控制权(HLDA/BGNT)。
- 传输阶段:DMA 控制器直接控制地址总线和数据总线,执行源到目标的搬运,每次传输后自动更新地址和计数器。
- 完成阶段:当传输计数器归零时,DMA 控制器向 CPU 发送中断信号,通知传输完成。
QNX BSP 关键点: 在 QNX 系统中,DMA 控制器通常被抽象为
io-dma 资源管理器。BSP 移植时需要正确初始化 DMA 控制器的基地址、时钟使能以及中断路由,否则会导致系统启动时 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 大小不匹配导致的溢出。
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 下的通道分配状态,确认请求线号与通道号是否匹配。
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;
}