CPU是一家企业的老板,平时不仅要干自己分内的活(比如做做数学题),还要打点公司大大小小的事情。但是悲催的是,为了省钱,他并没有雇一个帮忙跑腿的助理,只有一辆用来送资料的小推车(总线)。(啥?为啥要有小推车?这年头,资料都是一堆一堆的,自己扛根本扛不动)
如果一些**小客户(低优先级外设)一个电话打过来说要点什么资料,他就会让他们等一等,等自己忙完手上的东西就去送;但是如果是大客户(高优先级外设)**可不能得罪啊,CPU就得赶紧放下手中活,亲自推着小推车去送资料。
长此以往,CPU顶不住了,心想:我干嘛为了省这点钱,白白干那么多体力活啊?这工作效率也太低了,而且我整天去给别人送资料,不知道的还以为我就是一个文员呢。不行,我得雇一个跑腿的。
于是,CPU雇了一个叫DMA的跑腿小哥,还把自己的办公室腾出来一小块地方供DMA使用。以后如果有客户打电话来要资料,他就会把客户的信息、要什么资料,从哪里拿,拿到哪里去都告诉跑腿小哥让他去送。小哥一看这资料这么多,搬过去岂不是累死?这时,他想起办公室有一辆小推车,于是就去和CPU申请使用权限。等申请下来后,DMA去跑腿,而CPU就把这省下来的时间用来干其他更重要的事情,比如做数学题哈哈。
自从DMA开始接手跑腿工作后,CPU轻松多了,公司的经营状况也越来越好了。CPU心想,我早干嘛去了……
在没有DMA技术出现以前,外部设备(比如打印机)想要想要从存储器中读取信息需要经过下图的步骤:

1、没有外设请求时,CPU执行主程序。
2、打印机 (外设) 要接收数据时,都得先向CPU发送中断请求。待CPU响应中断后,就会从存储器中将数据传输到打印机。而这段时间里,可以看到CPU需要停止执行主程序来传输数据,大量占用了CPU花在执行主程序(比如高性能计算等)上的时间。
3、所以,如果有很多的外设一直跟请求接收数据,结果就是,低优先级的外设等很久都没有响应,而高优先级的外设如果一直请求,那么CPU就需要经常停下手中的活来传输数据,会导致效率低下。
因此我们就需要给CPU请一个跑腿,让他来帮忙搬运数据,从而解放CPU。CPU需要做的就是给这个跑腿交代一些信息(从哪里拿,拿多少,搬去哪里等),并且赋予他实权去占用总线进行跑腿工作,传输数据,最后再收尾一下就好了(看还需不需要继续跑腿,需要的话就告诉他相关信息)。
而这个跑腿就是接下来要登场的DMA小哥。
DMA, Direct Memory Access,叫直接存储器访问。这里的这个“直接”指的是可以不用CPU的干预,直接访问存储器的意思。具体就是用来提供在外设和存储器之间、存储器与存储器之间或者外设和外设之间的高速数据传输,从而减轻CPU的负担。
注意,前面提到的中断响应方式是通过软件进行的,而DMA则是一个硬件,是一块独立的芯片,叫协处理器DMAC。他主要就是用于数据传输控制的。
对于CPU来说,DMA就是个普通的外设,是个slave,只是负责跑腿而已;
但是这个外设背靠着CPU这个大老板,还是有那么权力的。所以对于普通的外设而言,DMA就是master的角色。
DMA可以分为自带DMA和公用DMA。对于一些比较大的IP,会自带DMA功能;而向SPI、UART等小IP,基本使用公用的DMA。
一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。
外设发起请求,告诉CPU说要进行数据传输(此时DMA是不知道的)。CPU需要对DMA进行配置,也就是告诉DMA是读还是写、从哪里,拿多少数据,传到哪儿去。CPU完成对DMA的配置后,就继续执行原来的程序去了。
具体的预置信息如下:
当外设准备好发送的数据(输入)或者上次接收的数据已经处理完毕(输出)时,它便向DMA接口发送请求,让DMA去向CPU提出占用总线的申请。
若有多个DMA同时申请,则按照优先级排序。
DMA控制器收到请求后,向CPU发起总线控制权的请求,等待CPU的响应。当CPU响应之后,便把总线(包括地址、数据和控制)的控制权交给DMA。
以数据从存储器传输到外设为例。接管总线控制权后,DMA通过总线从存储器中取数据,然后发送给外部设备。至于这个数据是先在DMA里缓存一下再发送给外设还是直接发送给外设,就得具体看DMA工作模式了。
结束传输后,DMA向CPU发起中断请求,将总线控制权还给CPU。CPU响应DMA的请求时,会停止原程序的执行,做一些DMA的后处理。包括:
FIFO模式下,可以将要传输的多个数据存储在FIFO缓冲器中,当达到了阈值后,就自动把存储的数据一次性发到目标地址。
一般外设内部也有FIFO,所以DMA也可以直接将数据从源地址传输到目标地址。
我们前面说到,DMA就是给CPU当跑腿的。而他们又只有一个小推车(总线)。那么如果有一天DMA和CPU同时都要用到这个小推车,那么谁可以先用咧?其实反而是DMA的优先级高一些。因为他跑腿的都是一些比较着急的单(高速外设),所以反而会把小推车先让给他用。
具体可以细分为以下三种情况:

当客户(外设)要求传送一批数据时,DMA跑腿小哥会向CPU发送停止信号,要求CPU把小推车(总线)交出来。之后DMA小哥用小推车运送数据。结束后,就把小推车还给CPU(交还总线控制权)。
优点:控制简单
缺点:DMA在访问主存时,CPU基本上不工作或者保持原状态,未充分发挥CPU对主存的利用率
CPU可以把自己的工作情况分为两种,一种是需要亲自用小推车去送资料的业务,另一种是其他业务。在CPU进行其他业务时,就可以把小推车交给DMA,让DMA去搬运资料。
这种方法适用于CPU的工作周期比主存存取周期长的情况。例如,CPU的工作周期为1.2us,主存的存取周期小于0.6us,那么可以将一个CPU周期分为C1和C2两个分周期,其中C1专供DMA访存,C2专供CPU访存。
优点:不需要总线使用权的申请、建立和归还过程。CPU既不需要停止主程序的运行也不进入等待状态,即完成了DMA的数据传送。
缺点:硬件逻辑更为复杂,也就是把CPU访存和不访存的硬件部分分开;而且CPU和DMA访存可能并不均匀,所以在分配主存利用的时候可能效率也不是那么高。
外设请求DMA传送会有三种情况:
1、此时CPU不需要访问主存(比如CPU在做乘法,时间比较长,不用访存),此时不冲突
2、此时如果CPU正在访存,就必须等存取周期结束后,CPU才能让出总线占有权。就是说,得等CPU用完小推车把数据送完了,才能够腾出小推车来。
3、如果DMA和CPU同时要求访问主存,就产生了冲突。此时DMA的优先级比较高,先占用总线。因为DMA服务的外设多为高速外设,如果DMA需要访存了,说明数据缓冲寄存器被写满了,如果不及时访存,高速外设传输来的数据就会对先前的数据产生覆盖。
这时,DMA要窃取一两个存取周期,也就是CPU在执行访问主存指令过程中插入了DMA请求,并挪用了一两个存取周期,使CPU延缓了一两个存取周期再访问主存。
就是说,DMA和CPU同时要用小推车时,因为DMA服务的客户比较急,所以一般情况下都先让DMA用,用完了再给CPU。
第三种方式既实现了与外设的数据传送,又较好地发挥了主存与CPU的效率,是一种广泛采用的方法。
其实,这里提到的节约时间,指的是把下方标出的红框里的时间。没有DMA时,传输数据的阶段CPU就没法去干其他更重要的事情,比如高性能计算等。而有了DMA后,CPU就只需要进行DMA初始化以及完成时的后处理即可。在总线被DMA占用做数据传输的阶段,CPU就可以进行一些内部的高性能计算(比如乘法等,不需要总线),所以说可以并行运行,节约时间。

唐朔飞的《计算机组成原理》第3版的205页提到
在DMA接口开始工作之前,CPU必须给它预置如下信息。
- 给DMA控制逻辑指明数据传输的方向是输入(写主存)还是输出(读主存)
- 给DMA设备地址寄存器送入设备号,并启动设备。
- 向DMA主存地址寄存器送入交换数据的主存起始地址。
- 对字计数器赋予交换数据的个数
所以其实都要。外设一共会发送两次请求。
下面用一个实际的例子来介绍DMA。

介绍
Q&A:
1、DMA与CPU如何竞争总线
2、为什么使用两个DMA?
3、两个DMA会不会发生竞争?
4、同样是DMA,DMA1和2的应用区别?
5、DMA1为什么只有1个master口连在矩阵上?