DMA 本来不属于 CPU 体系架构部分的内容,只因为在开发中经常要用到其相关的知识,所以这里就其基本概念、工作原理、常见问题做一个总结。
概述
DMA 的英文拼写是“Direct Memory Access”,汉语的意思就是直接内存访问。
DMA 既可以指内存和外设直接存取数据这种内存访问的计算机技术,又可以指实现该技术的硬件模块(对于通用计算机 PC 而言,DMA 控制逻辑由 CPU 和 DMA 控制接口逻辑芯片共同组成,嵌入式系统的 DMA 控制器内建在处理器芯片内部,一般称为 DMA 控制器,DMAC)。
DMA 是一个控制器!
DMA 内存访问
使用 DMA 的好处就是它不需要 CPU 的干预而直接服务外设,这样 CPU 就可以去处理别的事务,从而提高系统的效率。
对于慢速设备,如 UART,其作用只是降低 CPU 的使用率,但对于高速设备,如硬盘,它不只是降低 CPU 的使用率,而且能大大提高硬件设备的吞吐量。
因为对于这种设备,CPU 直接供应数据的速度太低,因 CPU 只能一个总线周期最多存取一次总线,而且对于 ARM,它不能把内存中 A 地址的值直接搬到 B 地址。它只能先把 A 地址的值搬到一个寄存器,然后再从这个寄存器搬到 B 地址。也就是说,对于 ARM,要花费两个总线周期才能将 A 地址的值送到 B 地址。
而 DMA 就不同了,一般系统中的 DMA 都有突发(Burst)传输的能力,在这种模式下,DMA 能一次传输几个甚至几十个字节的数据,所以使用 DMA 能使设备的吞吐能力大为增强。
考虑到虚拟内存技术,注意这几个点:
- DMA 使用物理地址,程序是使用虚拟地址的,所以配置 DMA 时必须将虚拟地址转化成物理地址。
- 因为程序使用虚拟地址,而且一般使用 cache 地址,所以 Cache 中的内容与其物理地址(内存)的内容不一定一致,所以在启动 DMA 传输前一定要将该地址的 cache 刷新,即写入内存。
- OS 并不能保证每次分配到的内存空间在物理上是连续的。尤其是在系统使用过一段时间而又分配了一块比较大的内存时。所以每次都需要判断地址是不是连续的,如果不连续就需要把这段内存分成几段让 DMA 完成传输
嵌入式设备的 DMA
直接存储器存取(DMA)控制器是一种在系统内部转移数据的独特外设,可以将其视为一种能够通过一组专用总线将内部和外部存储器与每个具有 DMA 能力的外设连接起来的控制器。它之所以属于外设,是因为它是在处理器的编程控制下来执行传输的。值得注意的是,通常只有数据流量较大(kBps 或者更高)的外设才需要支持 DMA 能力,这些应用方面典型的例子包括视频、音频和网络接口。
一般而言,DMA 控制器将包括一条地址总线、一条数据总线和控制寄存器。高效率的 DMA 控制器将具有访问其所需要的任意资源的能力,而无须处理器本身的介入,它必须能产生中断。最后,它必须能在控制器内部计算出地址。
一个处理器可以包含多个 DMA 控制器。每个控制器有多个 DMA 通道,以及多条直接与存储器站(memory bank)和外设连接的总线,如图所示。在很多高性能处理器中集成了两种类型的 DMA 控制器。
第一类通常称为“系统 DMA 控制器”,可以实现对任何资源(外设和存储器)的访问,对于这种类型的控制器来说,信号周期数是以系统时钟(SCLK)来计数的,以 ADI 的 Blackfin 处理器为例,频率最高可达 133MHz。
第二类称为内部存储器 DMA 控制器(IMDMA),专门用于内部存储器所处位置之间的相互存取操作。因为存取都发生在内部(L1-L1、L1-L2,或者 L2-L2),周期数的计数则以内核时钟(CCLK)为基准来进行,该时钟的速度可以超过 600MHz。
每个 DMA 控制器有一组 FIFO,起到 DMA 子系统和外设或存储器之间的缓冲器的作用。对于 MemDMA(Memory DMA)来说,传输的源端和目标端都有一组 FIFO 存在。当资源紧张而不能完成数据传输的话,则 FIFO 可以提供数据的暂存区,从而提高性能。
因为通常会在代码初始化过程中对 DMA 控制器进行配置,内核就只需要在数据传输完成后对中断做出响应即可。你可以对 DMA 控制进行编程,让其与内核并行地移动数据,而同时让内核执行其基本的处理任务―那些应该让它专注完成的工作。
在一个优化的应用中,内核永远不用参与任何数据的移动,而仅仅对 L1 存储器中的数据进行读写。于是,内核不需要等待数据的到来,因为 DMA 引擎会在内核准备读取数据之前将数据准备好。图 2 给出了处理器和 DMA 控制器间的交互关系。
由处理器完成的操作步骤包括:建立传输,启用中断,生成中断时执行代码。返回到处理器的中断输入可以用来指示“数据已经准备好,可进行处理”。
数据除了往来外设之外,还需要从一个存储器空间转移到另一个空间中。例如,视频源可以从一个 视频端口直接流入 L3 存储器,因为工作缓冲区规模太大,无法放入到存储器中。我们并不希望让处理器在每次需要执行计算时都从外部存储读取像素信息,因此为 了提高存取的效率,可以用一个存储器到存储器的 DMA(MemDMA)来将像素转移到 L1 或者 L2 存储器中。
到目前为止,我们还仅专注于数据的移动,但是 DMA 的传送能力并不总是用来移动数据。
在最简单的 MemDMA 情况中,我们需要告诉 DMA 控制器源端地址、目标端地址和待传送的字的个数。每次传输的字的大小可以是 8、16 或者 12 位。 我们只需要改变数据传输每次的数据大小,就可以简单地增加 DMA 的灵活性。例如,采用非单一大小的传输方式时,我们以传输数据块的大小的倍数来作为地址增量。也就是说,若规定 32 位的传输和 4 个采样的跨度,则每次传输结束后,地址的增量为 16 字节(4 个 32 位字)。
参考文献
https://nieyong.github.io/wiki_cpu/CPU%E4%BD%93%E7%B3%BB%E6%9E%B6%E6%9E%84-DMA.html