简介:运算器实验配套的计算机组成原理实验报告文档,面向正在学习计算机硬件课程、需要完成运算器实验与报告撰写的高校学生。文档围绕8位可控加减法器、4位快速加法器与16位快速加法器三类实验展开,逐项记录设计要求、方案设计、实验步骤、故障调试、测试与分析,涉及全加器串行进位、先行进位、CLA74182与74128的进位生成和传递、补码减法、溢出检测及隧道标签连线等具体知识点。资源包共1个docx文件,约1.26MB,内容完整,可作为实验报告模板与电路搭建、调试的对照材料。该文档已有2564人学习,读者可从中获取三类加法器的设计思路、进位链组织方式、测试用例与预期结果对照,以及常见故障的排查经验,便于完成课程实验并复习二进制运算与进位机制。
1. 从“加法结果差一位”切入:这套运算器实验要交付什么
做运算器实验时,最容易被忽略的不是连线,而是“结果差一位”到底该怪全加器、进位链,还是测试向量。计算机组成原理里的 8 位可控加减法器、4 位快速加法器、16 位快速加法器,看起来是三个独立电路,实际是一条从串行进位到组内、组间先行进位的设计链。这份实验报告适合正在做课设、毕设前期实验,或者要交一份完整实验报告的人:先明确 Sub、Cout、OF、G*、P* 这些接口,再把 8 位串行、4 位 CLA、16 位两级 CLA 逐级搭起来,最后用测试向量和波形把“能跑”变成“可验收”。如果只照着图连完就截图,后面排错会很被动。
2. 需求与总体架构:从 8 位串行到 16 位两级先行进位的模块划分
2.1 三个实验对象的输入输出边界
这套运算器实验不是一上来就画 16 位大图,而是先把三个子电路的接口边界定清楚。8 位可控加减法器面对的是两个 8 位操作数 X、Y,一个加减控制信号 Sub,输出和数 S、最高进位 Cout 和有符号溢出位 OF;4 位快速加法器面对两个 4 位操作数,除了和数 S 和进位 Cout,还要输出 4 位成组进位生成函数 G 和成组进位传递函数 P;16 位快速加法器则是把 4 位快速加法器当成积木,向上再拼一层组间先行进位。
| 模块 | 主要输入 | 主要输出 | 关键控制/标志 |
|---|---|---|---|
| 8 位可控加减法器 | X[7:0]、Y[7:0]、Sub | S[7:0]、Cout、OF | Sub=0 加法,Sub=1 减法 |
| 4 位快速加法器 | X[3:0]、Y[3:0]、Cin | S[3:0]、Cout、G、P | 组内先行进位 |
| 16 位快速加法器 | X[15:0]、Y[15:0]、Cin | S[15:0]、Cout、G*、P* | 组内先行 + 组间先行 |
这张表看着简单,实际是后面所有连线检查的底稿。很多实验报告只写“实现加法器”,没有把接口写死,导致画图时把 Cout 和 OF 混在一起,或者把 4 位块的 G、P 当成最终结果。我一般会先把这张表抄到报告的需求分析里,再开始画电路,后面每接一根线都回来对一遍。
2.2 为什么不是一步做 16 位:串行、组内先行、组间先行的折中
8 位可控加减法器用 8 个一位全加器串行进位,结构最直观,缺点是进位要一级一级传,位数越多延迟越明显。4 位快速加法器换成先行进位,用 CLA74182 这类器件并行算出 C1、C2、C3、C4,四个全加器只负责求和,进位不再逐级等待。到了 16 位,如果继续用一整条串行进位链,延迟会继续累加;如果直接做 16 位全先行,扇入和连线复杂度又会上去。比较稳妥的折中是“组内先行、组间先行”:每 4 位做一个快速加法器块,块内用 CLA 算进位,块间再用一级 CLA 算 C4、C8、C12、C16。
这里的关键不是背公式,而是理解每一层负责什么。8 位加减法器负责把补码减法和溢出检测讲清楚;4 位快速加法器负责把 Gi、Pi、C1-C4 和 G*、P* 的关系讲清楚;16 位快速加法器负责把四个 4 位块组织起来,让块间进位也走先行路径。三层任务分开,报告里的方案设计才不会写成一大段空话。
2.3 总体数据通路和进位链划分
从数据通路看,8 位加减法器的 X、Y 先进入条件取反环节:Sub=0 时 Y 原样进入全加器,Sub=1 时 Y 按位取反,同时最低位进位 C0 接 Sub,完成“取反加一”。8 个全加器串起来后,C8 作为最高进位 Cout,C7 和 C8 异或得到 OF。4 位快速加法器则把 X、Y 分别送入 Gi、Pi 生成逻辑,再送入 CLA74182 得到 C1-C4,四个全加器用这些进位求和。16 位快速加法器把 16 位分成四个 4 位块,每块输出自己的 G*、P*,第二级 CLA 用这些成组信号算出块间进位。
注意:原料里出现过 CLA74182 和 CLA74128 两种写法,通常以实验箱器件库的标注为准。连线前先核对引脚定义,不要只看名字相似就直接替换。
进位链的划分建议在报告里单独画一张“进位关系表”,把 C0、C4、C8、C12、C16 和四个 4 位块对应起来。这样后面调试时,看到 C8 不对就能立刻定位到第二级 CLA 或第二块的 G*、P*,而不是从头开始猜。
3. 8位可控加减法器实现:Sub、补码与溢出位的落线方法
3.1 加减控制:Y 按位异或 Sub,最低位进位接 Sub
8 位可控加减法的核心公式可以写成:Sub=0 时,S=X+Y;Sub=1 时,S=X-Y=X+~Y+1。落到电路上,Y 的每一位先和 Sub 做异或,得到 Y';再把最低位全加器的进位输入 C0 直接接 Sub。这样 Sub=1 时,Y' 是按位取反的结果,C0=1 完成加一,不需要额外做一套减法器。
module addsub8( input [7:0] X, input [7:0] Y, input Sub, output [7:0] S, output Cout, output OF ); wire [8:0] C; wire [7:0] Yx; assign Yx = Y ^ {8{Sub}}; // Sub=1 时 Y 按位取反 assign C[0] = Sub; // 减法时最低位加 1 genvar i; generate for (i = 0; i < 8; i = i + 1) begin: fa_chain assign S[i] = X[i] ^ Yx[i] ^ C[i]; assign C[i+1] = (X[i] & Yx[i]) | (X[i] & C[i]) | (Yx[i] & C[i]); end endgenerate assign Cout = C[8]; // 最高位进位 assign OF = C[8] ^ C[7]; // 符号位进位与最高进位异或 endmodule这段代码里,Yx是条件取反后的加数,C[0]是加减控制带来的最低位进位,C[i+1]是每一位向高位的进位。Cout取C[8],表示 8 位运算的最高进位;OF取C[8]和C[7]的异或,用来判断有符号运算是否溢出。参数上最容易被改错的是C[0]:如果只把 Y 取反却忘了把C[0]接 Sub,减法结果会稳定差 1。
3.2 8 位串行进位链的搭建与全加器复用
在实验箱上搭 8 位串行加减法器,通常先放 8 个一位全加器,按低位到高位排开。每个全加器的 X 输入接 X[i],Y 输入接Y[i] ^ Sub,进位输入接前一级的进位输出,最低位全加器的进位输入接 Sub。这样 8 个全加器共用同一套结构,加法时 Sub=0,Y 原样进入;减法时 Sub=1,Y 取反且最低位加 1。
报告里的实验步骤可以写得具体一点:先接第 0 位全加器,确认S0和C1;再接第 1 位,确认进位能正常传递;依次接到第 7 位,最后引出C8作为 Cout。每接完两位就做一次小测试,比如X=00000000, Y=00000001, Sub=0看 S 是否为00000001,Sub=1看 S 是否为11111111。这种分段验证比全部接完再上电更省时间,也方便在报告里写“故障与调试”的过程。
3.3 溢出检测与测试分析:C8 与 C7 异或怎么用
有符号溢出不能简单看 Cout。以 8 位有符号数为例,0x7F + 0x01 = 0x80,正数加正数得到负数,发生了溢出;0x80 + 0x80 = 0x00,负数加负数得到非负,也发生了溢出。前者 Cout 可能为 0,后者 Cout 为 1,所以只看最高进位会漏判。比较稳妥的检测方式是把最高数据位的进位 C7 和最高进位 C8 做异或:OF = C8 ^ C7。
| Sub | X | Y | S | Cout | OF | 说明 |
|---|---|---|---|---|---|---|
| 0 | 0x7F | 0x01 | 0x80 | 0 | 1 | 正溢 |
| 0 | 0x80 | 0x80 | 0x00 | 1 | 1 | 负溢 |
| 1 | 0x00 | 0x01 | 0xFF | 0 | 0 | 0-1=-1,不溢出 |
| 1 | 0x80 | 0x01 | 0x7F | 1 | 1 | -128-1 溢出 |
这张表建议直接放进测试与分析小节。报告里不要只写“经检验电路正常”,而要写清楚输入是什么、输出是什么、为什么这个输出说明溢出位接对了。尤其是减法溢出,很多同学会用加法溢出的直觉去套,结果 OF 和 Cout 对不上,最后只能靠猜。
4. 4位与16位快速加法器实现:CLA 两级展开和进位接口
4.1 4 位先行进位:Gi、Pi、C1-C4 与 G*、P*
4 位快速加法器的重点是先行进位。对第 i 位,定义进位生成函数Gi = Xi & Yi,进位传递函数Pi = Xi ^ Yi,那么和数Si = Pi ^ Ci-1,进位Ci = Gi | (Pi & Ci-1)。CLA74182 的作用就是把这些进位并行算出来,而不是让它们一级一级串过去。对于 4 位块,还要输出成组进位生成函数 G* 和成组进位传递函数 P*,供 16 位扩展时使用。
module cla4( input [3:0] X, input [3:0] Y, input Cin, output [3:0] S, output Cout, output Gg, output Pg ); wire [3:0] G, P, C; assign G = X & Y; // 逐位进位生成 assign P = X ^ Y; // 逐位进位传递 assign C[0] = Cin; assign C[1] = G[0] | (P[0] & C[0]); assign C[2] = G[1] | (P[1] & G[0]) | (P[1] & P[0] & C[0]); assign C[3] = G[2] | (P[2] & G[1]) | (P[2] & P[1] & G[0]) | (P[2] & P[1] & P[0] & C[0]); assign Cout = G[3] | (P[3] & G[2]) | (P[3] & P[2] & G[1]) | (P[3] & P[2] & P[1] & G[0]) | (P[3] & P[2] & P[1] & P[0] & C[0]); assign S = P ^ C; // 每一位和数 assign Gg = G[3] | (P[3] & G[2]) | (P[3] & P[2] & G[1]) | (P[3] & P[2] & P[1] & G[0]); assign Pg = P[3] & P[2] & P[1] & P[0]; endmodule这段代码里,G和P是逐位生成、传递信号,C[0]是进位输入,C[1]到C[3]是块内进位,Cout是第 4 位向高位的进位。Gg和Pg是这个 4 位块的成组输出,后面 16 位扩展会用到。实验箱上对应的接法是:X、Y 先接生成、传递逻辑,CLA74182 的输出 C1-C4 接到四个全加器的进位输入,C0 一定要接进位输入,不能悬空。
4.2 16 位扩展:四个 4 位块加一个二级 CLA
16 位快速加法器不是把 16 个全加器重新串一遍,而是把 16 位分成四个 4 位块,每块内部先用 4 位快速加法器算,再把四个块的 G*、P* 送入第二级 CLA,算出块间进位 C4、C8、C12、C16。这样第一级负责块内进位,第二级负责块间进位,整体仍然是先行进位结构。
module add16( input [15:0] X, input [15:0] Y, input Cin, output [15:0] S, output Cout, output Gg, output Pg ); wire [3:0] Gblk, Pblk; wire [3:0] Cblk; // Cblk[0]=Cin, Cblk[1]=C4, Cblk[2]=C8, Cblk[3]=C12 assign Cblk[0] = Cin; cla4 u0(.X(X[3:0]), .Y(Y[3:0]), .Cin(Cblk[0]), .S(S[3:0]), .Cout(), .Gg(Gblk[0]), .Pg(Pblk[0])); cla4 u1(.X(X[7:4]), .Y(Y[7:4]), .Cin(Cblk[1]), .S(S[7:4]), .Cout(), .Gg(Gblk[1]), .Pg(Pblk[1])); cla4 u2(.X(X[11:8]), .Y(Y[11:8]), .Cin(Cblk[2]), .S(S[11:8]), .Cout(), .Gg(Gblk[2]), .Pg(Pblk[2])); cla4 u3(.X(X[15:12]), .Y(Y[15:12]), .Cin(Cblk[3]), .S(S[15:12]), .Cout(), .Gg(Gblk[3]), .Pg(Pblk[3])); assign Cblk[1] = Gblk[0] | (Pblk[0] & Cin); assign Cblk[2] = Gblk[1] | (Pblk[1] & Gblk[0]) | (Pblk[1] & Pblk[0] & Cin); assign Cblk[3] = Gblk[2] | (Pblk[2] & Gblk[1]) | (Pblk[2] & Pblk[1] & Gblk[0]) | (Pblk[2] & Pblk[1] & Pblk[0] & Cin); assign Cout = Gblk[3] | (Pblk[3] & Gblk[2]) | (Pblk[3] & Pblk[2] & Gblk[1]) | (Pblk[3] & Pblk[2] & Pblk[1] & Gblk[0]) | (Pblk[3] & Pblk[2] & Pblk[1] & Pblk[0] & Cin); assign Gg = Gblk[3] | (Pblk[3] & Gblk[2]) | (Pblk[3] & Pblk[2] & Gblk[1]) | (Pblk[3] & Pblk[2] & Pblk[1] & Gblk[0]); assign Pg = Pblk[3] & Pblk[2] & Pblk[1] & Pblk[0]; endmodule这段顶层代码里,四个cla4实例分别处理低 4 位到高 4 位,Cblk[0]是总进位输入,Cblk[1]、Cblk[2]、Cblk[3]分别是 C4、C8、C12,Cout是 C16。第二级进位的写法遵循同一套G* + P*C结构,只是把每一个 4 位块当成一个“大位”来看。参数上要重点看Cblk的索引:C4 必须送到第二块的 Cin,C8 送到第三块,C12 送到第四块,漏掉任何一个都会出现“低 4 位对、高 4 位错”的现象。
4.3 从 4 位到 16 位的连线顺序和测试点
实际连线时,我一般按“先块内、再块间、最后总输出”的顺序走。第一步,单独测一个 4 位快速加法器,用输入X=1000, Y=0100, Cin=0,预期S=1100, Cout=0, P*=0, G*=0。第二步,把四个 4 位块都接好,但先不接第二级 CLA,手动给每块 Cin 做测试,确认每块都能独立出结果。第三步,接第二级 CLA,重点测 C4、C8、C12 是否按公式产生。第四步,跑 16 位整组测试,用原料里的例子:X=0000 1100 0000 1100,Y=1000 0100 1000 0100,Cin=0,预期S=1001 0000 1001 0000, Cout=0, P*=0, G*=0。
| 测试层级 | 输入 | 预期输出 | 观察点 |
|---|---|---|---|
| 4 位块 | X=1000, Y=0100, Cin=0 | S=1100, Cout=0 | 块内 C1-C4 |
| 16 位整组 | X=0C0C, Y=8484, Cin=0 | S=9090, Cout=0 | C4、C8、C12、C16 |
测试点不要只盯 S。C4、C8、C12 和 G*、P* 更能暴露进位链问题。很多电路 S 看起来对,是因为错误刚好被低位进位抵消了,换一组数据就翻车。把中间进位一起记到报告里,后面的故障分析才有依据。
5. 常见问题与排查:从波形到进位链的定位顺序
5.1 先用测试向量锁定错误范围
排查运算器电路,最怕一上来就盯着整张大图看。比较省时间的做法是先准备一组参考测试向量,用脚本算出预期结果,再拿实验输出逐项对照。8 位加减法器重点看 Sub、Cout、OF;4 位快速加法器重点看 C1-C4 和 G*、P*;16 位快速加法器重点看 C4、C8、C12、C16。下面这段 Python 只做参考计算,不依赖具体实验箱,适合在写报告前先把预期值列出来。
def addsub_ref(x, y, sub, bits=8): mask = (1 << bits) - 1 yx = y if sub == 0 else (~y) & mask total = x + yx + sub s = total & mask cout = (total >> bits) & 1 sign = 1 << (bits - 1) if sub == 0: of = int((x & sign) == (y & sign) and (s & sign) != (x & sign)) else: of = int((x & sign) != (y & sign) and (s & sign) != (x & sign)) return s, cout, of vectors = [ (0x7F, 0x01, 0), # 正溢 (0x80, 0x80, 0), # 负溢 (0x00, 0x01, 1), # 0-1 (0x80, 0x01, 1), # -128-1 溢出 ] for x, y, sub in vectors: s, cout, of = addsub_ref(x, y, sub) print(f"X={x:02X} Y={y:02X} Sub={sub} -> S={s:02X} Cout={cout} OF={of}")这段脚本里,mask用来截断到 8 位,yx根据 Sub 决定是否按位取反,total把最低位加一也一起算进去。cout取第 8 位,of用符号位判断,适合生成报告里的测试表。4 位和 16 位加法器可以把bits改成 4 或 16,去掉 Sub 逻辑后直接算x+y+cin。
5.2 五类高频翻车点:现象、原因、解决
第一类:加法正常,减法总差 1。现象是 Sub=0 时结果对,Sub=1 时 S 总比预期小 1。原因通常只做了 Y 按位取反,忘了把最低位进位 C0 接 Sub。解决办法是把第 0 位全加器的进位输入直接接 Sub,并在报告里把S=X+~Y+1的补码关系写清楚。
第二类:CLA 的 C0 悬空,4 位加法时对时错。现象是某些输入下 C1-C4 不按预期变化,换一组数又正常。原因是 CLA74182 的进位输出是 C0 的函数,C0 不接或接错,内部先行进位就失去参考。解决办法是先查 CLA 的 C0 引脚,再查四个全加器的进位输入是否分别接到 C1-C4。
第三类:16 位低 4 位对,高 4 位错。现象是 S[3:0] 正常,S[7:4] 以后开始乱。原因多半是 C4、C8、C12 没有送到下一块的 Cin,或者第二级 CLA 的 G*、P* 接反。解决办法是按 C0、C4、C8、C12、C16 的顺序逐级量电位,先把块间进位打通,再看数据位。
第四类:Cout 对,OF 不对。现象是最高进位看起来正常,但有符号溢出判断和预期相反。原因是把 Cout 直接当成了 OF,或者只看了最高位进位,没有引出 C7。解决办法是把 C7 和 C8 同时引到异或门,按OF=C8^C7接,测试时专门跑正溢和负溢两组向量。
第五类:单块结果对,级联后 G、P不对。** 现象是单独测 4 位块没问题,接到 16 位顶层后成组输出异常。原因是 Gi、Pi 的定义和成组 P 的定义混用了,或者实验箱器件库里的 P 端按或逻辑标注,而仿真代码里用了异或。解决办法是固定一套定义:逐位和数用Pi=Xi^Yi,成组传递按器件库要求接;仿真和实验箱分别核对,不要一边改代码一边改连线。
注意:排错时先断后级、再测前级,不要在全连状态下反复换芯片。先把 4 位块单独测通,再测第二级 CLA,最后接 16 位整组,顺序反了很容易把一个小问题放大成一片错误。
6. 验收与答辩:把三张电路图讲成一条进位链
验收时不要从第一张图开始逐门讲,先讲一条主线:8 位加减法器解决“Sub 控制补码减法和 OF 检测”,4 位快速加法器解决“块内先行进位和 G*、P* 生成”,16 位快速加法器解决“四个 4 位块的组间先行进位”。这条线讲清楚,老师问什么都能挂回去。具体操作上,我一般按下面这张表走一遍。
| 验收项 | 检查方法 | 合格标准 |
|---|---|---|
| 8 位加减法 | Sub=0/1 各跑一组加法、减法 | S、Cout、OF 与参考值一致 |
| 4 位快速加法 | 测 C1-C4 和 G*、P* | 块内进位不串位 |
| 16 位扩展 | 测 C4、C8、C12、C16 | 块间进位逐级正确 |
| 溢出判断 | 跑正溢、负溢、不溢出 | OF 不跟着 Cout 乱跳 |
| 报告一致性 | 电路图、测试表、波形截图 | 接口名和引脚名对得上 |
答辩时最容易被问的是“为什么 16 位不直接串行”和“OF 为什么不是 Cout”。前一个问题用延迟和扇入解释:串行进位链越长,高位越等低位;两级 CLA 是把长链拆成块内和块间两段。后一个问题直接画 C7、C8 两个进位,指出有符号溢出看的是符号位进位和最高进位是否一致。测试数据不要只放成功的那一组,留一组曾经出错的向量,说明现象、原因和修改动作,反而更像真实做过的实验。
从那以后我每次带运算器实验,都强制先做接口表、再做单块测试、最后做整组联调,不让自己一上来就陷进大图里。需要对照原始文档时,按项目标题取用即可,里面的电路图和测试记录可以按自己实验箱的器件库替换后使用。希望帮到你。
本文还有配套的精品资源,点击获取