1. DMA技术概述——让CPU从数据搬运中解放出来

各位同学,今天开始我跟大家聊DMA。DMA,全称Direct Memory Access,直接存储器访问。

说白了,就是硬件自己搬数据,不用CPU一句一句指令去挪。你想想看,要是每次从网卡收一个包、从ADC读一个采样,CPU都得停下手里的事去搬运——那CPU还能干正事儿吗?

1.1 DMA原理——一个“硬件搬运工”

DMA的核心思想很简单:在系统总线上挂一个专用控制器(DMA Controller),它负责在外设和存储器之间,或者存储器和存储器之间,直接搬数据。搬运期间,CPU可以继续做运算、跑逻辑,两不耽误。

我早年做一款工业相机项目时,图像数据每帧2MB,如果让CPU逐像素搬运,帧率直接砍半。后来换上DMA,CPU只负责配置和中断处理,吞吐量立刻翻倍。嗯,那个坑踩得值。

DMA工作三要素

  • 源地址:数据从哪来。例如外设FIFO、内存地址X。
  • 目的地址:数据搬到哪去。例如内存缓冲区地址Y。
  • 传输长度:搬多少个字节/字。通常由CPU提前配置。

一次典型的DMA传输流程就像自助快递代收——CPU告诉DMA:“从A点取100个包裹,放到B点”,DMA自己搞定路线和交接,然后给CPU回一个“好了”的信号。

CPU搬运 vs DMA搬运对比
维度CPU直接搬运DMA搬运
开销每条指令都在消耗总线周期只消耗配置和中断处理周期
CPU利用率搬运期间CPU被占满搬运期间CPU可以跑其他任务
传输速度受CPU时钟频率限制由总线带宽决定,通常更高
代码复杂度简单循环需配置DMA寄存器+中断服务
我的习惯:设计大型系统时,我会先统计数据搬运的带宽需求——如果超过CPU吞吐量的10%,就坚决上DMA。别等跑起来卡顿了才补,那会儿改架构可就哭了。

1.2 DMA与CPU交互——如何“礼貌”地共享总线

DMA不是独立存在的,它必须和CPU配合。它们共享同一套系统总线(如AXI4、AHB),所以谁先走、谁让路,必须有个约定。

交互流程概括

  1. 配置阶段:CPU写DMA控制器的寄存器——源地址、目的地址、长度、传输模式(单次/循环)等。
  2. 请求阶段:外设(比如UART接收到了数据)向DMA发送DMA请求信号(DREQ)。
  3. 仲裁阶段:DMA向总线仲裁器申请总线控制权(BUSREQ)。仲裁器根据优先级决定把总线交给DMA还是CPU。
  4. 传输阶段:DMA获得总线后,直接发起读/写事务,数据在外设FIFO和内存之间流动。
  5. 结束阶段:传输完成后,DMA向CPU发中断(或者轮询状态位)。CPU知道数据已经就绪,可以处理了。

这里有个细节容易栽跟头:总线仲裁的优先级。我曾经在一个多DMA的系统里,两个外设同时请求,结果低优先级的外设一直吃不到总线,数据溢出丢包。后来我加了一个轮询仲裁模式,才解决问题。

注意:Xilinx的AXI_DMA(比如AXI DMA IP核)使用AXI4协议,总线仲裁由互联模块自动处理。但如果你用的是自定义DMA,仲裁逻辑必须自己写,debug的时候特别痛苦——建议直接用Xilinx的IP,别手撸仲裁。

1.3 DMA应用场景——三种经典搬运模式

DMA常见的三种传输方向,我来逐个拆解一下。

DMA三大应用场景
传输方向英文简称典型例子CPU介入程度
外设 → 内存Peripheral-to-MemoryADC采样数据搬到DDR只配置和响应中断
内存 → 外设Memory-to-PeripheralSPI发送缓冲区数据只配置和触发一次
内存 → 内存Memory-to-Memory两块DDR区域间拷贝搬运期间几乎不占用CPU

外设到内存(Peripheral-to-Memory)

这个我最常用。比如FPGA采集的视频数据,通过AXI DMA直接流到DDR。CPU只需要在帧完成中断后读帧头即可。我在一个高速数据采集卡项目中,用这种方式实现了200MB/s的持续写入,CPU占用率不到5%。爽。

内存到外设(Memory-to-Peripheral)

比如I2S音频播放。CPU把PCM音频数据准备好放在DDR里,然后触发DMA,DMA把数据喂给I2S TX FIFO。CPU可以同时处理UI,不会因为喂数据导致卡顿。

内存到内存(Memory-to-Memory)

这个模式在Xilinx DMA配置里稍微有点特殊——需要设置源和目的都是内存地址。常用于图像数据拷贝、协议包重组。不过老实说,除非你真的需要解放CPU去干别的事,否则用memcpy就够了。DMA的配置延迟有时候反而不如Cache优化后的memcpy快。嗯,这个问题我们到后面“性能优化”那章再细聊。

关键抉择:什么时候用Memory-to-Memory DMA?当拷贝数据量大(>256字节)且CPU空闲率紧张时,值得用。小数据量(几个字节)就别折腾DMA了,CPU直接搬更快。
DMA系统架构与数据流向 CPU 内存(DDR) DMA控制器 外设(如UART) 总线仲裁器 总线请求/授权 授权 数据写入 DMA请求 数据读出 关键交互:CPU配置DMA → 外设发起请求 → 仲裁器分配总线 → DMA搬数据 → 中断通知CPU Memory-to-Peripheral 和 Memory-to-Memory 方向类似,只是源/目的地不同

上面这张图就是DMA系统的主干。你可以看到,DMA控制器是CPU的“数据搬运替身”,它与外设、内存直接打交道,而CPU只需通过总线仲裁器和中断来协调。

我觉得你如果能理解这张图的流向,DMA的原理就算掌握一半了。剩下的一半,是我们后面要花大量篇幅讲的——如何用Xilinx的DMA IP核在FPGA里把这些节点搭起来、打通数据通道。