ARTICLE DETAIL

资讯详情

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

Logisim实战:8位可控加减法器设计五步流程与溢出检测

Logisim实战:8位可控加减法器设计五步流程与溢出检测 在数字逻辑与计算机组成原理的课程设计里8位可控加减法器算是一个绕不开的经典题目。很多同学第一次接触Logisim面对一堆引脚、子电路和隧道标签往往不知道从哪里下手。我当年做这个实验的时候也是对着真值表发呆了半天后来把全加器、异或门和溢出判断这几块拆开来看才发现整个电路其实就三层结构底层是全加器链中间是加减控制逻辑顶层是溢出和标志位输出。这篇文章就把我踩过的坑和总结出来的五步流程完整写出来从半加器开始一路搭到8位可控加减法器每一步都配上电路结构和关键参数说明。不管你是刚装好Logisim的新手还是已经画过四位全加器想进一步扩展的进阶者都能照着这个思路把电路跑通。1. 先搞清楚8位可控加减法器到底在做什么1.1 从“加法”到“加减一体”的核心思路8位可控加减法器名字听起来挺唬人拆开看就三件事第一它能做8位二进制加法第二它能做8位二进制减法第三它有一个控制端用来决定当前做的是加法还是减法。关键问题在于数字电路里并没有一个独立的“减法器”器件减法在硬件层面是通过加法来实现的。具体来说A减B等价于A加上B的补码。在二进制里一个数的补码等于它的反码加一而反码就是把每一位取反。所以减法A-B可以写成A (~B) 1。这个“加一”从哪来答案是利用加法器最低位的进位输入。当控制信号为减法模式时我们把B的每一位取反后送入加法器同时把最低位的进位输入置为1这样整个电路就完成了A (~B) 1的运算结果就是A-B。当控制信号为加法模式时B保持不变送入加法器最低位进位输入置为0电路做的就是普通的AB。这就是“可控”两个字的全部含义——用一个信号同时控制B是否取反以及最低位进位是否置一。理解了这个原理后面画电路的时候就不会迷路。很多同学卡住的地方在于他们试图分别画一个加法器和一个减法器然后用多路选择器切换。这样做不是不行但电路规模会翻倍而且多路选择器的位宽也要跟着上去得不偿失。正确的做法就是上面说的补码思路一个加法器链加上一排异或门就够了。1.2 为什么选择行波进位而不是超前进位在Logisim里实现多位加法器常见的选择有两种行波进位加法器Ripple Carry Adder和超前进位加法器Carry Lookahead Adder。行波进位就是把n个全加器串起来低位的进位输出接到高位的进位输入结构简单直观但进位信号要一级一级传下去延迟随位数线性增长。超前进位则是通过额外的逻辑门提前算出每一位的进位延迟小但电路复杂。对于8位可控加减法器这个实验来说我强烈建议用行波进位。原因有三点第一Logisim是仿真软件不存在真实的门延迟问题行波进位的“慢”在仿真里根本体现不出来第二行波进位的电路结构清晰调试的时候一眼就能看出哪一位的进位断了第三超前进位虽然快但额外逻辑门容易引入错误对于初学者来说排查难度大。所以除非你的实验明确要求对比两种加法器的延迟否则行波进位是性价比最高的选择。1.3 溢出检测为什么不能只看最高位进位溢出是加减法器设计里最容易出错的地方。很多同学第一反应是如果最高位有进位输出那就是溢出了。这个判断在无符号数加法里是对的但在有符号数补码表示的加减法里是错的。举个例子8位补码能表示的范围是-128到127。如果计算1271结果是128超出了正数范围发生了溢出。但127的二进制是011111111的二进制是00000001相加得到10000000最高位进位输出是0并没有进位。反过来-128加-1等于-129也溢出但最高位进位输出是1。所以只看最高位进位根本判断不了溢出。正确的溢出判断方法是比较最高位的进位输入和进位输出。如果这两个信号不同就说明发生了溢出。用逻辑表达式写就是Overflow Cin(最高位) XOR Cout(最高位)。这个结论的推导过程在教材里都有我这里只强调一点在Logisim里连线的时候最高位的全加器有两个进位相关引脚一个是来自低位的进位输入一个是送往更高位的进位输出把这两个信号引出来接到一个异或门上输出就是溢出标志。这个细节在画图时很容易漏掉因为其他位的全加器不需要引出这两个信号。2. 从半加器到全加器底层模块的搭建与验证2.1 半加器理解加法的最简模型半加器是加法器的最小单元它接收两个1位输入A和B输出和S以及进位C。真值表很简单00得0进001得1进010得1进011得0进1。对应的逻辑表达式是S A XOR BC A AND B。在Logisim里你可以直接用异或门和与门各一个搭出来也可以封装成一个子电路方便后面调用。我建议在Logisim里先建一个名为“HalfAdder”的子电路输入引脚两个输出引脚两个。画好之后不要急着往上搭先单独测试一下。Logisim的仿真功能可以手动切换输入引脚的值你依次把00、01、10、11四种情况过一遍确认输出和真值表一致。这个步骤看起来多余但后面全加器出问题的时候你会庆幸自己验证过底层模块。2.2 全加器把进位输入纳入考虑全加器比半加器多了一个进位输入Cin输出和S以及进位输出Cout。它的真值表有8行但逻辑表达式可以拆成两个半加器加一个或门来实现第一个半加器处理A和B得到中间和S1和中间进位C1第二个半加器处理S1和Cin得到最终和S和中间进位C2最后C1和C2通过或门得到Cout。这种实现方式在Logisim里非常直观而且复用了刚才验证过的半加器子电路。在Logisim里建一个“FullAdder”子电路把两个HalfAdder子电路实例拖进去再加一个或门。输入引脚三个A、B、Cin输出引脚两个S、Cout。同样画完之后单独测试。全加器的8种输入组合都要过一遍重点看Cin为1时进位是否正确传递。我见过不少同学在这里出错原因是把两个半加器的进位输出接反了或者或门的输入接错了。测试的时候如果发现S对但Cout不对基本就是进位链的问题。2.3 在Logisim中封装子电路的注意事项Logisim的子电路功能是这个软件最实用的特性之一。你画好一个全加器之后右键点击电路画布选择“创建子电路”给它命名然后就可以在主电路里像调用普通元件一样调用它。但有几个细节容易踩坑第一子电路的引脚顺序。Logisim默认按照你放置引脚的位置来排列子电路符号上的引脚顺序如果你放得乱七八糟调用的时候很容易接错线。建议在子电路里把输入引脚统一放在左侧输出引脚统一放在右侧并且从上到下按照A、B、Cin的顺序排列。第二隧道标签的使用。在复杂的电路里连线会变得非常密集这时候用隧道标签Tunnel来代替长距离连线是个好习惯。但隧道标签的命名要规范比如“A0”表示A的第0位“C1”表示第1位的进位。命名混乱是后期调试噩梦的根源。第三子电路的测试。封装成子电路之后原来的输入输出引脚会变成子电路符号上的接口你没法直接在子电路内部切换输入值了。所以一定要在封装之前完成测试封装之后如果发现问题还得回去改子电路所有调用它的地方都会受影响。3. 8位行波进位加法器的组装与进位链排查3.1 把8个全加器串起来有了验证过的全加器子电路组装8位行波进位加法器就是体力活了。在Logisim主电路里拖入8个FullAdder实例从上到下排列分别对应第0位到第7位。第0位的Cin接一个输入引脚后面会用来做加减控制第0位的Cout接第1位的Cin第1位的Cout接第2位的Cin以此类推直到第7位的Cout接一个输出引脚这是最终的进位输出。每一位的A和B分别接对应的输入引脚。比如第0位的A接A0B接B0第1位的A接A1B接B1。输出S0到S7分别接输出引脚。这样整个加法器的骨架就搭好了。这里有一个容易忽略的点第7位的Cout不仅是最终进位输出还要用来做溢出判断。所以这个信号要同时引到一个输出引脚和一个异或门。同样第7位的Cin也要引出来。在Logisim里你可以直接从第7位全加器的Cin引脚拉一条线到异或门不需要额外加引脚。3.2 进位链断裂的典型症状与定位方法行波进位加法器最常见的问题就是进位链断裂。症状表现为低位的结果正确但高位的结果莫名其妙。比如计算00000001 00000001正确结果是00000010但如果第0位到第1位的进位线断了结果就会变成00000000。定位方法很简单从第0位开始逐位检查进位连线。在Logisim里你可以把鼠标悬停在连线上软件会显示当前这条线的逻辑值。先让A0和B0都为1看第0位的Cout是不是1然后看第1位的Cin是不是也是1再看第1位的Cout是否正确。这样一位一位往上查很快就能找到断点。另一个常见问题是进位线接错了位。比如第2位的Cout接到了第4位的Cin跳过了第3位。这种错误在连线密集的时候很容易发生。我的建议是每接完一位的进位线就立刻用鼠标悬停确认一下信号值不要等全部接完再查。3.3 用Logisim的仿真功能做逐位验证Logisim的仿真功能可以手动切换输入引脚的值也可以用一个时钟信号自动变化。对于加法器这种组合逻辑电路手动切换就够了。我通常会用以下几组测试数据测试编号A二进制B二进制预期S预期Cout1000000000000000000000000020000000100000001000000100311111111000000010000000014101010100101010111111111051111111111111111111111101第3组测试特别重要它验证了进位从最低位一路传到最高位的情况。如果这一组的结果不对说明进位链中间有断点。第5组测试则验证了全1相加的情况结果应该是11111110并产生进位输出。4. 加减控制逻辑异或门阵列与最低位进位4.1 用异或门实现B的可控取反加减控制的核心是一个控制信号通常记为Sub。当Sub0时做加法当Sub1时做减法。对于B的每一位我们用一个异或门一个输入接Bi另一个输入接Sub。异或门的特性是当Sub0时输出等于Bi当Sub1时输出等于Bi取反。这样一排8个异或门就实现了B的可控取反。在Logisim里你可以用一个8位的异或门元件Logisim支持多位宽元件把B的8位输入和Sub信号需要扩展成8位分别接到异或门的两个输入端。但要注意Logisim的多位异或门要求两个输入位宽相同所以Sub信号需要用一个分线器Splitter扩展成8位每一位都是Sub的值。或者更简单的方法是用8个1位异或门每个异或门的一个输入接Bi另一个输入统一接Sub。两种方法效果一样后者更直观但连线更多。我个人的习惯是用8个1位异或门因为这样在调试的时候可以单独看每一位的取反情况。如果用多位元件一旦某一位出问题排查起来反而麻烦。4.2 最低位进位输入的处理前面说过减法模式下需要给最低位一个进位输入1。这个信号可以直接用Sub来控制把Sub接到第0位全加器的Cin引脚。当Sub0时Cin0做加法当Sub1时Cin1配合B的取反完成A (~B) 1的减法运算。这里有一个细节需要注意第0位全加器的Cin引脚在加法器内部是连接到第0位全加器的进位输入端的但在Logisim里如果你用的是自己封装的FullAdder子电路它的Cin是一个输入引脚你直接把这个引脚接到Sub信号上就行。不要试图在子电路内部修改那样会影响所有调用该子电路的地方。4.3 控制信号与数据通路的时序关系虽然这是一个组合逻辑电路不存在时钟同步的问题但控制信号Sub的变化会同时影响异或门阵列和最低位进位。在Logisim仿真的时候如果你手动切换Sub的值会看到输出S在瞬间发生变化。这是正常的因为组合逻辑的输出只取决于当前输入。但有一个现象需要注意当Sub从0变到1的瞬间如果A和B的值也在变化输出可能会出现短暂的中间状态。这在Logisim里表现为输出闪烁但在实际硬件中就是竞争冒险。对于这个实验来说只要最终稳定状态正确就行不需要考虑瞬态问题。不过如果你后续要把这个电路扩展到时序逻辑里就需要考虑加寄存器来同步信号了。5. 溢出检测与标志位输出的完整实现5.1 溢出判断Cin与Cout的异或前面已经讲过溢出的判断依据是最高位的进位输入和进位输出是否相同。在Logisim里从第7位全加器的Cin和Cout各引一条线到一个异或门异或门的输出就是溢出标志Overflow。当Overflow1时表示有符号数运算结果超出了8位补码的表示范围。这里要特别注意溢出标志只对有符号数运算有意义。如果你把输入当作无符号数那么溢出应该看最终的进位输出Cout而不是这个Overflow标志。在实验报告里通常需要同时给出Cout和Overflow两个标志分别对应无符号溢出和有符号溢出。5.2 零标志与符号标志的引出除了溢出标志一个完整的加减法器通常还需要零标志Zero和符号标志Sign。零标志表示结果S是否为全零可以用一个8输入或非门来实现把S0到S7全部接到或非门的输入输出就是Zero。当S全为0时或非门输出1否则输出0。在Logisim里你可以用一个8位或非门元件把S的8位输入接进去输出就是Zero。符号标志就是结果的最高位S7。在补码表示中S7为1表示结果是负数S7为0表示结果是非负数。这个标志不需要额外逻辑直接从S7引一条线出来就行。5.3 完整电路的整合与最终测试把所有模块整合起来整个8位可控加减法器的结构如下输入是A[7:0]、B[7:0]和Sub中间是8个异或门组成的B取反阵列、8个全加器组成的行波进位链输出是S[7:0]、Cout、Overflow、Zero、Sign。最终测试的时候我建议用以下几组数据覆盖所有关键情况测试场景ABSub预期S预期Overflow预期Zero加法无溢出000000110000010100000100000加法正溢出011111110000000101000000010加法负溢出100000001111111100111111110减法无溢出000010000000001110000010100减法结果为负000000110000100011111101100减法结果为零000001010000010110000000001这六组数据覆盖了加法、减法、正溢出、负溢出、结果为零、结果为负这六种情况。如果全部通过说明你的电路功能完整。6. 我在Logisim里踩过的那些坑6.1 隧道标签重名导致的隐形短路Logisim的隧道标签用起来很方便但有一个致命问题同一个电路里名字相同的隧道标签会自动连接在一起。这意味着如果你在电路的不同位置不小心用了同一个标签名就会产生隐形的短路。我当年做这个实验的时候在B的取反阵列和全加器链之间用了“B0”这个标签结果在另一个地方也用了“B0”表示别的东西导致信号冲突仿真结果完全不对。排查了半天才发现是标签重名。避免这个问题的方法是建立一套命名规范比如所有输入信号用“A0-A7”“B0-B7”所有中间信号用“BX0-BX7”表示B取反后的信号所有进位信号用“C0-C8”。这样就不会混淆。6.2 多位元件与1位元件的混用陷阱Logisim支持多位宽的元件比如你可以直接放一个8位异或门也可以放8个1位异或门。两种方式在功能上等价但混用的时候容易出问题。比如你把8位异或门的输出接到8个1位全加器的输入Logisim会自动把8位信号拆成8个1位信号但拆分的顺序是从低位到高位还是从高位到低位取决于元件的属性设置。如果顺序搞反了结果就是高低位颠倒。我的建议是要么全用多位元件要么全用1位元件不要混用。如果必须混用一定要用分线器Splitter明确指定每一位的对应关系不要依赖自动拆分。6.3 子电路引脚顺序与外部连线错位前面提到过子电路引脚顺序的问题这里再强调一下。当你封装一个全加器子电路时Logisim会根据引脚在画布上的位置自动排列子电路符号上的引脚顺序。如果你把Cin引脚放在A引脚的上面那么子电路符号上Cin就会排在A的前面。调用的时候如果按照A、B、Cin的顺序接线就会接错。解决方法是在封装之前把输入引脚按照A、B、Cin从上到下排列输出引脚按照S、Cout从上到下排列。封装之后把鼠标悬停在子电路符号的引脚上Logisim会显示引脚名称确认顺序正确之后再连线。6.4 仿真时输入信号未初始化导致的误判Logisim在打开电路的时候所有输入引脚的默认值都是0。但如果你在电路里用了寄存器或者时钟信号初始状态可能是不确定的。对于这个组合逻辑电路来说输入引脚默认0不会有大问题但如果你在测试的时候忘记设置某个输入引脚的值就会得到错误的结果。我的习惯是每次开始测试之前先把所有输入引脚手动设置为0然后按照测试表格逐行设置。不要依赖Logisim的默认值因为默认值可能被你之前的操作改变过。7. 从8位加减法器延伸出去还能做什么7.1 扩展到16位或32位加法器8位加减法器的结构可以直接扩展到16位或32位只需要增加全加器和异或门的数量进位链相应延长。但位数增加之后行波进位的延迟会变得明显。在Logisim里虽然仿真速度很快但如果你要做一个32位加法器建议还是考虑超前进位或者分组进位的方式否则电路规模会很大画起来很累。扩展的时候溢出判断的逻辑不变仍然是最高位的Cin和Cout异或。但要注意最高位的编号变了比如16位加法器的最高位是第15位溢出判断要用第15位的Cin和Cout。7.2 与ALU其他模块的整合思路8位可控加减法器是ALU算术逻辑单元的核心部件之一。一个完整的ALU还需要支持与、或、非、异或等逻辑运算以及左移、右移等移位操作。你可以把加减法器作为一个子电路然后在ALU顶层用多路选择器根据操作码选择不同的运算结果。整合的时候要注意加减法器的输出S是8位逻辑运算的输出也是8位多路选择器的位宽要匹配。另外加减法器产生的标志位Overflow、Zero、Sign在ALU层面通常也需要输出用于条件跳转等操作。7.3 在Logisim中做自动化测试的尝试Logisim本身不支持脚本化测试但你可以用它的“组合逻辑分析”功能来生成真值表然后和预期结果对比。具体操作是在菜单栏选择“项目”-“分析组合逻辑”Logisim会自动扫描所有输入组合并生成输出真值表。对于8位加减法器来说输入有17位A的8位、B的8位、Sub的1位总共131072种组合Logisim跑起来会比较慢但可以只选择部分输入位进行分析。另一个方法是把电路导出为Verilog代码然后用外部仿真工具做自动化测试。Logisim支持导出Verilog但导出的代码可读性一般适合做功能验证不适合做精细的时序分析。8. 一些让电路更整洁的Logisim使用技巧8.1 用分线器管理多位信号分线器Splitter是Logisim里最实用的元件之一。它可以把一个多位信号拆成多个1位信号也可以把多个1位信号合并成一个多位信号。在8位加减法器里A和B的输入可以用分线器拆成8个1位信号分别接到各个全加器。输出S也可以用分线器合并成一个8位信号方便观察。分线器的属性里可以设置“扇出”和“位宽”建议把输入分线器的扇出设为“面向东”输出分线器的扇出设为“面向西”这样连线会比较整齐。8.2 用探针和标签提高可读性Logisim的探针Probe可以显示连线上的信号值对于调试非常有用。你可以在关键节点上放探针比如每一位的进位输出、溢出标志、零标志等。探针的显示格式可以设置为二进制、十进制或十六进制根据你的习惯选择。标签Label则是给连线起名字方便识别。标签和隧道标签不同标签只是视觉上的标注不会影响电路连接。我通常会给进位线加上“C1”“C2”这样的标签这样一眼就能看出是哪一位的进位。8.3 电路布局的几点经验Logisim的电路画布是无限大的但布局混乱会让调试变得困难。我的经验是输入引脚放在最左侧输出引脚放在最右侧中间按照信号流向从左到右排列。全加器链从上到下排列每一位占一行。异或门阵列放在全加器链的左侧紧挨着B的输入。连线尽量走直线避免交叉。如果必须交叉用隧道标签代替长距离连线。颜色方面可以用不同的颜色区分不同类型的信号比如数据线用黑色控制线用红色进位线用蓝色。Logisim支持修改连线的颜色在连线的属性里设置就行。8.4 保存与版本管理Logisim的电路文件是.circ格式建议每完成一个阶段就另存一个版本比如“加减法器_v1_全加器完成.circ”“加减法器_v2_加法器完成.circ”“加减法器_v3_最终版.circ”。这样如果后面改错了可以随时回退到之前的版本。另外Logisim的文件里包含了所有子电路的定义所以不需要单独保存子电路文件。但如果你在多个项目里复用同一个子电路可以把它导出为一个单独的.circ文件然后在其他项目里通过“项目”-“加载库”-“Logisim库”来引用。9. 关于实验报告和验收的一点个人建议做这个实验的时候很多同学把电路跑通就结束了实验报告随便写几句。但根据我的经验实验报告里的测试数据和调试过程才是真正体现你工作量的地方。建议在报告里附上完整的测试表格每一组测试数据都截图保存特别是溢出和零标志的测试结果。调试过程中遇到的问题和解决方法也值得写进去比如“进位链第3位断裂导致高位结果错误通过逐位检查进位信号定位到断点”。验收的时候老师通常会随机给几组输入让你现场演示。这时候如果你对电路结构非常熟悉能快速说出每一位的作用和信号流向验收就会很顺利。反之如果只是照着别人的电路抄了一遍被问到细节就答不上来。所以建议在画电路的时候每一步都想清楚为什么要这样做而不是机械地连线。最后分享一个我自己的习惯在电路画完之后我会把整个电路从头到尾给自己讲一遍假装在给别人讲解。如果讲到某个地方卡住了说明那里我还没完全理解需要回去再看。这个方法帮我发现了不少隐藏的问题也让我对加减法器的理解更加深入。
返回列表