news 2026/10/5 6:16:51

Logisim移位相加乘法器设计详解:从数据通路到状态机控制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Logisim移位相加乘法器设计详解:从数据通路到状态机控制

Logisim 的计组实验做到乘法器这一站,基本就意味着课程设计要开始动真格了。我是在捋西电计组课设基本模型机之前,先被这个实验卡了将近两天,调试过程中把移位加法、寄存器使能、状态机时钟这些概念重新过了一遍才真正跑通。这篇把乘法器背后的设计思路、Logisim 里的连线要点、测试用例和排错经验一次说清楚,希望能让正在做这个实验的同学少走一晚上弯路。

这个实验表面上是“用 Logisim 搭一个乘法器”,实际上考察的是你对数据通路和控制逻辑的整体理解。乘法器不是孤立的组合电路,它里面有寄存器、加法器、移位逻辑、计数器、状态判断,几乎把前面所有实验的基础模块都串起来了。无论你之后是继续做单总线 CPU 设计,还是应付考研 408 里的计组题,把这个实验吃透都特别值。

1. 这个实验到底在训练什么

1.1 乘法器不是“一个电路”,是一台小机器

很多人第一次听说乘法器实验,第一反应是“乘法不就是一堆加法?”话是没错,但计组实验里的乘法器,重点不在“怎么算出乘法”,而在“怎么用寄存器时序把乘法算出来”。

你可以把乘法器理解成一台迷你计算机:被乘数和乘数要先存进寄存器,然后控制器根据乘数的某一位决定“加不加被乘数”,加完再移位,如此反复。整个过程里,数据是在时钟边沿下一拍一拍流动的。这和你在 Logisim 里搭一个单纯的全加器阵列完全不是一回事。

计组课安排乘法器实验,目的就是让你把前面学过的寄存器、ALU、移位、控制状态机这些东西,放到一个真实的问题里组合起来。等你把乘法器调通,再去碰单总线 CPU 设计里的取指、译码、执行周期,你会觉得那些控制信号的写法非常眼熟。

1.2 Logisim 适合做什么,不适合做什么

Logisim 是一个教学用的数字逻辑仿真工具,它的最大优点是“看得见”。每个寄存器的值可以点开看,每根线的电平可以用探针量,时钟可以手动一步步打。调试乘法器这种带反馈的数据通路,Logisim 比写 Verilog 直观太多。

但 Logisim 也有自己的脾气,它对组合逻辑环路的检查很严格,有时候你只是想让某根线“暂时不动”,它就会给你报错。还有就是时钟问题,如果多个寄存器共用一个时钟源,仿真时容易出现“看起来全都在变,但结果就是不对”的情况。后面我会专门讲这些坑。

2. 乘法器的几种实现思路,以及为什么实验常用移位相加

2.1 回到手算竖式:一位一位乘过去

十进制的竖式乘法大家都会:把乘数每一位分别和被乘数相乘,得到部分积,再把所有部分积按位对齐相加。二进制也一样,只是每一位只有 0 和 1,所以部分积要么是被乘数本身,要么是 0。

问题是,如果直接按竖式搭电路,n 位乘 n 位需要 n 个 n 位加法器同时工作,这就是组合逻辑阵列乘法器。它速度非常快,但电路规模很大。对计组实验来说,更经典的做法是“移位相加”:串行地一位一位处理乘数,用一个加法器反复累加,把部分积逐步移位累积起来。

移位相加乘法器本质上是拿时间换面积。它需要的硬件很少,一个加法器、几个寄存器、一个移位器就够了,但需要 n 个时钟周期才能算完。这个思路和 CPU 里“用有限硬件资源完成复杂运算”的思想完全一致,所以教材和实验课普遍采用它。

2.2 原码一位乘、补码一位乘和 Booth 算法

如果乘数和被乘数都是无符号数,直接按“最低位判断、加被乘数、右移”的流程做就行,这叫原码一位乘。原码一位乘的逻辑很简单:先把符号位单独拎出去异或,数值部分按无符号数相乘,最后再拼上符号位。

如果要计算补码数,事情就麻烦一点。补码的最高位是符号位,不能当普通数值位处理,所以出现了补码一位乘,也就是 Booth 算法。Booth 算法的核心是引入一个额外的附加位 Q(-1),每次根据乘数最低位和附加位的差值来决定“加被乘数”“减被乘数”还是“不变”,然后做算术右移。

