3. QNX DMA 子系统架构

在 QNX BSP 移植中,DMA(直接内存访问)子系统是连接外设与内存的关键数据通路。QNX 的 DMA 框架采用分层设计,将硬件差异封装在引擎抽象层之下,向上提供统一的通道管理与描述符链接口。理解这一架构是排查 DMA 中断映射失败、传输超时等问题的前提。

核心要点:QNX DMA 子系统通过“引擎抽象层 → 通道管理 → 描述符链 → 完成回调”四层结构,实现平台无关的 DMA 传输控制。BSP 移植的核心工作是将 SoC 的 DMA 控制器适配到该框架中。

3.1 DMA 引擎抽象层

DMA 引擎抽象层位于 QNX 内核与硬件 DMA 控制器之间,定义了一组标准接口,用于屏蔽不同 SoC 的 DMA 寄存器布局、传输模式与中断逻辑差异。BSP 开发者需要实现一个 dma_engine_entry 结构体,并将其注册到系统。

接口函数 功能描述 BSP 移植关注点
dma_engine_init() 初始化 DMA 控制器,复位寄存器、配置时钟 时钟门控、复位时序、电源域
dma_engine_channel_alloc() 分配一个物理 DMA 通道 通道号映射、硬件互斥锁
dma_engine_transfer() 启动一次 DMA 传输 源/目的地址对齐、传输宽度、突发长度
dma_engine_isr() DMA 中断服务例程入口 中断状态寄存器读取、清除标志位
dma_engine_status() 查询当前传输状态 忙/完成/错误状态位映射
移植提示:引擎抽象层通常不直接处理中断向量绑定,而是通过 InterruptAttach() 将 ISR 挂接到系统中断框架。DMA 引擎只负责在 ISR 中读取硬件状态并调用上层回调。
DMA 引擎抽象层架构 QNX 内核 DMA 框架 DMA 引擎抽象层 dma_engine_entry 接口表 SoC DMA 控制器硬件 BSP 移植工作 实现接口函数 注册引擎实例 绑定中断服务

3.2 通道管理

DMA 通道是传输操作的逻辑单元。QNX 的通道管理器负责通道的分配、释放、优先级仲裁以及中断路由。在 BSP 移植中,需要将硬件通道号映射到逻辑通道 ID,并配置每个通道的中断触发方式。

通道状态机

  • 空闲(IDLE):通道未分配,等待上层请求。
  • 就绪(READY):通道已分配,描述符链已挂载,等待启动。
  • 传输中(BUSY):DMA 正在搬运数据,硬件状态寄存器为忙。
  • 完成(DONE):传输完成,等待 ISR 确认并触发回调。
  • 错误(ERROR):传输异常(总线错误、描述符格式错误等)。
常见故障:通道分配后无法进入 BUSY 状态,通常是因为描述符链地址未正确写入硬件寄存器,或通道使能位未置位。排查时应先读取硬件通道控制寄存器的实际值。
DMA 通道状态机 IDLE READY BUSY DONE ERROR 释放通道

3.3 描述符链

描述符链是 QNX DMA 实现连续传输的核心机制。每个描述符定义了一次传输的源地址、目的地址、传输长度、控制标志以及指向下一个描述符的指针。硬件 DMA 控制器自动遍历链表,完成所有传输后触发中断。

描述符结构(典型定义)

typedef struct dma_descriptor {
    uint32_t    src_addr;       // 源物理地址
    uint32_t    dst_addr;       // 目的物理地址
    uint32_t    transfer_size;  // 传输字节数
    uint32_t    ctrl_flags;     // 控制标志(中断使能、链结束等)
    struct dma_descriptor *next; // 下一个描述符指针
} dma_descriptor_t;
控制标志位 含义 典型值
DMA_CFG_INT_EN 当前描述符完成后触发中断 0x01
DMA_CFG_CHAIN_END 当前描述符是链中最后一个 0x02
DMA_CFG_SRC_INC 源地址递增 0x04
DMA_CFG_DST_INC 目的地址递增 0x08
调试技巧:在描述符链挂载后,通过 JTAG 或内核调试接口读取硬件 DMA 控制器的当前描述符指针寄存器,确认其值与预期链表头地址一致。这是验证描述符链是否成功挂载的最直接方法。
描述符链结构 描述符 1 src: 0x8000_0000 dst: 0x9000_0000 size: 1024 next: &desc2 描述符 2 src: 0x8000_0400 dst: 0x9000_0400 size: 512 next: &desc3 描述符 3 src: 0x8000_0600 dst: 0x9000_0600 size: 256 next: NULL 链尾

3.4 传输完成回调机制

当 DMA 硬件完成一个描述符或整个链的传输后,会触发中断。QNX 的 DMA 框架在 ISR 中读取硬件状态,遍历已完成的描述符,并调用上层注册的回调函数。回调机制是连接 DMA 传输与上层业务逻辑(如网络协议栈、文件系统)的桥梁。

回调注册流程

  1. 上层模块调用 dma_channel_callback_attach(),传入通道 ID 和回调函数指针。
  2. DMA 框架将回调函数保存在通道控制块的回调链表中。
  3. 硬件 ISR 触发后,框架根据中断状态寄存器判断哪些通道完成传输。
  4. 框架遍历对应通道的回调链表,依次调用回调函数,并传入传输状态与描述符指针。
  5. 回调函数执行完毕后,框架清除中断标志位,释放描述符(若配置为自动释放)。
中断映射故障点:回调函数未被调用,最常见的原因是中断未正确映射到 CPU。排查步骤:1) 确认 InterruptAttach() 返回值为 OK;2) 在 ISR 入口处添加 log() 打印,验证中断是否到达;3) 检查中断控制器寄存器,确认 DMA 中断是否被屏蔽或优先级过低。
传输完成回调流程 DMA 硬件完成 中断控制器 DMA ISR 回调分发器 上层回调函数 关键检查点 ① 中断号 ② 中断触发 ③ 状态读取 ④ 回调注册 ⑤ 回调执行

3.5 架构总结与故障排查对照

架构层 核心职责 常见故障现象 排查方向
引擎抽象层 硬件寄存器操作、ISR 入口 DMA 控制器未响应、时钟未使能 检查引擎初始化函数、时钟门控寄存器
通道管理 通道分配、状态维护、中断路由 通道分配失败、状态卡在 IDLE 验证通道号范围、中断绑定、状态寄存器
描述符链 传输参数配置、链表遍历 传输数据错误、链遍历中断 检查描述符地址对齐、next 指针有效性
完成回调 中断处理、回调通知 回调未执行、死锁、数据未更新 确认中断到达、回调函数指针、cache 一致性
BSP 移植检查清单:
✅ 引擎抽象层:实现全部 5 个接口函数,注册到系统 DMA 框架。
✅ 通道管理:配置通道数量、中断优先级、仲裁策略。
✅ 描述符链:确保描述符位于非 cache 区域或执行 cache 刷新操作。
✅ 完成回调:验证中断号映射,回调函数中注意 cache 一致性维护。