目录
- [1. 引言](#1. 引言)
- [2. 基础知识:二进制加法](#2. 基础知识:二进制加法)
- [3. 半加器(Half Adder)](#3. 半加器(Half Adder))
- [3.1 真值表](#3.1 真值表)
- [3.2 逻辑表达式](#3.2 逻辑表达式)
- [3.3 电路实现](#3.3 电路实现)
- [4. 全加器(Full Adder)](#4. 全加器(Full Adder))
- [4.1 真值表](#4.1 真值表)
- [4.2 逻辑表达式](#4.2 逻辑表达式)
- [4.3 电路实现](#4.3 电路实现)
- [5. 四位加法器的构建](#5. 四位加法器的构建)
- [5.1 结构图](#5.1 结构图)
- [5.2 Verilog 实现](#5.2 Verilog 实现)
- [5.3 测试平台](#5.3 测试平台)
- [5.4 进位链与延迟分析](#5.4 进位链与延迟分析)
- [6. 用 Python 模拟四位加法器](#6. 用 Python 模拟四位加法器)
- [7. 四位加法器的应用](#7. 四位加法器的应用)
- [7.1 4 位 ALU 设计示例](#7.1 4 位 ALU 设计示例)
- [8. 扩展到八位加法器](#8. 扩展到八位加法器)
- [8.1 级联方式与进位传递](#8.1 级联方式与进位传递)
- [8.2 Verilog 实现](#8.2 Verilog 实现)
- [8.3 简要说明](#8.3 简要说明)
- [8. 总结](#8. 总结)
摘要
本文从二进制加法规则出发,依次讲解半加器、全加器,并通过级联四个全加器构建四位加法器,再扩展到八位加法器。读者将掌握行波进位与超前进位的延迟分析,并学会用 Verilog 实现加法器与 ALU、用 Python 模拟其行为。
1. 引言
加法器是数字电路中最基础、最重要的运算单元之一,几乎所有的算术运算(减法、乘法、除法)最终都可以归结为加法运算。四位加法器(4-bit Adder)能够对两个四位二进制数进行加法运算,并输出四位结果和进位信号。本文将带你从最基础的半加器出发,逐步构建出完整的四位加法器,并给出可运行的代码实现。
2. 基础知识:二进制加法
在深入加法器之前,我们先回顾一下二进制加法的基本规则。二进制只有 0 和 1 两个数字,其加法规则如下:
- 0 + 0 = 0,进位为 0
- 0 + 1 = 1,进位为 0
- 1 + 0 = 1,进位为 0
- 1 + 1 = 0,进位为 1
当两个二进制数相加时,如果某一位的和超过 1,就需要向高位进位。例如:
1011
+ 0110
------
10001
从最低位开始逐位相加,每一位的运算不仅需要考虑当前位的两个输入,还需要考虑来自低位的进位。
3. 半加器(Half Adder)
半加器是最简单的加法电路,它只处理两个一位二进制数的相加,不考虑来自低位的进位。半加器有两个输入(A、B)和两个输出(和 S、进位 C)。
3.1 真值表
| 输入 A | 输入 B | 和 S | 进位 C |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 |
3.2 逻辑表达式
从真值表可以推导出:
- 和 S = A ⊕ B(异或)
- 进位 C = A · B(与)
3.3 电路实现
verilog
module half_adder(
input A,
input B,
output S,
output C
);
assign S = A ^ B; // 异或
assign C = A & B; // 与
endmodule
4. 全加器(Full Adder)
全加器在半加器的基础上增加了进位输入(Cin),能够处理来自低位的进位。全加器有三个输入(A、B、Cin)和两个输出(和 S、进位输出 Cout)。
4.1 真值表
| 输入 A | 输入 B | 进位输入 Cin | 和 S | 进位输出 Cout |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 | 0 |
| 0 | 1 | 0 | 1 | 0 |
| 0 | 1 | 1 | 0 | 1 |
| 1 | 0 | 0 | 1 | 0 |
| 1 | 0 | 1 | 0 | 1 |
| 1 | 1 | 0 | 0 | 1 |
| 1 | 1 | 1 | 1 | 1 |
4.2 逻辑表达式
- 和 S = A ⊕ B ⊕ Cin
- 进位输出 Cout = (A · B) + (Cin · (A ⊕ B))
4.3 电路实现
verilog
module full_adder(
input A,
input B,
input Cin,
output S,
output Cout
);
assign S = A ^ B ^ Cin;
assign Cout = (A & B) | (Cin & (A ^ B));
endmodule
5. 四位加法器的构建
四位加法器由四个全加器级联而成,前一级的进位输出连接到后一级的进位输入。最低位的进位输入(Cin)通常接地(置 0)。
5.1 结构图
渲染错误: Mermaid 渲染失败: Setting FA3 as parent of FA3 would create a cycle
5.2 Verilog 实现
verilog
module adder_4bit(
input [3:0] A,
input [3:0] B,
input Cin,
output [3:0] S,
output Cout
);
wire c1, c2, c3;
full_adder fa0(
.A(A[0]), .B(B[0]), .Cin(Cin),
.S(S[0]), .Cout(c1)
);
full_adder fa1(
.A(A[1]), .B(B[1]), .Cin(c1),
.S(S[1]), .Cout(c2)
);
full_adder fa2(
.A(A[2]), .B(B[2]), .Cin(c2),
.S(S[2]), .Cout(c3)
);
full_adder fa3(
.A(A[3]), .B(B[3]), .Cin(c3),
.S(S[3]), .Cout(Cout)
);
endmodule
5.3 测试平台
5.4 进位链与延迟分析
上面通过级联四个全加器构建的四位加法器,属于行波进位加法器(Ripple Carry Adder,RCA)。它的特点是结构简单、易于理解,但存在一个明显的性能瓶颈------进位信号需要逐级向后传播,形成一条长长的「进位链」。
5.4.1 关键路径延迟
所谓关键路径(Critical Path),是指从输入到输出经过逻辑门级数最多、延迟最大的那条路径。对于行波进位加法器,关键路径正是进位信号的传播路径。
假设每个全加器产生进位输出需要经过两级门延迟(记为 2T),那么四位行波进位加法器的进位传播延迟为:
T_carry = 2T × 4 = 8T
也就是说,最高位的进位输出(Cout)要等最低位的进位逐级「爬」上来才能稳定,总共需要 8 个门延迟。相比之下,每一位的和 S 可以在进位到达后很快算出,因此整体速度由进位链决定。
推广到 n 位行波进位加法器,其关键路径延迟为:
T_carry = 2T × n
随着位宽增加,延迟线性增长。这也是为什么行波进位加法器不适合用于高位宽、高性能的运算单元。
5.4.2 超前进位加法器(Carry Lookahead Adder)
为了缩短进位链,超前进位加法器(Carry Lookahead Adder,CLA) 被提出。它的核心思想是:提前并行计算出每一位的进位,而不是等待低位进位逐级传来。
为此,我们定义两个中间信号:
- 生成信号(Generate) :
G_i = A_i · B_i,表示第 i 位本身就能产生进位(两个输入都为 1)。 - 传播信号(Propagate) :
P_i = A_i ⊕ B_i,表示第 i 位能把低位的进位继续向上传递(两个输入不同)。
于是第 i 位的进位可以写成:
C_{i+1} = G_i + P_i · C_i
展开后,每一位的进位只依赖输入 A、B 和最低位进位 C0,不再依赖前一级的进位结果。例如四位超前进位加法器的进位为:
C1 = G0 + P0 · C0
C2 = G1 + P1 · G0 + P1 · P0 · C0
C3 = G2 + P2 · G1 + P2 · P1 · G0 + P2 · P1 · P0 · C0
C4 = G3 + P3 · G2 + P3 · P2 · G1 + P3 · P2 · P1 · G0 + P3 · P2 · P1 · P0 · C0
所有进位可以并行 计算,因此关键路径延迟从「随位宽线性增长」降为常数级。对于四位超前进位加法器,进位输出大约只需 3~4 级门延迟,远小于行波进位加法器的 8T。
下面给出一个完整的 4 位超前进位加法器 Verilog 实现,包含生成信号 G、传播信号 P 的计算,以及并行进位 C1~C4 的赋值逻辑:
verilog
module cla_4bit(
input [3:0] A, // 加数 A
input [3:0] B, // 加数 B
input Cin, // 最低位进位输入
output [3:0] S, // 和
output Cout // 进位输出
);
wire [3:0] G; // 生成信号:G[i] = A[i] & B[i]
wire [3:0] P; // 传播信号:P[i] = A[i] ^ B[i]
wire [4:1] C; // 各级进位:C[1]~C[4]
// 计算每一位的生成信号 G 与传播信号 P
assign G[0] = A[0] & B[0]; // 第 0 位:两个输入都为 1 时产生进位
assign G[1] = A[1] & B[1]; // 第 1 位
assign G[2] = A[2] & B[2]; // 第 2 位
assign G[3] = A[3] & B[3]; // 第 3 位
assign P[0] = A[0] ^ B[0]; // 第 0 位:两个输入不同时传播进位
assign P[1] = A[1] ^ B[1]; // 第 1 位
assign P[2] = A[2] ^ B[2]; // 第 2 位
assign P[3] = A[3] ^ B[3]; // 第 3 位
// 并行计算各级进位(只依赖输入 A、B 和 Cin,不依赖前一级进位)
assign C[1] = G[0] | (P[0] & Cin); // C1 = G0 + P0·Cin
assign C[2] = G[1] | (P[1] & G[0]) | (P[1] & P[0] & Cin); // C2 = G1 + P1·G0 + P1·P0·Cin
assign C[3] = G[2] | (P[2] & G[1]) | (P[2] & P[1] & G[0]) | (P[2] & P[1] & P[0] & Cin);
assign C[4] = 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] & Cin);
// 每一位的和:S[i] = A[i] ^ B[i] ^ C[i] = P[i] ^ C[i]
assign S[0] = P[0] ^ Cin; // 最低位进位输入为 Cin
assign S[1] = P[1] ^ C[1]; // 第 1 位进位为 C[1]
assign S[2] = P[2] ^ C[2]; // 第 2 位进位为 C[2]
assign S[3] = P[3] ^ C[3]; // 第 3 位进位为 C[3]
assign Cout = C[4]; // 最高位进位输出
endmodule
这段代码中,所有进位 C[1]~C[4] 都是通过组合逻辑并行计算出来的,彼此之间没有依赖关系,因此关键路径延迟被压缩到常数级,这正是超前进位加法器相比行波进位加法器的核心优势。
5.4.3 性能对比
| 类型 | 关键路径延迟(4 位) | 位宽扩展性 | 硬件复杂度 |
|---|---|---|---|
| 行波进位加法器(RCA) | 8T(随位宽线性增长) | 差,位宽越大越慢 | 低,结构简单 |
| 超前进位加法器(CLA) | 约 3~4T(常数级) | 好,位宽增大延迟基本不变 | 高,逻辑门数量多 |
小结:行波进位加法器胜在结构简单、面积小,适合对速度要求不高的场景;超前进位加法器以更多逻辑门为代价,换取了更短的进位链和更高的运算速度,常用于高性能处理器和 ALU 中。实际工程中,也常采用「超前进位 + 行波进位」混合的分组进位设计,在速度与面积之间取得平衡。
verilog
module adder_4bit_tb;
reg [3:0] A, B;
reg Cin;
wire [3:0] S;
wire Cout;
adder_4bit uut(
.A(A), .B(B), .Cin(Cin),
.S(S), .Cout(Cout)
);
initial begin
A = 4'b1011; B = 4'b0110; Cin = 1'b0;
#10;
// 期望结果:1011 + 0110 = 10001,S=0001,Cout=1
$display("A=%b B=%b Cin=%b => S=%b Cout=%b", A, B, Cin, S, Cout);
A = 4'b0101; B = 4'b0011; Cin = 1'b0;
#10;
// 期望结果:0101 + 0011 = 1000,S=1000,Cout=0
$display("A=%b B=%b Cin=%b => S=%b Cout=%b", A, B, Cin, S, Cout);
A = 4'b1111; B = 4'b0001; Cin = 1'b0;
#10;
// 期望结果:1111 + 0001 = 10000,S=0000,Cout=1
$display("A=%b B=%b Cin=%b => S=%b Cout=%b", A, B, Cin, S, Cout);
$finish;
end
endmodule
6. 用 Python 模拟四位加法器
如果你没有硬件开发环境,也可以用 Python 来模拟四位加法器的行为,便于理解其工作原理。
python
def full_adder(a, b, cin):
"""单比特全加器"""
s = a ^ b ^ cin
cout = (a & b) | (cin & (a ^ b))
return s, cout
def adder_4bit(A, B, cin=0):
"""四位加法器:A、B 为 4 位二进制列表(低位在前)"""
S = [0] * 4
carry = cin
for i in range(4):
S[i], carry = full_adder(A[i], B[i], carry)
return S, carry
# 测试:1011 + 0110 = 10001
A = [1, 1, 0, 1] # 低位在前,即二进制 1011
B = [0, 1, 1, 0] # 低位在前,即二进制 0110
S, cout = adder_4bit(A, B)
print("S =", S[::-1], "Cout =", cout) # 输出:S = [0, 0, 0, 1],Cout = 1
7. 四位加法器的应用
四位加法器在实际工程中有广泛的应用场景:
- 算术逻辑单元(ALU):加法器是 ALU 的核心部件,配合补码电路可以实现减法运算。下面给出一个简单的 4 位 ALU 设计示例,展示如何利用加法器实现加法和减法。
7.1 4 位 ALU 设计示例
减法运算可以通过补码实现:A - B = A + (~B + 1)。因此,我们只需在加法器的基础上增加一个控制信号 Sub,当 Sub=1 时对 B 取反并令进位输入为 1,即可完成减法。
verilog
module alu_4bit(
input [3:0] A,
input [3:0] B,
input Sub, // 0:加法,1:减法
output [3:0] Result,
output Cout
);
wire [3:0] B_eff;
// 减法时对 B 取反(补码),加法时直接使用 B
assign B_eff = Sub ? ~B : B;
// 复用四位加法器:减法时进位输入置 1,实现 +1
adder_4bit u_add(
.A(A),
.B(B_eff),
.Cin(Sub),
.S(Result),
.Cout(Cout)
);
endmodule
测试平台:
verilog
module alu_4bit_tb;
reg [3:0] A, B;
reg Sub;
wire [3:0] Result;
wire Cout;
alu_4bit uut(
.A(A), .B(B), .Sub(Sub),
.Result(Result), .Cout(Cout)
);
initial begin
// 加法:5 + 3 = 8
A = 4'b0101; B = 4'b0011; Sub = 1'b0;
#10;
$display("5 + 3 = %d (Result=%b, Cout=%b)", Result, Result, Cout);
// 减法:5 - 3 = 2
A = 4'b0101; B = 4'b0011; Sub = 1'b1;
#10;
$display("5 - 3 = %d (Result=%b, Cout=%b)", Result, Result, Cout);
// 减法:9 - 6 = 3
A = 4'b1001; B = 4'b0110; Sub = 1'b1;
#10;
$display("9 - 6 = %d (Result=%b, Cout=%b)", Result, Result, Cout);
$finish;
end
endmodule
这里 Sub 同时充当了取反使能信号和最低位进位输入:减法时 B_eff = ~B、Cin = 1,正好完成 A + (~B) + 1 = A - B;加法时 B_eff = B、Cin = 0,即普通加法。这样一个简单的 ALU 就同时支持了加法和减法两种运算。
8. 扩展到八位加法器
掌握了四位加法器的设计后,扩展到八位加法器就非常直观了:只需实例化两个 adder_4bit 模块,将低四位加法器的进位输出连接到高四位加法器的进位输入即可。这种「模块级联」的方式与之前用全加器级联构建四位加法器的思路完全一致,只是把「全加器」这个基本单元换成了「四位加法器」这个更高层的模块。
8.1 级联方式与进位传递
八位加法器的结构如下:
- 低四位 :
adder_4bit处理 A3:0 与 B3:0,其进位输入接外部Cin,进位输出记为c_mid。 - 高四位 :另一个
adder_4bit处理 A7:4 与 B7:4,其进位输入接c_mid,进位输出即为最终的Cout。
进位信号从最低位开始,先经过低四位加法器内部的进位链,再通过 c_mid 传入高四位加法器,继续在高四位内部逐级传播。因此八位行波进位加法器的关键路径延迟约为 2T × 8 = 16T,正好是四位加法器的两倍,符合「延迟随位宽线性增长」的规律。
8.2 Verilog 实现
verilog
module adder_8bit(
input [7:0] A,
input [7:0] B,
input Cin,
output [7:0] S,
output Cout
);
wire c_mid; // 低四位到高四位的进位
// 低四位
adder_4bit low(
.A(A[3:0]),
.B(B[3:0]),
.Cin(Cin),
.S(S[3:0]),
.Cout(c_mid)
);
// 高四位
adder_4bit high(
.A(A[7:4]),
.B(B[7:4]),
.Cin(c_mid),
.S(S[7:4]),
.Cout(Cout)
);
endmodule
8.3 简要说明
- 两个
adder_4bit模块通过中间进位线c_mid串联,低四位的进位输出恰好是高四位的进位输入,进位传递逻辑与四位加法器内部完全一致。 - 这种层次化设计体现了硬件描述语言的核心思想:自顶向下、逐层复用。只要底层模块接口稳定,就可以像搭积木一样不断扩展位宽。
- 同理,若要构建十六位加法器,只需再实例化两个
adder_8bit模块,或直接实例化四个adder_4bit模块,级联方式完全相同。
8. 总结
本文从二进制加法规则出发,依次介绍了半加器、全加器,并通过级联四个全加器构建了四位加法器。核心要点如下:
- 半加器处理两个一位输入,不考虑进位输入。
- 全加器增加进位输入,是构建多位加法器的基本单元。
- 四位加法器通过级联四个全加器实现,前级进位输出接后级进位输入。
- 最低位的进位输入通常置 0。
掌握了四位加法器的设计思路,你就可以进一步扩展到八位、十六位加法器,甚至设计更复杂的算术运算单元。