408 统考里经常考 Booth 算法的计算过程。比如让你算两个补码相乘的中间步骤,或者问你“Booth 算法一次判断几位乘数决定一次操作”。实验时候如果只要求无符号乘法,可以用原码一位乘;如果要求支持负数,就必须上 Booth。

2.3 阵列乘法器:到底什么时候用

阵列乘法器是纯组合逻辑,一旦输入稳定,经过一段传播延迟后直接输出结果,不需要时钟。它的优点是吞吐率高,缺点是硬件多。n 位阵列乘法器大约需要 n 的平方个全加器,位数一上去面积就很恐怖。

在实验里,我建议不要把阵列乘法器作为首选。因为纯组合逻辑电路在 Logisim 里看起来简单,但连线极多,查错很痛苦。而且计组实验的隐藏要求是“用可控时序实现运算”,你要的是一台能由外部控制何时开始、何时结束的乘法器,而不是一个黑盒。

当然,如果你想理解 408 里“并行乘法器为什么快”,可以在移位相加乘法器做完之后,额外搭一个 2 位或 4 位的阵列乘法器感受一下。但作为实验八的主体,老老实实做移位相加更符合课程目标。

3. Logisim 实操:搭一个可复现的 4 位无符号乘法器

3.1 顶层端口规划:先想清楚谁进谁出

打开 Logisim 新建电路之前,先把端口定下来。我做的是一个 4 位无符号数乘法器,输入两个 4 位数,输出一个 8 位乘积。顶层端口如下:

端口名方向位宽作用
X输入4 位被乘数
Y输入4 位乘数
Start输入1 位开始信号,高电平有效
CLK输入1 位时钟,控制所有寄存器同步更新
Reset输入1 位异步清零,复位所有状态
Product输出8 位最终乘积
Done输出1 位计算完成标志,高电平有效

端口定好后,再去定内部寄存器。我用了这几个:

  • M 寄存器:4 位,存被乘数 X,整个计算过程中不变。
  • Q 寄存器:4 位,初始存乘数 Y,之后每周期右移一位,最低位 Q0 决定当前要不要加被乘数。
  • A 寄存器:5 位,存部分积的高位,注意这里比乘数多一位,用来存中间进位。
  • 计数器:2 位就够了,但用 3 位更保险,用来记录循环次数。

A 寄存器为什么要 5 位?因为每次加法可能是“当前部分积 + 被乘数”,两个 4 位数相加最多产生 5 位结果。如果 A 只有 4 位,最高位的进位就直接丢了,结果必错。

3.2 数据通路连线要点:先画数据,再画控制

连线之前,在纸上把数据通路画出来,这一步绝对不能省。我最后在 Logisim 里的连线方式是这样的:

加法器用 5 位加法器,两个输入分别是 A 寄存器当前值和被乘数选择器的输出。选择器的作用是:当 Q0 为 1 时,把 M 寄存器扩展成 5 位送给加法器;当 Q0 为 0 时,送 0。

加法器的输出记为 S。注意,S 是“加完之后的完整部分积”,但乘法器每个周期要求“加完再右移”,所以不能直接把 S 送回 A,要先把 S 逻辑右移一位再送回 A。具体接法:A 寄存器的输入取 S 的高 4 位,即 S[4:1],最高位接 0。这样 A_next 就等于 S >> 1。

Q 寄存器的输入接法有点绕,但理解了就很简单:Q 要右移,Q 的最高位要接收从 A 那边移过来的位。在“加完再右移”的模型里,移入 Q 最高位的是加法器输出 S 的最低位,也就是 S[0]。所以 Q 寄存器的输入 = {S[0], Q[3:1]}。最高位是 S[0],低三位是原来 Q 的高三位。

M 寄存器只在 Start 信号有效时载入 X。Q 寄存器在 Start 有效时载入 Y,其他时间接收右移拼接结果。A 寄存器在 Start 有效时清零,其他时间接收 S >> 1。计数器在 Start 有效时清零,其他时间每周期加一。

用 Logisim 里的话说,就是给 A、Q、计数器都接一个 2 选 1 多路选择器:一侧是“初始化值”,另一侧是“运行周期值”,选择端由 Start 控制。M 寄存器的使能端也要接 Start,保证运行期间 M 不动。

3.3 控制器怎么设计:计数器和状态位就够了

很多同学卡在“什么时候算结束”上。其实 4 位无符号乘法只需要循环 4 次,所以一个计数器就够。我用的方案是:Start 有效时计数器清零,之后每个 CLK 上升沿计数器加一。当计数器计到 4 时,输出 Done 为 1,Q 和 A 里的最终结果保持不变。

