3、DMA基础回顾:DMA控制器工作原理、DMA传输类型与描述符链

直接存储器访问(DMA)是嵌入式系统中实现高速数据传输的关键硬件机制。它允许外设或内存块之间直接交换数据,而无需CPU逐字节干预。理解DMA控制器的工作原理、传输类型以及描述符链结构,是后续分析Cache失效导致DMA传输故障的前提。

3.1 DMA控制器工作原理

DMA控制器本质上是一个专用的状态机,负责在总线矩阵上扮演“总线主控”角色。其核心工作流程包含以下阶段:

  1. 请求阶段:外设(如UART、ADC、定时器)或软件通过硬件信号线向DMA控制器发出传输请求。
  2. 仲裁阶段:当多个DMA通道同时请求时,仲裁器根据优先级(固定或可编程)选择当前服务通道。
  3. 源地址读取:DMA从源地址寄存器指向的内存或外设数据寄存器中读取数据。
  4. 目的地址写入:将读取的数据写入目的地址寄存器指向的目标位置。
  5. 计数更新与结束判断:传输计数器减1,若计数器归零则触发完成中断,否则等待下一请求。
关键指标: DMA传输延迟通常由“请求到仲裁延迟 + 总线获取延迟 + 单次传输时钟数”决定。在Cache失效场景下,若DMA直接访问内存而Cache中存有脏数据,将导致数据不一致。
DMA控制器内部状态机与总线连接 DMA控制器 状态机 + 通道仲裁 外设请求 (UART/ADC/Timer) 系统内存 SRAM / DDR 总线矩阵 (AHB/AXI/系统总线) 地址/数据/控制信号 说明:DMA作为总线主控,直接通过总线矩阵访问内存,绕过CPU的Cache层级

3.2 DMA传输类型

根据源和目的地址的归属,DMA传输主要分为以下三种类型。每种类型对Cache一致性的影响不同。

传输类型 源地址 目的地址 典型应用 Cache失效风险
内存到内存 内存缓冲区A 内存缓冲区B 数据搬移、帧缓冲拷贝 高:源/目的都可能被Cache缓存
外设到内存 外设数据寄存器 内存缓冲区 ADC采样、UART接收 中:目的内存可能被Cache缓存
内存到外设 内存缓冲区 外设数据寄存器 DAC输出、UART发送 中:源内存可能被Cache缓存
故障排查要点: 在“内存到内存”传输中,若CPU在DMA传输前写入了源缓冲区,但数据仅停留在Cache中(写回策略),DMA直接从物理内存读取时将拿到旧数据。这是最常见的Cache一致性问题。

3.3 DMA描述符链

描述符链(Descriptor Chain)是高级DMA控制器支持的一种链表式传输管理机制。每个描述符包含本次传输的配置信息,并通过指针链接到下一个描述符,形成链式结构。DMA控制器自动遍历链表,完成多段不连续传输。

典型描述符结构(以32位系统为例):

struct dma_descriptor {
    uint32_t src_addr;      // 源地址
    uint32_t dst_addr;      // 目的地址
    uint32_t control;       // 控制字(传输大小、中断使能、传输类型等)
    uint32_t next_desc;     // 下一个描述符指针(NULL表示链尾)
};

描述符链的工作流程:

  • 初始化:软件在内存中构建描述符数组,设置每个描述符的地址和传输参数,并将最后一个描述符的next_desc置为NULL。
  • 启动:将第一个描述符的地址写入DMA控制器的描述符基址寄存器,然后使能通道。
  • 自动遍历:DMA完成当前描述符后,自动读取next_desc指针,加载下一个描述符,继续传输。
  • 完成中断:当DMA遇到next_desc == NULL或传输计数器归零时,触发完成中断。
DMA描述符链结构 描述符 1 src: 0x2000_0000 dst: 0x2000_1000 size: 1024 bytes next: &Desc2 描述符 2 src: 0x2000_1000 dst: 0x4000_0000 size: 512 bytes next: &Desc3 描述符 3 src: 0x2000_2000 dst: 0x2000_3000 size: 2048 bytes next: NULL (链尾) DMA控制器:自动遍历描述符链 注意:描述符链本身存储在内存中,若被Cache缓存,DMA读取描述符时可能读到过时数据 → 描述符链所在内存区域通常需要配置为“非Cache”或“强序”属性
工程实践建议: 在配置DMA描述符链时,务必确保描述符结构体在内存中对齐到4字节或8字节边界。同时,对于描述符链所在的内存区域,建议使用mmap或链接脚本将其放置在非Cache段,或使用硬件Cache维护指令(如ARM的DCache_CleanInvalidate)在修改描述符后执行刷新操作。