ARTICLE DETAIL

资讯详情

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

Logisim实战:补码一位乘与Booth算法电路搭建指南

Logisim实战:补码一位乘与Booth算法电路搭建指南 1. 补码一位乘到底在解决什么问题1.1 从一次被扣分的实验说起很多同学第一次接触计算机组成原理的实验做到乘法器那一节时都会遇到同一个坎课本上讲原码一位乘讲得清清楚楚符号位单独处理数值位无脑加加加就行。可一到补码一位乘画风突变什么Booth算法、什么附加位、什么加减交替看得人一头雾水。我当年做这个实验的时候第一次交上去的电路被助教打回来理由很简单——符号位处理错了两个负数相乘结果变成了负数。这个问题的根源在于补码把符号位和数值位统一编码了你不能再像原码那样把符号拎出来单独算。补码一位乘的核心思路就是让符号位也参与运算通过一套巧妙的“判断-加减-移位”规则直接得到乘积的补码。它解决的不是“能不能算”的问题而是“能不能用同一套硬件、同一套流程把有符号乘法算对”的问题。Logisim这个工具特别适合拿来啃这块硬骨头。它不像Verilog那样一个*号就完事逼着你把每一个门电路、每一次移位都画出来。画完之后你对补码乘法的理解会从“背规则”变成“看透本质”。这篇内容适合两类人一类是正在做数字电路实验、被补码乘法折磨的学生另一类是想用Logisim练手、搞懂计算机底层运算逻辑的爱好者。哪怕你之前没碰过Logisim跟着走也能把电路搭出来。1.2 为什么偏偏选Logisim来做这件事有人会问现在都用Verilog、用Vivado了为什么还要用Logisim这种“画图工具”我的看法是Logisim的价值在于可视化。你写Verilog的时候一个assign {sum, cout} a b cin;就把加法器搞定了但你根本看不到进位是怎么一级一级传下去的。Logisim不一样你得亲手把全加器摆出来把进位线连起来这时候你才会真正理解“延迟”是怎么产生的。补码一位乘涉及大量的移位和条件加减用Logisim做你能直观地看到每一步之后寄存器里的值变成了什么。这种“看得见”的反馈对建立直觉特别重要。而且Logisim的电路文件很小方便分享和存档你做完之后可以直接把.circ文件发给同学参考。后面我会把关键模块的设计思路讲透你照着搭就行。1.3 补码一位乘的算法本质在动手画电路之前得先把算法逻辑理清楚。补码一位乘最经典的实现是Booth算法也叫“加减交替法”。它的核心规则可以用一句话概括根据乘数最低位和附加位的组合决定是加被乘数、减被乘数还是不加不减然后整体算术右移一位。具体来说在乘数寄存器的最低位后面额外挂一个触发器叫“附加位”初始值为0。每次迭代看两位当前最低位和附加位。如果是01说明遇到了一段连续的1的末尾需要加被乘数如果是10说明遇到了一段连续的1的开头需要减被乘数00和11则什么都不做。判断完之后把乘数寄存器和附加位一起算术右移一位同时把部分积的最低位挤进乘数寄存器的高位。这个规则为什么成立简单说Booth算法把乘数中连续的1串看成“一个大数减去一个小数”比如0111可以看成1000 - 0001这样就把多次加法压缩成了两次加减法。对于补码来说这种处理天然兼容符号位不需要单独判断正负。2. 动手前的准备工作与核心模块拆解2.1 Logisim环境准备与版本选择Logisim的版本选择有个小坑。网上流传的版本很多有原版Logisim 2.7.1还有Logisim-evolution。我建议用Logisim-evolution因为它对中文支持更好而且修复了原版的一些bug。下载的时候认准官方渠道别去乱七八糟的网站下有些捆绑了广告插件。安装完之后建议做两件事第一把字体调大一点默认字体在画复杂电路时看着费眼第二打开“项目”菜单里的“选项”把“电路仿真”里的“时钟频率”调低一些比如调到2Hz或4Hz这样你单步调试的时候能看清每一步的变化。如果频率太高信号一闪而过根本来不及观察。还有一个实用技巧Logisim支持“子电路”功能你可以把全加器、移位寄存器这些常用模块封装成子电路主电路里直接调用。这样画出来的图干净不容易乱。我后面讲的方案就是基于子电路来组织的。2.2 核心模块一8位可控加减法器补码一位乘需要做加法和减法所以得先有一个能根据控制信号决定做加法还是减法的模块。最直接的做法是用8个全加器串成行波进位加法器然后把减法的控制信号接到最低位的进位输入同时把被减数按位取反。具体连接方式是这样的设A和B是两个8位输入Sub是控制信号。当Sub0时做AB当Sub1时做A-B。实现上把B的每一位和Sub做异或异或的结果送入全加器的B端同时把Sub接到最低位全加器的进位输入。这样当Sub1时B被取反且最低位加了1正好是补码的“取反加一”也就是-B。这里有个细节要注意溢出问题。8位补码的表示范围是-128到127两个数相加可能会溢出。但在Booth算法中我们做的是部分积的累加最终结果用16位表示中间过程的溢出其实是被允许的因为高位会被后续的移位和累加吸收掉。不过为了调试方便我还是建议在加法器上挂一个溢出标志用最高位进位和次高位进位的异或来产生这样你能看到什么时候发生了溢出。2.3 核心模块二带附加位的移位寄存器这个模块是整个电路的关键。它需要容纳三个东西部分积8位、乘数8位、附加位1位。总共17位但部分积和乘数可以共用一个16位的寄存器附加位单独用一个触发器。移位操作是算术右移不是逻辑右移。区别在于算术右移时最高位保持不变也就是符号位扩展逻辑右移时最高位补0。补码乘法必须用算术右移否则负数会算错。实现算术右移的方法是把寄存器的第7位最高位直接连到第6位第6位连到第5位以此类推最低位连到附加位附加位原来的值丢弃。这样移位之后最高位保持原值实现了符号扩展。在Logisim里你可以用一个8位的移位寄存器子电路来实现这个功能然后把两个这样的子电路串联起来中间用一根线传递最低位到附加位。附加位用一个D触发器时钟信号和移位寄存器共用。2.4 核心模块三Booth判断逻辑这个模块负责根据乘数最低位和附加位的组合产生加、减、不加不减的控制信号。真值表很简单乘数最低位附加位操作00不加不减01加被乘数10减被乘数11不加不减用逻辑表达式表示就是Add (~Q0) Q_1Sub Q0 (~Q_1)。其中Q0是乘数最低位Q_1是附加位。这两个信号分别接到加减法器的控制端。注意Add和Sub不会同时为1所以不用担心冲突。在Logisim里用一个非门、两个与门就能实现。输入是Q0和Q_1输出是Add和Sub。简单吧但就是这个小模块决定了整个算法的正确性。2.5 核心模块四计数器与控制器整个乘法过程需要迭代8次对于8位乘法。所以需要一个3位的计数器从0数到7数到7的时候产生一个“完成”信号停止迭代。计数器每个时钟周期加1时钟信号由主控时钟提供。控制器的作用是协调各个模块的时序在每个时钟周期先让Booth判断逻辑根据当前Q0和Q_1产生Add/Sub信号然后加减法器计算出新的部分积接着移位寄存器在时钟边沿到来时完成移位最后计数器加1。这个顺序不能乱否则会算错。在Logisim里你可以用一个“时钟”组件产生周期信号然后用“分频”或者“边沿触发”来控制时序。建议用D触发器来搭建寄存器因为D触发器是边沿触发的时序更稳定。3. 完整电路搭建与实操步骤3.1 第一步搭建全加器与8位加减法器先新建一个电路命名为“FullAdder”。输入A、B、Cin输出Sum、Cout。真值表大家都熟直接画Sum A XOR B XOR CinCout (A AND B) OR (Cin AND (A XOR B))。用两个异或门、两个与门、一个或门搞定。然后新建“AddSub8”电路。把8个FullAdder子电路摆成一列进位线串联。输入A[7:0]、B[7:0]、Sub输出S[7:0]、Cout、Overflow。B的每一位先和Sub异或再接入全加器的B端。Sub接到最低位全加器的Cin。Overflow用最高位进位和次高位进位异或得到。搭完之后测试一下设A000001015B000000113Sub0结果应该是000010008Cout0。Sub1时结果应该是000000102因为5-32。如果不对检查异或门和进位线的连接。注意Logisim里的连线颜色代表信号状态深绿色是1浅绿色是0蓝色是未知红色是冲突。如果看到红色线说明有多个输出连到了同一根线上赶紧检查。3.2 第二步搭建算术右移寄存器新建“ShiftReg8”电路。用8个D触发器串联每个触发器的D端接前一个触发器的Q端但最高位的D端接自己的Q端保持符号位。时钟信号统一接到所有触发器的触发端。输入D[7:0]用于并行加载Load信号控制是加载还是移位。具体实现每个触发器前面加一个2选1多路选择器。Load1时选择外部输入的DLoad0时选择前一级的Q。最高位的多路选择器在Load0时选择自己的Q实现符号扩展。最低位的Q输出到外部用于连接到附加位。附加位用一个单独的D触发器D端接ShiftReg8的最低位Q时钟共用。这样每次移位最低位就进入附加位附加位原来的值丢弃。测试方法加载一个负数比如10000001-127然后连续移位观察最高位是否保持1。如果变成了0说明符号扩展没做对。3.3 第三步搭建Booth判断逻辑新建“BoothLogic”电路。输入Q0、Q_1输出Add、Sub。按照前面的逻辑表达式连接Add NOT(Q0) AND Q_1Sub Q0 AND NOT(Q_1)。用两个非门、两个与门即可。这个模块很简单但建议加一个LED指示灯显示当前是加、减还是不动。调试的时候一眼就能看出判断逻辑对不对。3.4 第四步搭建顶层乘法器电路新建“BoothMultiplier”电路这是主电路。需要以下组件两个8位输入引脚被乘数M和乘数Q一个16位输出引脚乘积P一个AddSub8子电路两个ShiftReg8子电路分别用于部分积和乘数一个BoothLogic子电路一个3位计数器一个时钟信号源若干控制信号Start、Reset、Done连接逻辑如下初始状态部分积寄存器清零乘数寄存器加载Q附加位清零计数器清零。每个时钟周期BoothLogic根据乘数寄存器最低位和附加位产生Add/Sub。AddSub8根据Add/Sub计算部分积寄存器当前值加减被乘数M。计算结果送回部分积寄存器的输入端。下一个时钟边沿部分积寄存器和乘数寄存器同时右移附加位更新。计数器加1。当计数器计到7时Done信号置1停止时钟输出乘积。这里有个关键点部分积和乘数要一起移位。也就是说部分积的最低位要移到乘数的最高位乘数的最低位要移到附加位。在Logisim里你可以把两个ShiftReg8串联起来部分积的最低位输出接到乘数寄存器的最高位输入。3.5 第五步参数计算与位宽选择为什么选8位因为8位补码乘法结果是16位Logisim的画布刚好放得下不会太拥挤。如果你要做16位乘法结果就是32位电路会变得很庞大调试起来也麻烦。建议先用8位把原理验证清楚再扩展到16位。迭代次数为什么是8次因为乘数是8位每一位都要处理一次。Booth算法每次处理乘数的一位结合附加位所以需要8个时钟周期。计数器从0数到7正好8个周期。部分积的初始值为什么是0因为乘法的本质是累加一开始还没有任何累加结果所以是0。被乘数M在整个过程中保持不变根据Booth判断结果决定加还是减。3.6 第六步完整仿真与验证搭好电路后用几组典型数据测试被乘数M乘数Q预期结果说明00000011 (3)00000101 (5)0000000000001111 (15)正正得正11111101 (-3)00000101 (5)1111111111110001 (-15)负正得负00000011 (3)11111011 (-5)1111111111110001 (-15)正负得负11111101 (-3)11111011 (-5)0000000000001111 (15)负负得正测试的时候先按Reset然后按Start观察每个时钟周期后部分积和乘数的变化。如果结果不对重点检查三个地方Booth判断逻辑的真值表、算术右移的符号扩展、部分积和乘数的串联顺序。提示Logisim的“仿真”菜单里有“时钟单步”功能可以一个周期一个周期地走特别适合调试这种迭代算法。4. 常见问题与排查技巧实录4.1 结果符号位错误怎么办这是最常见的问题。两个负数相乘结果应该是正数但你算出来是负数。原因通常是算术右移没做对最高位没有保持符号扩展导致负数在移位过程中变成了正数。排查方法单独测试ShiftReg8子电路。加载一个负数比如10000001连续移位8次观察最高位是否始终为1。如果某一次变成了0检查最高位触发器的D端是不是接了自己的Q端。如果接的是前一级的Q那就是逻辑右移不是算术右移。还有一种可能是部分积的初始值没清零。如果部分积寄存器上电后是随机值第一次累加就会出错。确保Reset信号能把所有触发器清零。4.2 迭代次数不对导致结果偏差有时候结果看起来“差不多对”但最后几位不对。这通常是迭代次数少了一次或多了一次。Booth算法对于n位乘法需要n次迭代。如果你只迭代了7次最后一位就没处理到。检查计数器是不是从0数到7。如果从1数到8也是8次但初始状态可能不对。建议用0到7这样第0个周期处理最低位第7个周期处理最高位逻辑更清晰。另外Done信号的产生时机要注意。应该是计数器计到7的那个周期结束时产生Done而不是计到8。如果Done早了一个周期最后一次移位就没执行。4.3 加减法器溢出但结果正确调试的时候你可能会发现中间某一步加减法器的Overflow标志亮了但最终结果是对的。这是正常的。因为Booth算法中间的部分积可能会超出8位补码的表示范围但高位会在后续移位中被“挤”出去最终16位结果是对的。比如部分积是01111111127加上被乘数000000011得到10000000-128溢出标志亮了。但在16位结果中这个-128会被正确解释为128的一部分。所以不要因为看到溢出就认为电路错了关键看最终16位结果。不过如果你用的是8位输出而不是16位输出那溢出就会导致结果错误。所以务必用16位输出。4.4 Logisim电路文件打不开或仿真卡死有时候你辛辛苦苦搭的电路保存后重新打开发现某些连线断了或者仿真的时候Logisim直接卡死。这通常是两个原因一是电路里有“振荡”回路比如某个信号自己连到自己导致无限循环二是时钟频率设得太高Logisim来不及刷新。解决方法检查所有连线确保没有输出直接连到自己的输入。时钟频率调到2Hz以下。如果还是卡死把电路分成几个子电路逐个测试定位问题模块。另外Logisim-evolution对中文路径支持不太好保存电路文件的时候尽量用英文路径避免出现乱码或打不开的情况。4.5 常见问题速查表现象可能原因解决方法结果符号位错误算术右移未做符号扩展检查最高位触发器D端接自己的Q最后几位不对迭代次数少一次计数器改为0到7共8次中间溢出但结果对正常现象确保输出为16位仿真卡死时钟频率过高或存在振荡回路降低频率检查连线电路文件打不开中文路径或版本不兼容用英文路径换Logisim-evolution部分积初始值不为0Reset信号未连接确保Reset能清零所有触发器乘数最低位和附加位判断反了BoothLogic输入接反交换Q0和Q_1的接线4.6 独家避坑技巧第一个技巧用LED阵列显示中间状态。在部分积、乘数、附加位、计数器的每一位上都挂一个LED调试的时候一眼就能看出哪一步不对。Logisim的“探针”组件也很好用可以显示信号的当前值。第二个技巧先做4位版本验证逻辑。8位电路有17个触发器连线复杂一旦出错很难定位。先用4位做一遍4位乘法只需要4次迭代电路小容易调试。4位验证通过后直接复制扩展成8位省时省力。第三个技巧保存多个版本。每完成一个模块就另存一个文件比如“FullAdder.circ”、“AddSub8.circ”、“BoothMultiplier_v1.circ”。这样如果后面改错了可以回退到之前的版本不用从头再来。第四个技巧用Logisim的“组合逻辑分析”功能。在“项目”菜单里有“分析组合逻辑”选项可以自动生成真值表和逻辑表达式。对于BoothLogic这种小模块可以用它来验证你的逻辑表达式对不对。5. 从8位到16位的扩展思路5.1 位宽扩展的核心改动8位版本跑通之后扩展到16位其实不难主要是工作量翻倍。需要改的地方有全加器从8个变成16个移位寄存器从8位变成16位计数器从3位变成4位16次迭代部分积和乘数的串联方式不变。但有一个细节要注意16位乘法的结果是32位Logisim的画布要足够大。建议把部分积寄存器和乘数寄存器分别放在上下两行中间用总线连接这样布局清晰。另外16位版本的时钟周期是16次仿真时间会变长。建议把时钟频率调到1Hz或者用“时钟单步”功能手动控制。5.2 性能优化减少迭代次数Booth算法有一个改进版本叫“改进Booth算法”每次看三位当前位、前一位、附加位可以一次处理两位乘数迭代次数减半。对于16位乘法只需要8次迭代。但判断逻辑会复杂一些需要处理8种组合。如果你已经把基础版跑通了可以尝试改进版。真值表如下Q1 Q0 Q_1操作000不加不减001加M010加M011加2M100减2M101减M110减M111不加不减其中2M可以通过把M左移一位得到。这样每次迭代移两位效率翻倍。不过电路复杂度也上去了建议先把基础版吃透再挑战。5.3 电路文件的组织与分享做完之后建议把电路文件整理一下。主电路命名为“BoothMultiplier”子电路分别命名为“FullAdder”、“AddSub8”、“ShiftReg8”、“BoothLogic”。这样别人打开你的文件一眼就能看懂结构。如果要把电路文件分享给同学记得把Logisim的版本号写在文件名里比如“BoothMultiplier_LogisimEvolution_2.15.circ”。因为不同版本的Logisim电路文件格式可能不兼容写清楚版本可以避免对方打不开。另外Logisim支持导出图片。在“文件”菜单里有“导出图片”选项可以把电路图导出成PNG方便放到实验报告里。导出的时候选择高分辨率这样打印出来也清晰。6. 我踩过的那些坑与最终体会第一次做补码一位乘的时候我犯了一个低级错误把Booth判断逻辑的Q0和Q_1接反了。结果就是本该加的时候减了本该减的时候加了算出来的结果完全不对。我对着电路查了两个小时最后用真值表逐行对比才发现是输入接反了。所以接线的时候一定要对照真值表一根一根检查别凭感觉。第二次踩的坑是算术右移。我一开始用的是逻辑右移负数移位后最高位补0结果负数变成了正数。后来在ShiftReg8的最高位加了一个反馈线让最高位触发器的D端接自己的Q端问题才解决。这个细节课本上不会专门讲但实际做的时候一定会遇到。第三次是计数器的问题。我一开始让计数器从1数到8结果第8个周期结束时Done信号没产生电路一直在跑。后来改成0到7Done在计到7时产生就正常了。所以计数器的起止值要和迭代次数严格对应差一个都不行。最后分享一个调试心得不要一次性把整个电路搭完再测试。先搭全加器测再搭加减法器测再搭移位寄存器测最后搭顶层。每搭一个模块就单独验证确保没问题再往上集成。这样即使出错也能快速定位是哪个模块的问题。我见过太多同学一口气搭完结果仿真不对又不知道错在哪只能从头查起浪费大量时间。这个电路做完之后我对补码乘法的理解完全不一样了。以前是背规则现在是看本质。Booth算法之所以巧妙是因为它把“符号位参与运算”这件事变得自然不需要任何特殊处理。这种设计思想在后来的流水线CPU设计、浮点运算单元设计里都会反复出现。所以花时间把这块啃透绝对值得。
返回列表