但这里有个细节:如果计数器一直加下去,到 5、6、7 之后结果会不会被破坏?会。所以要把计数器的使能控制住。最简单的方法是加一个 D 触发器作为“完成标志”。当计数器等于 4 时,完成标志置 1,这个完成标志反过来禁止计数器继续加。下次 Reset 或 Start 时再清掉。

如果你想让乘法器看起来更像一个状态机,可以分成三个状态:IDLE、WORK、DONE。IDLE 状态等 Start,WORK 状态循环 n 次,DONE 状态置 Done 信号。但在 Logisim 实验里,用计数器加 D 触发器已经能完成任务,代码量更少,也不容易错。状态机的好处是逻辑更清晰,适合后面做单总线 CPU 时复用,这个我放到第五节说。

3.4 单步运行观察寄存器变化

搭完电路别急着看结果,先用一个简单例子手工验证。我用的是 X=3,Y=5,也就是 0011 × 0101,期望结果是 00001111,十进制 15。

在 Logisim 里把时钟设为手动步进,每按一次 CLK,记一次 A、Q、S 的值:

周期旧 A旧 QQ0加法器输出 SA 新值Q 新值
初始0000001011---
1000000101100011000011010
2000011010000001000001101
3000001101100011000011110
4000011110000001000001111

第 4 个周期结束后,A = 00000,Q = 1111,拼接成 00001111,正好是 15。看到这个表,你就明白“加完再右移”到底是怎么在寄存器之间流动的了。

最终输出 Product 只需要把 A 的低 4 位和 Q 拼起来。因为 A 的最高位在这个算法里永远是 0,所以直接 Product = {A[3:0], Q} 就行。

4. 测试用例与常见问题排查

4.1 边界测试用例:把乘法器拷打一遍

电路跑通一个例子不代表正确。我每次调完都会跑一组边界用例,这些用例能覆盖绝大多数连线错误和位宽问题。

用例XY期望乘积实际结果说明
0×0000000000000000000000000全零路径
0×15000011110000000000000000被乘数为 0
1×15000111110000111100001111倍乘为自身
15×15111111111110000111100001最大数,检查进位
3×5001101010000111100001111最常规用例
10×10101010100110010001100100检查多位进位

如果 15×15 算出来不是 225,问题基本出在 A 寄存器位宽不够,或者加法器进位没接对。如果 1×15 都不对,那大概率是 Start 初始化逻辑出了问题,Q 寄存器没有在开始前正确载入 Y。

4.2 常见报错和现象速查表

Logisim 报错分两种,一种是编译时报错,一种是仿真现象不对。下面这些是我实际踩过的坑,直接整理成速查表:

现象或报错大概率原因排查方法
输出始终为 0Q 或 A 寄存器没接到 CLK;Start 初始化没生效手动步进,看每个周期 A/Q 是否变化
结果只有低 4 位对,高 4 位不对A 寄存器位宽不够;Product 拼接顺序错检查 A 是否为 5 位,检查拼接是不是 A[3:0] + Q
组合回路报错,黄色波浪线某根线从输出直接回到同一个逻辑的输入,中间没有寄存器顺着报警路径找,确认 MUX 选择端是不是接到了组合输出
结果在几个值之间乱跳多个寄存器使用了不同时钟源,或时钟步进时同时触发多级状态所有寄存器统一接同一个 CLK,用 Reset 先清零
Start 按了没反应Start 信号作为组合条件没有经过时钟同步,导致初始化只在瞬间有效把 Start 当成寄存器输入的选择条件,而不是直接强置寄存器
手动步进一次后,A 和 Q 都变了但 Done 不变计数器没有正确产生完成信号,或者完成标志没有锁存单独拉一根线出来看计数器当前值

Logisim 里有一个特别好用的排查工具是探针。你可以在 A、Q、加法器输出 S、计数器输出上各拉一个探针,然后手动步进,观察每个周期数据怎么流动。如果某一个周期 S 的值和你手工算的不一样,问题就锁定在那一段组合逻辑上。

4.3 一个容易忽略的复位问题

寄存器清零分同步清零和异步清零。Logisim 里寄存器自带的 CLR 端是异步清零,只要 CLR 为 1,输出立刻变 0,不受时钟控制。这个特性在初始化时很有用,但也容易出问题。

如果你用 Reset 同时清零 A、Q、计数器,而 Reset 信号一直为 1,那么所有寄存器都会一直被清零,Start 按多少次都没用。正确做法是:Reset 是高电平有效,平时保持 0;点一下置 1 再恢复 0。或者用 Start 信号去控制“初始化数据的载入”,而不是用 Reset 一直卡住电路。

