简介:面向华中科技大学计算机组成原理课程、由谭志虎教授指导的运算器设计实验报告,适合正在学习Logisim数字逻辑设计、需要完成头歌实验平台相关实验的本科生参考。内容围绕8位可控加减法器、CAL182电路、4位与16位快速加法器以及32位MIPS运算器展开,逐一给出设计原理、电路结构、溢出检测方法和实验验证结果,并附有结果分析与心得体会。资源包共1个doc文档,大小约779KB,结构完整、便于直接查阅与对照。目前已有1548人学习下载,说明其在同课程学习者中具有较高参考价值。读者可借此理解串行进位与并行进位链的差异、CLA74182与CLA74128组件的使用方式,以及有符号与无符号运算的溢出判断逻辑,从而更高效地完成实验报告撰写与电路调试。
1. 从串行进位到先行进位:这份运算器实验报告到底在解决什么
很多人第一次在 Logisim 里搭 8 位加法器,都会觉得“连线连对了就行”,结果一跑到 16 位甚至 32 位,波形图上全是红色,进位像多米诺骨牌一样一级一级往下传,仿真速度肉眼可见地变慢。这份华中科技大学谭志虎老师课程下的运算器设计实验报告,核心就在处理这个矛盾:串行进位加法器结构简单、级联直观,但高位必须等低位算完才能动,位宽一上去延迟就爆炸。实验从 8 位可控加减法器起步,经 4 位先行进位 74182、4 位与 16 位快速加法器,最后落在一个能跑加减、移位、乘除、比较的简易 32 位 MIPS 运算器上。它适合正在做计算机组成原理实验、想搞清“组内并行、组间并行”到底怎么落地的人,也适合复习溢出判断与多路选择器结构的同学。
2. 8 位可控加减法器:补码减法与 OF 溢出检测怎么搭
8 位可控加减法器是整个运算器实验的地基,它把补码运算、可控加减和溢出检测三件事压在一个电路里。理解它的关键不是记住“sub 取反加一”,而是搞清楚为什么同一条加法链能同时服务有符号和无符号运算,以及 OF 和 Cout 为什么不能混用。
2.1 全加器级联与补码运算的对应关系
一位全加器 FA 的输入是 Xi、Yi、Cin,输出 Si 和 Cout,进位输出满足 Cout = XiYi + (Xi⊕Yi)Cin。把 8 个 FA 的 Cout 接到下一个 FA 的 Cin,就得到串行进位加法器。补码的好处是符号位可以和平常数据位一样参与运算,所以 [X+Y]补 = [X]补 + [Y]补,这条链天然同时支持有符号和无符号加法,区别只在溢出的判定方式上。
减法走的是 [X-Y]补 = [X]补 + [-Y]补,而由 [Y]补 求 [-Y]补 的做法是“全部位取反再加一”。因此在电路上给每一位加一个异或门,sub 作为控制信号与 Yi 异或:sub=0 时输出还是 Yi,做加法;sub=1 时输出是 Yi 取反,同时把 sub 接到最低位 FA 的进位输入端,等价于加一的动作。
# 8 位可控加减法器的逐位逻辑(伪代码表达,便于对照电路) for i in range(8): Yi_ctrl = Y[i] XOR sub # sub=1 时对 Y 取反 C_in = sub # sub=1 时最低位加一 for i in range(8): S[i], C_out = full_adder(X[i], Yi_ctrl[i], C_in) C_in = C_out OF = C_out[7] XOR C_out[6] # 符号位进位与最高数据位进位异或上面的sub同时控制“取反”和“加一”,是可控加减法器的标准做法;XOR门在 sub=0 时透传原值,在 sub=1 时完成按位取反。溢出标志 OF 用最高位进位 C7 与次高位进位 C6 的异或得到,注意不是用最终进位 Cout,Cout 在有符号运算里只代表无符号溢出。
2.2 sub 控制信号与 OF/Cout 的判定边界
溢出检测有两种常见写法:一是最高数据位进位与符号位进位是否一致,二是根据操作数和结果的符号位判断。报告里给出的第二条逻辑是 V = Xf·Yf·Sf' + Xf'·Yf'·Sf,含义是两个操作数都是正数结果却为负,或者两个操作数都是负数结果却为正。这两种写法在同一个电路里是等价的,选哪一种看电路图的连线方便程度。
| 输入 X | 输入 Y | sub | 结果 S | Cout | OF | 含义 |
|---|---|---|---|---|---|---|
| 0000 0000 | 0000 0001 | 0 | 0000 0001 | 0 | 0 | 正数相加不溢出 |
| 0100 1000 | 0100 0001 | 0 | 1000 1001 | 0 | 1 | 两正数相加得负,溢出 |
| 0000 0111 | 1000 0111 | 1 | 1000 0000 | 0 | 1 | 正数减负数得负,溢出 |
三组数据对上了规律:OF=1 出现在真结果超出 8 位有符号范围 [-128,127] 的时候,而 Cout 只反映第 8 位的无符号进位。搭电路时最容易犯的错是把 Cout 直接当溢出标志,一旦换成有符号场景,判定就会整体错位。
提示:接 FA 时先单独搭一位并测四种输入组合,确认 S 和 Cout 全部正确,再复制成 8 位,能省掉大量排查时间。
3. CLA74182 先行进位:把进位链从串行改成并行
串行加法器的延迟与位宽成正比,8 位还忍得住,16 位、32 位就得换思路。先行进位(Lookahead Carry)的核心是把每一位的进位展开成只和原始输入及 C0 相关的表达式,砍掉对前一级进位的依赖。
3.1 进位生成函数 G 与进位传递函数 P 的推导
对第 i 位定义两个中间量:Gi = XiYi 表示本位一定产生进位,Pi = Xi⊕Yi 表示本位会传递低位来的进位。把这两种情况代入全加器进位公式,就得到 C1 = G1 + P1C0,再往下展开:
- C1 = G1 + P1C0
- C2 = G2 + P2G1 + P2P1C0
- C3 = G3 + P3G2 + P3P2G1 + P3P2P1C0
- C4 = G4 + P4G3 + P4P3G2 + P4P3P2G1 + P4P3P2P1C0
这四组表达式里,C1 到 C4 只依赖 X、Y 和 C0,彼此之间不再有依赖链,所以可以并行计算。在此基础上定义成组进位生成函数 G* = G4 + P4G3 + P4P3G2 + P4P3P2G1 和成组进位传递函数 P* = P4P3P2P1,它们是把 4 位打包成一个整体后往上级传递的接口。
# 4 位先行进位核心表达式 G[i] = X[i] AND Y[i] P[i] = X[i] XOR Y[i] C1 = G[0] OR (P[0] AND C0) C2 = G[1] OR (P[1] AND G[0]) OR (P[1] AND P[0] AND C0) C3 = G[2] OR (P[2] AND G[1]) OR (P[2] AND P[1] AND G[0]) OR (P[2] AND P[1] AND P[0] AND C0) C4 = G[3] OR (P[3] AND G[2]) OR (P[3] AND P[2] AND G[1]) OR (P[3] AND P[2] AND P[1] AND G[0]) OR (P[3] AND P[2] AND P[1] AND P[0] AND C0)AND和OR的嵌套反映的是“多级进位同时评估”的思路,级数越深对应的乘积项越长。C0只出现在每一项的末尾,说明它只是一个输入,不参与迭代;P和G是每位独立算出来的,吃的是原始 X、Y,不依赖低位进位。
3.2 74182 输入输出对照与逐位进位验证
报告里给出的三组 74182 数据可以拿来当自检用例。第一组输入 0000 0001 1,即 P=0001、G=0000、C0=1,带入表达式得 C1=1,输出 00。第二组 G1=1、P1=1、P2=1、C0=0,推出 C1=1、C2=1。第三组 G3=G4=1、P3=P4=1、C0=0,得到 C4=1、C3=1、G*=1。
| P (4 位) | G (4 位) | C0 | C1 | C2 | C3 | C4 | G* |
|---|---|---|---|---|---|---|---|
| 0001 | 0000 | 1 | 1 | 0 | 0 | 0 | 0 |
| — | G1=1 | 0 | 1 | 1 | 0 | 0 | — |
| 1111 | 1111 | 0 | — | — | 1 | 1 | 1 |
这些组数据的价值在于,它们专门挑了进位传递连续触发和进位生成单点触发的边界情况,搭完电路后逐组打进输入看输出,能快速确认展开表达式没有写错。
注意:74182 的 P、G 引脚是每位独立输入,不是“把四位当整体的结果”,成组量 G*、P* 是芯片内部算完从专门引脚输出的,接线时不要接反。
4. 4 位到 16 位快速加法器:组内并行与组间并行怎么接
拿到 74182 之后,加法器的分工是明确的:74182 负责并行算出 C1 到 C4,加法器本体负责用 Pi⊕Ci 得到各位的半和。4 位快速加法器就是这套分工的最小闭环,16 位则是在 4 位基础上做组合。
4.1 半和 Si = Pi⊕Ci 的生成路径
4 位快速加法器里,每一级不再用全加器内部再推一次进位,而是把 Ci 从 74182 直接引进来,本位输出 Si = Pi⊕Ci。Pi 已经在前面算过,所以整条路径是“P/G 生成 → 74182 并行进位 → XOR 出和”,而不是“FA 链式进位”。
# 4 位快速加法器数据通路 for i in range(4): P[i] = X[i] XOR Y[i] G[i] = X[i] AND Y[i] C = CLA74182(P, G, C0) # 并行输出 C1..C4 for i in range(4): S[i] = P[i] XOR C[i] # 半和与进位异或得到结果位 Cout = C[3]报告里给的一组数据是 X=1000、Y=0100、Cin=0,输出 S=1100、Cout=0、P*=0、G*=0。按有符号看,1000 是 -8,加 4 得 -4,即 1100,正确;按无符号看,8+4=12 也对应 1100。这组用例正好说明了同一份硬件同时兼容两种解释,只是溢出判定的语境不同。
4.2 16 位扩展的组间进位拼接
16 位快速加法器把输入切成 4 个 4 位片段,每个片段内部用 4 位快速加法器算局部 S0-S3,同时输出自己的 P*、G*,再把四个片段的 P*、G* 送进上一级 74182 算组间进位,最终把组间进位回灌到各片段的进位输入端。这就是“组内先行进位、组间先行进位”的完整结构。
| 位宽 | 进位结构 | 关键器件 | 延迟特征 |
|---|---|---|---|
| 4 位 | 组内并行 | 1 片 74182 | 单级并行 |
| 16 位 | 组内 + 组间并行 | 5 片 74182(4 内 + 1 间) | 两级并行 |
| 32 位 | 两级并行 + 功能单元 | CLA74182 并行进位 + 2 个 16 位加法器 | 用于 MIPS 运算器 |
报告给出的 16 位用例是 X=0000 1100 0000 1100、Y=1000 0100 1000 0100、Cin=0,输出 S=1001 0000 1001 0000、Cout=0、P*=0、G*=0。把它和预期结果逐片段对比,能确认四个片段的进位拼接没有出现偏移,尤其是最高片段用到的组间进位是否正确回灌。
提示:16 位接完后不要急着做 32 位,先拿低位片段全 1、高位片段全 0 的组合测一次,验证进位是否能从低位片段正确传到高位片段,这是组间进位最容易出问题的位置。
5. 32 位 MIPS 运算器:多路选择器、比较器与溢出收口
32 位 MIPS 运算器把前面搭好的积木拼成一台能执行多种运算的部件。加法和减法由 32 位快速加法器承担,移位、乘除、比较各自独立实现,最后由多路选择器按操作码 S 选出一路结果。
5.1 S 操作码与多路选择器的选通逻辑
报告的验证数据里有两条很关键的操作码用例:S=0011 对应乘法,输入 X=14、Y=6 得到 Result=84;S=0 对应逻辑左移,X=6 左移 2 位得到 12。这说明多路选择器的选通信号 S 是直接对应 MIPS 指令里的功能字段的,设计时把 S 接到 mux 的 select 端,每一路输入就是一个独立功能单元的输出。
# MIPS 运算器功能选择(以报告用例为准) S = 0011 -> Result = X * Y # 乘法单元 S = 0000 -> Result = X << shamt # 逻辑左移,shamt 来自低位字段 S = 0010 -> Result = X + Y # 加法(未在用例中出现,接线同理) S = 0110 -> Result = X - Y # 减法 equal = (X == Y) # 比较器独立输出S的位宽决定了最多能挂多少个功能单元,equal通常不参与 mux 选通,而是作为独立标志位输出给后续控制逻辑。要注意比较器比较的是原始操作数,不是 mux 的输出,否则移位和乘法的结果会被误判为操作数相等。
5.2 有符号溢出检测在 32 位下的实现
32 位运算器的溢出检测和 8 位是同一套逻辑,只是把符号位换成第 31 位。V = Xf·Yf·Sf' + Xf'·Yf'·Sf,Xf、Yf 是两个操作数的第 31 位,Sf 是结果的第 31 位。这个表达式在 32 位下依然成立,因为补码加法溢出的本质是“两个同号数相加结果变号”,与位宽无关。
# 32 位有符号溢出检测 Xf = X[31] Yf = Y[31] Sf = Result[31] V = (Xf AND Yf AND (NOT Sf)) OR ((NOT Xf) AND (NOT Yf) AND Sf)AND、OR、NOT的组合把两种非法符号组合穷举出来,比起用第 31 位和第 30 位的进位异或,这种写法在已经有多路选择器的结构里更容易和比较器、零标志位挂在一起。搭 32 位电路时,把 V 单独引出来接 LED 或探针,验证报告里那两条乘除和移位用例时就能顺带确认溢出逻辑没有被 mux 干扰。
注意:32 位加法器的进位回灌路径比 16 位深一层,如果 Logisim 里出现“结果延迟一个时钟才稳定”的现象,先检查组间进位有没有被误接到寄存器输入,而不是进位表达式本身写错。
6. Logisim 调试技巧:用子电路和探针把 32 位运算器拆开验
32 位电路一旦全部画在一张图里,调试基本靠猜。更省事的做法是从一开始就用子电路分层:8 位可控加减法器一个子电路,4 位快速加法器一个子电路,16 位再引用 4 位,32 位只做拼接和 mux。这样每层都能单独测,出问题时先定位到层,再定位到具体位。
验证顺序上,先用 8 位那三组报告数据跑通 OF 和 Cout,再用 74182 的 P/G/C0 三组用例确认进位展开式,接着测 16 位的四个片段拼接,最后才组装 32 位。每层验证时把关键信号用 Logisim 的探针(Probe)引出来,尤其是 C1 到 C4、P*、G*、OF 这几个量,比只看最终 Result 更容易发现是哪一级进位算错。
一个容易忽略的点是 Logisim 里引脚的“输出/输入”方向和位宽。子电路端口如果位宽设成 1 位,把 8 位信号接上去会被静默截断,波形上表现为高位永远是 0。常见做法是搭完子电路先单独仿真一次端口,确认位宽和方向都符合预期,再往上引用。另外,74182 这类芯片图建议照着手册引脚顺序画,把 P、G 按位并排排列,能大幅降低接线时接错相邻位的概率。
| 调试目标 | 观察信号 | 常见异常表现 | 排查方向 |
|---|---|---|---|
| 8 位溢出 | OF、C7、C6 | OF 恒为 0 或恒为 1 | 检查 C7 与 C6 是否接到同一异或门 |
| 74182 进位 | C1-C4、G*、P* | C2 始终等于 C1 | 检查 P2、G1 是否接反 |
| 16 位拼接 | 各片段 P*、G* | 高位片段结果恒为 0 | 检查组间进位是否回灌 |
| 32 位选通 | S、Result | 任意 S 都出同一结果 | 检查 mux select 是否接错位 |
如果想再往深走一步,可以在 32 位运算器后面串一个寄存器组,用时钟驱动做成单周期数据通路,把 equal、V、Result 一起锁存下来观察。这样能顺带验证运算器和后续状态单元之间的握手逻辑,比单纯看组合电路输出更接近真实的数据相关与结构相关场景。
本文还有配套的精品资源,点击获取