ARTICLE DETAIL

资讯详情

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

Logisim实战:8位可控加减法器设计与补码原理详解

Logisim实战:8位可控加减法器设计与补码原理详解 1. 为什么我劝你别再手算补码了刚学计算机组成原理那会儿我最头疼的就是补码运算。课本上给你一个公式告诉你负数补码等于原码取反加一然后让你手算一堆二进制加减法。考试的时候还能慢慢推真到了要理解CPU里面ALU怎么工作的时候光靠手算根本串不起来。后来我在Logisim里从零搭了一个8位可控加减法器才真正把全加器、补码、溢出判断这些东西串明白了。这篇文章就是把我整个搭建过程、踩过的坑、以及一些课本上不会讲的细节完整记录下来。如果你正在学数字逻辑、计算机组成原理或者单纯想找个Logisim练手项目这篇内容应该能帮你省不少时间。先说清楚这个项目到底做什么。我们要用Logisim搭建一个8位可控加减法器它有两个8位输入端A和B一个控制端Sub一个8位输出端Result外加进位标志CF和溢出标志OF。当Sub等于0时电路执行A加B当Sub等于1时电路执行A减B。减法在硬件层面是通过加上B的补码来实现的所以核心逻辑就是“用加法器统一处理加减法”。这个项目涉及的核心知识点包括全加器的级联、补码的硬件实现方式、可控加减法的控制逻辑、溢出检测的两种方法、以及Logisim中隧道和分线器的使用技巧。适合已经学过基本门电路、想通过一个完整项目巩固组合逻辑设计的人。零基础也能跟但建议先把与门、或门、异或门、全加器的真值表过一遍。提示本文配套的电路文件我会在文末说明获取方式但强烈建议你先跟着步骤自己搭一遍直接打开文件看只能看个热闹。2. 整体设计思路与方案选型2.1 为什么选8位而不是4位或16位你可能会问为什么偏偏选8位。4位全加器是很多教材的标配结构简单搭起来快。但4位的问题在于它太简单了简单到你搭完之后感觉什么都没学到。8位刚好卡在一个微妙的位置它足够长能让你感受到级联进位链的延迟问题又足够短在Logisim里手动连线不会让你崩溃。16位当然更接近真实CPU的数据宽度但在Logisim里手动连16个全加器光是隧道标签就能让你眼花。而且8位刚好能表示-128到127的范围做溢出测试的时候127加1溢出到-128这个经典案例用8位演示刚刚好。所以8位是一个教学效果和实操成本之间的最佳平衡点。2.2 减法转加法的硬件逻辑这是整个设计的核心思想。在二进制世界里减法A减B等价于A加上B的补码。B的补码怎么来对B的每一位取反然后加1。注意这里有一个非常巧妙的硬件优化我们不需要单独做一个“取反加一”的电路而是可以利用加法器本身的进位输入。具体来说当Sub等于1时我们把B的每一位和Sub做异或运算。异或门的特性是一个输入为0时输出等于另一个输入一个输入为1时输出等于另一个输入取反。所以当Sub等于0时B经过异或门后不变当Sub等于1时B经过异或门后每一位都取反。同时我们把Sub直接接到加法器最低位的进位输入Cin上。这样当Sub等于1时B取反后加1正好就是B的补码。整个减法就变成了A加B的补码。这个设计的优雅之处在于它没有增加额外的加法器只是加了一组异或门就把加减法统一到了一个加法器上。这也是真实CPU中ALU的经典做法。2.3 溢出判断的两种方案对比溢出判断有两种常见方法我在搭建过程中都试过这里把对比列出来。判断方法原理优点缺点双符号位法用两个符号位参与运算结果两个符号位不一致则溢出逻辑简单只需一个异或门需要额外的符号位数据宽度要扩展进位异或法最高位进位Cout与次高位进位C7异或结果为1则溢出不需要扩展数据宽度直接利用现有进位需要引出次高位进位信号我最终选了进位异或法因为它不需要改变数据宽度8位就是8位不会变成9位。具体来说溢出标志OF等于Cout异或C7其中Cout是第7位全加器最高位的进位输出C7是第6位全加器到第7位全加器的进位。这个方法的原理是对于有符号数只有当两个同符号数相加结果符号改变时才是溢出而这种情况必然导致最高位进位和次高位进位不一致。3. 核心模块拆解与实操要点3.1 全加器从真值表到电路全加器是整个电路的基本单元。一个全加器有三个输入A、B、Cin两个输出Sum和Cout。真值表如下ABCinSumCout0000000110010100110110010101011100111111从真值表可以推出Sum等于A异或B异或CinCout等于AB加Cin乘以A异或B。在Logisim里你可以直接用两个异或门和一个或门搭出来也可以用Logisim自带的“全加器”组件。我建议第一遍自己用门电路搭这样你能真正理解进位是怎么产生的。搭的时候注意一个细节Cout的表达式AB加Cin(A异或B)中A异或B这个信号被Sum的计算复用了。所以你可以先算A异或B然后这个信号分两路一路再异或Cin得到Sum另一路和Cin相与后再和AB相或得到Cout。这样比直接按表达式搭少用一个异或门。3.2 8位级联进位链的连接方式把8个全加器级联起来就是把第i位的Cout接到第i1位的Cin。最低位的Cin接Sub信号最高位的Cout作为最终进位标志CF。这里有一个课本上经常一笔带过但实际搭建时很关键的点进位链的延迟。在Logisim里信号传播是需要时间的虽然软件会帮你处理但如果你把电路频率设得太高或者用时钟驱动这个组合电路就可能出现毛刺。对于纯组合逻辑的加减法器我们不需要时钟输入变化后输出会经过一段传播延迟后稳定。在Logisim里你可以通过“模拟”菜单里的“逐步传播”来观察进位信号一级一级传上去的过程非常直观。连线的时候我强烈建议用隧道标签而不是直接拉线。8个全加器如果全部直接连线画面会非常乱。给每一位的进位信号起一个名字比如C1、C2一直到C7用隧道标签连接电路图会清爽很多。Logisim的隧道标签是全局的所以命名要唯一不要和输入输出标签重名。3.3 可控加减法的控制逻辑控制逻辑的核心就是一组异或门。B的每一位Bi和Sub做异或结果作为对应全加器的B输入。同时Sub接到最低位的Cin。这里有一个容易犯的错误有人会把Sub接到最高位的Cin或者所有位的Cin。只有最低位的Cin接Sub才是正确的因为补码的“加一”只需要在最低位加一次。如果你把Sub接到所有位的Cin那就变成了B取反后加上11111111结果完全不对。另外异或门的两个输入顺序无所谓但建议把Bi放在上面Sub放在下面这样电路图看起来更整齐。8个异或门可以并排排列Sub信号用一条竖线连到所有异或门的下输入端。3.4 溢出标志的引出位置溢出标志OF等于Cout异或C7。Cout是第7位全加器的进位输出C7是第6位全加器到第7位全加器的进位也就是第6位全加器的Cout。在Logisim里你需要把这两个信号引出来接一个异或门。注意不要接错C7是第6位的Cout不是第7位的Cin。虽然它们电气上连在一起但如果你用隧道标签要确保标签名对应正确。我建议在每一位全加器的Cout处都放一个隧道标签命名规则统一为Cout0到Cout7这样C7就是Cout6不容易搞混。4. 完整搭建流程与关键步骤4.1 Logisim环境准备与项目创建首先你得有Logisim。官网可以下载到最新版中文版也有热心网友做的汉化。安装过程没什么好说的一路下一步就行。打开之后新建一个项目保存为“8位可控加减法器.circ”。在开始画电路之前先做两件事。第一在“项目”菜单里找到“选项”把“模拟”标签下的“传播延迟”设成“逐步传播”或者一个较小的值这样你能看到信号传播的过程。第二规划好你的画布布局。我建议把输入引脚放在左边输出引脚放在右边全加器阵列放在中间控制逻辑放在下方。这样信号流向从左到右符合阅读习惯。4.2 搭建第一个全加器并封装在画布上放两个输入引脚分别命名为A和B再放一个输入引脚命名为Cin。然后放两个异或门、两个与门、一个或门。按照3.1节的逻辑连接第一个异或门输入接A和B输出接第二个异或门的一个输入第二个异或门的另一个输入接Cin输出就是Sum。第一个异或门的输出再和Cin接一个与门A和B接另一个与门两个与门的输出接或门或门输出就是Cout。搭好之后选中所有组件右键选择“封装子电路”命名为“全加器”。封装之后这个子电路就可以像普通组件一样复制粘贴了。封装的好处是你只需要搭一次全加器后面7个直接复制而且如果发现全加器有问题改一个地方所有实例都会更新。注意封装子电路时确保输入输出引脚的“输出”属性设置正确。输入引脚要勾选“输出”输出引脚不要勾选。这个属性在Logisim里有点反直觉但记住“引脚的方向是相对于子电路外部而言”就好理解了。4.3 级联8个全加器复制8个全加器子电路排成一列。最低位的全加器Cin接Sub信号Cout接一个隧道标签命名为C1。第二位的Cin接C1Cout接C2以此类推。最高位的Cout接隧道标签Cout同时作为进位标志CF输出。每一位的A输入接8位输入引脚A的对应位B输入接对应异或门的输出。这里需要用分线器Splitter把8位输入拆成8个1位信号。Logisim的分线器设置输入位宽8输出位宽1扇出8。分线器的输出从上到下对应第0位到第7位注意不要接反了。B端的异或门阵列8个异或门每个异或门的一个输入接B的对应位另一个输入全部接Sub。异或门的输出接对应全加器的B输入。4.4 输出整合与标志位连接8个全加器的Sum输出用分线器合并成一个8位信号接输出引脚Result。分线器设置输入位宽1扇出8输出位宽8。注意合并的时候顺序要和拆分时一致第0位在最下面还是最上面取决于你的分线器设置建议统一用“从上到下依次是第0位到第7位”的约定。CF直接接最高位的Cout。OF接一个异或门输入分别是Cout和C7即第6位的Cout。输出接溢出标志引脚。到这里核心电路就搭完了。你可以开始测试了。在输入引脚上设置值比如A等于000001015B等于000000113Sub等于0Result应该是000010008CF等于0OF等于0。然后把Sub改成1Result应该是000000102验证5减3等于2。4.5 测试用例设计与验证光测一组数据不够我整理了一组覆盖各种情况的测试用例你可以照着跑一遍。测试场景ABSub预期Result预期CF预期OF正数加正数无溢出000001010000001100000100000正数加正数有溢出011111110000000101000000001负数加负数有溢出100000001111111100111111111正数减正数无溢出000001010000001110000001010正数减正数有借位000000110000010111111111000负数减正数111111110000000111111111000注意CF在减法中的含义和加法不同。加法中CF等于1表示无符号数溢出减法中CF等于0表示有借位。这个细节很多教材不讲但你测试的时候会发现。5. 常见问题与排查技巧实录5.1 结果不对但不知道从哪查这是最常见的问题。我的排查思路是“从低位到高位逐位检查”。先把Sub设为0只做加法。给A和B设置最简单的值比如A等于00000001B等于00000000看Result是不是00000001。如果不是问题出在第0位全加器。如果第0位对了把B改成00000010看第1位。这样逐位排查很快就能定位到出问题的全加器。如果所有位单独测都对但多位同时输入结果不对那大概率是进位链的问题。检查每一位的Cout有没有正确接到下一位的Cin。用隧道标签的话检查标签名有没有写错比如把C3写成了C4。5.2 减法结果总是差1这个问题的根源几乎肯定是Cin接错了。回顾一下减法时Sub等于1最低位Cin应该等于1这样B取反后加1才是补码。如果你把Sub接到了所有位的Cin或者最低位Cin接了0结果就会差1。检查方法把Sub设为1A设为00000000B设为00000001正确结果应该是11111111-1的补码。如果结果是11111110说明少加了1。5.3 溢出标志一直不对先确认你用的是Cout异或C7而不是Cout异或Cin。C7是第6位的Cout不是第7位的Cin。虽然它们电气上相连但如果你用隧道标签要确保标签名对应的是第6位的输出。另一个常见错误是把OF接成了Cout和C7的与或者或。必须是异或因为只有两个进位不同时才表示溢出。5.4 Logisim连线太乱怎么办这是新手最头疼的问题。我的建议是第一用隧道标签代替长距离连线但标签名要有规律比如输入用A0到A7B用B0到B7进位用C1到C7。第二用分线器把多位信号合并成一条线减少连线数量。第三把全加器封装成子电路这样主电路里只有8个方块而不是8堆门电路。第四善用“编辑”菜单里的“对齐”功能把组件排列整齐。5.5 分线器顺序搞反了分线器的输出顺序是可以设置的。默认情况下分线器的输出从上到下对应输入的低位到高位。但如果你在属性里改了“外观”或者“位序”顺序可能反过来。我的建议是统一用默认设置然后在分线器旁边标注“上第0位”。如果你发现结果的高低位反了比如输入00000001结果变成了10000000那就是分线器顺序问题调整一下分线器的“位序”属性即可。6. 从加减法器延伸出去还能做什么搭完这个8位可控加减法器你其实已经掌握了ALU的核心。接下来可以尝试几个扩展方向。第一个方向是增加逻辑运算功能。在ALU里除了加减法通常还有与、或、异或、非等逻辑运算。你可以加一个2位的功能选择端用多路选择器在加法结果、与结果、或结果之间切换。这样你的电路就从加减法器升级成了一个简易ALU。第二个方向是加入移位功能。移位在硬件里就是连线的重新排列不需要门电路。你可以加一个移位控制端用多路选择器选择是否移位、左移还是右移。这样你的ALU就更完整了。第三个方向是接入寄存器。把Result输出接到一个8位寄存器的输入寄存器的输出再反馈回A或B的输入。加上时钟信号你就能做累加器了。这个方向会把你引向时序逻辑和简单CPU的设计。第四个方向是用Logisim自带的“分析电路”功能让它自动生成真值表或者布尔表达式。你可以用这个功能验证你搭的全加器是否和真值表一致。在“项目”菜单里找到“分析电路”设置输入输出Logisim会自动帮你列出所有输入组合对应的输出。我个人在实际操作中的体会是这个电路搭一遍不够至少搭三遍。第一遍照着教程走能跑通就行。第二遍不看教程凭记忆搭遇到卡壳的地方再查。第三遍尝试优化比如减少门电路数量、用更清晰的布局、加一些测试辅助电路。三遍下来全加器和补码这些东西就真正长在你脑子里了。最后分享一个小技巧在Logisim里你可以把常用的子电路保存到一个单独的库文件里以后新建项目直接加载这个库就不用每次都重新搭全加器了。在“项目”菜单里找到“加载库”和“保存库”把全加器、分线器这些常用组件存进去下次直接拖出来用。这个习惯能帮你省下大量重复劳动的时间。
返回列表