ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

从NAND到CPU:Turing Complete通关思路与全成就攻略

从NAND到CPU:Turing Complete通关思路与全成就攻略 最近一直在玩一个比较冷门但后劲很大的游戏——《Turing Complete》。如果你以为它只是个画逻辑门电路的解谜小游戏那看完这篇你可能得改改看法。2.1版本更新后我重新从第一关打到全成就收工整个过程下来最大的感受是这游戏比很多正经教材更适合讲清楚“计算机到底是怎么跑起来的”。我打算把自己这套通关思路、全成就路线、以及反复读档重来才悟出来的优化经验完整写出来。里面有大量针对具体关卡的“先做什么、后做什么、为什么要这么做”的拆解也有我踩过的坑。如果你正卡在某个节点或者想补全成就又不想无脑刷这篇应该能帮上忙。1. 2.1改版之后这个游戏真正该玩的不只是搭电路先说个结论《Turing Complete》表面上是一个个逻辑门搭建关卡但实际上它是一条从“与非门”一路走到“可编程CPU”的完整上升路径。2.1版本更新后我明显感觉关卡节奏和评价体系都有微调整体更偏向“引导玩家自己发现规律”而不是照着说明书拼积木。1.1 游戏到底在让你做什么从NAND到CPU的完整旅程游戏开局只给你一种门——与非门NAND。随后你要用NAND拼出非门、与门、或门、异或门再往上做半加器、全加器、加法器、选择器、解码器然后进入存储电路做锁存器、D触发器、寄存器、RAM最后开始设计指令集、写汇编、搭数据通路、实现取指-译码-执行一步一步逼近一颗真正能跑程序的CPU。这个过程特别像“把计算机组成原理这门课变成了一场足够上头的闯关游戏”。它不直接告诉你答案而是让输入输出定义摆在那你得自己想怎么用已有的元件满足需求。2.1版本对部分关卡的输入输出格式做了调整尤其在后半程的汇编关卡提示变得更少逼着你更早建立自己的思维框架。这其实是好事因为考试的时候没人给你列公式。1.2 2.1版本的变化以及为什么通关思路要跟着变作为老玩家2.1版本给我的整体感受是前期更友好后期更硬核。前几关对新手来说容错率更高UI的连线提示也优化了不再出现“线叠在一起看不清”这种事。但到了架构阶段新增或调整过的关卡对“字节数”的评价更严格也就是说你不再能靠堆一堆冗余指令过关还得考虑每条指令的编码长度和整体程序体积。这一点直接影响了通关思路前期关卡先把功能跑通不必追求最优门数理解原理最重要。中期存储与ALU关卡开始有意识地做模块化设计方便复用。后期架构关卡指令集要先做减法能用一条指令解决的事绝对不发明第二条。全成就阶段才需要回头优化单个关卡的门数、字节数、芯片面积等评价指标。所以这篇攻略我不会只按“每一关怎么搭”来写那样太啰嗦也没必要。我会按“不同阶段要建立的思维模式”来拆把卡住的人的共性瓶颈打开你自然知道后面每一关该怎么下笔。2. 数字电路阶段用最少逻辑门堆出最顺手的ALU数字电路部分覆盖了从逻辑门到运算单元的路径。很多玩家在前几关觉得轻松一到加法器和ALU就卡住本质原因是你还在“试线路”而不是“做设计”。2.1 前几关的逻辑门基本功不建议跳着做从NAND拼NOT、AND、OR、XOR这几关看起来没难度但这里是唯一一次你可以零成本建立“门级直觉”的机会。我见过不少玩家直接抄了最优解就往下走结果后面搭全加器时看不懂进位是怎么被多条线路交织出来的。我的建议是哪怕你已经知道答案也亲手用NAND至少拼一次XOR。因为XOR是后续加法器的核心也是你在《Turing Complete》里第一次需要“斜着看真值表”的关卡。等你发现XOR其实可以从“两个输入不一样时为1”这个语义出发来设计而非死记电路结构时你就算入门了。在这个阶段我一般做的事是每关先列真值表确认输入和输出的每一位对应关系。在脑子里过一遍“如果我是数据我会走哪条线”。搭完之后把方案保存一份再做一次“精简版”不影响进度。2.2 半加器到全加器进位链是如何拖慢你的半加器很容易一个XOR搞定本位一个AND搞定进位。但全加器开始出现第三个输入——来自低位的进位Cin。从这时起你需要把“一位加法”看成“三位输入、两位输出”的组合逻辑。全加器的标准搭法是用两个半加器级联进位用OR合并。这个方案门数不是最少的但结构最清晰适合第一遍理解。你真正要警惕的是后续多bit加法器如果把每个全加器的进位输出串接到下一级的进位输入位数多了以后进位要从最低位一直“冒泡”到最高位信号延迟非常大。在游戏里这个延迟不会让电脑蓝屏但会影响你在后续更高阶关卡里做“时序优化”时的思路。因为2.1版本新增的关卡里有明确的“延迟评价”我后来为了压低延迟把行波进位结构换成了更快的进位选择结构。做法不复杂对每个4bit小组同时计算“进位为0”和“进位为1”两种情况的结果再用实际进位信号选过去。门数多了但延迟肉眼可见地下降了。这类“面积换速度”的取舍是这个游戏后半程的核心主题。你越早理解它后面CPU关卡越顺手。2.3 ALU的设计顺序先定输入输出再动手搭门ALU关卡是我见过玩家劝退率最高的地方之一。因为它的功能太多加、减、与、或、移位、比较……一股脑全丢给你很多人一上来就想用一堆逻辑门把所有功能平行搭出来结果线路乱成一团。我的解法是先把顶层输入输出定死再确定每个功能对应的控制信号最后才画内部电路。举个例子减法可以做成“加法器 取反 进位输入置1”也就是用补码算减法。这样你不需要单独为减法设计一套电路只需要控制信号能选择“是否对B取反”“最低位进位是否为1”。比较大小也可以复用减法器看结果的符号位和零标志位就能判断大于、小于还是相等。所以ALU的搭建顺序实际是确定输入A、B、控制信号OP。确定输出结果Result、零标志Zero、进位/溢出标志。设计控制信号编码比如00加法、01减法、10与、11或。用多路选择器把各功能模块的输出汇聚到Result总线上。再基于结果生成标志位。按照这个顺序哪怕门多也乱不到哪去。我当时把整个ALU模块命名成子电路后后面CPU搭建一下子就清爽了。建议你也尽早养成“模块化”的习惯这在2.1版本的关卡评价里也是加分项。3. 存储与状态锁存器之后的思维切换如果说ALU阶段还是“组合逻辑”那从锁存器开始游戏就进入了另一个维度拥有状态的电路。这是很多玩家从“能玩”到“玩不明白”的分水岭。3.1 锁存器第一次遇到“反馈”锁存器Latch关卡第一次引入了“反馈回路”一个门的输出连回自己的输入。很多第一次玩的人懵了这还能叫电路吗但它恰恰是存储的起点。SR锁存器靠两个或非门或与非门交叉耦合实现“置位/复位”的记忆能力。你在游戏里搭出来之后试着拨动输入会发现输出不是立刻变化的而是被“记住”了。这种打破直觉的体验其实比教科书上任何一句“锁存器具有记忆功能”都管用。但我要提醒一点SR锁存器只是一个入门概念。真正在《Turing Complete》里大量使用的是边沿触发的D触发器也就是在时钟上升沿或下降沿才采样输入。不要在SR锁存器上过度纠结“怎么把输入变成更合理”因为工程师自己也觉得电平触发方式太容易受干扰后面D触发器才是主角。3.2 D触发器和寄存器时序逻辑的节拍感D触发器可以理解成一个“听话的存储位”时钟边沿到来时把D输入锁存到Q输出。游戏里的寄存器通常是多个D触发器并排共享同一个时钟信号从而一次存下一整组bit。这个阶段最容易犯的错误是把时钟想象成“电源”觉得没有时钟信号电路就不工作。实际上时钟更像是一支乐队的指挥它不产生任何数据只是告诉每个乐手“现在该演奏了”。你要做的是保证数据在时钟边沿到来的那一刻是稳定的这就引出了“建立时间”“保持时间”的概念。游戏里不会用术语考你但当你发现某条线路的数据总是不对时十有八九是“该同步的信号没有同步”。我自己的排查套路是这样的先从输出往回追看哪个寄存器的输出和预期不一致。检查它的时钟输入是否和别的寄存器同源。再检查它的数据输入是不是“异步变化的组合逻辑输出”。如果数据来自复杂组合逻辑中间必须插一级寄存打拍否则时序一定出问题。3.3 RAM与寻址为什么要按字节组织RAM关卡会把“存储”从几个寄存器扩大到一整个内存阵列。你可能已经会用解码器选地址、用读写信号控制数据进出但真正让你头疼的是“如何高效地组织地址和数据总线”。我记得自己在做RAM时一直纠结地址是给word地址还是byte地址游戏里有很多关卡用字节寻址意味着一个地址对应8位数据。如果你把16位数据线接到两个字节上就得处理地址对齐问题。这个设计问题其实很现实CPU设计里天天遇到。我的建议是在这一阶段就养成“按字节寻址按字读取”的习惯。默认情况下一个地址一个字节如果CPU数据总线是16位那就连续取两个字节拼成一个字。这个思维一旦建立后面做指令取指、立即数读取都会顺畅很多。4. CPU架构阶段面对真实工程决策到这一步你已经拥有了运算器、寄存器、内存接下来就是把它们用线连起来再加一套控制逻辑让它们按指令自动运行。2.1版本在这一段加入了更多“方案权衡”的内容没有标准答案只有更好或更省的选择。4.1 指令集设计少即是多的RISC思路很多玩家第一次设计指令集时恨不得发明几十条指令觉得这样功能强大。结果后面实现控制逻辑时痛不欲生每条指令都要专门的译码和通路越写越乱。我自己后来走了典型的RISC思路只保留基础指令LOAD、STORE、ADD、SUB、AND、OR、JUMP、分支跳转等。相同类别的指令共用一套数据通路只通过ALU操作码区分。存储访问一律通过LOAD/STORE完成不搞复杂的寻址模式。指令长度固定方便取指和译码。减少指令数量不是偷懒而是在降低整个CPU的复杂度。你会在搭建过程中发现多一条指令意味着多好几个控制信号、多好几个MUX选路最终导致你布线时多花几个小时。我第二遍通关时刻意把指令集控制在10条以内整机搭建速度反而快了非常多。4.2 数据通路总线、多路选择器、立即数数据通路是CPU的“公路网”。指令从内存取出来、送到寄存器堆、进ALU、再写回每一步都需要选对来源和去向。在游戏里你没法画抽象总线只能一根线一根线连但可以用“窄总线”和宽总线来模拟一组线的批量传输。我比较推荐先把数据通路画在纸上或脑子里再动手连线。一个常规的取指–执行流程大概是这样PC程序计数器把地址送到内存。内存返回指令。指令被拆成操作码和操作数/立即数。操作码进入控制单元控制单元生成一堆选择信号。操作数送往寄存器堆或立即数扩展器。ALU对读出的数据做运算。结果按需写回寄存器或送到内存做读写。你在游戏里要做的就是把这条路径上的每一个“岔路口”用MUX接好。每当你发现某个数据流不到想去的位置先别急着加线停下来看看是不是MUX的选择信号没拉对。这种情况我至少遇到十次。4.3 控制逻辑硬布线与微码控制逻辑是最考验耐心的一部分。它接收指令操作码输出各部件控制信号。你可以用一大片逻辑门做硬布线控制也可以做一个简单的微码ROM每个操作码对应一行“微码”每一行里每一位直接控制一条信号线。从全成就角度讲我建议两个方案都尝试一下。硬布线会让你真正明白“译码即逻辑”微码则让你体会“用存储代替逻辑”。多数关卡里微码方案实现更快、出错好调试但硬布线方案门数往往更低。二者没有绝对优劣关键是你得能快速切换思路。我自己常用“先把微码表列出来再决定用ROM还是门逻辑实现”的方式。因为微码表本质上就是真值表从真值表到门电路是基本功列清楚了再动手比边想边接要高效得多。4.4 汇编通关用指令写程序的思维转换到了汇编关卡你已经不是在“搭电路”而是在“写程序”。区别在于这些程序跑在你亲手设计的CPU上。这种自己动手做出编译器/汇编器的链路体验相当奇妙。第一次编写汇编程序时我总本能地想把内存操作放在一条指令里完成比如“ADD [R1], R2”结果发现我的CPU根本没支持这种寻址。后来我只能老老实实地先从内存LOAD到临时寄存器。用ALU计算。再STORE回内存。这种“先加一个寄存器中转”的习惯其实正是RISC的精髓。它看起来更啰嗦但每一条指令都很简单硬件实现容易频率能跑得更高。你在游戏里会明显感觉到用简单指令拼出的循环、数组访问、递归调用逻辑反而更清晰。5. 全成就达成顺序与隐藏条件拆解《Turing Complete》150多个关卡一步步体验下来成就系统也算一大乐趣。它不靠“玩得久”解锁而是逼你把每一关吃透。下面是我整理的成就路线尽量按“一遍通关就能顺手拿到大部分”的顺序来。5.1 通用成就类型与达成顺序建议根据我玩下来的经验成就大致可以分成四类流程成就推进到指定章节就算达成基本不会漏。评价成就某个关卡达到特定门数、字节数或延迟标准。全局成就比如“完成所有固定关卡”“集齐所有升级芯片”等。特殊玩法成就用一些非常规方式通关特定关卡比如“只用一个加法器实现所有运算”或“用二进制视角进入某关键关卡”这类挑战。如果你是一周目我的建议是不要急着刷评价成就。先把流程成就拿完因为你只有在掌握全部工具后才知道哪些优化思路是可行的。我第一遍玩时很多关卡的门数评价是“B”但我完全不知道还能怎么省等看完后面关卡的组件后再回来想才发现“哦原来可以用结果寄存器直接兼当输入源”。比较推荐的成就路线是第一遍通关全流程解锁所有工具和关卡顺手拿流程成就。第二遍挑自己最熟的关卡按评价标准把门数/字节数压到成就要求。第三遍针对特殊玩法成就按提示重做对应关卡。最后补齐所有隐藏成就。5.2 最优解强迫症门数/字节成就怎么刷刷门数成就要点不在于“少设计功能”而在于“复用已有结构”。比如你已经有了一个支持AND和OR的ALU再想支持XOR没必要单独加一个XOR电路可以用AND/OR/一些选择器组合出来。其实很多功能模块之间绝大多数输出都是可复用的你要做的是把共同部分抽取成同一块电路再用MUX选右端不同的部分输出。字节数成就的刷法不同它关注的是你写的程序在内存里占多少字节。想要体积小核心不是“用更多指令把事做完”而是“尽量减少立即数和大地址访问”。比如常见的循环尽量用寄存器保存循环变量而不是每次从内存读。还有就是把常用数据集中放在低地址让指令编码更短。这和我们平时写代码优化很不一样因为CPU和内存布局都掌握在你手里你可以设计出最“顺”的布局。我记得自己刷某个关卡字节成就时反复重写程序十几遍最后把两个子函数压成了一个只改了一个过程中的跳转地址整个程序体积直接小了近三分之一。那种快乐犹如下棋时突然发现一步妙手。5.3 隐藏成就与自定制关卡隐藏成就一般和“用预料之外的方式解决问题”有关。比如有些关卡你只需要做一位加法但如果用更宽的处理单元也照样能过说不定就跳出隐藏成就。另一类隐藏成就在自由搭建模式里比如完成某个给定模板的挑战或者在其他玩家上传的方案里学习并做出来。我的建议是全成就最后阶段别硬猜多看看社区。2.1版本更新后有玩家做了很多“极致优化”题它们不算主线但用最小规模完成某一特定任务的成就感极强。如果你在成就列表看到类似“在某一关使用少于XX个门”的描述不要急着整个重来先想一想哪种运算可以“一笔带过”。我一直觉得《Turing Complete》的隐藏成就更像是设计者给玩家的彩蛋它不是让你照抄而是让你发现自己已经能灵活运用前面学的一切了。6. 几个我踩过、而且大概率你也会踩的坑最后这段不是攻略是血泪史。如果你刚开始玩这些坑能躲一个是一个。6.1 布线美观与门数优化的矛盾我第一遍做复杂关卡时总觉得线路越规整越好横平竖直为了不交叉宁可用更多门。但后来发现这种“美观优先”的心态会害你多花很多不必要的逻辑门。游戏评价只看硬指标不看美观。你要学会接受杂乱但功能正确的连接先保证能过再考虑优化。6.2 信号竞争问题在时序电路里同一个信号驱动不同输入端时路径长短会导致信号到达时间不一致。游戏对此模拟得不算复杂但当出现“有时能存对有时存错”的诡异现象时基本就是信号竞争。我当时排查了很久最后发现是一条组合逻辑的输出直接接进了寄存器时钟端。改成由统一时钟驱动后问题就消失了。6.3 存档方案过于单一这游戏允许你在每关保存多个方案。强烈建议每完成一个重要功能就另存一次。我在优化ALU时原本的方案其实已经过了但我手贱想再压一个门结果越改越乱最后还忘了原本能过的版本只能重搭。从那以后我每步优化前都会复制一份绝不直接用原方案改。6.4 卡关时的思路转换如果搭了两小时还没过别硬顶。倒杯水去看看后面关卡的名字或者回看之前几关自己存的方案。很多时候问题不在于“不够努力”而是你还在用旧的思维框架。比如我做多周期CPU卡了整整一个周末怎么都觉得控制信号写不完后来发现是因为我一直想让一个时钟周期干太多事愣是把单周期硬做成多周期太累了。跳出来重新划分周期后立刻通顺。《Turing Complete》最让我上瘾的地方不是通关那一刻的“叮”一声而是你在自己搭出来的机器上跑通任意一段程序时忽然理解了为什么教科书上总说“程序就是状态机”。这一遍2.1版本重打我特意每一关都自己先做一遍再对比最佳方案收获比第一次通关大得多。如果时间允许强烈建议你也试试这种“先自己写、再抄作业”的玩法。它会让你不仅玩懂这个游戏还能看明白身边每一台电脑。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进