想把手头的电脑从“能用”拆到“能懂”,最硬核也最过瘾的一条路,就是自己从零开始搭一个CPU出来。别一听就慌了,这个“手搓”不是让你去光刻机下造芯片,而是用最底层的逻辑门,一点一点拼出一台能跑程序的计算机。我前阵子刚把一个8位教学CPU完整跑通,从画逻辑门符号到最后看着自己写的程序在仿真里算对结果,那个瞬间的满足感,比看任何架构书都来得实在。这篇文章就准备把这套从逻辑门到CPU的完整路线原原本本捋一遍,适合正在学计算机组成原理、准备考计算机相关专业,或者纯粹想搞清楚“CPU到底是怎么思考问题”的开发者收藏。
这条路线看起来长,其实是一层一层垒上去的。你不需要一开始就懂流水线、乱序执行这些复杂玩意儿,只需要跟着四个阶段走:先认识逻辑门,再做出存储单元,然后拼出运算器和控制器,最后把数据通路串起来,一台能跑指令的计算机就成了。
1. 整体路线与核心思路
1.1 为什么要从逻辑门开始,而不是直接学CPU
很多同学学计算机组成原理,一上来就是指令周期、寻址方式、流水线,结果越听越虚。原因很简单:CPU不是做出来的,是“长”出来的。它由一大堆功能部件组成,功能部件由寄存器、ALU、控制器组成,而这些部件的底层全部是寄存器堆、加法器、译码器,再往下就是逻辑门。
你没看错,整台计算机的根基,就是与门、或门、非门、异或门这几种再简单不过的东西。它们的组合能构成加法器,加法器加上寄存器能构成运算器,运算器加上控制器和存储器,就是一台完整的计算机。
这是我强烈建议你跟着走一遍手搓路线的第一个理由:倒着学只会背概念,顺着做才能真正通透。我自己当年学计算机组成原理时,总觉得“控制信号”是个玄学,直到亲手在仿真软件里搭出单总线CPU,看到每个时钟沿控制器发出的那串1和0,才发现原来译码、执行本来就是一张查表流程。
1.2 四个阶段,把大目标拆成能落地的小任务
一台可以运行的计算机,可以粗略拆成四个递进的实现阶段。我按这个顺序推进,每个阶段都设了明确的产出物,做完一个阶段再往下走,心里特别有底。
- 阶段一:逻辑门与组合逻辑。先搞清楚每种门的符号、真值表、布尔表达式,然后用它们搭出多路选择器、译码器、加法器。产出物:能算1位加法、能选择数据通路的组合电路。
- 阶段二:时序逻辑与存储单元。从D触发器开始做寄存器、计数器、寄存器堆。产出物:能保存数据、能在时钟驱动下工作的存储电路。
- 阶段三:数据通路与运算器。把寄存器堆、ALU、PC、存储器用总线连起来。产出物:能按要求读取指令、计算数据、写回结果的核心部件。
- 阶段四:控制器与整机运行。设计指令集,写微程序或者用硬布线状态机生成各部件控制信号。产出物:能自动执行一段程序并得到正确结果的完整CPU。
这个拆法也呼应了热词里最常见的“单总线CPU设计实验”“多周期MIPS CPU设计Logisim”——它们本质都是阶段三和阶段四的融合练习。
1.3 工具选型:为什么用 Logisim 这类仿真器
我见过不少人一上来就买FPGA开发板或者真芯片,不是说不行,但第一次做设计,我强烈建议先用仿真软件。原因特别现实:
- 调试成本低。真芯片出了问题,你得拿示波器量、拿逻辑分析仪抓,初学者大概率直接懵。仿真器可以随时暂停、单步、观察每根线的电平,出错了能回退重连。
- 元件库齐全。RAM、寄存器、ALU、多路选择器甚至内置的ROM都有,拉出来就能用,能让你把精力集中在架构设计而不是搭门电路上。
- 反馈直观。一眼看到数据从哪个部件流到哪个部件,控制信号怎么驱动总线,这种可视化的理解效率是任何文字描述都比不了的。
我用的是 Logisim-evolution 这个分支版本,相比老版 Logisim 多了不少修复和特性,支持子电路、隧道标签、时钟源频率配置,手搓CPU刚刚好。
2. 打好地基:8种逻辑门与布尔代数
2.1 八种逻辑门的符号、真值表与数学表达式
这一节算是最基础的知识,但我还是想按自己的理解重新过一遍。因为后面你搭的一切,从加法器到ALU,本质上只是在反复组合这几种门的运算规则。
为了方便对照,我把八种常见逻辑门整理成了表格。这里的符号我按教科书里最常用的表述来写:与门是“D型门”的矩形符号,逻辑表达式用乘号表示与,加号表示或,上划线表示非。
| 门名称 | 英文缩写 | 布尔表达式 | 真值表要点(A、B为输入,Y为输出) | 典型用途 |
|---|---|---|---|---|
| 与门 | AND | Y = A·B | 只有A、B都为1,Y才为1 | 使能控制、位屏蔽 |
| 或门 | OR | Y = A+B | A、B任一为1,Y就是1 | 信号合并、事件判断 |
| 非门 | NOT | Y = ¬A | 输入取反 | 取反操作、逻辑求补 |
| 与非门 | NAND | Y = ¬(A·B) | 与门结果再取反 | 万能门,可搭出任何逻辑 |
| 或非门 | NOR | Y = ¬(A+B) | 或门结果再取反 | 万能门,RS触发器的核心 |
| 异或门 | XOR | Y = A⊕B | A、B不同则Y为1,相同则为0 | 加法、比较、校验 |
| 同或门 | XNOR | Y = ¬(A⊕B) | A、B相同则Y为1,不同则为0 | 等值比较器 |
| 缓冲器 | Buffer | Y = A | 输出等于输入,但起隔离/放大作用 | 驱动增强、总线隔离 |
画符号的时候要注意,同一种门有两种表示法:一种是矩形符号,国家标准和IEEE都推荐,输入在左、输出在右,门类型写在框内;另一种是“烟斗形”符号,很多老教材和图里还在用,看老资料时容易混淆,但只要认准真值表就不会乱。
举个立刻能用的例子:1位半加器就是把异或门和与门同时接在A、B两个输入上。异或门的输出就是“和”,与门的输出就是“进位”。这就是组合逻辑最简单的应用,后面全加器、加法器都是这么扩出来的。
2.2 逻辑门背后的物理实现与三态门
了解逻辑门,不可能绕开物理层。现实中绝大多数逻辑门用CMOS工艺实现,CMOS管分PMOS和NMOS两类,一个用来拉高电平、一个用来拉低电平,配合起来就能实现反相器。你再把反相器组合起来,就能得到与非门、或非门、传输门等等。
这里最关键的物理概念是“高阻态”。普通逻辑门的输出只有0和1两种状态,而三态门额外提供第三种状态——高阻态(Z)。高阻态的意思是输出端对外相当于断路,不会主动驱动总线。这个特性在单总线CPU里太重要了,因为多个部件要共享同一组数据线,某时刻只能允许一个部件把数据放到总线上,其他部件必须处于高阻态放行,否则两路信号打架,仿真器直接报红色冲突,实物就直接烧毁。
2.3 组合逻辑设计的小技巧
动手设计组合逻辑时,有几个心得特别值钱:
- 优先用与非门或或非门当万能基础件。NAND门自身就是“完备”的,任何布尔函数都能只用与非门搭出来。有时候用库里最方便的门去化简,比硬凑某种门更省事。
- MUX多路选择器是数据通路的开关。一个2选1MUX本质上就是两个与门加一个或门,用一个选择信号决定哪路输入通过。CPU里的数据通路到处是MUX,比如PC是选下一条地址还是选跳转地址。
- 德摩根定律要刻在脑子里。
¬(A·B) = ¬A + ¬B,这条定律在做逻辑化简时高频出现,尤其是把“与非”翻译成“或非”的时候,能帮你省下一大片门电路。
3. 从组合逻辑到“会记忆”的电路:触发器与寄存器
3.1 组合逻辑的最大短板:记不住状态
前面的加法器、选择器,输入一变输出立刻跟着变,这叫组合逻辑。可CPU不能光靠组合逻辑工作——它需要在执行完一条指令后记住当前状态,到下一条指令时再更新。存储能力是CPU和纯粹计算器的本质区别。
能“记住”的最小单元是触发器。最简单的版本是RS触发器,它可以由两个或非门或者两个与非门交叉连接构成。RS触发器有两个输入,置位S和复位R,输出端Q和反相端Q,当S和R同时为0时,它能保持原状态不变;当输入改变时,输出翻转并锁存。但RS触发器有个输入组合(S=1且R=1)不允许出现,使用时需要特别小心。
改进后的D触发器就优雅得多:一个时钟输入Clk,一个数据输入D。时钟有效沿到来时,输出更新为当前D的值,其余时间输出保持不动。这个“边沿触发”的特性是数字电路的分水岭,它让所有部件有了统一的节拍,就像乐队有了指挥。
3.2 从D触发器到寄存器、寄存器堆和PC
n位寄存器就是n个D触发器并联,共享同一个时钟,每个触发器存一位。给寄存器加上“写使能”信号后,写使能为1且时钟沿到来时才更新,否则保持不变,这在CPU里用来控制哪些寄存器该写入数据。
寄存器堆则是多个寄存器组成的小型存储阵列,一般有两个读端口和一个写端口。为什么需要多端口?因为一条指令往往要同时读出两个源操作数送给ALU。你如果自己用触发器搭寄存器堆,会发现这个“同时读两个数”的需求直接决定了内部线路的组织方式。
PC(程序计数器)本质是一个计数器,每个时钟周期加1,让PC指向下一条指令。遇到跳转指令时,把目标地址直接装入PC。这里有个小设计:如果用加法器实现PC+1,就可以省掉专门的计数器芯片,这也是单总线CPU实验里最常见的做法。
3.3 时序逻辑设计中的高频踩坑点
我自己第一次搭时序电路时被坑得最惨的就是时钟处理。你可能会遇到这几个典型问题:
- 电平触发和边沿触发搞混。如果使用高电平锁存,在高电平期间输入的任何抖动都会进入寄存器,最后数据像喝醉酒一样乱跳。遇到这种情况,检查时钟端口属性是否是“上升沿触发”。
- 所有寄存器必须用同一个时钟源。有人图方便给不同模块单独加时钟脉冲,结果各部件节拍不一致,整个CPU就像几个不同步的人在合唱,怎么调都乱。
- 别忘了复位信号。PC初值如果不初始化为0,而是随机值,程序一启动就会乱跑。设计电路时一定要加一个全局复位,并且保证上电一瞬间PC、寄存器堆、控制器状态机都清零。
4. 算得动也要算得对:ALU与加法器的设计
4.1 从半加器到全加器,再到并行加法器
加法器是ALU的核心。1位半加器只能处理两个一位数相加,结果有“和”与“进位”。但你处理多位二进制数时,低位的进位必须传给高位,所以完整的一位加法器实际上是全加器:输入是A、B,还有来自低位的进位Cin,输出是和S以及向高位的进位Cout。
全加器的布尔表达式是:
S = A ⊕ B ⊕ Cin Cout = (A·B) + (Cin·(A⊕B))多位加法器怎么搭?最简单的串行加法器就是n个全加器从低到高一字排开,前一级的Cout接到后一级的Cin。这种设计虽然简单直接,但走一条进位链下去,位数越长延迟越高,速度提不上来。所以才有了超前进位加法器,利用逻辑公式把进位并行计算出来,这也是现代CPU里高频加法器的原型。手搓CPU时用串行加法器就好,教学目的完全够用。
4.2 ALU的架构与一个多功能ALU实例
ALU是CPU的“计算心脏”,它本质上是一个能执行多种运算的组合逻辑电路,输入两个操作数A和B,通过控制信号F选择运算模式,输出结果Result,还附带几个标志位。
- Z(Zero):结果为0时置1。
- N(Negative):结果最高位为负时置1。
- C(Carry):无符号加法产生进位时置1。
- V(Overflow):有符号数加减法发生溢出时置1。
一个公共教学版ALU可以支持八种运算:加减、与、或、异或、非、左右移位、直通。内部实现一般是先写一个真值表,然后用译码器或MUX选出不同运算结果中的一种。比如可以做一个4位控制信号,分别控制加法器、与门、或门、异或门的输出,最后用一个多路选择器根据高位控制线选通。
4.3 设计ALU时最容易犯的错
标志位的判断是我认为整个ALU设计里最烦的部分。Z标志你得用一个大的或非门把所有结果位并起来,结果全是0时才输出1。V溢出标志的更严格,必须检查两个操作数符号和结果符号。溢出和进位是完全不同的概念:两个正数相加结果为负,说明是符号数溢出;最低位之外多出来一位,那是无符号数溢出。很多人在这里开始犯迷糊,我建议先只做无符号运算,跑通后再加有符号判断。
5. 存储与总线:CPU和内存之间的“信息高速公路”
5.1 存储器的结构:地址线、数据线、控制线
存储器已经不是逻辑门这个级别的部件了,但它是CPU必不可少的“外挂大脑”。RAM的核心结构其实是一个“巨大无比”的寄存器阵列,通过地址译码器选中某一行,然后由读写控制决定把这一行读出到数据线上,或者把数据线上的值写入这一行。
我理解存储器连接要点时,习惯把它简化成三组线:
- 地址线(Address bus):CPU给出想访问的单元编号,宽度决定能访问多少地址。比如16位地址能访问64K地址空间。
- 数据线(Data bus):用于传数据,双向。8位数据线一次能传一个字节。
- 控制线(Control bus):读信号、写信号、时钟信号等,控制什么时候读、什么时候写。
这里有个特别容易混淆的点:地址线是单向的,CPU发出去,RAM接收;数据线是双向的,读的时候RAM往CPU传,写的时候CPU往RAM传。所以数据线必须通过三态门接入总线,否则读写状态冲突时,两路输出会硬碰硬。
5.2 单总线结构的组织原理
单总线CPU的核心特征是:所有功能部件的输入输出都连接到一组公共数据总线上,数据在一组线上传来传去。举个直观的画面:寄存器堆、PC、ALU、内存、指令寄存器、地址寄存器,它们各自通过三态门挂在同一条总线上。
这种结构的好处是极其简洁清晰。一次总线操作只允许一个部件把数据放到总线上,而其余部件必须把输出置为高阻态。比如要把PC的值送给MAR,控制器就打开PC输出端的三态门,同时确保其他设备全部“闭口”,数据就顺顺利利流进了MAR。单总线的代价也很明显:同一时刻只能做一个数据传递,多条数据流必须串行分时进行,所以一个多周期指令会被拆成好几个微步骤执行,但教学演示恰恰需要这种“慢而清晰”。
5.3 连接CPU和存储器时的接口细节
在实际连接中,给存储器加上读使能和写使能引脚后,要注意时序顺序:先给地址,等地址稳定后再给读使能,让数据总线上的数据有足够时间稳定出来。如果读和写同时拉高,或者地址和数据线的方向没控制好,结果就是读出垃圾数据或者写入错位。
另外我习惯单独做一个MAR寄存器来锁存地址。为什么不能直接把PC或寄存器堆的输出接到RAM的地址线上?因为单总线上同一时刻只允许一个数据源,如果PC一边输出到地址线,一边还要往总线上发下一条指令地址,总线就冲突了。用一个MAR先把地址从总线锁存下来,后面的RAM地址读取就用MAR里的稳定状态,CPU的其他部件可以继续忙别的事。这是理解和设计单总线CPU时非常关键的一环。
6. 指挥中心:控制器与指令的执行流程
6.1 指令从哪儿来:取指、译码、执行
CPU能“思考问题”,靠的是按指令办事。这里的思考本质是查表加搬运:从内存里取一条指令,解析出操作码和操作数,按操作码对应的流程去打开各种三态门、置位所用寄存器、触发ALU计算。
最基本的执行流程是:
- 取指:PC把地址通过总线送到MAR,RAM根据MAR地址读出指令,指令进入IR(指令寄存器)。
- 译码:控制逻辑分析IR里的操作码,确定这是一条加法指令还是跳转指令,然后生成对应的控制信号。
- 执行:控制信号驱动数据通路完成运算,比如把寄存器A和寄存器B送入ALU,结果写回目标寄存器,PC加1或跳转。
这个过程在全周期里重复执行,就是所谓“CPU的思考”。你看着它傻乎乎地按步执行,会觉得一点都不神秘,但它确实就是现代计算机最底层的模样。
6.2 硬布线控制器和微程序控制器的思路对比
控制器本身也有两种主流实现路线,手搓的时候二选一。
硬布线控制器就是用一个有限状态机,用逻辑门直接生成控制信号。状态转换用组合逻辑和触发器实现,速度很快,但设计起来复杂,稍改指令集就要重新改逻辑电路,典型应用是RISC和精简架构。
微程序控制器换了个思路:把每条指令对应的控制信号序列预先写进一个叫控制存储器的ROM里,每条控制信号序列叫“微指令”。CPU执行指令时,不过是从ROM里逐条取出微指令,再把微指令的各个位直接接到各个控制端上。这种实现方式灵活得多,新增指令只需要往ROM里多写几行数据,也是Logisim单总线CPU实验里最常用的一种。
两种思路各有利弊:硬布线执行速度快,适合追求性能;微程序控制结构规整,适合教学、适合扩展到复杂指令。我建议第一次做单总线CPU用微程序,因为逻辑门设计状态机实在太烧脑。
6.3 单总线CPU微程序控制器的关键细节
微程序控制器有三个核心字段,理解它们就掌握了大半:
- 控制字段:每一位对应一个控制信号,比如“寄存器堆写使能”“PC加载”“ALU操作选择”“总线数据源选择”。这一串位就是真正的“命令”。
- 下地址字段:指定下一条微指令在控制ROM中的地址,用于实现顺序执行或跳转。
- 判别条件字段:决定是否根据外部状态(比如ALU的Z标志或指令操作码)来改变下一条微指令的地址。
条件判别测试逻辑是这里最容易犯晕的地方。它的本质是回答一个问题:下一条微指令地址是怎么算出来的?它可以是顺序地址+1,可以直接跳转到下地址,也可以根据某个条件位的值进行选择。比如指令译码后,要根据操作码跳到对应执行流程的起始微地址,就需要用操作码作为索引去查一个散转表,选出正确的入口。
我用一个简单的加法指令ADD $R3, $R1, $R2来演示微程序流程:
1. PC -> MAR ; 把PC值送到MAR锁存 2. RAM -> IR ; 读出指令到IR 3. PC = PC + 1 ; PC加1,准备取下一条指令 4. 译码操作码 ; 根据IR高几位查表,跳到ADDroutine 5. 寄存器堆读出R1、R2 ; 把两个源操作数送到ALU输入锁存器 6. ALU加运算 ; 执行加法 7. ALU结果写入R3 ; 写回目标寄存器 8. 回到取指流程 ; 开始下一条指令这些微步骤在微程序ROM里对应着8条微指令,每条微指令的控制字段负责打开对应的三态门、使能对应的写信号。这一套跑通了,你真的会觉得CPU的“智能”不过是一张精心设计的查表。
7. 动手实操:用Logisim从零搭建一个可跑程序的单总线MIPS CPU
7.1 项目规划与指令集选择
我建议一条指令的宽度和寄存器都用8位先跑通,指令集只选最基础的五类八条:加法、减法、与、或、装数(从内存读)、存数(写内存)、相等跳转、无条件跳转。
编写指令集时,我给每类操作分配了不同操作码。例如:
- 0000:ADD Rd, Rs, Rt
- 0001:SUB Rd, Rs, Rt
- 0010:AND Rd, Rs, Rt
- 0011:OR Rd, Rs, Rt
- 0100:LW Rt, Offset(Rs)
- 0101:SW Rt, Offset(Rs)
- 0110:BEQ Rs, Rt, Offset
- 0111:J Target
这个精简指令集足够写一段有意义的程序了。我在Logisim里建了主电路和几个子电路模块,包括寄存器堆(RegFile)、ALU、控制ROM(ControlStore)、总线接口等,每一层单独独立调试,最后再拼装。
7.2 关键连通步骤与中间检查
搭建不是一次性把所有部件拖进去,而是分部件做完再集成。我按下面顺序来,每个环节都要有测试基准:
- 做寄存器堆:先搭4个8位寄存器,验证写使能有效时,寄存器能在时钟沿更新,读端口数据正确。
- 做ALU:写好加减与或四个运算,验证控制信号分别选通时结果正确,Z标志能输出。
- 做PC与MAR:确认PC初始化为0,每个时钟沿加1,能从总线上装载跳转地址。
- 接单总线:把寄存器堆输出、ALU输出、PC输出、内存数据输出分别通过三态门挂到总线上,确保某个时刻只有一个源输出驱动总线,否则线会变红。
- 写控制ROM:根据前面微程序表,把每一条微指令的二进制控制字段填进ROM的十六进制值里。
- 联调:把控制器输出的控制信号连到各个使能端,用模拟时钟跑程序。
这里我想特别强调使用Tunnel隧道标签。单总线CPU的控制信号非常多,如果全部拉线连到各个使能,到最后会乱成一团。用Tunnel标签给它命名,比如PC_LOAD、MEM_WR、BUS_SRC等,再在部件引脚上放相同名字的Tunnel,信号就对上了,线路图干净了不止一个量级。
7.3 写一段测试程序并跑通
我把一段“把内存两个数相加存回内存”的汇编程序翻译成指令机器码,先存放在ROM或RAM里作为测试程序:
LW R1, 0(R2) ; 从内存地址R2处读取第一个数到R1 LW R3, 4(R2) ; 从内存地址R2+4处读取第二个数到R3 ADD R1, R1, R3 ; R1 = R1 + R3 SW R1, 8(R2) ; 把结果存到内存地址R2+8处 J HALT ; 停机通过单步执行,我可以看到每个周期总线上的数据、PC值、微指令控制信号。当我看到第三步时总线上出现两个数相加后的结果,并且写回R1成功,那种“原来CPU就是这么算过来的”的感觉,确实只有亲手搭一遍才能体会。
8. 常见问题排查与经验教训
8.1 仿真中的典型故障与排查思路
手搓CPU几乎不可能一次跑通,遇到过的问题五花八门,我把高频率的几个整理在下面,方便你照着排查。
| 表现 | 最可能的原因 | 排查方法 |
|---|---|---|
| 总线出现红色或冲突 | 多个源同时驱动总线 | 检查三态门使能信号,确认同一时刻只有一路源有效 |
| PC不乱跳但指令执行乱 | IR锁存时机不对,时钟沿控制不当 | 检查IR的时钟相位是否和其他部件一致 |
| 运算结果差1或者多1 | 进位链处理错误,或者PC提前加了1 | 画一个小例子手动推一步,检查每个微步骤的顺序 |
| 控制信号一直为0 | 控制ROM没写入,或者微地址没归零 | 查看控制ROM的初始数据,确保译码入口正确 |
| 程序只执行一遍 | 没有循环或停机的跳转目标写错 | 检查BEQ/J指令的目标地址编码 |
排查工具上,我强烈建议开着Logisim的“时钟单步”功能。一开始不要用连续高速时钟,而是手动按一次时钟走一步,盯着关键信号走完一条指令。你会在这种慢速推演中,发现至少一半问题都出在某一个控制信号没在正确节拍上。
8.2 从仿真到FPGA与物理实现的补充
仿真跑通后,如果你还想更进一步,把设计烧到FPGA上是一个很好的延伸。但要注意几个变化点:
- 仿真里用Tunnel传递的逻辑关系要换成物理引脚的连接,FPGA开发工具会帮你自动布线,但约束文件必须写清楚时钟、复位和总线冲突的处理。
- 时钟频率要受时序约束约束。仿真中时钟随便设都没问题,但FPGA有实际的建立时间和保持时间要求,组合逻辑延迟太长会导致时序违例,实际运行就会随机出错。
- 三态门在FPGA里通常对应输出使能信号,千万不要随便用代码写高阻态赋值,因为有些内部总线结构对高阻态的映射并不高效。
如果你连FPGA的板子都准备好了,还可以把Logisim工程手动用Verilog或VHDL重写一遍,这反而是学习硬件描述语言的好素材。
8.3 给新手的不定期建议
最后再分享几个我踩过坑后总结出来的经验:
- 一次只改一个变量。改完某段控制逻辑后,先只测试跟它有关的一条指令,不要同时调三条不同指令的微代码。
- 把模块做小、分步验证。寄存器堆单独验证、ALU单独验证、PC单独验证,这些验证结果满意了,再谈整机联调。
- 把控制信号命名规范化。信号名一乱,排查时两眼一抹黑,我习惯用“部件_动作”的格式,比如PC_LOAD、REG_WR、MEM_RD。
- 保留一份能正常工作的中间版本。每次重大改动前先复制一个工程文件,万一改崩了还能回退,省得重头拼。
结尾:一点真实体会
说实话,这条路走下来,最大的收获不是“我会用Logisim”,而是彻底理解了一个道理:CPU尽管看起来无所不能,但它的“思考”本质上是一环扣一环的信号搬运和查表。所谓手搓CPU,真正锻炼的是把复杂问题拆碎、再在正确层级组合起来的能力。计算机组成原理里那些抽象名词,到了亲手点亮跑通的那一刻,全都有了具体的形象。如果你也在学的路上卡壳,不用急,从逻辑门开始,一步一步往上搭,等到看见自己写的程序在电路里算出正确答案,很多原先想不通的问题都会突然变得通透。按照这个路线走下去,你会慢慢发现,那台看起来遥不可及的计算机,其实比你想象的简单得多。