计组这份笔记,我前前后后断断续续写了快两年。最开始是西电课堂上跟着老师划重点,后来准备408考研又把几乎所有内容推倒重来了一遍,等到课设阶段用Verilog亲手搭出一个能跑指令的单周期CPU,才发现前两遍的理解都浮在表面。同一门课,三种完全不同的学法,沉淀下来的东西远比课件本身多得多。
后台一直有学弟学妹问计组到底怎么学。说实话,这门课在计算机课程体系里的位置很特殊——前接数电和汇编,后接操作系统和体系结构,卡在中间,不少人在第一周就被补码乘法、流水线冒险这些概念劝退。如果你正在西电读这门课,或者正为408的计组部分发愁,又或者马上就要开始做计组课设,这篇笔记应该能给你一个比较完整的坐标系:它不替代教材,但能告诉你哪些地方必须较真、哪些地方可以放过,以及考试和课设各自到底想要你掌握什么。
1. 计组这门课到底在学什么——先搞懂全貌再谈细节
1.1 一条指令的完整旅程
计组虽然知识点又碎又多,但最核心的主线其实只有一条:一条指令从内存走到CPU再走回来,整个过程中数据怎么表示、怎么存储、怎么运算、怎么控制。把这根线拎起来,后面所有章节都能往上面挂。
指令的旅程大致是这样的:CPU先根据PC寄存器里的地址去内存取指令,送到指令寄存器;控制单元对指令译码,产生一堆控制信号;然后从寄存器堆或者立即数里取操作数,送进ALU运算;运算结果要么写回寄存器,要么写进内存,最后还要根据分支指令更新PC。这个过程在教材里被拆成了取指、译码、执行、访存、写回五个阶段,单周期CPU里一个时钟周期走完,流水线CPU里则让多条指令在不同阶段重叠执行。
很多人学计组最大的问题,是把它当成一门"背知识点"的课。但考试和课设真正想检验的,是你能不能在脑子里把这个过程完整地跑起来。我后来复习408时有个习惯:每学一个部件,就在一张白纸上画一遍它在这条链路上处于什么位置、和上下游交换什么信号。画过几遍之后,Cache、ALU、寄存器堆这些名词就不再是孤立的概念,而是一条流水线上不同工位的名字。
1.2 为什么很多人觉得计组难:三个认知断层
我的体会是,计组之所以让人头大,不是因为它计算量大,而是它制造了三个"认知断层"。
第一个断层是抽象层次。计组夹在数字电路和操作系统之间,既要跟门电路、触发器这种底层硬件打交道,又要跟汇编指令、进程内存这种偏软件的概念共存。很多人还停留在"程序就是C语言"的思维里,突然要理解一个加法指令在硬件上是怎么一步步完成的,确实需要一点时间适应。
第二个断层是时序思维。数电里学过时钟、触发器,但到了计组,你要理解一条指令在一个时钟周期里做什么、多个部件如何同步、流水线里为什么会有冒险。这种"顺着时间轴推理状态"的思维方式,跟写代码时候的"顺序执行"完全不同,需要专门练。
第三个断层是性能权衡。计组里几乎没有绝对正确的方案,只有不同约束下的取舍。Cache大小不是越大越好,指令集不是越复杂越好,甚至流水线级数也不是越多越好。这种"Trade-off"思维,是这门课想真正教会你的东西,也是408大题最喜欢出题的地方。
如果能把这三个断层补上,计组其实是一门非常有美感的课。下面几章我把笔记里最核心的内容按"校内考试+考研+课设"三个视角分别梳理,你可以按需对号入座。
2. 西电计组课堂笔记精华:重点章节与高频考点
2.1 数据的表示与运算:补码、浮点数这些"地基"
校内考试特别喜欢在第一章出计算题,这部分也是整个计组的地基。重点就三个:定点数的表示、补码运算、浮点数表示。
定点数要搞清楚原码、反码、补码、移码的关系和转换。这里有一个我到现在还在用的速记方法:补码的实质是"让符号位参与运算",所以A减B可以统一变成"A加B的补码",硬件只需要做加法。补码的表示范围为什么比原码多一个数?因为补码里0只有一种表示,省出来的全0编码可以多表示一个负数-2^(n-1)。这个细节考研选择题反复考。
浮点数部分核心是IEEE754标准。单精度float一共32位:1位符号、8位阶码、23位尾数,阶码用移码表示,偏置值是127。注意规格化数的尾数前面隐藏了一个1,所以实际尾数精度是24位。考试常见的计算套路是:给一个十进制数,让你转成IEEE754的十六进制表示,或者反过来。我的建议是把转换步骤写成固定的流程——先定符号,再化成二进制科学计数法,再算阶码真值加偏置,最后拼尾数并补零——每一步都机械执行,基本不会错。
2.2 存储系统:Cache、主存、虚存的三级存储体系
存储章节是计组考试的"题量担当",Cache尤其重要。学习思路可以这样理解:为什么需要Cache?因为CPU太快、主存太慢,速度差距接近两个数量级。Cache存在的理论依据是局部性原理——想想厨房里常用的调料一定放在灶台边而不是储物间,程序也是同样的道理。
Cache部分有三个必考的点。第一是映射方式:直接映射、全相联映射、组相联映射。核心要搞懂地址怎么划分,比如按字节编址时,一个地址里哪些位是块内偏移、哪些位是索引、哪些位是标记。第二是替换算法:LRU(最近最少使用)最常考,要会画访问序列对应的Cache状态表。第三是写策略:写直达(Write Through)和写回(Write Back),前者简单但每次写都要访问主存,后者带脏位标记,性能更好但实现复杂。
主存部分要理解DRAM为什么需要刷新、地址为什么要复用。虚存部分重点是分页式管理里CPU怎么通过页表把虚拟地址翻译成物理地址,以及TLB(快表)在其中的角色。408的真题里,Cache地址结构分析和虚存页表两级查询都是高频大题模型,值得花大量时间刷。
2.3 CPU与指令流水线:单周期、多周期到流水线
CPU章节是整门课的高潮。校内考试一般会要求你分析一条指令在单周期数据通路里经过哪些部件、控制信号是什么,或者给你一个有问题的流水线图让你找冒险。
数据通路是骨架,控制信号是灵魂。学习建议是拿指令逐个过:以MIPS指令集为例,R型指令(如add)不需要访存,I型指令(如lw)需要ALU算地址再读内存,分支指令(如beq)需要比较操作数并改PC。每条指令流过数据通路时,哪些多路选择器选哪条路、寄存器堆要不要写、内存要不要读,这些都由控制单元输出的信号决定。我在课设阶段亲自设计过一遍之后,再看书上那些真值表,感觉就像看自己写过的代码一样亲切。
流水线部分的核心是三种冒险:结构冒险(硬件资源不够用)、数据冒险(指令之间数据依赖)、控制冒险(分支跳转)。相应的解决思路也要烂熟于心:结构冒险靠资源分离(比如指令存储器和数据存储器分开),数据冒险靠转发和停顿,控制冒险靠分支预测和延迟槽。408大题非常喜欢结合流水线时序图,让你找某条指令在哪一步需要停顿,或者判断某个数据能不能被转发,这类题一定要练到熟练。
3. 408计组视角:考研命题规律与复习侧重点
3.1 408计组的分值分布与命题风格
如果目标是考研,校内课程学完之后还必须切换到408的视野重新梳理一遍。这几年408真题里,计组选择题稳定在11到12道,每道2分,另外综合题部分会有一道计组大题,分值在8到12分之间。四舍五入,计组在408满分150分里占了约40分,比很多人想象中更重要。
408计组的命题风格非常稳定:选择题以定性理解和简单计算为主,喜欢在细节上挖坑;大题则偏向定量分析和系统设计,要么是Cache与存储器系统的计算,要么是流水线时序图的分析,要么是C语言程序对应的机器级表示。你还会发现408特别喜欢把计组和操作系统结合出题——比如虚拟存储部分既考Cache页内偏移又考页表查询,这就要求两门课的知识能互相打通。
3.2 408计组与校内课程的差异和衔接
说实话,西电校内计组的课后题和考试风格,与408真题是有些错位的。校内更偏硬件细节和实验,408则更注重宏观理解和计算能力。如果你两线作战,建议做到"两手抓":课堂实验认真做,用硬件设计反向加深对原理的理解;408复习则单独刷王道或者天勤的辅导书,把历年真题按章节刷透。两边的重点并不矛盾,只是考察角度不同——比如同样的Cache知识点,校内可能问你某个信号的波形,408则让你根据访存序列算出命中率。
我觉得衔接的关键在于"用408的题来检验校内的理解"。每复习完一个章节,先做校内作业,再拿408对应章节的真题做一遍,会明显发现自己哪些概念是"会背但不会算",哪些是"会算但没理解"。这个过程很痛苦,但提分效果立竿见影。
3.3 易错题与典型陷阱整理
刷题过程中我整理了一批反复出错的点,列个表吧:
| 陷阱知识点 | 错误理解 | 正确思路 |
|---|---|---|
| Cache地址划分 | offset=块内字节数 | 等于块大小内字节编号的位数;还要注意按字节编址时先乘块内字节数 |
| DRAM刷新 | 认为刷新会改变存储数据 | 刷新只是重新充电,数据不变;刷新期间DRAM无法被CPU访问 |
| IEEE754的偏置值 | 单精度偏置是128 | float是127,double是1023;移码表示阶码时要加偏置 |
| PC的自增时机 | 认为执行完一条指令才加 | 取指阶段就PC+4,分支指令通过额外逻辑修改PC |
| 流水线停顿 | 认为停顿只影响一条指令 | 停顿会导致后续所有指令都延后一个周期 |
| 补码右移 | 算术右移和逻辑右移混用 | 有符号数补码右移是算术右移,高位补符号位 |
这些坑基本都在"看似简单、实则差一层窗户纸"的位置。我的建议是准备一个错题本,专门记录这种"再错一次就剁手"的细节题,考前反复过。
4. DRAM扩展专题:从原理到真题的完整拆解
4.1 DRAM芯片的基本结构:为什么需要扩展
DRAM扩展是计组考试里非常爱考的一类计算题,同时也是408考生容易失分的部分。要理解扩展,首先要明白DRAM芯片的本质——它就是一个用晶体管和电容存储数据的部件,一个电容存一个位(bit)。电容会漏电,所以DRAM必须定期刷新;电容的充放电是模拟量,所以读取时需要灵敏放大器来判别0和1。
DRAM芯片在给CPU提供服务时有两个显著特点。第一是地址复用:芯片通常只引出一半的地址线,先送行地址,由行地址选通信号RAS锁存,再送列地址,由列地址选通信号CAS锁存,这样地址引脚数量可以减半。第二是需要刷新:刷新有集中式、分散式、异步式三种方式,考试常考刷新对CPU访存的影响。比如集中刷新,在一段时间内均匀地逐行刷新,刷新期间CPU必须等待,这个"死区"时间要会算。
单独一颗DRAM芯片的容量是很有限的,比如常见的16K×8位芯片,16K就是2^14个字,8位就是每个字8比特。实际一个64K×16位的存储器需要多颗芯片组合才能构成,这个过程就涉及位扩展、字扩展和字位同时扩展。
4.2 位扩展、字扩展、字位同时扩展的计算方法
三种扩展方式本质上是在回答两个问题:数据位不够宽怎么办?存储单元数量不够多怎么办?
位扩展是并联思路:几颗芯片的数据引脚并行接到数据总线的不同位上,同时工作,相当于加宽数据通路。字扩展是串联思路:几组芯片按地址范围分段,每次只选中其中的一组工作,相当于加深存储容量。两者可以叠加,就是字位同时扩展。
具体计算方法分三步走。第一步,确定总芯片数:芯片数 =(目标总容量 / 单芯片容量)。注意容量要换算成"字数×位数"的形式来乘除。比如用16K×8位芯片组成64K×16位存储器,那么字数扩大64K/16K=4倍,位数扩大16/8=2倍,总芯片数就是4×2=8颗。第二步,确定地址线供给:片内地址线的根数由芯片的字数决定,16K=2^14,所以单芯片要14根地址线;整个存储器的总容量是64K=2^16,所以CPU送出来的地址是16位。第三步,确定片选信号:16位地址中,低14位作为片内地址,高2位就要通过译码器产生片选信号,2-4译码器输出4个信号,每个低电平有效,对应4组芯片,这就是字扩展。
这个过程中最容易犯的错误是混淆"地址线"和"片选"。地址线是CPU发给存储器的,片选信号是译码器对高位地址译码后生成的。同样,数据线的位数不等于芯片数据引脚数,芯片数据引脚数乘以组内并联的芯片数才等于总数据线数。
4.3 一个完整例题:64K×16位的存储器设计
我把上面提到的8颗芯片的设计过程完整走一遍,这个例子在408真题和西电期末里都换过皮出过。
目标是用16K×8位的DRAM芯片,组成一个64K×16位的主存储器。计算过程如下:
- 总芯片数:(64K/16K)×(16/8)=4×2=8颗。
- 分组方式:先把2颗芯片并联成一组,数据线加宽到16位;再把这样的4组按地址串联起来,构成64K地址空间。
- 地址分配:CPU地址总线共16位(64K=2^16),低14位(A0~A13)作为片内地址直接送到所有芯片;高2位(A14、A15)送入2-4译码器,译码输出Y0~Y3,分别作为4组芯片的片选信号。
- 数据分配:每一组内的2颗芯片,一颗接数据总线D0~D7,另一颗接D8~D15。
- 读操作时序:CPU先发出16位地址,译码器根据A14、A15选中对应的组,该组芯片的片选信号有效;同时RAS和CAS信号按地址复用的节奏锁存行、列地址;数据经过一段时间稳定后出现在D0~D15总线上。
这里有一个非常容易被忽略的点:由于DRAM地址复用,CPU给出的低14位地址不能一次性锁存,而是要先锁存行地址、再锁存列地址。教材里经常问"该DRAM芯片的地址引脚数目是多少",答案不是14,因为行、列分时传送,引脚数目约为7根加两根控制信号。这种题看清"地址复用"四个字就不会掉坑。
刷新计算也是同一个考纲里的常客。假设一组DRAM有1024行,刷新周期为2ms,集中刷新时每行的刷新耗时约等于一个存取周期(比如100ns),那么一次完整刷新需要1024×100ns约0.1ms,占总时间比例约5%。这些数字真题里经常换着来,公式就一个:刷新时间 = 行数 × 单行刷新时间。
5. 西电计组课设实战记录:单周期CPU从零到能跑
5.1 课设题目和我的实现方案
西电计组课程设计流传比较广的题目是设计并实现一个支持若干条指令的CPU。可以选MIPS指令集的子集,也可以自定一套简单指令集。我当时选择的方案是用Verilog在Vivado里实现一个单周期MIPS CPU,指令集选了add、sub、and、or、slt、lw、sw、beq、j这9条,目标是在仿真环境里正常跑完一个简单的冒泡排序循环。
为什么选单周期而不是流水线?因为我的目标是先拿到一个"完全正确"的基准版本。单周期CPU每个时钟周期只执行一条指令,控制逻辑简单,出错容易定位;流水线虽然性能更好,但要处理冒险、转发、停顿,调试难度直接翻倍。如果时间紧张,先完成单周期、再在此基础上扩展流水线,是我更推荐的路子。
课设的第一步不用急着写代码,而是先在纸上把数据通路图画出来。我当时画了一张A4纸的图,把PC、指令存储器、寄存器堆、ALU、数据存储器、控制单元用连线串起来,每一条指令走哪个通路都标上颜色。这个图纸画明白了,后面写Verilog基本就是翻译。
5.2 模块划分与仿真调试
代码层面我按功能划分了模块,总共五个:PC模块负责寄存当前指令地址并更新(顺序执行时PC+4,分支时加载目标地址);指令存储器IM用只读的case语句实现,存放测试程序;寄存器堆RegFile有32个寄存器,支持两个读端口和一个写端口;ALU模块根据控制信号ALUOp执行加减与或比较零等运算;控制单元CU根据指令操作码输出全部控制信号。
有一个我当时没想透但很重要的问题:寄存器堆的写入时机。单周期CPU中一条指令的结果要在一个时钟周期内写回寄存器。如果使用上升沿触发写入,则要确保写地址、写数据在上升沿前已经稳定,否则会写错数据。我后来采用的做法是寄存器堆用时钟上升沿写入,但数据通路中为写入留出充足的建立时间。仿真过程中我发现如果不小心把写入逻辑写成组合逻辑,会出现"读到了旧值"这种疑难杂症。
仿真调试我用的是Vivado自带的仿真器,测试程序从初始化指令存储器开始,写了一个累加1到10的循环,并在仿真波形里观察寄存器堆R1的值是否递增、PC跳转是否正常。还有一个小技巧:在寄存器堆里给每一个写操作加上$display显示打印,这样即使波形看不明白,终端日志也能追踪哪一步错了。
5.3 踩坑实录:仿真正常但综合报错
课设踩的坑比学的知识还多,说几个最典型的。
第一个坑:仿真正常但综合时报错。原因是我在RTL代码里用了initial块给存储器初始化,这个写法仿真器能处理,但综合成真实电路时不可综合。解决方案是把初始化逻辑放到一个只读存储器模型里用case语句列举,或者写成支持$readmemh加载十六进制文件的代码。
第二个坑:分支指令的PC更新时机。早期版本里我把beq的比较结果在写入PC之前就使用,结果顺序执行到beq时,下一条指令的地址已经变成"当前PC+4"了,导致跳转总是延迟一条指令。后来看了教材上的数据通路才意识到,单周期CPU中beq必须在同一个周期内完成计算并修改下一条PC,这就要求ALU的比较结果必须在那之前稳定。理顺这个之后,我把beq的地址计算和比较逻辑专门画了一个局部图,才彻底解决。
第三个坑:上板验证时序不收敛。仿真全对,但烧到FPGA板子后频繁出错。排查了一圈,发现是时钟频率太高,组合逻辑的延迟超过了周期。解决办法是降频到10MHz,并且给关键路径加上时序约束。这个经历让我真实体会到了"仿真通过只是第一步,时序收敛才是硬件设计的最终检验"这句话的含义。
如果你做课设时时间紧张,我强烈建议"先仿真、后上板,仿真彻底通过再碰板子"。上板前一定要有一个全指令的测试程序,覆盖R型指令、访存指令、分支和跳转,而不是只测一条简单的加法,不然基本一上板就露馅。
6. 计组学习路线、资料与给后来者的建议
6.1 学习节奏与三条主线
计组的知识量不算小,但如果能顺着三条主线推进,效率会高很多。
第一条线是"数据通路与控制":从一条指令的完整执行流程出发,把PC、寄存器堆、ALU、存储器串起来,这是CPU部分的主线。第二条线是"存储层次":从寄存器、Cache、主存到磁盘,按速度从快到慢、容量从小到大排列,理解每一层存在的意义和上下层之间的交接机制。第三条线是"数据表示与运算":明白0和1在计算机里如何表示数字、如何参与计算,这条线的成果会在操作系统和体系结构课程里一直复用。
时间安排上,如果是在校学习,我的建议是每章学完就用笔记整理成"A4一页图",比如Cache映射画一张、数据通路画一张、流水线冒险画一张。别小看这些手绘图,它们比PPT截图好用得多。到了考研复习阶段,再用王道系列辅导书把真题按章节刷一遍,让知识从"学过"变成"会算"。
6.2 资料与工具清单
教材方面,西电计组课堂用的教材通常是以经典的计算机组成原理教材为底,配合老师的PPT。考研复习则普遍使用王道考研的计算机组成原理辅导书,配合历年408真题。想往深了挖的同学可以看袁春风的《计算机组成与系统结构》,它对Cache和虚存的讲解非常细腻,但前置知识要求高一些,适合二刷时作为补充。
工具方面,除了课设必用的Vivado或Quartus,我还推荐两个学习利器。一个是Logisim,一个图形化的数字逻辑仿真工具,可以拖拽搭建CPU数据通路,很多学校计组实验就在用它。另一个是各类CPU在线模拟器,比如MIPS单周期模拟器、MIPS流水线模拟器,你可以单步执行指令,观察每一拍每个寄存器和信号的值,对理解流水线冒险非常有帮助。
6.3 几条实在的建议
最后说几点只有踩过坑才会有体会的建议。
第一,不要死记硬背,要"算给自己看"。计组的结论大多可以通过一个小例子推导出来。比如Cache组相联映射为什么不改标记位数?你拿一个8块Cache、2路组相联的例子手工画一遍就明白了。第二,重视真题和课设的结合。408真题里那道流水线大题,和课设中处理器模块的时序分析,底层是同一套知识。我在课设完成后回去刷真题,明显感觉理解上了一个台阶。第三,笔记要做成"能复用的东西",而不是抄写课件。我这份计组笔记里有很多我自己的错误记录和思考过程,这正是它能反复读的原因。
我个人的体会是,计组是少数几门"学的时候觉得抽象、做完课设觉得通透、考研时觉得庆幸"的课。它不会像算法那样让你刷题刷到上头,但会在每一个看似不起眼的细节里塑造你对计算机底层运行机制的直觉。如果你现在正被Cache、流水线或者DRAM扩展折磨得头疼,希望这篇笔记能帮你少走一点弯路。毕竟,能把一条指令从取指到写回的完整旅程讲清楚,很多曾经觉得高不可攀的知识,其实不过是一步一个脚印走出来的地图。