1. 为什么DMA是408大题里最容易丢分的“送分题”
很多人复习计算机组成原理,看到I/O控制方式这一章会觉得“不就是程序查询、中断、DMA三种嘛”,翻书五分钟就过了。结果一到真题,尤其是24年45题那种综合型大题,直接懵掉——DMA的传送过程到底谁在控制总线?周期挪用和停止CPU访存有什么区别?中断和DMA到底谁优先级高?这些问题如果只背结论,考场上稍微换个问法就翻车。
我当年第一轮复习也是这样,觉得自己看懂了,做题才发现全是漏洞。后来把唐朔飞教材、白中英教材、王道单科册里所有DMA相关的题目刷了一遍,才真正把这块吃透。这篇文章就把DMA方式从底层原理到408考点,再到实际芯片里的DMA控制器设计,完整拆一遍。不管你是正在准备408考研,还是学STM32时被串口DMA、ADC多通道DMA搞得头大,这篇内容都能帮你把“DMA”这三个字背后的逻辑彻底理清楚。
DMA,全称Direct Memory Access,直接存储器访问。它的核心价值就一句话:让数据在主存和I/O设备之间直接搬运,不需要CPU一条一条指令地中转。这个“不需要CPU参与”到底省了多少事,后面我会用具体的数据算给你看。先记住一个结论:DMA方式下,CPU只在传送开始前做初始化,传送结束后处理中断,中间的数据搬运全部由DMA控制器硬件完成。
2. DMA方式的核心原理拆解
2.1 三种I/O控制方式的本质区别
要理解DMA,必须先搞清楚它和程序查询、中断方式的本质差异。我用一个生活化的类比来说明。
假设你是一个公司的老板(CPU),需要把一仓库货物(数据)从A仓库(主存)搬到B仓库(I/O设备)。
程序查询方式:你亲自一趟一趟搬,每搬一趟还要问仓库管理员“下一件到了没”。CPU完全被占用,效率极低。这种方式下,CPU和I/O设备是串行工作的。
中断方式:你安排一个员工去搬,每搬完一件就打电话向你汇报,你放下手头工作去处理。虽然比查询方式好,但每搬一件就打断你一次,如果数据量大,你基本啥正事都干不了。中断方式下,CPU和I/O设备是并行工作的,但每次传送都要打断CPU。
DMA方式:你直接雇了一个搬运队长(DMA控制器),告诉他“从A仓库第X号位置开始,搬N件到B仓库”,然后你该干嘛干嘛。搬运队长自己完成所有搬运工作,搬完了再通知你一声。整个过程中,CPU只在开始和结束时有参与,中间完全解放。
这三种方式的对比,用表格看得更清楚:
| 对比维度 | 程序查询方式 | 中断方式 | DMA方式 |
|---|---|---|---|
| CPU参与程度 | 全程参与 | 每次传送都参与 | 仅开始和结束参与 |
| CPU与I/O并行性 | 串行 | 并行 | 并行 |
| 数据传送方向 | 经CPU中转 | 经CPU中转 | 主存与I/O直接传送 |
| 传送单位 | 字(节) | 字(节) | 数据块 |
| 中断频率 | 无中断 | 每字中断一次 | 每块中断一次 |
| CPU开销 | 极大 | 较大 | 极小 |
| 适用场景 | 简单低速设备 | 中低速设备 | 高速大批量设备 |
这张表是408选择题的高频考点。尤其是“传送单位”和“中断频率”这两行,几乎每年都换着花样考。
2.2 DMA控制器的内部结构
DMA控制器不是一颗简单的芯片,它内部有几个关键寄存器,这些寄存器就是408大题里经常让你计算和判断的核心。
主存地址寄存器(AR):存放要访问的主存地址。每传送一个字,这个寄存器自动加1(或减1),指向下一个要访问的存储单元。
字计数器(WC):记录还剩多少字没传送。每传送一个字,计数器减1。当计数器减到0时,表示传送完毕,DMA控制器向CPU发出中断请求。
数据缓冲寄存器(DR):暂存每次传送的数据。对于输入设备,数据先从设备读到DR,再写到主存;对于输出设备,数据先从主存读到DR,再写到设备。
设备地址寄存器(DAR):存放I/O设备的地址,用于选择具体的设备。
控制/状态逻辑:负责协调整个传送过程,包括总线请求信号的发出、DMA请求的响应、传送方向的确定等。
中断机构:当传送完成时,向CPU发出中断请求,通知CPU做后续处理。
这些寄存器的工作过程,我用一个具体的例子来说明。假设要从磁盘读取4096个字节到主存起始地址为0x1000的区域:
- CPU初始化DMA控制器:AR = 0x1000,WC = 4096,DAR = 磁盘设备地址,方向 = 输入
- CPU启动DMA控制器,然后继续执行其他程序
- 磁盘准备好一个字节的数据,向DMA控制器发出DMA请求
- DMA控制器向CPU发出总线请求(HOLD信号)
- CPU响应,让出总线控制权(HLDA信号)
- DMA控制器获得总线控制权,将数据从磁盘经DR写入主存AR指向的地址
- AR加1,WC减1
- 释放总线,CPU继续执行
- 重复3-8,直到WC = 0
- DMA控制器向CPU发出中断请求
- CPU响应中断,做传送结束的后续处理
这个过程里,最关键的是第4-8步。DMA控制器和CPU之间通过HOLD和HLDA信号线进行总线控制权的交接。这个交接过程就是408考试里“总线仲裁”和“周期挪用”的知识点来源。
2.3 DMA传送的三种方式及其选择逻辑
DMA控制器获得总线控制权后,具体怎么传送数据,有三种方式。这三种方式是408选择题和大题的高频考点,必须彻底搞清楚。
停止CPU访存方式:DMA控制器获得总线控制权后,CPU完全停止访问主存,直到整个数据块传送完毕。这种方式控制简单,但CPU在传送期间完全不能访存,效率低。适合数据块很大、传送速度要求高的场景。
周期挪用方式:DMA控制器每次只挪用CPU的一个或几个存储周期来传送数据,传送完一个数据后立即归还总线控制权。CPU在DMA传送间隙可以继续访存。这种方式兼顾了CPU和DMA的需求,是实际系统中最常用的方式。
交替访存方式:将CPU的存储周期分成两个时间片,一个给CPU用,一个给DMA用。这种方式不需要总线控制权的申请和归还,效率最高,但硬件实现复杂,且要求CPU和DMA的访存周期匹配。
这三种方式的对比:
| 方式 | CPU访存影响 | 控制复杂度 | 适用场景 |
|---|---|---|---|
| 停止CPU访存 | 完全停止 | 简单 | 大数据块高速传送 |
| 周期挪用 | 部分影响 | 中等 | 通用场景 |
| 交替访存 | 无影响 | 复杂 | 高速缓存一致性要求高的场景 |
这里有一个容易混淆的点:周期挪用方式下,DMA控制器每次挪用的是一个存储周期,而不是一个指令周期。存储周期远短于指令周期,所以CPU的感知是“偶尔慢了一点点”,而不是“完全停下来了”。
2.4 DMA与中断的优先级关系
这是一个经典考点:DMA请求和中断请求同时发生时,谁先被响应?
答案是DMA请求优先级高于中断请求。原因很简单:DMA传送的是数据块,如果DMA请求得不到及时响应,数据可能丢失(比如磁盘旋转过去了);而中断请求通常对应的是已经完成的事件,晚一点处理不会丢数据。
在硬件实现上,DMA请求通常通过HOLD信号直接送到CPU的总线仲裁逻辑,而中断请求通过INTR信号送到CPU的中断控制逻辑。总线仲裁的优先级天然高于中断控制。
这个知识点在408里经常以选择题形式出现,问“DMA请求和中断请求同时发生时,CPU先响应哪个”,答案永远是DMA。
3. 408真题里的DMA考点全解析
3.1 24年45题DMA部分拆解
24年45题是一道综合型大题,涉及DMA控制器的初始化、传送过程计算、以及与其他知识点的结合。这道题的核心考点包括:
第一问:DMA控制器的寄存器初始化值计算。题目给出主存起始地址、传送数据量、磁盘扇区大小等参数,要求计算AR、WC、DAR的初始值。这里的关键是注意地址的单位(字节还是字)和计数器的位宽。
第二问:DMA传送的时间计算。题目给出磁盘的传输速率、DMA控制器的开销等,要求计算传送一个数据块的总时间。这里需要区分“数据传输时间”和“DMA控制开销时间”,两者相加才是总时间。
第三问:CPU在DMA传送期间能执行多少条指令。这是一个综合计算题,需要用到CPU的主频、指令执行速度、DMA占用总线的时间比例等参数。计算逻辑是:先算出DMA传送期间CPU被剥夺总线的时间,再乘以CPU的指令执行速率。
这道题的难点在于第三问,很多同学只算了数据传输时间,忘了算DMA控制器的预处理和后处理开销。我在第一次做这道题时也踩了这个坑,后来总结出一个原则:凡是涉及DMA的时间计算,必须把DMA控制器的所有开销都算进去,包括请求总线、获得总线、地址递增、计数器递减、释放总线等环节。
3.2 唐朔飞教材课后题里的DMA经典题型
唐朔飞《计算机组成原理》课后题里,DMA相关的题目主要集中在第7章。我挑几道最有代表性的说一下。
题目类型一:DMA传送方式判断。给出一个具体的传送场景,问应该采用哪种DMA传送方式。这类题的关键是看数据块大小和实时性要求。数据块大、实时性高选停止CPU访存;通用场景选周期挪用;要求CPU完全不受影响选交替访存。
题目类型二:DMA控制器与CPU的总线交互。画出DMA控制器与CPU之间的信号连接图,说明HOLD、HLDA、DMA请求、DMA响应等信号的作用。这类题在408里通常以选择题形式出现,问某个信号的作用或某个时刻总线的控制权在谁手里。
题目类型三:DMA传送效率计算。给出CPU的指令周期、DMA的传送速率、数据块大小等参数,计算DMA传送对CPU性能的影响。这类题的核心公式是:CPU性能损失 = DMA占用总线时间 / 总时间。
3.3 王道单科册里的DMA易错点整理
王道单科册里DMA部分的易错点,我整理了一个速查表:
| 易错点 | 正确理解 | 常见错误 |
|---|---|---|
| DMA传送单位 | 数据块 | 误认为是字或字节 |
| DMA中断时机 | 传送结束后 | 误认为是每传送一个字 |
| DMA与中断优先级 | DMA高于中断 | 误认为中断高于DMA |
| DMA控制器是否占用CPU | 不占用CPU,占用总线 | 误认为占用CPU |
| 周期挪用的挪用对象 | 存储周期 | 误认为是指令周期 |
| DMA传送方向 | 双向(主存到设备、设备到主存) | 误认为只能单向 |
| DMA控制器的位置 | 在主存和I/O设备之间 | 误认为在CPU内部 |
这张表里的每一条,都是我在刷题过程中真实踩过的坑。尤其是“DMA控制器是否占用CPU”这一条,很多人搞不清楚“占用总线”和“占用CPU”的区别。DMA控制器占用的是总线控制权,CPU本身还在执行指令,只是不能访存而已。
4. 从408到实际芯片:DMA控制器的工程实现
4.1 STM32里的DMA控制器架构
408考的是原理,但如果你学软件或者做嵌入式,迟早要跟真实的DMA控制器打交道。STM32的DMA控制器就是一个典型的工程实现,它的架构和408教材里的描述高度一致,但多了很多工程细节。
STM32F103系列有2个DMA控制器,每个控制器有7个通道。每个通道可以独立配置源地址、目的地址、传输数据量、传输方向、数据宽度等参数。这些参数对应的就是408里讲的AR、WC、DAR等寄存器。
STM32 DMA的关键配置参数:
- 方向:外设到存储器、存储器到外设、存储器到存储器
- 数据宽度:字节(8位)、半字(16位)、字(32位)
- 地址递增模式:源地址和目的地址是否自动递增
- 循环模式:传送完成后是否自动重新加载计数器
- 优先级:多个通道同时请求时的仲裁优先级
- 中断使能:传送完成、半完成、错误等事件是否触发中断
这些参数里,地址递增模式和循环模式是408里没有详细展开但工程中极其重要的概念。地址递增模式决定了DMA是连续访问一片内存还是反复访问同一个地址;循环模式决定了DMA是一次性传送还是周期性传送。
4.2 串口DMA和SPI DMA的配置要点
串口DMA和SPI DMA是STM32里最常用的两个DMA应用场景。它们的配置逻辑和408里的DMA原理完全对应,但有一些工程上的坑需要注意。
串口DMA配置要点:
串口DMA通常用于不定长数据的接收和发送。接收时,DMA从串口数据寄存器搬运数据到内存缓冲区;发送时,DMA从内存缓冲区搬运数据到串口数据寄存器。
配置串口DMA时,最容易踩的坑是数据宽度不匹配。串口的数据寄存器是8位的,如果DMA配置成16位或32位传输,会导致数据错位。我实测下来,串口DMA必须配置成字节传输,且源地址和目的地址的递增模式要根据实际需求设置。
另一个坑是DMA传输完成中断和串口空闲中断的配合。串口接收不定长数据时,通常用DMA接收+串口空闲中断来判断一帧数据接收完毕。这个组合的配置逻辑是:DMA负责搬运数据,串口空闲中断负责通知CPU“这一帧数据收完了”。
SPI DMA配置要点:
SPI DMA通常用于高速数据传输,比如驱动LCD屏幕、读取Flash数据等。SPI是全双工总线,所以DMA需要同时配置发送和接收两个通道。
配置SPI DMA时,最关键的是时钟极性和相位的配置。如果SPI的时钟极性和相位配置错误,DMA搬运的数据全是乱的。这个坑我在驱动一块SPI LCD时踩过,调了半天以为是DMA配置问题,最后发现是SPI模式设错了。
4.3 ADC多通道采集DMA的实战配置
ADC多通道采集+DMA是STM32里最经典的组合之一。它的工作逻辑是:ADC依次扫描多个通道,每转换完一个通道,DMA就把转换结果搬运到内存数组里。这样CPU不需要干预,就能自动采集多个通道的数据。
配置ADC多通道DMA的步骤:
- 配置ADC为扫描模式,设置通道数量和顺序
- 配置ADC为连续转换模式(如果需要连续采集)
- 配置DMA通道,源地址为ADC数据寄存器,目的地址为内存数组
- 配置DMA为循环模式,数据宽度为半字(ADC是12位,用16位存储)
- 使能ADC的DMA请求
- 启动ADC和DMA
这里有一个容易忽略的细节:ADC的DMA请求必须在ADC启动之前使能。如果先启动ADC再使能DMA请求,第一次转换的结果可能会丢失。我在第一次配置ADC DMA时就犯了这个错误,导致采集到的第一个数据总是0。
另一个细节是内存数组的大小必须和ADC通道数量匹配。如果数组大小小于通道数量,DMA会越界写入,导致内存数据被破坏。这个错误在调试时很难发现,因为程序不会崩溃,只是数据莫名其妙不对。
5. DMA方式的知识延伸与常见误区
5.1 DMA与Cache的一致性问题
这是一个408里不常考但实际工程中极其重要的问题。DMA直接访问主存,不经过Cache。如果CPU在Cache里缓存了某块主存的数据,而DMA又直接修改了主存里的这块数据,就会导致Cache和主存的数据不一致。
解决这个问题有两种常用方法:
方法一:DMA传送时使Cache无效。在DMA写入主存后,CPU访问这块数据前,先把Cache里对应的缓存行标记为无效,强制CPU从主存重新读取。
方法二:DMA传送时写穿Cache。DMA写入主存的同时,也更新Cache里对应的数据。这种方法硬件实现复杂,但性能更好。
在408考试里,这个问题通常以选择题形式出现,问“DMA方式下是否需要考虑Cache一致性”,答案是“需要,但具体处理方式取决于系统设计”。
5.2 DMA与虚拟内存的交互
DMA控制器使用的是物理地址,而CPU使用的是虚拟地址。当DMA传送涉及的数据跨越页面边界时,可能会出现物理地址不连续的情况。这个问题在实际系统中通过“散射-聚集”(Scatter-Gather)DMA来解决。
散射-聚集DMA允许DMA控制器处理不连续的物理内存块。它使用一个描述符链表,每个描述符包含一个物理地址和对应的数据长度。DMA控制器依次读取描述符,完成不连续内存块的传送。
这个知识点在408里不考,但在实际驱动开发中经常遇到。如果你以后做Linux驱动或者嵌入式开发,迟早要跟散射-聚集DMA打交道。
5.3 常见误区纠正
误区一:DMA传送不需要CPU参与。正确理解:DMA传送的初始化(设置寄存器)和结束处理(中断服务)都需要CPU参与,只是中间的数据搬运不需要CPU逐字干预。
误区二:DMA传送速度一定比中断方式快。正确理解:DMA的优势在于CPU开销小,而不是绝对速度快。对于少量数据的传送,DMA的初始化开销可能比中断方式还大。
误区三:DMA控制器可以同时处理多个设备的请求。正确理解:一个DMA控制器在同一时刻只能为一个设备服务。多个设备同时请求时,需要通过优先级仲裁决定先服务哪个。
误区四:DMA传送过程中CPU完全不能访存。正确理解:这取决于DMA的传送方式。停止CPU访存方式下CPU确实不能访存,但周期挪用和交替访存方式下CPU可以访存。
6. 复习DMA的实操建议与避坑指南
6.1 408复习的刷题策略
DMA这块内容,我的建议是分三步走:
第一步:吃透教材。唐朔飞教材第7章和白中英教材第5章是必读的。重点看DMA控制器的结构图、三种传送方式的对比、以及DMA与中断的优先级关系。
第二步:刷真题。408历年真题里DMA相关的题目,我统计了一下,选择题大概每年1-2道,大题每隔几年出一道。24年45题是最新的一道综合大题,必须反复做三遍以上。
第三步:做模拟题。王道单科册和二十套模拟题里的DMA题目,重点做那些涉及计算的。计算题最能检验你是否真正理解了DMA的工作过程。
6.2 嵌入式开发的DMA调试技巧
如果你是在做嵌入式开发,调试DMA时这几个技巧能帮你省很多时间:
技巧一:先用小数据量测试。不要一上来就传几KB的数据,先用几个字节测试DMA的基本功能。确认基本功能正常后,再增加数据量。
技巧二:用示波器或逻辑分析仪看时序。DMA的问题很多时候是时序问题,光看代码看不出来。用逻辑分析仪抓一下总线信号,能快速定位问题。
技巧三:检查DMA的中断标志位。STM32的DMA有多个中断标志位(传输完成、半完成、错误等),调试时先把所有中断标志位都使能,通过串口打印出来,能快速判断DMA卡在哪一步。
技巧四:注意DMA和CPU的访存冲突。如果DMA传送速率很高,CPU又频繁访存,可能会出现总线冲突。这时候需要调整DMA的优先级或传送方式。
6.3 常见问题速查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| DMA传送的数据全是0 | 源地址或目的地址配置错误 | 检查AR和DAR的初始值 |
| DMA传送的数据错位 | 数据宽度配置错误 | 检查源和目的的数据宽度是否匹配 |
| DMA传送不完整 | 计数器配置错误 | 检查WC的初始值是否正确 |
| DMA传送完成后没有中断 | 中断使能未配置 | 检查DMA的中断使能位 |
| DMA传送速度慢 | 优先级配置过低 | 调整DMA通道的优先级 |
| DMA传送导致程序崩溃 | 内存越界 | 检查目的地址的缓冲区大小 |
| DMA和CPU数据不一致 | Cache一致性问题 | 使Cache无效或使用写穿模式 |
这张表里的每一个问题,都是我在实际项目中真实遇到过的。尤其是“DMA传送的数据全是0”和“DMA传送导致程序崩溃”这两个,新手几乎必踩。
6.4 一个容易被忽略的细节:DMA的地址对齐
DMA传送时,源地址和目的地址的对齐方式会影响传送效率。如果地址没有对齐到数据宽度的边界,DMA控制器可能需要额外的总线周期来完成传送。
比如,32位数据宽度的DMA传送,如果源地址是0x1001(不是4的倍数),DMA控制器需要先读取0x1000-0x1003,再读取0x1004-0x1007,然后拼接出需要的数据。这会增加总线开销,降低传送效率。
在STM32里,如果地址不对齐,DMA控制器会自动处理,但效率会降低。所以在配置DMA时,尽量保证源地址和目的地址都对齐到数据宽度的边界。
这个细节在408里不考,但在实际工程中很重要。我在做一个高速数据采集项目时,就因为地址不对齐导致DMA效率下降了30%。后来把缓冲区地址强制对齐到4字节边界,效率立刻恢复正常。
7. 从DMA看计算机组成原理的学习方法
DMA这个知识点,表面上看是一个独立的I/O控制方式,但实际上它串联了总线、存储器、中断、Cache等多个章节的内容。学好DMA,不仅能拿下I/O这一章的分数,还能加深对其他章节的理解。
我个人的学习方法是:每学一个知识点,都问自己三个问题——它解决了什么问题?它是怎么解决的?它的代价是什么?
用这三个问题套DMA:
- 解决了什么问题?解决了CPU在数据搬运中开销过大的问题。
- 怎么解决的?用专门的硬件(DMA控制器)代替CPU完成数据搬运。
- 代价是什么?增加了硬件复杂度,引入了Cache一致性问题,需要总线仲裁机制。
这三个问题的答案,就是DMA这一章的核心逻辑。把这条逻辑线理清楚,不管题目怎么变,你都能找到解题的切入点。
最后分享一个我复习时用的小技巧:把DMA的传送过程画成流程图,贴在书桌前,每天看一遍。画流程图的过程就是梳理逻辑的过程,比单纯看书效果好得多。我当年画了不下十遍,到最后闭着眼睛都能把DMA的传送过程完整复述出来。