第5章是《计算机组成原理》整门课里第一道真正的分水岭。前面几章讲数据怎么表示、存储怎么组织、指令系统长什么样,作业题大多是套公式、画个图就能过去;到了第5章,题目开始让你写微操作序列、画数据通路、设计控制器、算流水线效率,很多同学第一次体会到"书看懂了,题一提笔就卡壳"。我当年做这一章的时候,取指周期的微操作序列前后改了四遍,微程序那道大题因为漏算了判别字段的位数,白白丢掉一大半分;后来做课程实验又踩了流水线气泡的坑,才真正把原理和题目对上了。
这篇把第5章的习题按题型拆开讲,重点不在给你一份可以抄的答案,而在于把每一类题的解题套路、参数是怎么算出来的、常见的坑在哪里说透。不管你是期末前两周开始突击,还是做完实验回头对一遍原理,都可以照着这篇的思路从头走一遍。全篇围绕中央处理器这一条主线展开,涉及指令周期、数据通路、控制器、流水线和中断五个板块,力求让你看完之后能自己独立把同类题做出来。
1. 第5章到底在考什么:先看清知识地图再动笔
很多人做这一章的题感觉吃力,根本原因不在知识点难,而在于前四章是"分零件讲",第5章是"把零件装起来让它动起来"。如果你脑子里还是零散的寄存器、存储器、指令格式,做数据通路题时就会不知道该把哪个部件接到哪里。所以动笔之前,先花二十分钟把整章的知识地图理清楚,后面做题的效率会完全不一样。
1.1 第5章在整门课里的位置
前四章基本是静态的:第1章讲系统概述和性能指标,第2章讲数据的表示与运算,第3章讲存储系统,第4章讲指令系统。这四章的共同特点是"给出结构、问你怎么算"。到了第5章,视角切换成动态的——CPU 内部到底怎么一条一条地把指令执行下去。取指、译码、执行这些动作在时间上怎么排布,控制信号由谁产生、什么时候产生,多条指令能不能重叠执行,外部设备怎么打断 CPU 的节奏,这些问题全部集中在中央处理器这一章里。
理解了这层定位,你就明白为什么第5章的题目往往"题干很短、要求很多"。一道题可能只给你一张数据通路图,然后问"写出 ADD (R0), R1 的执行周期微操作序列",看起来信息很少,实际上考的是你对数据通路、寄存器传输语言、指令语义三件事的综合理解。这也是它和前面几章最大的区别:前几章的题有明确公式,第5章的题需要你先建立模型,再往模型里填内容。
1.2 五个板块与它们的出题方式
把这一章的内容拆开,核心就是五块。我整理成了下面这张表,你可以对照自己的作业和历年题看看,哪一块的题你最没底。
| 板块 | 核心概念 | 常见题型 | 相对难度 |
|---|---|---|---|
| 指令周期与数据通路 | 取指/间址/执行/中断周期、微操作序列、寄存器传输 | 写微操作序列、画数据通路、补控制信号 | 中 |
| 控制器 | 硬布线、微程序、微指令格式、编码方式 | 设计逻辑表达式、算控制存储器容量、写微指令 | 高 |
| 指令流水线 | 结构相关、数据相关、控制相关、吞吐率与加速比 | 判断相关类型、计算性能指标、分析冲突解决 | 中高 |
| 中断与异常 | 中断响应、中断隐指令、多重中断、屏蔽字 | 写屏蔽字、分析处理次序、比较与子程序调用 | 中 |
| 时序系统 | 时钟周期、机器周期、指令周期、定长与不定长 | 时间计算、CPI 与执行时间估算 | 低 |
从难度分布能看出来,控制器和流水线是丢分的重灾区,这两块的题往往分值也最高。我的建议是:指令周期和时序系统的题必须拿满分,因为它们是基础,做错了说明模型没建起来,后面全塌;控制器和流水线的题至少要拿到七成,方法是把套路背熟、把典型例题走通。
1.3 做题前的三个准备动作
第一,拿一张白纸,把"指令周期包含哪几个子周期、每个子周期里 CPU 大概做什么"画成一条时间轴。别去看书,凭记忆画,画不出来的地方就是你的薄弱点。
第二,把寄存器传输语言的书写约定固化下来。比如PC → MAR表示 PC 的内容送到 MAR,M(MAR) → MDR表示按 MAR 的地址读存储器,结果送到 MDR,(PC) + 1 → PC表示 PC 自增。这套写法是全章通用的"普通话",不熟的话题目读起来就像看外语。
第三,准备两栏草稿:左边写微操作,右边写这条微操作需要的控制信号。养成这个习惯之后,做控制器设计题会顺手很多,因为本质上控制器就是把右边的控制信号用逻辑电路产生出来。
2. 指令周期与数据通路:最容易"看着懂、答不对"的章节
这一章的题,答案写出来都挺短,但得分率一直不高。原因很实在:课本上的微操作序列是标准化的写法,而同学自己写的时候往往会漏掉中间寄存器、把自增动作放错位置、或者默认"内存可以直接和 ALU 相连"。这类错误在批改时一眼就能看出来,因为它违背了数据通路的基本约束。这一节我们把时间概念和微操作写法一块儿捋清楚。
2.1 指令周期、机器周期、时钟周期三兄弟的关系
这三个概念的关系是层层包含:时钟周期是 CPU 的最小时间单位,由主频决定,比如主频 500MHz,时钟周期就是 2ns;机器周期又叫 CPU 周期,是完成一个基本操作需要的时间,一个机器周期可能包含一个或多个时钟周期,也就是常说的定长机器周期和不定长机器周期;指令周期是执行一条指令所需的全部时间,通常由若干个机器周期组成,包括取指周期、间址周期、执行周期,需要响应中断时还要加上中断周期。所以关系式可以写成:指令周期 = 若干机器周期,机器周期 = 若干时钟周期。
这里有个非常容易考的时间计算题,我把过程完整写一遍。设某机主频 500MHz,也就是时钟周期 Tc = 1/500MHz = 2ns。取指周期需要 4 个时钟,间址周期需要 2 个时钟,执行周期平均需要 6 个时钟,中断周期需要 3 个时钟。现在有一段程序共 1000 条指令,其中 20% 的指令需要间址,5% 的指令在执行结束后响应了中断。求这段程序的执行时间和平均 CPI。
先算每条指令的基本开销:取指 4 个时钟,执行 6 个时钟,合计 10 个时钟。间址指令额外加 2 个时钟,被中断的指令额外加 3 个时钟。总时钟数 = 1000 × 10 + 1000 × 0.2 × 2 + 1000 × 0.05 × 3 = 10000 + 400 + 150 = 10550 个时钟周期。执行时间 = 10550 × 2ns = 21100ns = 21.1μs。平均 CPI = 10550 / 1000 = 10.55。
提示:算这类题的时候,中断周期的开销一定要单独加上,不能混进"执行周期"里。因为中断是当前指令执行完之后才响应的,它在时间轴上是独立的一段,这是课本题里最常见的一个扣分点。
2.2 取指周期微操作序列怎么写才规范
取指周期是全章最基础的模板,必须背到能默写。标准的四步写法是这样的:
T0: PC → MAR T1: M(MAR) → MDR, (PC) + 1 → PC T2: MDR → IR T3: OP(IR) → CU第一步把 PC 的内容送到地址寄存器 MAR,这是访问存储器的前提;第二步是存储器读操作,读出的内容进 MDR,同时 PC 自增指向下一条指令;第三步把指令从 MDR 送到指令寄存器 IR;第四步把指令的操作码字段送进控制单元译码。这里有两个细节经常被忽略:一是 PC 的自增必须和访存同时进行,不能放在访存之前或之后单独占用一个节拍,因为自增和读存储器是两个互不干扰的硬件动作,可以并行;二是数据必须经过 MDR 中转,因为存储器数据线通常直接连到 MDR,IR 没有直接连存储器的通路。
还有一种变式是题目把 PC 自增和 PC → MAR 合并,写成T0: PC → MAR, (PC) + 1 → PC。这在有些教材里是可以接受的简写,因为 MAR 是边沿触发的寄存器,PC 送到 MAR 的同时自增,实际硬件上没问题。但如果你不确定阅卷老师接受哪种写法,就按标准四步写,多写一步不会扣分,少写一步可能就丢了。
2.3 数据通路题的通杀思路:从寄存器传输语言倒推
数据通路题看起来千变万化,其实思路就一条:先想清楚这条指令要干什么,再想清楚数据从哪来到哪去,最后想清楚有没有现成的通路。我把教材里最常考的几条指令整理成了一张对照表,你可以对着自己作业里的指令往里套。
| 指令 | 语义 | 执行周期微操作序列 |
|---|---|---|
| ADD R1, R2 | (R1)+(R2)→R1 | (R1)→A;(R2)→ALU 输入端;ADD;结果→R1 |
| ADD (R0), R1 | ((R0))+(R1)→R1 | (R0)→MAR;读存储器 M(MAR)→MDR;MDR→A;(R1)与 A 相加;结果→R1 |
| LDA (R0), R1 | ((R0))→R1 | (R0)→MAR;M(MAR)→MDR;MDR→R1 |
| STA R1, (R0) | (R1)→(R0) | (R0)→MAR;(R1)→MDR;写存储器 MDR→M(MAR) |
| JMP 偏移量 | 转移 | Ad(IR)→PC |
| STP | 停机 | CU 发出停机信号 |
这张表里的关键点是 A 暂存器。很多同学会直接写(MDR) + (R1) → R1,这是错的,因为 MDR 和 R1 都连着 ALU 的输入端,而 ALU 的输出端连的是暂存器或者寄存器堆的写端口,而 ALU 的两路输入是组合逻辑,不能自己咬自己的尾巴。所以必须先把 MDR 的内容暂存到 A,腾出 MDR 之后,再从存储器取新数据或者让另一个操作数进 ALU。
注意:写微操作序列时,每一条微操作左右两侧的部件之间都必须有实际通路存在。做题时如果你的序列里出现了"存储器→ALU"或者"IR→PC"这种写法,基本可以判定是错的,因为教材给出的数据通路里没有这两条线。这个自检方法能帮你捞回不少分。
3. 控制器:硬布线与微程序,两道大题的分水岭
控制器这一块是整个第5章的理论高地。硬布线控制器考的是组合逻辑设计的思路,微程序控制器考的是微指令格式和存储容量的计算。这两类题的分值通常都不低,而且题型比较固定,属于"套路吃透就能拿分"的类型,值得你专门花时间攻一下。
3.1 硬布线控制器的设计流程与字段法
硬布线控制器的本质是一个组合逻辑电路:输入是操作码译码信号、节拍电位和状态标志,输出是一堆微命令。设计流程我总结成五步。
第一步,列出所有机器指令,把每条指令划分成若干节拍,写出每个节拍里的微操作。第二步,把所有微操作里出现过的控制信号列一张总表,比如PC→MAR、MDR→IR、R1→A等等,每个信号取一个名字。第三步,画一张真值表,行是"指令 × 节拍",列是控制信号,需要发出该信号的格子填 1。第四步,对每个控制信号写出与或表达式并化简。第五步,画逻辑图。
第三步的真值表通常是这样的形式:
| 指令 | T0 | T1 | T2 | PC→MAR | MDR→IR | R1→A |
|---|---|---|---|---|---|---|
| LDA | 1 | 1 | 1 | T0 | T2 | 0 |
| ADD | 1 | 1 | 1 | T0 | T2 | T2 |
化简之后PC→MAR = T0(所有指令在 T0 节拍都要送地址),MDR→IR = T2(所有指令在 T2 节拍都要送指令),而R1→A只在 ADD 指令的 T2 节拍有效,表达式就是"ADD 操作码译码信号 AND T2"。这就是硬布线的核心思想:先找共性,再找个性。
如果控制信号特别多,手工化简会很痛苦。实际工程里还有一条"辅助函数法":把指令译码信号先组合成几个中间信号,再用中间信号去产生具体微命令。比如LDA + ADD + STA合起来是"需要访存的指令",用这个中间信号去控制 MAR 的加载端,逻辑层数能少很多。课本题里如果提到"化简",通常就是希望你用这个方法。
3.2 微程序控制:微指令格式、下地址字段、编码方式
微程序控制器的思路是把控制信号"存起来",用一条条微指令代替硬布线的逻辑电路。这个思路的好处是规整、容易修改,代价是多了一个控制存储器,速度比硬布线慢一些。一份微指令的格式一般分三部分:控制字段,用来直接或编码后产生微命令;下地址字段,指明下一条微指令的地址,或者给出转移条件的判别字段;判别字段,决定后续地址是顺序取还是根据条件跳转。
按控制字段的编码方式分,微指令分三种类型,对比起来看最清楚:
| 类型 | 编码方式 | 优点 | 缺点 | 微指令字长 |
|---|---|---|---|---|
| 水平型 | 直接编码,一位对应一个微命令 | 并行能力强、执行速度快 | 字长很长、控制存储器利用率低 | 长 |
| 垂直型 | 编码后由译码器产生微命令 | 字长短、控制存储器省 | 并行能力差、需要多次译码 | 短 |
| 混合型 | 分组字段直接编码 | 兼顾速度与字长 | 译码电路稍复杂 | 中等 |
考试里最常考的是混合型的计算题,也就是"字段直接编码"。规则是:把互斥的微命令放在同一段里,用二进制编码区分,每段必须留出一个编码表示"本段不发任何微命令"。假设有 30 个微命令,分成 6 组,每组 5 个微命令,那么每段需要的位数是 ceil(log2(5+1)) = 3 位,6 段合计 18 位。如果不分组直接编码,需要 30 位,一下省了 12 位。
提示:算每组位数的时候千万别忘了加那个"空操作"编码。5 个微命令配 3 位刚好,因为 2 的 3 次方等于 8,能表示 8 种状态,5 个微命令加 1 个空操作只用掉 6 个,还剩 2 个冗余编码。如果你算成 5 个微命令配 3 位(log2(5) 向上取整)也能得到 3,但换成 4 个微命令的时候,log2(4)=2,而正确答案应该是 3,因为要留空操作。这一步错了后面全错。
3.3 微程序容量与时序计算
这类题的计算量不大,但步骤要写清楚。来看一道典型题:某机采用微程序控制器,控制存储器有 512 个单元,微指令字长 36 位,其中下地址字段 9 位、判别字段 2 位、控制字段 25 位。一条机器指令对应的微程序平均包含 16 条微指令,控制存储器的存取时间为 40ns。求控制存储器容量、一条机器指令的微程序执行时间。
容量计算:512 × 36bit = 18432bit,换算成字节是 18432 / 8 = 2304B,也就是 2.25KB。如果你的答案习惯用位表示,写 18432 位也对。
时间计算:一条机器指令执行 16 条微指令,每条微指令需要一次控制存储器访问,也就是 40ns,所以微程序执行时间 = 16 × 40ns = 640ns。
这里有个隐含的验证点值得提一句:下地址字段 9 位,能寻址 2 的 9 次方等于 512 个单元,正好和控制存储器的 512 个单元匹配。如果题目给的下地址字段是 8 位而控制存储器是 512 个单元,那就说明设计不合理,或者需要额外的手段来扩展寻址范围。做题时顺手做一下这个校验,能发现题目里的隐含考点。
另外,如果题目问的是"微指令周期与机器周期的关系",通常的设定是一条微指令占一个时钟周期,一条机器指令由若干条微指令组成,于是机器周期就是若干微指令周期之和。有些教材会让微指令周期等于一个时钟周期,有些会让它等于一个机器周期,具体要看题干给的假设,不能想当然。
4. 指令流水线:结构性相关、数据相关与控制相关
流水线这一章在考试里的地位很特殊:概念题和计算题各占一半,概念题靠背,计算题靠套公式,但如果对相关性的理解不到位,计算题里的"气泡数"就会算错。而且这部分内容在实验里也经常出现,做 Verilog 流水线实验的时候,如果不理解数据相关,仿真波形会乱得一塌糊涂。
4.1 三种相关的识别方法
结构性相关是硬件资源冲突造成的。最典型的是存储器冲突:一条指令在取指阶段要访问指令存储器,另一条指令在访存阶段要访问数据存储器,如果只有一个存储器端口,两条指令就撞车了。解决办法是分离指令存储器和数据存储器,或者在冲突时让其中一条停顿一个周期。
数据相关是数据依赖造成的,细分有三类:写后读(RAW)、读后写(WAR)、写后读反过来的写后写(WAW)。在标准的五段流水线里,最常考的是 RAW,也就是后面指令要用前面指令还没算出来的结果。比如:
I1: ADD R1, R2, R3 I2: SUB R4, R1, R5I2 要读 R1,而 I1 的写回要等到第五段,这就产生了三个周期的数据相关。识别方法是:看指令的源寄存器和前面几条指令的目的寄存器有没有交集,有就是 RAW。
控制相关是转移指令造成的。转移指令在译码或者执行阶段才知道要不要跳转,而这时后面的指令可能已经被取进来了,取错了就得清空流水线,代价是几个周期的气泡。识别方法是:看指令是不是转移、调用、返回这类改变 PC 的指令。
4.2 流水线性能计算:吞吐率、加速比、效率
三个公式必须先背下来:吞吐率 TP = 指令条数 / 流水线执行时间;加速比 S = 顺序执行时间 / 流水线执行时间;效率 E = 顺序执行时间 / (段数 × 流水线执行时间)。对于把指令划分为 k 段、每段耗时相等(每个时钟周期为 Tc)、连续执行 n 条指令的情况,流水线执行时间 = (k + n - 1) × Tc,这是所有计算题的出发点。
来看完整例题。某处理器采用 5 段流水线,每段耗时均为 2ns,连续执行 200 条指令。顺序执行时间 = 200 × 5 × 2 = 2000ns。流水线执行时间 = (5 + 200 - 1) × 2 = 408ns。加速比 = 2000 / 408 ≈ 4.90,接近但达不到理论最大值 5。吞吐率 = 200 / 408 ≈ 0.490 条/ns,也就是约 490 MIPS。效率 = 2000 / (5 × 408) ≈ 98.04%。从数字上能看出来,当指令条数远大于段数时,流水线的效率趋近于 1。
如果加上数据相关的开销呢?假设 15% 的指令会因数据相关停顿 1 个周期,那就要额外插入 200 × 0.15 × 1 = 30 个气泡,流水线执行时间变成 (4 + 200 + 30) × 2 = 468ns,加速比降到 2000 / 468 ≈ 4.27。这就是为什么实际处理器的加速比总是明显低于段数,相关带来的停顿吃掉了相当一部分性能。
再看不等于长段的情况,这类题更能拉分。设四段流水线的段耗时分别是 3ns、2ns、4ns、2ns,执行 100 条指令。时钟周期必须取最慢的那一段,也就是 4ns,因为所有段必须同步推进。流水线执行时间 = (4 + 100 - 1) × 4 = 412ns。顺序执行时间 = 100 × (3+2+4+2) = 1100ns。加速比 = 1100 / 412 ≈ 2.67,远低于段数 4,瓶颈段的拖累非常明显。
如果把 4ns 的瓶颈段再细分一次,变成 2ns + 2ns,流水线变成 5 段,时钟周期降到 3ns(最慢段是 3ns),流水线执行时间 = (5 + 100 - 1) × 3 = 312ns,加速比 = 1100 / 312 ≈ 3.53。这个对比很说明问题:增加段数不一定提升性能,关键要看能不能把最慢的那一段拆开。
4.3 流水线冲突的解决方案与答题模板
结构性相关一般靠增加硬件资源解决,比如指令和数据分开的哈佛结构、双端口寄存器堆。数据相关的解决手段有三条:停顿(插入气泡)、转发(也叫旁路,把 ALU 的输出直接送到需要它的输入端,不用等写回)、编译器调度(在编译阶段重排指令,把无关指令插到相关指令之间)。控制相关的解决手段包括延迟转移(把一条必然执行的指令放在转移指令后面,无论跳不跳都执行)、分支预测(猜一个方向先执行,猜错就清空)、以及提前计算转移地址(把判断逻辑挪到译码阶段,减少停顿)。
答题的时候我推荐用这个模板:先判断相关类型,再指出冲突发生在哪两级之间,然后给解决方案,最后补一句代价。比如"这是 RAW 数据相关,发生在 I1 的写回段和 I2 的译码段之间,采用转发技术可以消除两个周期的停顿,只在 I1 是访存指令且 I2 紧接其后时需要保留一个周期停顿"。这样写层次清楚,阅卷时容易给分。
5. 中断与异常:容易被忽略但几乎必考
中断系统在整章里显得有点独立,但它和流水线一样是必考内容,而且小题多、容易拿分。常见的考法有三种:问中断隐指令做了哪几件事、给几个中断源写屏蔽字并分析处理次序、比较中断与子程序调用的区别。
5.1 中断响应、中断隐指令、断点保存
中断的响应条件可以概括成三条:有中断请求、CPU 内部允许中断(中断允许触发器为 1)、一条指令执行结束。第三条特别关键,中断只在指令边界上被响应,不能在指令执行到一半时插入,这是为了保证程序的可恢复性。
中断响应之后,硬件会自动执行一串"看不见的操作",也就是中断隐指令。它不是真正的指令,不需要程序员写,由硬件在中断周期内完成,通常包括四件事:关中断,防止响应过程中被新的中断打断;保存断点,把当前 PC 的值压栈或者存到指定单元;形成中断服务程序的入口地址,可能来自向量表,也可能来自硬件排队电路的编码;把控制权交给中断服务程序。
注意:断点和现场是两回事。断点通常指 PC 的值,由硬件自动保存;现场指通用寄存器和状态寄存器的内容,一般由中断服务程序用指令保存。题目问"哪些工作由硬件完成"时,只答断点相关的前三件事,不要答现场保存。
5.2 中断屏蔽字与多重中断
这类题的套路非常固定,我们用一个例子走通。设四个中断源 A、B、C、D,硬件优先级从高到低为 A > B > C > D。现在要求改变处理次序,实际处理顺序为 A > D > C > B。求每个中断源的中断屏蔽字。
思路是这样的:屏蔽字里的 1 表示屏蔽该中断源,0 表示允许。每个中断源在处理时,需要屏蔽掉所有优先级比自己低的中断源,以及自己本身,而对自己优先级高的中断源保持开放,这样才能被高级中断打断。按这个规则写出的屏蔽字如下表:
| 中断源 | 对 A | 对 B | 对 C | 对 D |
|---|---|---|---|---|
| A | 1 | 1 | 1 | 1 |
| D | 0 | 1 | 1 | 1 |
| C | 0 | 1 | 0 | 1 |
| B | 0 | 0 | 0 | 1 |
逐行验证一下:A 优先级最高,处理时不允许任何中断打断它,所以四个位置全填 1。D 在处理时应该能被 A 打断,但不能再被 B、C 打断,所以只对 A 开放,其余三个填 1。C 在处理时可以被 A、D 打断,不能被 B 打断,同时屏蔽自己,所以对 A、D 填 0,对 B、C 填 1。B 最低,只屏蔽自己。
处理次序的验证方法是:谁在运行的时候能打断别人,谁就排在前面。A 能打断所有其他源,排第一;D 能打断 C 和 B,排第二;C 能打断 B,排第三;B 谁都不能打断,排最后。得到 A > D > C > B,和题目要求一致。
5.3 中断与异常、子程序调用的区别
这三者的区别经常出现在选择题和简答题里。异常是 CPU 内部在执行指令过程中检测到的特殊情况,比如除零、溢出、非法操作码、缺页,它和当前指令的执行是同步的;中断一般来自外部设备,和当前指令的执行是异步的。子程序调用是程序主动发起的,地址由指令给出,调用前需要程序员保存现场;中断是硬件被动的,入口地址由硬件给出或者查表得到,断点由硬件保存。把这三条差异记住,相关题目基本不会错。
6. 高频错题复盘与自检清单
做完题之后一定要回头复盘,尤其是错题。我把带实验班和批改作业时最常见的问题整理成了一张速查表,你可以对着自己的答案逐条检查。
6.1 十个高频错误速查表
| 序号 | 错误表现 | 错误原因 | 正确做法 |
|---|---|---|---|
| 1 | PC 自增单独占一个节拍 | 混淆了"并行"和"顺序" | 与访存同节拍完成 |
| 2 | 数据不经过 MDR 直接进 IR | 忽略通路约束 | 存储器数据先到 MDR 再到 IR |
| 3 | ALU 输入直接写 MDR | 忘了需要暂存器 | 加一个 A 暂存器 |
| 4 | 微指令编码组不留空操作 | 遗漏互斥编码规则 | 每段位数 = ceil(log2(n+1)) |
| 5 | 计算机器周期时用平均段耗时 | 忽略同步时钟 | 取最慢段耗时 |
| 6 | 流水线气泡数漏算 | 只算结构相关 | 数据相关也要计入 |
| 7 | 屏蔽字把自己填 0 | 忽略自屏蔽 | 处理某个源时必须屏蔽自己 |
| 8 | 中断周期并入执行周期 | 时序概念不清 | 中断周期独立计算 |
| 9 | 效率公式分子分母颠倒 | 公式记忆混乱 | 效率 = 顺序时间 /(段数×流水线时间) |
| 10 | 硬布线真值表漏写指令行 | 未列全指令 | 所有机器指令都要列 |
这张表里的第 4 条和第 6 条杀伤力最大,因为它们会连带后面所有计算一起错。第 4 条错一位,微指令字长和容量全错;第 6 条少算气泡,加速比和吞吐率全错。做完题之后,建议专门花五分钟检查这两处。
6.2 答题自检清单
- 微操作序列里,每一条微操作的两个部件之间是否真的存在通路?
- 每个节拍内的微操作是否可以并行?有没有把必须串行的动作硬塞进一个节拍?
- 控制器设计题是否列全了所有机器指令?状态标志是否作为输入考虑了?
- 微指令分组编码时,每段是否预留了空操作编码?
- 流水线计算题的时钟周期是否取了最慢段?气泡数是否算全?
- 中断屏蔽字是否满足"屏蔽所有低优先级加自身"的规则?
- 所有时间单位是否统一?ns、μs、MHz 之间有没有换算错误?
6.3 用实验反哺理论
如果你有条件做硬件实验,强烈建议自己动手搭一个简易的五段流水线。用 Verilog 写的话,核心就是几个寄存器把各级隔开,再单独处理冒险。下面这段代码展示了最基本的流水线寄存器结构,思路是每个时钟周期把上一级的结果打入下一级:
always @(posedge clk or negedge rst_n) begin if (!rst_n) begin if_id_valid <= 1'b0; id_ex_valid <= 1'b0; end else if (!stall) begin if_id_valid <= fetch_valid; if_id_pc <= pc; if_id_instr <= instr_mem[pc]; id_ex_valid <= if_id_valid; id_ex_a <= reg_file[if_id_instr[19:15]]; id_ex_b <= reg_file[if_id_instr[24:20]]; end end跑一遍仿真你会直观地看到:不加转发的时候,加法后面紧跟一个用结果的减法,波形上确实会空一拍;加上转发之后,那一拍就被填满了。这种"看得见"的验证比看十遍教材都管用。我当年就是在这个实验里第一次真正理解了旁路的意义,之前做题全靠背公式,做完实验之后再看相关的题,几乎是条件反射。
7. 把这一章学会之后,你实际能得到什么
说点实在的。计算机组成原理第5章的知识,短期看是为了期末,长期看是理解现代处理器性能的底层参照系。你做移动端开发时遇到的启动速度优化、做后端时遇到的高并发指令吞吐、做算法时遇到的分支预测失败代价,背后都是这一章的原理在起作用。懂了流水线和相关性,你再看一些性能分析工具给出的 IPC(每周期指令数)、分支预测失败率这些指标,就不会觉得是一堆抽象数字,而能对应到具体的硬件行为上。
也正因为如此,做第5章的习题时别把自己当成一台"套公式的机器"。每写完一条微操作序列,问自己一句"硬件上真的能这么走吗";每算完一个加速比,问自己一句"这个数字离理论值差在哪"。带着这两个问题做题,你会发现这一章的题其实很有逻辑,只是要求你先把模型建对,剩下的都是顺水推舟。
我在实际操作中的体会是,这一章最有效的复习方式是"默写加复述"。合上书,把数据通路从 PC 开始画到写回结束,一边画一边说出每条线的作用;把五段流水线画出来,标出每个冒险可能发生的位置和对应的解法。能画出来、说清楚,题就不会做错。反过来,如果只是反复看答案,考试时换个指令、换个段数,照样会卡住。