我在实验里习惯把 Start 和 Reset 分开:Reset 负责总复位,Start 负责开始计算。这样即使 Start 一直为 1,也只会在第一个时钟周期载入初值,之后的每个周期会正常执行移位累加,不会重复初始化。

5. 从乘法器到单总线 CPU 设计:这个实验的真正后劲

5.1 控制状态机的可复用性

做完乘法器,你会发现它的控制部分其实就是一个小的有限状态机:等待开始、进入循环、完成输出。这个套路和单总线 CPU 设计里的取指周期、执行周期非常像。单总线 CPU 设计 Logisim 里,你需要根据操作码和当前状态产生一系列控制信号,本质上和你根据 Q0 决定“加还是不加”是同一个思路。

所以我在做乘法器的时候,特意没有把控制逻辑写成一坨乱七八糟的门电路,而是用“状态 + 计数器”的方式组织。等后面做模型机时,这套控制逻辑可以直接扩展成微程序控制器或者硬布线控制器。很多同学到课设时觉得状态机难,是因为没有在前面乘法器实验里把状态编码、状态转移、输出译码这些基本功练熟。

5.2 扩展方向:负数、更大位宽和数码管显示

如果实验要求支持负数乘法,就得在原码一位乘的基础上引入符号位处理,或者直接用 Booth 算法。Booth 算法做起来比原码一位乘麻烦,但本质差别只在控制逻辑,数据通路基本还是那套寄存器和加法器。你只需要把“根据 Q0 判断”改成“根据 Q0 和 Q(-1) 的组合判断”,然后多一个附加位寄存器。

位数扩展也很简单,把 M、Q、加法器位宽都改成 8 位,A 改成 9 位,计数器循环次数改成 8,Product 改成 16 位。其余逻辑完全不需要动。这个扩展过程本身就是很好的练习,能检验你到底理解了原理,还是只会照着 4 位连线。

很多课设还要求把结果显示在七段数码管上。Logisim 里有现成的七段数码管组件,你只需要把 8 位乘积拆成高 4 位和低 4 位,分别接两个数码管的输入。这里的难点不是数码管,而是进制转换和位宽匹配。我做的时候习惯在电路里先加一个十六进制转七段数码管的子电路,方便以后复用。

5.3 一点个人体会

我调乘法器最大的收获,不是学会了“怎么算乘法”,而是真正理解了为什么 CPU 里要有一个“时钟”来同步所有操作。刚开始我的 A 寄存器和 Q 寄存器没有统一用一个时钟,结果每次步进,数据总是差一拍。后来我强制自己把所有需要同步更新的寄存器都接到同一个 CLK 上,问题立刻消失。

还有一点想提醒大家:Logisim 里看起来连对了,不等于电路真的对。一定要动手在纸上把状态表推一遍,尤其是“加法器输出 S 的最低位会移入 Q 的最高位”这个细节,不然你自己都会被仿真结果骗过去。把 3×5 这个例子用手推完,再去看仿真波形,很多疑问会瞬间解开。

乘法器实验做完后,我明显感觉到自己对寄存器传输级设计的理解上了一个台阶。后面再去看单总线 CPU 设计里的那些控制信号,不再觉得它们是一堆乱线,而是能看出“这个信号是为了让数据在哪里停一拍,那个信号是为了让哪个寄存器在哪个周期载入”。这种从“连电路”到“设计数据通路”的转变,大概就是计组实验真正想让你完成的事。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 6:16:05

赴美参加学术会议全流程指南:从签证到入境

过去几年我先后以学生、合作者、带队教师几种身份跑过好几趟美国的国际学术会议,从第一次手忙脚乱在机场被地勤反复核对材料,到后面敢帮整个课题组的师弟师妹统一代办签证预约,整套流程闭着眼都能走完。很多第一次出国的硕博生一听到“赴美参…

作者头像 李华
网站建设 2026/10/5 6:15:24

DexProtector内存态匿名段检测原理与dex重组绕过思路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 6:15:18

ePWM与SDFM同步配置详解:从寄存器到Driverlib的电流采样对齐实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 6:15:10

东华考研OJ进阶篇:并查集、Dijkstra、LIS与拓扑排序实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 6:15:03

工业嵌入式存储选型:MRAM与STM32F373RC的SPI驱动实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 6:14:47

用hybrid混合势函数跑通FeCMnSiTi五元合金分子动力学模拟

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华