ARTICLE DETAIL

资讯详情

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

Logisim实战:8位可控加减法器设计原理与实现

Logisim实战:8位可控加减法器设计原理与实现 1. 从零理解8位可控加减法器到底在做什么1.1 这个电路的本质一个会“变脸”的加法器很多人第一次接触8位可控加减法器脑子里冒出来的第一个问题就是为什么叫“可控”它到底控制了什么说白了这个电路的核心能力就是——你给它一个控制信号它就能在“加法模式”和“减法模式”之间自由切换。加法和减法在硬件层面其实是同一套逻辑只不过减法多了一步“取补码”的操作。我在带新手做Logisim实验的时候发现大家最容易犯的错误就是把加法和减法当成两个完全独立的电路去设计。一旦你这么想电路规模会直接翻倍而且调试起来非常痛苦。正确的思路是减法 A - B 等价于 A (-B)而 -B 在补码表示下等于 B 的按位取反再加1。所以一个可控加减法器的核心结构就是在加法器的B端输入前面加一层可控的反相器用异或门实现然后把控制信号同时接到最低位的进位输入上。这个设计思路的好处非常明显整个电路只需要一个8位行波进位加法器作为核心外加8个异或门和一个控制信号扇出就能同时支持加法和减法。电路规模小、逻辑清晰、调试方便这也是为什么它成为了计算机组成原理课程中最经典的实验之一。1.2 为什么选择行波进位而不是超前进位在Logisim里搭8位加法器你面前有两条路行波进位加法器和超前进位加法器。超前进位在速度上确实有优势它通过并行计算进位来减少延迟但对于8位这个规模来说行波进位的延迟完全在可接受范围内。更重要的是行波进位加法器的结构极其规整——8个全加器首尾相连进位像波浪一样从低位传到高位这种规整性让电路调试变得非常直观。我实测过在Logisim的仿真环境下8位行波进位加法器的进位传播延迟大概在几十纳秒的量级取决于你的仿真时钟设置对于教学实验和中小规模数字系统设计来说完全够用。而且行波进位加法器的溢出检测逻辑非常清晰只需要比较最高位的进位输入和进位输出是否一致就能判断是否发生了有符号溢出。超前进位虽然快但溢出检测的逻辑会复杂不少对于刚上手Logisim的同学来说行波进位是更稳妥的选择。1.3 全加器整个电路的原子单元8位加法器说到底就是8个全加器的级联。全加器的真值表大家应该都背过三个输入A、B、Cin两个输出Sum、Cout。Sum等于A、B、Cin三者的异或Cout等于AB BCin ACin。在Logisim里你可以直接用两个异或门、两个与门、一个或门搭出来也可以直接调用Logisim内置的“全加器”组件。我的建议是第一个全加器一定要自己用基本门电路搭一遍。为什么因为只有你自己搭过你才能真正理解进位是怎么产生的、和是怎么算出来的。搭完一个之后后面的7个直接复制粘贴就行。Logisim支持子电路封装你可以把全加器做成一个子电路然后在主电路里实例化8次这样电路图会非常干净。注意用Logisim封装子电路的时候记得把全加器的输入输出引脚方向设置正确。A、B、Cin设为输入Sum、Cout设为输出。如果方向设反了在主电路里连线的时候会非常别扭。2. 核心细节拆解从全加器到可控加减法器2.1 全加器的三种实现方式与选型建议在Logisim里实现全加器我总结下来有三种常见做法每种都有各自的适用场景实现方式所需门电路优点缺点适用场景基本门电路搭建2个异或门2个与门1个或门理解原理最透彻连线较多容易出错第一次学习全加器原理Logisim内置全加器直接调用组件快捷方便看不到内部结构快速搭建大型电路子电路封装先搭好再封装兼顾原理与效率需要额外封装步骤推荐用于8位加法器我个人的习惯是先用基本门电路搭一个全加器验证真值表没问题之后把它封装成子电路命名为“FullAdder”。然后在主电路里放8个“FullAdder”实例把它们的进位链串起来。这样既保证了原理的正确性又让主电路图保持了整洁。2.2 可控反相器的设计异或门的关键作用可控加减法器的“可控”二字体现在哪里就体现在B端输入前面的那8个异或门上。每个异或门的一个输入是B的对应位另一个输入是控制信号Sub。当Sub0时异或门输出等于B因为X⊕0X电路执行加法当Sub1时异或门输出等于B的反因为X⊕1~X电路执行减法。这里有一个非常关键的细节控制信号Sub不仅要接到8个异或门上还要接到最低位全加器的进位输入Cin上。为什么因为减法需要“取反加1”异或门完成了“取反”而那个“加1”就是通过把Sub接到最低位的Cin来实现的。当Sub1时最低位全加器的Cin1相当于在取反后的B上加了一个1正好完成了补码转换。提示如果你忘了把Sub接到最低位的Cin上减法结果会差1。这是新手最常犯的错误之一调试的时候如果发现减法结果总是比预期少1先检查这里。2.3 溢出检测的两种思路与电路实现溢出检测是8位有符号加减法器必须处理的问题。两个8位有符号数相加结果可能超出-128到127的表示范围这时候就需要溢出标志来指示结果无效。溢出检测有两种主流方法第一种方法是比较最高位的进位输入和进位输出。对于有符号加法如果Cout(最高位)和Cin(最高位)不同就说明发生了溢出。具体来说可以用一个异或门Overflow Cout7 ⊕ Cin7。这个方法的电路非常简单只需要一个异或门。第二种方法是比较两个操作数的符号位和结果的符号位。如果两个正数相加得到负数或者两个负数相加得到正数就说明溢出了。逻辑表达式是Overflow A7·B7·~S7 ~A7·~B7·S7。这个方法需要更多的门电路但逻辑更直观。我在实际设计中更倾向于第一种方法因为它只需要一个异或门而且和行波进位加法器的结构天然契合。你只需要把最高位全加器的进位输入和进位输出引出来接一个异或门就行。2.4 8位行波进位加法器的进位链分析8位行波进位加法器的进位传播过程是这样的最低位全加器产生Cout0这个Cout0成为次低位的Cin1次低位产生Cout1依次类推直到最高位产生Cout7。整个进位链的延迟是8个全加器延迟的总和。在Logisim里你可以通过“仿真→时钟频率”来调整仿真速度观察进位传播的过程。我建议在调试阶段把时钟频率调低一些这样你能清楚地看到进位信号从低位向高位逐级传播的过程。等电路验证无误之后再把频率调高。这里有一个实操小技巧在进位链的每一级都放一个探针Probe这样你可以实时看到每一级的进位值。如果某一级的进位不对你可以快速定位到是哪个全加器出了问题。3. 实操过程5步搞定8位可控加减法器3.1 第一步搭建并验证全加器子电路打开Logisim新建一个电路命名为“FullAdder”。从左侧工具栏拖入以下组件两个异或门、两个与门、一个或门。按照全加器的逻辑表达式连线Sum A ⊕ B ⊕ CinCout (A · B) (B · Cin) (A · Cin)连线完成后用Logisim的“组合逻辑分析”功能验证真值表。具体操作是点击“项目→分析组合逻辑”把A、B、Cin设为输入Sum、Cout设为输出点击“计算真值表”。如果真值表与标准全加器一致说明子电路正确。验证通过后右键点击电路画布空白处选择“编辑电路外观”把全加器的引脚排列整齐。然后回到主电路在“项目→添加电路”中把FullAdder添加为子电路这样你就可以在主电路里直接调用它了。注意Logisim的组合逻辑分析功能非常实用建议每搭完一个子电路都跑一遍真值表验证。很多同学跳过这一步结果后面调试的时候花了大量时间排查一个本可以避免的错误。3.2 第二步级联8个全加器构建行波进位加法器在主电路命名为“AddSub8”中放置8个FullAdder子电路实例。把它们的进位链串起来第0位的Cout接第1位的Cin第1位的Cout接第2位的Cin以此类推直到第7位。输入方面A[7:0]和B[7:0]分别接到8个全加器的A端和B端。输出方面S[7:0]从8个全加器的Sum端引出Cout从第7位的Cout引出。这里有一个连线技巧使用Logisim的“分线器”Splitter来管理8位总线。你可以把A[7:0]通过一个分线器拆成8条单独的线分别接到每个全加器的A端。同样8个Sum输出通过分线器合并成S[7:0]。这样电路图会非常清晰不会出现一团乱麻的情况。分线器的配置方法是在分线器属性中设置“扇入”为8“扇出”为8位宽为1。然后把输入总线接到分线器的输入端输出端会分出8条线分别对应每一位。3.3 第三步加入可控反相器和减法控制信号在B端输入和全加器之间插入8个异或门。每个异或门的一个输入接B的对应位另一个输入统一接控制信号Sub。异或门的输出接到对应全加器的B端。同时把Sub信号接到最低位全加器第0位的Cin上。这样当Sub1时B端被取反且最低位加了1完成了补码转换。这里需要注意第0位全加器的Cin不再接地而是接Sub信号。其他7个全加器的Cin仍然接前一级的Cout。这个细节如果搞错了减法结果就会出错。提示在Logisim里你可以用“隧道”Tunnel来传递Sub信号这样不需要拉很长的线。给隧道命名“Sub”然后在需要的地方放置同名隧道即可。3.4 第四步实现溢出检测电路从第7位全加器引出Cout7和Cin7即第6位的Cout接一个异或门输出即为溢出标志Overflow。当Overflow1时表示有符号运算结果溢出。如果你还需要无符号进位标志Carry直接把Cout7引出来即可。Carry1表示无符号加法产生了进位或者无符号减法需要借位。在Logisim里你可以用“LED”或“探针”来显示Overflow和Carry的状态。我建议用不同颜色的LED红色表示Overflow绿色表示Carry这样一眼就能看出电路的工作状态。3.5 第五步全面测试与验证测试是验证电路正确性的关键步骤。我整理了一份测试用例表覆盖了加法、减法、溢出等各种情况测试场景A (十进制)B (十进制)Sub预期S预期Overflow预期Carry正数加法253005500负数加法-25-300-5501正数减法503012001负数减法-50-301-2000正溢出100500-10610负溢出-100-50010611边界测试12710-12810零测试000000在Logisim里逐项测试这些用例观察输出是否与预期一致。如果某一项不对按照“从低位到高位”的顺序逐级排查进位链通常问题都出在进位链的某个环节上。4. 常见问题与排查技巧实录4.1 减法结果总是差1怎么办这是新手遇到频率最高的问题。减法结果差199%的原因是Sub信号没有接到最低位全加器的Cin上。回顾一下补码减法的原理A - B A ~B 1。异或门完成了~B但那个“1”必须通过最低位的Cin来实现。如果你把最低位的Cin接地了那就只完成了A ~B结果自然差1。排查方法检查第0位全加器的Cin引脚确认它连接的是Sub信号而不是地。在Logisim里你可以把鼠标悬停在连线上查看网络名称确认信号来源。4.2 溢出标志始终为0或始终为1溢出标志异常通常有两种原因。第一种是Cin7和Cout7接反了。Overflow Cout7 ⊕ Cin7如果你把这两个信号接反了异或结果不变所以这个错误不会导致溢出标志异常。真正会导致溢出标志始终为0的原因是你误把Cout7和Cout7自己接了异或或者两个输入都接到了同一个信号上。第二种原因是最高位全加器的进位输入没有正确连接。如果Cin7悬空或者接地了那么Cin7始终为0Overflow就变成了Cout7的值这显然不对。排查方法是用探针分别观察Cin7和Cout7的值确认它们在各种输入组合下都能正确变化。4.3 Logisim仿真速度太慢或电路不响应Logisim的仿真速度受时钟频率设置影响。如果你发现电路响应很慢可以尝试以下操作点击“仿真→时钟频率”把频率调高比如调到1000Hz以上。如果电路完全不响应检查是否误触了“仿真→启用时钟”或者“仿真→重置仿真”。另一个常见问题是电路出现了振荡。如果电路中存在组合逻辑环路比如某个门的输出经过一系列逻辑又回到了自己的输入Logisim会陷入无限循环表现为电路“卡死”。排查方法是点击“仿真→组合逻辑分析”如果Logisim提示“检测到振荡”就说明存在环路。你需要找到并断开这个环路。4.4 分线器配置错误导致信号错乱分线器是Logisim里最容易配错的组件之一。常见错误包括扇入扇出设置反了、位宽设置不对、输入输出方向搞混。如果发现总线上的信号错乱先检查分线器的配置。正确的配置方法是假设你要把8位总线A[7:0]拆成8条单位线分线器的“扇入”应设为8“扇出”应设为8“位宽”设为1。输入接A[7:0]输出端会分出8条线从上到下分别对应第7位到第0位注意顺序。如果你发现位的顺序反了调整分线器的“输出位序”属性即可。4.5 常见问题速查表问题现象最可能原因排查方法解决方法减法结果差1Sub未接最低位Cin检查第0位Cin连接将Sub接到第0位Cin溢出标志异常Cin7或Cout7连接错误用探针观察两个信号重新连接异或门输入电路卡死存在组合逻辑环路运行组合逻辑分析断开环路信号错乱分线器配置错误检查扇入扇出和位宽重新配置分线器加法结果错误全加器进位链断裂逐级检查Cout-Cin连接重新连接进位链所有输出为0输入信号未连接检查A、B输入连接输入引脚4.6 独家避坑经验分享第一个经验先搭4位版本验证思路再扩展到8位。4位可控加减法器的结构和8位完全一样但连线少一半调试起来快得多。等4位版本验证无误后直接复制扩展到8位出错的概率会大大降低。第二个经验用Logisim的“日志”功能记录测试过程。点击“仿真→日志”可以记录每一次输入变化和输出结果。这样当你发现某个测试用例失败时可以回放日志看看是哪一步出了问题。第三个经验给关键信号起名字。在Logisim里你可以右键点击连线选择“编辑标签”给信号起一个有意义的名字比如“Carry0”、“Sum3”。这样在调试的时候你可以通过信号名快速定位问题而不是对着一堆无名线发呆。第四个经验保存多个版本。每完成一个关键步骤就另存一个版本比如“AddSub8_v1_全加器完成”、“AddSub8_v2_加法器完成”、“AddSub8_v3_减法完成”。这样如果后面改出了问题可以快速回退到上一个正确版本。5. 从8位到N位设计思路的扩展与迁移5.1 参数化设计让电路支持任意位宽8位可控加减法器的设计思路可以直接扩展到任意位宽。如果你需要16位、32位甚至64位的加减法器只需要增加全加器的数量和异或门的数量即可。控制逻辑、溢出检测逻辑完全不变。在Logisim里你可以通过“子电路”和“分线器”的组合来实现参数化设计。具体做法是把全加器封装成子电路然后在主电路里放置N个实例。分线器的扇入扇出设为N位宽设为1。这样你只需要修改分线器的参数就能快速调整电路位宽。不过需要注意的是随着位宽增加行波进位加法器的延迟会线性增长。对于32位以上的加法器建议考虑超前进位或分组进位结构。但在Logisim的教学实验中8位和16位用行波进位完全够用。5.2 从加减法器到ALU的演进8位可控加减法器是构建ALU算术逻辑单元的基础。一个完整的ALU除了加减法之外还需要支持与、或、非、异或等逻辑运算以及左移、右移等移位操作。你可以在加减法器的基础上增加一个多路选择器MUX根据操作码选择不同的运算结果。具体做法是把加减法器的输出接到MUX的一个输入端把逻辑运算单元的输出接到另一个输入端MUX的选择信号由操作码控制。这样就能实现一个简单的ALU。如果你在学计算机组成原理这个扩展练习非常值得做。5.3 在更大系统中的角色从加法器到CPU在单周期MIPS或多周期MIPS的Logisim设计中8位可控加减法器是ALU的核心组件。MIPS的ALU需要支持加法、减法、与、或、异或、小于比较等操作其中加减法就是由可控加减法器实现的。如果你正在做MIPS CPU的Logisim实验建议先把加减法器单独调通再集成到ALU中最后集成到CPU中。这种“自底向上”的设计方法可以大大降低调试难度。我见过很多同学一上来就搭整个CPU结果出了问题根本不知道是哪个模块的错最后花了几倍的时间才搞定。5.4 对比Verilog实现硬件描述语言的视角如果你熟悉Verilog可以对比一下用Verilog实现8位可控加减法器的代码module add_sub_8bit( input [7:0] A, input [7:0] B, input Sub, output [7:0] S, output Overflow, output Carry ); wire [7:0] B_xor; wire [8:0] C; assign B_xor B ^ {8{Sub}}; assign C[0] Sub; genvar i; generate for(i 0; i 8; i i 1) begin: adder_loop assign S[i] A[i] ^ B_xor[i] ^ C[i]; assign C[i1] (A[i] B_xor[i]) | (B_xor[i] C[i]) | (A[i] C[i]); end endgenerate assign Carry C[8]; assign Overflow C[8] ^ C[7]; endmodule这段代码的逻辑和Logisim电路完全对应B_xor是异或门阵列的输出C[0]是Sub信号C[8]是最高位进位输出Overflow是C[8]和C[7]的异或。如果你同时在做Logisim和Verilog的实验对照着看会理解得更深。5.5 实际工程中的加减法器设计考量在实际的芯片设计中加减法器的设计远不止Logisim里这么简单。工程师需要考虑功耗、面积、时序、可测试性等一系列问题。比如行波进位加法器虽然面积小但延迟大超前进位加法器延迟小但面积大。在实际项目中通常会根据具体的时序约束和面积约束来选择合适的结构。另外实际工程中还会用到进位选择加法器、进位跳跃加法器等变体这些结构在特定场景下能取得更好的面积-延迟积。不过对于Logisim教学实验来说掌握行波进位和超前进位这两种基本结构就足够了。我在实际使用Logisim做教学实验的过程中最大的体会是不要怕犯错但要学会快速定位错误。8位可控加减法器涉及全加器、进位链、补码运算、溢出检测等多个知识点任何一个环节出错都会导致结果异常。掌握一套系统的排查方法比记住电路图本身更重要。我通常的排查顺序是先验证全加器子电路再验证4位加法器然后验证4位减法器最后扩展到8位。每一步都确认无误后再进行下一步这样即使出了问题也能快速缩小排查范围。最后分享一个Logisim使用的小技巧你可以把常用的子电路比如全加器、分线器配置保存到一个模板文件里下次做新实验的时候直接导入省去重复搭建的时间。Logisim支持“项目→加载库→Logisim库”来导入外部电路文件这个功能在做系列实验的时候特别有用。
返回列表