完整教程)
1. 项目概述与整体设计思路1.1 4位先行进位加法器到底要解决什么问题如果你是计算机组成原理或者数字逻辑课程的学生大概率绕不过加法器这一关。很多教材先讲行波进位加法器也就是把4个全加器串联起来低位产生的进位慢慢往高位移。这种接法实现起来非常简单但有个让人头疼的问题高位必须等低位的进位算完才能工作4位还好要是扩展到32位、64位延迟会让人绝望。先行进位加法器英文叫Carry Look-Ahead Adder简称CLA核心思路就是“不等进位慢慢传”而是直接根据输入信号把每一位的进位并行算出来。听起来很玄乎其实背后的数学推导并不复杂核心就两个信号——生成信号G和传播信号P。这里我拿生活场景打个比方行波进位就像排队传话第一个人说给第二个第二个再传给第三个传到最后一个人那边早就过了半天先行进位就像群发消息一个信号发出去大家同时收到速度自然快得多。本次项目要做的就是在Logisim里从零开始把4位先行进位加法器完整搭出来不用任何高级语言纯靠门电路实现最终连上输入输出验证加减法都能跑通。这套电路图放在Logisim里可以直接运行验证无误后你还可以继续扩展成8位、16位甚至为自己的单周期CPU设计打基础。1.2 为什么选Logisim作为搭建平台很多同学问过我加法器用Verilog写几行就能搞定为什么还要用Logisim这种图形化工具折腾半天。我的看法是如果你只是想要一个能用的电路Verilog确实快但如果你想把进位的产生逻辑彻底搞清楚Logisim的“看得见摸得着”是HDL语言无法替代的。Logisim有几个明显的优势第一拖拽式的布线方式让门级电路的真实连接关系一目了然哪个信号从哪个引脚进去、经过几级门延迟、从哪里出来全部可视化第二它自带LED、数码管、隧道标签、时钟源这些常用组件调试起来非常方便第三工程文件是纯XML格式和同学之间交流电路特别方便发给谁都能直接打开跑第四Logisim完全免费并且跨平台Windows、macOS、Linux都有对应版本实验室和自己电脑装同一个版本就行。当然Logisim的版本选择有个小坑官方原版和Logisim-Evolution也就是进化版在部分组件命名和导入导出上略有差别。我这次用的是Logisim-Evolution 3.8.0版本界面更现代并且自带中文字库显示对新手更友好。如果你手里是原版也不用担心下面的搭建步骤核心操作完全一致只是个别菜单名称略有差异。1.3 项目完成后的效果预览在正式动手之前先给你吃一颗定心丸明确最终要搭出来的东西长什么样。整个电路可以拆成三个层级来看第一层是输入部分两个4位二进制数A和B各接一组拨动开关或引脚第二层是核心运算部分包括4个全加器单元和一组先行进位生成电路这是整个工程最关键的模块第三层是输出部分4位结果S加上最高位进位C4用LED或数码管显示。除了最基本的加法功能这个电路还能做减法。利用补码运算规则减去一个数等于加上这个数的补码也就是把B的每一位取反再加1。只要在B的输入端加一排异或门再给最低位进位C0送一个控制信号就能实现A加B、A减B两种模式的切换。测试时会用几组典型的边界值比如1111加0001、0111加1000还有减法里可能出现的负数结果确保不同情况下电路输出都正确。2. 先行进位核心原理从真值表到逻辑表达式2.1 单bit全加器与G、P信号的含义进入实操之前我建议先把理论基础夯实。很多人在Logisim里搭电路搭得飞快但遇到进位错误就不知道从哪里排查根本原因就是G和P这两个信号的含义没有吃透。回顾一下全加器的真值表输入有三个本位的两个加数Ai、Bi以及来自低位的进位Ci输出有两个本位和Si、向高位的进位Ci1。Si的表达式很简单就是三个输入做异或Si Ai ⊕ Bi ⊕ Ci。真正重要的是进位的表达式我们写成Ci1 Ai·Bi (Ai⊕Bi)·Ci如果你盯着这个式子看一会儿会发现问题可以拆成两个部分。第一部分Ai·Bi意思是只要Ai和Bi同时为1不管低位进位是什么这一位都必然会产生进位给下一位这叫“生成进位”记作Gi Ai·Bi。第二部分(Ai⊕Bi)·Ci意思是如果Ai和Bi中恰好有一个为1那么这一位会不会产生进位取决于低位的进位Ci相当于低位进位直接穿过这一位继续往上传递这叫“传播进位”记作Pi Ai⊕Bi。G是“天生就要进位”P是“顺水推舟帮你传进位”。这两个信号就是整个先行进位加法器的灵魂后面的所有计算都建立在这两个信号的基础上。请注意这里的Pi用的是异或门而不是或门这个细节后面会影响和Si的计算——因为Si Pi ⊕ Ci复用同一个异或结果可以少焊几个门电路。2.2 4位并行进位公式推导现在有了G和P我们可以重新看待每一级的进位。以4位加法器为例从C0开始逐级写出C1 G0 P0·C0C2 G1 P1·C1 G1 P1·G0 P1·P0·C0C3 G2 P2·C2 G2 P2·G1 P2·P1·G0 P2·P1·P0·C0C4 G3 P3·C3 G3 P3·G2 P3·P2·G1 P3·P2·P1·G0 P3·P2·P1·P0·C0看得出规律没有每一位的进位表达式都只和输入信号G、P以及C0有关它不需要等前一位的进位算完。这就是“并行计算进位”的核心每个C都是通过组合逻辑直接算出来的延迟只取决于逻辑门的级数而不是加法器的位数。当然公式展开后项数越来越多尤其是位数增大之后门电路的输入引脚数会爆炸——一个C4表达式里需要5个输入的与门。所以实际工程里太宽的并行进位会分组建分层结构比如16位加法器用4个4位CLA模块组成第二级先行进位但那是后话这里先不做扩展。我整理了一张行波进位和先行进位的延迟对比表方便你直观感受类型4位延迟16位延迟32位延迟复杂度行波进位3级门延迟12级门延迟24级门延迟低先行进位3级门延迟3级门延迟组间传递3级门延迟分组传递中这一差距在高位宽场景下非常明显也是为什么现代CPU里的整数加法器几乎都采用某种形式的先行进位结构。在Logisim里做性能对比也很直观连续点上好几组输入观察进位灯的变化速度行波进位明显会闪一下才稳定而先行进位几乎马上到位。2.3 群进位与模块化设计的思想这一步稍微拔高一点。实际上4位的CLA已经是一个可以独立使用的模块它有G、P输出可以给上层使用。这个设计思想和软件工程里的模块化如出一辙把单个4位加法器封装成“黑盒”盒子只对外暴露输入A、B、C0输出S、C4以及可选的组生成G*、组传播P*。当我们需要16位加法器时不需要把16位的进位公式全部展开那样太复杂了。只需要把4个4位CLA模块串联起来同时用第二层先行进位逻辑去并行计算每个4位组的进位输入。这里的组生成信号G* C4 (相关P组合)组传播信号P* P3·P2·P1·P0。感兴趣的可以在Logisim里搭第二层验证但在4位基础实验阶段先把单层电路跑通更实际。3. Logisim实操从半加器到4位CLA完整搭建3.1 准备工作与工程基础设置打开Logisim-Evolution第一步建议新建工程后就保存文件比如命名为“CLA_4bit.circ”。这个习惯很重要Logisim偶尔会在某些操作下异常退出不保存的话半天白干。工具栏里需要熟悉的组件集中在“布线”和“门电路”两个分类下。本次工程用到的最基本组件包括与门AND Gate、或门OR Gate、异或门XOR Gate、非门NOT Gate、引脚Pin、常量Constant、隧道标签Tunnel、LED。我建议在开始之前先把工具栏挨个点一遍至少先搞清楚输入引脚怎么旋转方向、连线怎么换色后面搭起来就顺手很多。显示效果方面有几个我常用的设置在“项目 → 电路选项”里可以把“电线颜色”改成“按位宽着色”这样多比特宽的总线会用粗线条显示和单比特细线区分明显排错时一眼就能看出哪里接错位宽。还会把引脚标签字体调大一点因为字太小看着费眼尤其在门电路密密麻麻的时候很影响心情。如果你用的是校园实验室电脑每次开机可能都是默认配置建议直接导出“偏好设置”保存一份下次导入即可。3.2 搭建半加器验证最小运算单元先别急着上去就是4位我习惯先搭一个1位半加器练手。半加器接收两个输入A和B输出本位和S以及进位Cout。公式非常简单S A⊕BCout A·B。在Logisim中拖入一个异或门和一个与门左侧接两个输入引脚右侧接两个输出引脚。连好线之后把输入引脚调成“拨动开关”交互方式用“手形工具”点击引脚即可切换电平。验证四种输入组合00给出S0、Cout001和10给出S1、Cout011给出S0、Cout1。如果验证不对优先检查门电路的方向和输入引脚是否接反。这个半加器的价值在于帮助你习惯Logisim的基本操作。特别是连线过程中的一个小技巧连线和被连线相交时如果中间没有节点它们是互不相干的当需要形成连接节点时要将终点连到已有线段上Logisim会自动打点。如果你画完线发现两个交叉点没有点那就是没有真正连通。3.3 全加器子电路搭建与封装半加器不处理来自低位的进位所以只能做最末位的加法。我们真正需要的是全加器输入有三个Ai、Bi、Ci输出有Si、Ci1。公式前面已经写过但这里再强调一下适合门级实现的两种写法Si Ai ⊕ Bi ⊕ CiCi1 Ai·Bi (Ai⊕Bi)·Ci在Logisim里新建一个子电路命名为“full_adder”。搭建顺序建议先做Si线用一个两个输入的异或门串起来第一步Ai和Bi异或得到内部信号X第二步X和Ci再异或得到Si。然后再做进位输出用一个与门输出Ai·Bi再用一个与门输出X·Ci最后把这两个输出通过或门得到Ci1。注意在电路图中使用隧道标签来标识内部信号X比直接拉很长的线干净很多。给子电路设置引脚左侧上方是Ai、下方是Bi和Ci右侧上方是Si、下方是Ci1。设置好子电路的布局之后以后每次调用它都是一个整洁的方块不再需要重复搭建内部逻辑。全加器搭好后先用真值表逐一验证确保8种输入组合全部正确再继续。3.4 4位CLA进位链电路逐级搭建这个环节是整个项目中最核心、也最容易踩坑的部分。先把前面的公式再列一遍作为对照C1 G0 P0·C0C2 G1 P1·G0 P1·P0·C0C3 G2 P2·G1 P2·P1·G0 P2·P1·P0·C0C4 G3 P3·G2 P3·P2·G1 P3·P2·P1·G0 P3·P2·P1·P0·C0在Logisim新建子电路“cla_4bit_control”先不急着连线把需要用到的输入引脚全部放好G0~G3、P0~P3共8个输入C0一个输入。输出有4个C1、C2、C3、C4。搭C1最简单一个或门搞定两个输入分别是G0和P0与C0的输出。C2需要三项G1直接用P1·G0用一个与门P1·P0·C0用多输入与门建议用三输入与门把三个信号接进去。把这几个结果接到或门上就得到C2。C3和C4按照同样的方式逐层展开注意有一些乘积项需要在中间引出P信号重复使用这时候用隧道标签把它连到对应的输入端口避免一条线拉得特别长导致图面太乱。需要特别提醒的一点是Logisim的或门和与门默认输入数量是2但你可以通过调整“扇入”属性修改输入引脚数量。为了和公式对应建议直接把或门扇入设置为公式的项数比如C3的或门就设置3个输入C4的或门设置5个输入这样图面上每个门的功能和公式一一对应不容易看错。3.5 四位CLA完整电路组装与连接进位控制电路搭好之后把它和全加器组合起来。新建顶层电路或者直接在主电路里操作。布局建议如下左边是A0~A3、B0~B3共8个输入引脚如果是减法扩展还有模式控制M中间是4个full_adder子电路实例右边是4位输出S和进位输出C4。下面说连接逻辑这一步是很多新手会连错的地方。每个full_adder的三个输入中Ai和Bi直接接对应的输入引脚Ci则必须来自cla_4bit_control对应输出的进位信号——注意不是来自低位的进位输出这是和行波加法器的根本区别。每个full_adder的进位输出引脚实际上在这个设计里只作为外层观察点使用不参与进位链但为了测试方便还是保留它并观察其电平尤其是在验证加法结果的时候可以对照。具体连接关系如下第一个全加器的Ci接C0输出S0它的Gi A0·B0、Pi A0⊕B0分别接cla_4bit_control的G0、P0。第二个全加器的Ci接C1输出S1Gi和Pi接控制模块的G1、P1。第三个和第四个以此类推。C4从控制模块输出作为最终进位输出引脚或接LED。如果你想要减法功能这一步就加一组异或门每个Bi和模式控制M异或当M1时Bi被取反同时把M接到C0相当于加1于是实现A加B的补码。此时的C4含义变成“借位”取反也就是无符号减法的借位标志。测试时验证A-B若结果为正则C4为1若结果为负则C4为0且输出S就是差值的补码形式。我在实际验证中用的几组测试样例建议你也试试模式AB预期S预期C4说明加法0110001110010常规进位测试加法1111000100001最大值溢出测试加法0111100011110边界反差测试减法0110001100111正数结果减法0011011011010负数补码结果4. 验证测试与常见问题排查4.1 测试用例设计与逐项验证很多同学搭完电路看到LED亮了两下就算完工这种做法我不提倡。一个组合逻辑电路必须经过完整的真值表验证尤其是进位链的每个输出都要逐一确认。以4位CLA为例最理想的方法是穷举所有2^9种输入组合A的16种乘以B的16种再加上C0的2种在Logisim中逐个切换输入引脚并记录输出。虽然这工作繁琐但你可以设计成只对A和B各取几组典型值再配合进位C0做交叉验证将工作量压缩到十几次开关点击。测试时我习惯把M和C0统一归到一组“控制输入”把A和B归到“数据输入”。界面布局上控制输入用红色标签数据输入用蓝色标签输出LED用单独区域放在最右侧并排好位置这样一眼就能看清楚当前测试条件是什么。测试加法时重点观察几个关键点第一S0到S3的每一位是否符合预期第二C4引脚是否为预期值第三如果加了“溢出标志”输出还要检查V标志是否只在正正得负或负负得正的情况下亮起。Logisim里观察电平变化的办法是通过LED颜色判断绿灯亮表示高电平灭表示低电平。为了更直观可以加一个“十六进制显示器”组件直接把4位结果以十六进制形式显示省去手算二进制转十六进制的麻烦。4.2 关键信号波形观察与调试方法Logisim虽然没有像示波器那样强大的波形窗口但通过它自带的“组合逻辑分析”功能可以生成真值表和化简表达式这一点对于校准电路非常有用。选中你要观察的组合逻辑电路区域后在“分析”菜单里选择“组合逻辑分析”Logisim会自动生成一个真值表方便比对。如果你需要观察更长时间内的信号变化情况可以依靠“时钟”组件和“探针”组件辅助。给输入端接上时钟源配合计数器可以产生周期变化的输入信号再用输出端的探针捕捉信号跳变。不过对于4位CLA这种纯组合逻辑最有效的调试方法依然是手动拨动输入引脚观察输出引脚变化。我还发现一个很实用的排查技巧在关键信号线上设置探针比如G3、P3、C3、C4这些位置点击不同的输入组合时观察这些探针的颜色变化。如果C4不正常先看G3、P3是否正确再看C3是否正确逐级缩小范围很快就能定位到哪一级的与门或者或门出了问题。这种“从前级往后级推”的方法比盯着整块电路发呆有效得多。4.3 常见错误速查表我整理了搭建过程中遇到频率最高的几类问题附上排查思路建议遇到报错或输出不对的时候先对号入座现象可能原因排查方法S输出全亮或全灭输入引脚位宽设置错误或接反检查引脚位宽属性确认输入引脚是1位检查是否误连到输出引脚加法结果正确但C4恒为0cla_4bit_control的C4输出没接到顶层引脚检查顶层电路C4是否引线到LED而非停在子电路内部中间某一级结果不对G或P接错位置对照公式逐一核对该级G、P与公式中乘积项的连线接线图上出现红色线两个输出引脚相连或位宽不匹配检查红色线段附近是否有两个输出被连在一起或者线的位宽属性不一致子电路引脚输入坐标错乱引脚方向设置不对选中引脚后在属性栏调整“朝向”为东、西、南、北输入统一朝外减法结果差了1C0在减法模式没有接到M信号减法模式下C0必须和M相连由M控制加1否则就是纯取反没加1多位数输入引脚被当成单个引脚引脚属性“位宽”默认是1未改为4把A、B输入引脚位宽改为4输出引脚S位宽也改为4排查的思路永远是从输出反推输入用探针锁定出问题的信号位置检查该信号应该等于什么、实际是什么再去检查生成该信号的逻辑门。4.4 关于“稳定”和“竞争冒险”的进一步说明你可能在网上看到过有人讨论组合逻辑电路的“毛刺”和“竞争冒险”问题。先行进位加法器的进位链由于各级门延迟不同在实际物理电路中确实存在毛刺风险但Logisim是纯行为级仿真它不会模拟真实门延迟所以这个问题在Logisim里看不出来。这对实验来说是好事至少不会因为毛刺导致调试困难。但在概念层面你仍然需要理解理论上先行进位的速度优势来自所有进位同时产生实际中信号依然要经过与门和或门的物理延迟。不同路径的G、P到达时间不同最终C4可能在很短的时间内出现抖动。这就是为什么实际CPU设计里常常会在输出端加锁存器或者做流水线寄存而Logisim实验通常不做这一步毕竟我们验证的是逻辑功能而不是时序行为。我想额外强调一点Logisim里设置的“组合逻辑分析”功能生成的真值表只能用于无反馈的组合电路如果你的电路里有反馈环比如把输出引回输入这个工具会无法解析。所以在调试时如果点击分析之后报错或表里全是X先检查是否误加了反馈线。5. 扩展思考从4位CLA到更复杂的CPU设计5.1 如何扩展到8位、16位搞定4位CLA之后一个很自然的想法是向高位扩展。直接展开8位并行进位公式的话C8表达式会非常长而且需要约9输入的大扇入门这在Logisim中既不美观也不符合工程实践。标准做法是用分组先行进位结构将8位分成两组4位第一组内部先行进位输出组进位C4第二组接收C4后再做内部先行进位。这样第二组仍然需要等待第一组的进位但整体延迟远小于行波进位。如果继续追求更高性能可以在组间也使用先行进位逻辑也就是所谓的“二级先行进位”。这时候每个4位CLA模块需要额外输出两个信号组传播信号P和组生成信号G。P* P3·P2·P1·P0G* C4在这个模块内部产生的进位与外部C0无关时。上层电路用这些信号来计算每个模块的进位输入从而让4个4位CLA模块也能并行工作。在Logisim中可以把4位CLA整体再封装为一个“黑盒”赋予它P和G的输出引脚以后搭建16位加法器就像搭积木一样方便。5.2 CLA在小型CPU设计中的位置标题里提到了单周期MIPS CPU设计这是学习计算机组成原理的学生一定会遇到的项目。在MIPS数据通路中ALU负责算术逻辑运算加法器是ALU的核心部件。如果你在Logisim里做单周期MIPS建议先将4位CLA验证通过再改成32位版本或者用16位简化版并用它实现add、sub、addi等指令。Logisim里做CPU的常见路线是先搭好ALU再搭建寄存器堆、指令存储器、数据存储器、控制单元最后串成完整的数据通路。加法器在ALU里属于基础运算单元所以它的正确性和性能直接关系到整个CPU项目的成败。如果加法器使用了行波进位那么在运行需要连续加法的程序时比如循环累加虽然功能上没错但课程演示时会感觉反应迟钝。用CLA之后ALU的运算速度快配合时钟和寄存器整体数据通路跑起来就大不一样。很多同学的CPU项目卡在控制信号的生成上但回头发现是ALU的加法器个别输出不对造成CPU行为完全错乱。所以我坚持认为先在单独工程里把CLA彻底验证完整是后面所有工作的前提。不要图省事直接抄网上的ALU电路图一定要亲手搭一遍理解了内部逻辑后面出了任何问题才有排查能力。这一点在我带过的所有课程设计小组中反复得到验证凡是自己搭过CLA的同学后期调CPU都相对顺利凡是直接复制粘贴的出问题后完全无从下手。5.3 后续还能玩出的花样除了单纯做加法器你还可以给这个CLA工程加一些组件做成一个可以在Logisim里演示的简易ALU加一个2选1多路选择器用控制信号切换加法/减法/与/或/异或等不同运算。增加一个比较器输出等于、小于、大于用于分支跳转指令的判定。增加一个零标志Z输出供CPU的条件分支使用。用“移位器”组件扩展出左移、右移功能这对应MIPS的sll、srl指令。如果你对性能分析感兴趣还可以在Logisim里接入“时钟”组件将加法器结果锁存到寄存器再配合计数器观测每周期运算结果的变化。这已经算是一个非常简易的流水线雏形了。更进阶的玩法是将4位CLA封装成子电路后反复例化4次再构建16位加法器体会“模块复用”带来的设计效率提升。我个人的体会是这个4位CLA项目虽然看起来只是一个算术电路但它隐藏着计算机体系结构里最核心的设计哲学把耗费时间的关键路径找出来用并行计算加速关键瓶颈。理解了这一点后面学乘法器、除法器、流水线控制、Cache替换策略的时候你会觉得所有硬件设计都围绕着“时间和空间的权衡”展开。希望这篇教程能帮你真正跨过“看懂原理”和“亲手搭出电路”之间的鸿沟。如果你在搭建过程中还有卡壳的地方把这篇文章反复对照一步一步来基本不会有问题。