ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Logisim搭建8位加减法器全攻略:从补码原理到电路仿真排错

Logisim搭建8位加减法器全攻略:从补码原理到电路仿真排错 1. 项目概述与原理铺垫讲真计算机组成原理课上那个“用Logisim搭一个8位加减法器”的实验几乎是每个学硬件的同学都绕不过去的坎。我在带实验课的时候见过太多人卡在同一个地方明明照着PPT的图连线仿真结果就是不对要么最高位莫名其妙变成1要么减法结果跟手算完全对不上。这玩意儿说难吧原理其实特别直白——就是一堆全加器拼起来说简单吧真上手练一遍里面藏着大量反直觉的细节。这篇文章是我把整套搭建过程重新捋了一遍之后整理出来的实操记录从补码原理到全加器设计从加法器扩展到加减法一体电路最后附上我在带实验时反复遇到的高频错误排查清单。不管你是刚打开Logisim还找不到元器件在哪的纯新手还是已经搭完但对某个bug百思不得其解的进阶玩家这篇内容应该都能帮你省下不少瞎折腾的时间。先明确一下我们要做的东西到底是什么。所谓8位加减法器输入是两个8位二进制数A和B再输入一个控制信号通常叫Sub或者M当M0时输出AB当M1时输出A-B。这里的减法不是直接拿B去做减法而是利用补码的性质把减法转化成加法——A减B等价于A加上B的补码而B的补码等于B按位取反再加1。这个“加1”的操作在电路里可以白嫖控制信号M让M同时连到每个全加器的进位输入M1的时候正好完成那个额外的1。听起来很顺对吧实际搭起来进位链、位宽、选择器配置这些细节随便一个都能让你调试半小时起步。2. 核心原理为什么用补码就能把减法变加法2.1 补码的本质与加减法统一我见过不少同学上来就照着电路图连线完全不理解为什么要搞一个“异或门进位输入”的花活。这里我强烈建议先花五分钟搞懂补码的本质不然后面出错了你都不知道该往哪个方向查。补码的核心思想一句话就能讲完对于一个n位二进制数它的相反数的补码等于按位取反后加1。为什么要这么干因为这样一来CPU里的加法器电路只需要做加法就够了减法完全复用同一套硬件。想想看如果不做这个统一你得给减法单独设计一套借位逻辑电路复杂度直接翻倍。举一个具体的例子用8位二进制验证一下计算5减3。5的二进制是0000 01013的二进制是0000 0011。按照补码规则-3的补码是先对0000 0011按位取反得到1111 1100再加1得到1111 1101。现在把5和-3的补码加起来0000 0101 1111 1101 ----------- 10000 0010注意这里产生了9位结果最高位的1是进位输出而低8位是0000 0010也就是十进制的2正好是我们想要的结果。这个多出来的进位学名叫做“溢出进位”在无符号数视角下它可能表示真溢出在有符号数视角下它会被丢弃具体看你的设计需求。这也是为什么减法和加法在硬件上可以共用一套全加器阵列的根本原因。2.2 全加器整个加法器的基本单元8位加减法器说白了就是把8个全加器串起来。一个全加器接收三个输入本位两个加数A_i、B_i以及来自低位的进位C_in输出本位和S_i以及向高位的进位C_out。用布尔表达式写就是S_i A_i ⊕ B_i ⊕ C_inC_out (A_i B_i) | (C_in (A_i ⊕ B_i))其中⊕是异或。你可以把全加器理解成“三个人投票”——A_i、B_i、C_in三个输入里至少两个是1C_out就是1。在Logisim里搭建全加器需要两个异或门、两个与门、一个或门或者用Logisim内置的加法器组件直接替代。不过作为实验课大家一般还是从门电路开始手搭一遍这是有道理的后面我会细说。2.3 加减控制信号如何嵌入全加器现在回到减法转换的关键一步。我们已经知道A-B等价于A加上B的补码而B的补码是B取反再加1。那么在电路层面怎么实现取反操作用异或门就能搞定把每个B_i和控制信号M做异或。当M0时B_i⊕0 B_i原样输出当M1时B_i⊕1 ¬B_i完成取反。这个技巧在Logisim里非常常用一排异或门搞定。加1操作更巧妙直接让M接入最低位全加器的进位输入C_in。当M1时最低位多加了1正好完成补码的“1”步骤。所以整体结构就是8个全加器串联每个B_i经过异或门后再进入全加器M同时控制所有异或门和最低位的进位输入。M0走加法M1走减法一套电路两种功能而且几乎没有额外硬件开销。3. 实操准备与工具选型3.1 为什么选择Logisim在动手之前先说说工具选择。Logisim是一款开源的数字逻辑仿真软件Java写的跨平台Windows和macOS都能跑。它最大的优点是交互性强拖拽元器件就能连线改完立刻看仿真结果特别适合教学场景。相比之下硬件描述语言Verilog或者VHDL虽然更贴近真实芯片设计流程但学习曲线陡峭初学者很容易被语法劝退。用Logisim做这个实验还有一个隐性优势它的错误提示虽然不算智能但连线状态高亮很直观蓝色表示未连接、绿色表示0、红色表示1排查问题的时候一眼就能看出哪根线没接好。这一点在后面的错误排查环节会反复用到。3.2 获取与初始配置Logisim的下载渠道我推荐官方源站或者国内一些高校的镜像站。下载得到的压缩包解压后运行logisim-generic-版本号.jar文件即可。需要说明的是较新版本比如2.7.x之后的进化版Logisim Evolution界面布局和老版有些差异功能更丰富但核心操作逻辑一致。启动后做两件事第一把画布网格对齐打开项目菜单→选项→勾选“显示网格”这样连线的时候元器件引脚能自动对齐省去手动微调的痛苦第二设置元件位宽偏好默认情况下Logisim的输入输出引脚默认位宽是1位我们需要在后续创建时手动改为8位这一步非常容易漏后面会单独强调。3.3 用到的元器件清单整个项目实际上只需要用到下面这些基础组件全部在Library面板的“门电路”“导线”“输入/输出”等分类下面能找到组件名称来源位置数量用途输入引脚Pin输入/输出3组A[8]、B[8]、M两个8位数和一个控制信号输出引脚Pin输入/输出2个S[8]、Cout8位结果和进位输出异或门XOR Gate门电路8个对B按位取反M1时全加器Adder算术单元8个执行逐位相加分线器Splitter导线1个将8位总线拆成8根单线常量Constant导线1个固定电平信号可选有人可能会问全加器直接用Logisim自带的Adder组件不就行了为什么还要自己搭我的建议是如果实验要求是验证电路功能直接用Adder没问题省时省力但如果课程要求门级实现很多学校的实验确实这么要求那就得老老实实把全加器用门电路搭出来。在后续章节里我两种方案都会覆盖先讲门级搭法再讲怎么替换成内置Adder快速验证。4. 逐模块搭建从全加器到8位加减法一体电路4.1 先搭一个全加器我的习惯是先搭好一个全加器并验证通过再复制成8份串联而不是直接在8位总线上焊电路。原因很简单一个全加器出错好排查8个全加器一起出错你根本不知道先看哪个。全加器的搭建方式在画布上放置两个异或门、两个与门、一个或门。逻辑接线如下第一个异或门接收A_i和B_i输出记为P。这个P代表“两个输入是否不同”它是求和项的一部分。第二个异或门接收P和C_in输出就是S_i。这一步完成S_i A_i ⊕ B_i ⊕ C_in。第一个与门接收P和C_in输出P C_in代表“低位的进位和当前位的奇偶性组合产生的进位”。第二个与门接收A_i和B_i输出A_i B_i代表“本位两个输入都是1时必然产生进位”。或门把这两个与门输出合并得到C_out (A_i B_i) | (P C_in)。连完之后接上输入输出引脚给几组测试向量验证比如A0、B0、C_in0时S0、C_out0A1、B1、C_in0时S0、C_out1A1、B1、C_in1时S1、C_out1。这三组能对上全加器基本就没问题了。这里插一句实操建议Logisim里默认输入引脚是1位宽全加器本身就1位完全够用。真正需要改位宽是后面做8位输入的时候到时候别忘。我个人在这上面栽过无数次跟头已经形成肌肉记忆了每次创建引脚第一件事就是看右下角的位宽属性。4.2 复制扩展成8位加法器全加器验证通过后把它整个选中CtrlC复制再CtrlV粘贴8份或者4份再进行镜像操作。布局的时候尽量拉成一条直线进位信号从右往左传或者从左往右看你习惯但方向一定要固定。每个全加器的C_out连接到相邻高位的C_in最低位的C_in接一个常量0做纯加法时最高位的C_out引到输出引脚作为进位标志。这里要注意一个关键点8个全加器串联之后如果你用总线8位宽导线连输入必须先经过分线器Splitter把总线拆成单根线再分别接到每个全加器的A_i和B_i输入端。分线器的位宽设置要匹配否则会出现明明连了线但信号传不过去的诡异情况。如果没有分线器也可以直接用8根独立的单线输入画布上会稍微拥挤一点但对初学者来说反而更好理解连线逻辑。我建议第一次搭的时候全部用单线等理解了数据流再优化布局换总线否则很容易因为分线器配置错误而陷入没必要的调试泥潭。4.3 集成减法控制异或门阵列与控制信号加法器稳定运行后开始集成减法能力。这一步的操作核心是在B的8根信号线进入全加器之前各插入一个异或门异或门的另一个输入统一接到控制信号M。当M1时B_i的每一位被取反同时M还要接到最低位全加器的C_in完成补码所需的1操作。接好之后整个电路的功能就齐了M0时B原样进入加法器最低位进位为0输出ABM1时B取反进入加法器最低位进位为1输出A(-B)等价于A-B。这里特别强调一下M信号除了连接8个异或门和最低位进位之外绝对不能再连到其他地方否则会引入多余的逻辑导致结果完全错乱。在实际连线时建议把M信号画成一条水平线然后用“分支”功能往下引线到8个异或门最后再拉到最低位全加器C_in。Logisim里沿着已有导线拖动鼠标可以自动创建分支点且会自动保持连通这个功能用熟了效率极高。4.4 显示结果输出引脚与LED指示灯很多同学做到这一步就忽略了一个重要环节结果的可视化。我建议在输出端接上两组东西一组是8位二进制结果显示适合后续做波形验证一组是转换成十进制显示的标签。Logisim里可以为输出引脚设置标签Label显示它所代表的物理含义。在这个实验中我习惯把8位结果引脚命名为“S[7:0]”进位输出命名为“Cout”控制信号命名为“M”这样后期排查时能一眼定位每个信号。如果你想更直观地看十进制结果可以再用一个“常量转换”思路接一组七段数码管Logisim里叫Hex Digit Display把8位结果拆成高4位和低4位分别接两个数码管。当然这一步不是必须的但用来检查最终结果是不是你想要的十进制值比盯着二进制猜快得多。这个扩展虽然简单但对加深理解很有帮助。5. 常见错误排查指南这一节是我最想写的部分。带实验那么多次见过的问题翻来覆去就是那么几个但每次都有新同学踩进同一个坑。下面我按出现频率从高到低列出来每条都附上现象、原因、排查手段和解决办法。5.1 位宽不匹配你看着连了线实际上信号根本没传过去Logisim里最阴间的错误就是这个。你看到导线连接正常颜色也是绿色表示有信号但输出值怎么都不对。原因大概率是输入引脚的位宽设置成了1位而你想要的是8位。此时导线虽然是绿色的但实际传递的只有1位信号。判断方法很简单把鼠标悬停在导线上看状态栏显示的位宽信息。如果确实出现了位宽不匹配最常见的情况是低位全加器的输入都是同一个1位信号导致高位完全悬空输出结果错得一塌糊涂。解决办法是在创建输入引脚时在属性面板里把“位宽”改成8然后再接线。如果你已经建好了双击引脚也能修改位宽但要注意改完之后所有连接到这个引脚的导线都会自动变成新位宽有时候反而会引发新的不匹配所以最好的做法是一开始就设置正确。5.2 进位链断裂最高位结果凭空消失8位加法器最经典的问题就是进位链断裂。具体现象是两个数相加的结果超过了8位能表示的范围比如25512568位结果S显示为0Cout这个进位输出你发现根本没接出来或者接出来但Simulation树里看不到信号。产生这个问题的原因绝大多数时候是某个全加器的C_out引脚没接到下一位的C_in上。Logisim里两个引脚只要靠近就会自动吸附连接但如果你手动调整过布局很容易出现引脚之间只“搭”了一点但没有真正连接的情况。此时Logisim不会报错因为你没有悬空引脚只是连到了错误的地方。排查方法从最高位往最低位扫描每个全加器之间的连线或者用“点亮导线追踪”功能选中导线按CtrlShift高亮路径看看是不是连续一贯的。推荐的做法是在设计阶段就把进位链沿线拉成直线不要东拐西拐这样排查成本最低。5.3 异或门方向接反减法结果变成负数补码显示如果你确认加法完全正常、但减法结果总是跟手算差一个“符号”大概率是异或门的输入方向接反了。具体来说B_i应该接异或门的一个输入M接另一个输入输出再进全加器。如果你不小心把输出接到了B_i的源端或者把M和B_i接反逻辑层不会报错但功能上就变成了“对B取反并加M”而不是“对B取反并加1”。这种错误特别隐蔽因为加法模式下M0异或门相当于直通你怎么接都对一旦切到减法M1立马暴露。这也是为什么我建议你先用加法模式验证一遍再切减法模式验证分步走省得所有问题混在一起。排查技巧在减法模式下分别给B输入全0和全1两种极端值。B0时A-B应该等于AB255时A-B应该等于A-255的低8位。如果这两种情况输出对不上手算结果就可以断定异或门链路有问题逐个检查每个异或门的输入来源即可。5.4 最低位进位输入未处理减法结果整体差1这个问题太常见了。很多同学搭完纯加法器然后只加了异或门阵列忘记把M信号引到最低位全加器的C_in。结果就是减法模式下B取了反但没有加上那个关键的1导致输出总是比正确结果小1。我见过不少同学对着这个差1的结果反复检查半天最后才发现是最低位进位没接。其实这个问题通过测试就能快速定位随便算一个减法如果结果比预期小190%是这个原因。补上这根线一切恢复正常。5.5 分线器配置错误总线信号错位如果你用了总线分线器的方案最常见的错误是分线器的位宽设置和输入不匹配。比如输入总线位宽是8分线器却设置成了4或者16导致信号拆出来之后错位严重全加器拿到的根本不是对应的那一位。排查方法先看分线器的属性面板确认“位宽”和“扇出”几个参数符合预期然后逐一检查分线器的每一根输出线是否依次连接到了对应的全加器输入。Logisim中分线器比较人性化的一点是每根输出线上会标注数字序号连线的时后注意序号对齐别把第0位接到了第1位的全加器上。5.6 仿真未开启或时序问题改了半天结果纹丝不动这个错误通常发生在新手刚入门时你连改了电路但仿真输出没反应。Logisim的仿真默认是实时进行的理论上不存在“需要手动触发”的情况。但有一种例外你改了电路之后输出引脚上显示的是电平但如果你把输出引脚和输入引脚叠在一起了电平会被输入自动覆盖造成“改了没用”的错觉。另外还要注意Logisim的仿真步进模式。如果你开启了“步进仿真”Step Simulation电路不会自动更新每点击一次才计算一次。这种情况下你修改了输入值但输出没变化是完全正常的。看到这个现象先检查工具栏上的仿真模式是否是“实时仿真”图标如果是步进模式切回来就好。5.7 数字显示错误你看到的是十六进制而不是十进制这个严格说不算电路错误而是读值问题。Logisim的输出引脚默认显示二进制但如果你给引脚设置了“十六进制”显示格式看到的结果会是十六进制这时候拿它和十进制手算结果对比当然对不上。解决办法右键输出引脚在“显示格式”里选择“二进制”或者“十进制”。我个人建议调试阶段用二进制因为可以和手算二进制结果逐位对比定位具体是哪一位出错十进制适合最后的功能验证确认整体数值是否合理。两种模式切换着用排查效率最高。6. 错误排查工具与方法进阶6.1 用Logisim的“点亮路径”功能排查断线Logisim有一个被很多人忽略但极其有用的功能选中一条导线后按CtrlShiftWindows或者CommandShiftmacOS会高亮整条连通的信号路径。这个功能在排查进位链断裂和位宽不匹配的时候简直是神技。你只需要从输入引脚开始顺着路径一路点亮下去看看信号在哪一段消失了问题就暴露了。我曾经排过一个学生的问题整个加法器看起来完美但减法功能永远不对。我用了这个功能从M信号一路点亮发现有一个异或门的M输入线没有真正连上只是因为靠得太近看起来像连上了。这种“视觉重合但电气未连通”的坑不点亮路径几乎无法发现。6.2 用好引脚标签和颜色管理在电路规模变大之后引脚标签的价值就体现出来了。我见过很多同学的接线图满画布都是无标签的引脚排查时完全靠猜哪个是A哪个是B。我的建议是每一个输入输出引脚都加上有意义的标签比如A[0]、B[7]、S[3]、Cout、M。标签既方便你自己检查也方便老师和同学帮你debug如果公开分享电路图的话。另外Logisim允许给导线设置颜色。我习惯把A相关的线设为蓝色、B相关设为红色、控制信号M设为绿色、输出S设为黑色。这样一眼就能看出线路走向排查时能快速建立空间感。这个习惯和IDE里的代码高亮一个道理看着舒服排错也快。6.3 用表格整理真值表快速验证搭建完成后的验证环节我建议不要随手按几个数就完事而是有系统地测试。可以准备一张真值表或测试向量表覆盖下面几类情况测试场景A输入B输入M预期S二进制预期Cout加法基础00000101000000110000010000加法边界11111111000000010000000001减法基础00000101000000111000000101减法跨000000011000001011111111100减法同数00001010000010101000000001全1加全111111111111111110111111101逐项测试并且在表格里打勾能极大减少“我觉得没问题但其实有问题”的错觉。尤其是减法跨0那一组也就是3-5很多人会因为结果是负数而忘记8位补码下应该显示11111110即-2的补码误以为电路出错。这一点对理解补码表示范围特别有帮助。6.4 模块化分阶段验证技巧我强烈建议不要把整个电路一口气搭完再测试而是分模块验证。具体来说分三个阶段第一阶段单独验证一个全加器的真值表确认这个最小的单元没问题。第二阶段搭出8位加法器先只测加法确保你能得到正确的AB结果包括进位输出。第三阶段才加入异或门阵列和控制信号测减法。每个阶段验收通过后再进入下一个阶段。这样做的最大好处是一旦整个系统出错你的排查范围被大幅压缩——要么是新增模块的问题要么是新旧模块的接口问题。很多同学一上来就试图一步到位结果8个全加器异或门阵列控制信号全部上线出了问题根本不知道从哪下手。分阶段虽然看起来慢实际上总耗时反而更短这也是所有工程项目的通用方法论。7. 功能验证与完整测试流程7.1 加法模式测试搭建完成并排除基础错误后进入正式的功能验证环节。首先验证加法模式把M置为0逐项输入上一节的测试向量观察S和Cout输出是否与手算结果一致。这里有一个容易被忽略的细节手算的时候要明确自己用的是无符号数还是补码视角。如果输入是11111111255加上000000011无符号视角下结果是256溢出到8位就变成了0Cout1但如果你把手算结果写成100000000也算你对关键是8位显示只有低8位。实验报告里建议同时记录S和Cout并说明溢出条件这样才算完整理解了这个电路的行为。实际测试时我建议随机挑几组数据交叉验证不要只用同一类数据。比如测试加法时既有不进位的12也有进位的127128、255255这些极端值。只有极端值通过你才能放心进位链的设计没有缺陷。7.2 减法模式测试加法验证通过后把M置为1开始验证减法。测试要点包括正数减正数、正数减负数这里通过补码视角、同数相减、跨0产生负数的情况。特别要关注跨0场景因为此时输出是负数补码很多同学在看到11111110之类的结果时第一反应是“坏了”但其实这正是补码的正确表现。我举个例子5-3应该等于2输出000000103-5应该等于-2输出11111110。为了理解为什么11111110是-2可以按补码逆向转换先减1得11111101再取反得00000010即十进制的2所以原值是-2。这个逆向运算过程虽然简单但对理解补码表示范围非常有帮助实验报告里写一写也是加分项。7.3 边界值与随机值混合测试除了系统的测试向量我还会加一轮随机测试。做法是自己在纸上写出10组随机的8位二进制数手算出加法和减法结果再对照电路输出。这一轮往往能暴露前面测试向量没有覆盖到的隐藏问题比如某个全加器的进位链在特定模式下不稳定通常还是连线问题。随机测试还有一个作用帮你验证减法复用加法器时不引入额外错误。因为我们已经确认加法模式下进位链工作正常减法只是在加法基础上多了取反和加1理论上只要加法器没问题、异或门没问题、最低位进位没问题减法就必然正确。如果随机测试中发现减法异常优先排查这三个点不用去怀疑加法器本体。8. 实战心得与进阶扩展8.1 我的踩坑总结带实验这几年我自己的踩坑清单翻来覆去就几条输入引脚忘了改8位宽、异或门输入接反、最低位C_in没接控制信号、算法没错但读值用了十六进制。这些错误看着都很蠢但真到实验室里周围全是杂音电脑屏幕又小连线密密麻麻出任何一条都够你折腾大半个小时。所以我一直鼓励学生先在纸上画好逻辑图标注好每个引脚的连接关系再上机连线而不是直接在Logisim里边想边画。另外一个很实际的建议每完成一个阶段就Project→Save另存为一个版本比如adder_4bit.circ、adder_8bit_add.circ、adder_8bit_sub.circ。万一后面改崩了可以随时回退到上一个稳定版本不用推倒重来。这个习惯在我自己做大项目时也一直保留Logisim虽然没有版本控制功能但人工“快照”完全够用。8.2 从8位到更宽的扩展思路如果做完8位觉得意犹未尽可以试着把同样的电路扩展到16位甚至32位。理论上只需要把全加器数量翻倍、输入输出引脚位宽改为16或32即可。但在实际操作中你会发现画布布局复杂度和排错难度是爆炸式增长的这时候就更需要模块化和分阶段验证的思路了。我个人的建议是如果想锻炼工程能力可以尝试做一个“参数化”的加法器思路把n个全加器用循环复制Logisim支持组件复制和重复粘贴再用总线方式组织输入输出而不是把16根线一根根手动连到16个全加器上。这样虽然前期配置分线器有点麻烦但扩展性极好改位宽相当于只改几个参数。8.3 结合七段数码管的显示扩展热词里提到“七段数码管电路怎么制作logisim”如果你已经做完加减法器顺手接上七段数码管做结果显示是很好的进阶练习。具体做法把8位结果拆成高4位和低4位各接一个七段数码管Hex Digit Display组件显示的内容就是十六进制的结果。注意低4位和高4位的拆线方式如果反了显示结果会和预期完全对不上。更好的做法是做一个“十进制显示”扩展用查表法或者转换逻辑把二进制转成BCD码再接数码管。这个功能看着不难但会让你对编码转换和组合逻辑设计有更深的理解也为后续设计更复杂的ALU算术逻辑单元打下基础。热词里的“多周期mips cpu设计logisim”、“单周期mips硬布线logisim”这些高级项目本质上都是在基础运算电路之上的系统级集成扎实掌握本项目的每个模块后面进阶才会顺畅。9. 写在最后Logisim这个工具很多人第一次用的时候觉得它“竟然还要手动连线”很烦人。但等到你真的把8位加减法器完整跑通那一刻看着输入一组二进制数、输出一组预期结果、控制信号M一切换就完成加减法切换那种“我在底层看到了计算机怎么用二进制算数”的直观感是光看书本永远得不到的。这也是我在这行待了十几年依然愿意花时间把这些基础实验细节写出来的原因。如果你在搭建过程中遇到了不在本文清单里的问题建议先从“信号是否真的到达”出发用点亮路径逐段追踪再回头检查位宽和引脚连接。大部分看起来玄学的bug追根到底都是一根线没连对而已。
返回列表