verilog加法器学习笔记:
1.半加器(Half adder)
半加器由2个输入和2个输出组成:
真值表:
Hdlbits例题:半加器
①创建一个半加法器。半加法器将两个比特相加(没有进位输入),并产生一个和和进位输出。(carry_in和carry_out应该翻译成进位输入和输出)
module top_module(
input a, b,
output cout, sum );
assign {cout,sum} = a+b;
endmodule
2.全加器(Fall adder)
全加法器由3个输入、2个输出信号组成,和半加法器相比,多了一个进位输入信号。
真值表:
Hdlbits例题:全加器
②创建一个全加法器。一个完整的加法器加三位(包括进位输入),并产生一个和和进位输出。
module top_module(
input a, b, cin,
output cout, sum );
assign {cout,sum} = a+b+cin;
endmodule
3.等波纹进位加法器(Ripple carry adder circuit,RCA)
波纹进位加法器里的这个“波纹”是一个很形象的比喻,描述了进位信号依次向前传递的模样。等波纹进位加法器用作多比特加法器上,下图所示的8位加法器里,最低的比特位先进行简单的全加器计算(A0+B0),生产的和作为最终sum的最低位sum0,然后其进位cout0参与下一位的全加器计算(A1+B1+cout0),其和为sum1,进位为cout1。依次推列,后面是(A2+B2+cout1)、(A3+B3+cout2)……这样的加法器有个很明显的缺陷,即每位的计算都要等低位计算完后才能开始计算(每次全加器计算都要等低一位的进位),会严重的浪费时间。
Hdlbits例题:等波纹进位加法器(该例题是做一个3bit的波纹进位加法器)
③现在您知道了如何构建一个完整的加法器,让它的3个实例来创建一个3位二进制波纹进位加法器。加法器将两个3位数字和一个进位数相加,得到一个3位和并执行。为了鼓励您实际实例化全加法器,还可以输出波纹进位加法器中每个全加法器的执行值。Cout2是最后一个满加法器的最后一个执行,也是您通常看到的执行。
module top_module(
input [2:0] a, b,
input cin,
output [2:0] cout,
output [2:0] sum );
wire wire_sum0;
wire wire_sum1;
full_adder full_adder_inst0
(
.a(a[0]),
.b(b[0]),
.cin(cin),
.sum(sum[0]),
.cout(cout[0])
);
full_adder full_adder_inst1
(
.a(a[1]),
.b(b[1]),
.cin(cout[0]),
.sum(sum[1]),
.cout(cout[1])
);
full_adder full_adder_inst2
(
.a(a[2]),
.b(b[2]),
.cin(cout[1]),
.sum(sum[2]),
.cout(cout[2])
);
endmodule
module full_adder(
input a,b,cin,
output sum,cout
);
assign {cout,sum} = a+b+cin;
endmodule
4.进位选择加法器(Carry select adder)
进位选择加法器相较于波纹进位加法器来说在时间上会得到优化,但本质上是一种空间换时间的思想,对逻辑进行了复用。
若是波纹进位加法器:用实现一个32位全加器为例,并使用16位全加器来实现的话,那么按照上文3中的思路,用第1个16位全加器计算两个32位输入的低位输入数据A[15:0]和B[15:0],等这个全加器计算完后,得出进位cout0和最终和sum的低16位sum[15:0],A[15:0]+B[15:0]=sum[15:0]+cout0然后这个进位cout0参与下一个加法器的计算,即A[31:16]+B[32:16]+cout0,得出最终和sum的高位sum[31:16]以及最终进位cout1。这个方法用了2个16位加法器,时间是2个16位全加器计算的时间和。
倘若是使用进位选择加法器,首先使用1个全加器计算输入数据的低16位,即A[15:0]+B[15:0] = sum[15:0]+cout(n),这里把进位cout写成cout(n),在计算低位时,我们同时计算高位A[31:16]+B[31:16],这个时候就有问题了,按波纹进位计算的思想,我们计算高位,是需要低位的进位值的,同时计算的话,我们低16位计算结果还没出来,那怎么办呢?我们的方法也是有的,低位的进位结果无非是两种情况,0或者1,即有进位或者无进位,我们把这两种情况全算一遍,最终等低位的进位结果出来后,我们再选择高位用哪个值,即:我们再用2个全加器,一个计算A[31:16]+B[31:16]+0,另一个计算A[31:16]+B[31:16]+1。等低位的进位结果出来,看cout(n)=1还是0,来选择我们高位结果是哪个。这个方法用了3个加法器+1个1/2选择器,时间的话耗时是1个16位全加器计算的时间+1个选择器计算时间。(选择器耗时比全加器少的多得多)
Hdlbits例题:进位选择加法器
④纹波进位加法器的一个缺点(见前面的练习)是加法器计算进位的延迟(在最坏的情况下是从进位开始)相当慢,并且第二阶段加法器直到第一阶段加法器完成后才能开始计算其进位。这使得加法器变慢。一个改进是进位选择加法器,如下所示。第一阶段加法器与之前相同,但我们复制了第二阶段加法器,一个假设携带=0,另一个假设携带=1,然后使用快速2对1多路复用器选择碰巧正确的结果。
module top_module(
input [31:0] a,
input [31:0] b,
output [31:0] sum
);
wire [15:0] sum1,sum2;
wire cout0;
always@(*)
begin
case(cout0)
0:sum[31:16] = sum1;
1:sum[31:16] = sum2;
default:;
endcase
end
add16 add16_inst0(
.a(a[15:0]),
.b(b[15:0]),
.cin(1'b0),
.cout(cout0),
.sum(sum[15:0])
);
add16 add16_inst1(
.a(a[31:16]),
.b(b[31:16]),
.cin(1'b0),
.cout(),
.sum(sum1)
);
add16 add16_inst2(
.a(a[31:16]),
.b(b[31:16]),
.cin(1'b1),
.cout(),
.sum(sum2)
);
endmodule
5.超前进位加法器(Carry-Lookahead Adder,CLA)
超前进位加法器是一种并行计算加法器,与前进波纹加法器比较,优点是:他的进位输入并不是来自上一个全加器的进位输入,而是来自一个超前进位的算法逻辑;另外它的进位延时为固定的门延时,和加法器的位数是无关的。但是缺点也很明显:随着加法器的位数增加,加法器的规模会越来越庞大、会很复杂。
这里推荐一下B站里郭天祥老师讲的数电基础(超前进位加法器这章): B站郭天祥讲超前进位加法器
一个讲的很好的博客:超前进位加法器原理详解
还有这位大佬讲的常用加法器总结(非常推荐):FPGA/IC常考加法器总结
超前进位加法器的verilog代码后续会尝试写出来。
这里用CSDN自带的InsCode AI创作助手(CSDN版的ChatGPT)写了一个出来:
下面是一个超前进位加法器的 Verilog 代码:
module carry_lookahead_adder(
input [3:0] a,
input [3:0] b,
input cin,
output [3:0] sum,
output cout
);
wire [3:0] g;
wire [3:0] p;
wire [3:0] c;
assign g = a & b;
assign p = a ^ b;
assign c[0] = cin;
// Generate carry for the first bit
assign c[1] = g[0] | (p[0] & c[0]);
// Generate carry for the second bit
assign c[2] = g[1] | (p[1] & c[1]);
// Generate carry for the third bit
assign c[3] = g[2] | (p[2] & c[2]);
// Generate sum and carry-out
assign sum = a + b + cin;
assign cout = g[3] | (p[3] & c[3]);
endmodule
该代码实现了一个四位的超前进位加法器,它采用了一种叫做“预先计算进位”的方法。在该方法中,通过将加法器拆分成多个部分来计算进位信号,而不是在全加器中进行计算。
具体而言,该加法器使用了三个信号数组:g、p 和 c。其中 g 表示每个位的“与”运算结果,p 表示每个位的“异或”运算结果,c
表示每个位的进位信号。这些数组被用来计算每个位的进位信号、和以及最终的进位输出信号。