逐光者933头像

四位加法器:从半加器到全加器的数字电路设计详解

目录

摘要

本文从二进制加法规则出发,依次讲解半加器、全加器,并通过级联四个全加器构建四位加法器,再扩展到八位加法器。读者将掌握行波进位与超前进位的延迟分析,并学会用 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
0000
0110
1010
1101

3.2 逻辑表达式

从真值表可以推导出:

  • 和 S = A ⊕ B(异或)
  • 进位 C = A · B(与)

3.3 电路实现

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
00000
00110
01010
01101
10010
10101
11001
11111

4.2 逻辑表达式

  • 和 S = A ⊕ B ⊕ Cin
  • 进位输出 Cout = (A · B) + (Cin · (A ⊕ B))

4.3 电路实现

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 实现

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 的赋值逻辑:

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 中。实际工程中,也常采用「超前进位 + 行波进位」混合的分组进位设计,在速度与面积之间取得平衡。

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 来模拟四位加法器的行为,便于理解其工作原理。

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,即可完成减法。

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

测试平台:

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 处理 A[3:0] 与 B[3:0],其进位输入接外部 Cin,进位输出记为 c_mid。
  • 高四位:另一个 adder_4bit 处理 A[7:4] 与 B[7:4],其进位输入接 c_mid,进位输出即为最终的 Cout。

进位信号从最低位开始,先经过低四位加法器内部的进位链,再通过 c_mid 传入高四位加法器,继续在高四位内部逐级传播。因此八位行波进位加法器的关键路径延迟约为 2T × 8 = 16T,正好是四位加法器的两倍,符合「延迟随位宽线性增长」的规律。

8.2 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。

掌握了四位加法器的设计思路,你就可以进一步扩展到八位、十六位加法器,甚至设计更复杂的算术运算单元。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2503_94015091/article/details/168145901

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--