一、半加法器(Half Adder)

输入: A A A B B B(代表相加的两个位)
输出: S S S(代表 A A A B B B的和)、 C out C_{\text{out}} Cout(进位)

计算公式:

  • S = A ⊕ B S=A\oplus B S=AB A A A B B B的异或)
  • C out = A B C_{\text{out}}=AB Cout=AB A A A B B B进行与操作,仅当 A A A B B B同时为 1 1 1时才进位)

图示:
半加法器

二、全加器(Full Adder)

输入: A A A B B B(相加的两个位)、 C in C_{\text{in}} Cin(上一位的进位)
输出: S S S C out C_{\text{out}} Cout

功能:计算 A + B + C in A+B+C_{\text{in}} A+B+Cin,输出 S S S和进位 C out C_{\text{out}} Cout

计算公式:

  • S = A ⊕ B ⊕ C in S=A\oplus B\oplus C_{\text{in}} S=ABCin(注意 A ⊕ B A\oplus B AB本质上等于 ( A + B )  mod  2 (A+B)\ \text{mod}\ 2 (A+B) mod 2
  • C out = A B + A C in + B C in C_{\text{out}}=AB+AC_{\text{in}}+BC_{\text{in}} Cout=AB+ACin+BCin(在 A , B , C in A,B,C_{\text{in}} A,B,Cin中至少有两个为 1 1 1则需进位)

图示:
全加器

三、进位传播加法器(Carry Propagate Adder, CPA)

输入:两个 N N N位输入 A , B A,B A,B;一位进位 C in C_{\text{in}} Cin
输出:一个 N N N位输入 S S S;进位 C out C_{\text{out}} Cout
实现方法:行波进位加法器、先行进位加法器、前缀加法器
图示:
进位传播加法器

1. 行波进位加法器(Ripple-Carry Adder)

构造方法:把 N N N个全加器串联起来,一级的 C out C_{\text{out}} Cout作为下一级的 C in C_{\text{in}} Cin
缺点:当 N N N较大时运算速度会慢下来
延迟: t ripple = N t FA t_{\text{ripple}}=Nt_{\text{FA}} tripple=NtFA,其中 t FA t_{\text{FA}} tFA为全加器的延迟
图示:
行波进位加法器

2. 先行进位加法器(Carry-Lookahead Adder, CLA)

特点:把加法器分成若干,在每块一得到输入进位时就快速算出此块的输出进位。例如, 32 32 32为加法器 = = = 8 × 4 8\times4 8×4位的块
原理:

  • 进位分两种:(考虑第 i i i位,也叫第 i i i列)
    • A i A_i Ai B i B_i Bi(均为 1 1 1时)本身就能产生进位——产生进位,令 G i = A i B i G_i=A_iB_i Gi=AiBi
    • A i A_i Ai B i B_i Bi 1 1 1,且在有上一位的进位 C i − 1 C_{i-1} Ci1时才能进位——传播进位,令 P i = A i + B i P_i=A_i+B_i Pi=Ai+Bi
  • 因此第 i i i位的进位 C i = A i B i + ( A i + B i ) C i − 1 = G i + P i C i − 1 C_i=A_iB_i+(A_i+B_i)C_{i-1}=G_i+P_iC_{i-1} Ci=AiBi+(Ai+Bi)Ci1=Gi+PiCi1
  • 推广到多位构成的块:
    • 一个块在不考虑进位输入的情况下也能发生进位——产生进位
    • 一个块在有进位输入时才能进位——传播进位
  • 定义 G i : j G_{i:j} Gi:j P i : j P_{i:j} Pi:j为从第 i i i到第 j j j为的产生和传播信号
    • 一个块产生进位( G i : j = 1 G_{i:j}=1 Gi:j=1)的条件:最高位产生进位,或最高位传播进位且前一位产生进位。设块的位数是 i : 0 i:0 i:0,则有递推公式 G i : 0 = G i + P i G i − 1 : 0 G_{i:0}=G_i+P_iG_{i-1:0} Gi:0=Gi+PiGi1:0。当 i = 3 i=3 i=3时有 G 3 : 0 = G 3 + P 3 ( G 2 + P 2 ( G 1 + P 1 G 0 ) ) G_{3:0}=G_3+P_3(G_2+P_2(G_1+P_1G_0)) G3:0=G3+P3(G2+P2(G1+P1G0))
    • 一个块传播进位( P i : j = 1 P_{i:j}=1 Pi:j=1)的条件:输入到块中的进位 C j − 1 C_{j-1} Cj1能经过块传到 C i C_i Ci,这就要求进行环环相扣的进位(连锁反应),即块中的每一列都能传播进位。则 P i : 0 = P i P i − 1 ⋯ P i P 0 P_{i:0}=P_iP_{i-1}\cdots P_iP_0 Pi:0=PiPi1PiP0
  • 因此块的生成和传播信号可由 C j − 1 C_{j-1} Cj1 G i : j G_{i:j} Gi:j P i : j P_{i:j} Pi:j快速算出: C i = G i : j + P i : j C j − 1 C_i=G_{i:j}+P_{i:j}C_{j-1} Ci=Gi:j+Pi:jCj1

延时: t CLA = t pg + t pg_block + ( N k − 1 ) t AND_OR + k t FA t_{\text{CLA}}=t_{\text{pg}}+t_{\text{pg\_block}}+\left(\frac Nk-1\right)t_{\text{AND\_OR}}+kt_{\text{FA}} tCLA=tpg+tpg_block+(kN1)tAND_OR+ktFA

  • t pg t_{\text{pg}} tpg:计算 P i P_i Pi G i G_i Gi所需要的时间(即单独的 AND \text{AND} AND OR \text{OR} OR门的延时)
  • t pg_block t_{\text{pg\_block}} tpg_block:计算 P i : j P_{i:j} Pi:j G i : j G_{i:j} Gi:j所需要的时间
  • t AND_OR t_{\text{AND\_OR}} tAND_OR:在 k k k位CLA块中计算 C out = G i : j + P i : j C in C_{\text{out}}=G_{i:j}+P_{i:j}C_{\text{in}} Cout=Gi:j+Pi:jCin的时间(共有 N k \frac Nk kN C L A CLA CLA块,但位数最高的块不需要进位给下一个块,所以这个块直接用行波进位加法器即可)
  • t FA t_{\text{FA}} tFA:全加器的延迟(关键路径有 k k k个全加器,就是位数最高的块的行波进位加法器里面的;其他块都可以提前结束计算,对总延迟没有影响)

N > 16 N>16 N>16时,CLA会比行波进位加法器快很多。然而,加法器的延迟仍然随 N N N的增长而线性增长。

图示:
先行进位加法器

3. 前缀加法器(Prefix Adder)

  • 策略:
    • 回顾 S i S_i Si的表达式: S i = ( A i ⊕ B i ) ⊕ C i − 1 S_i=(A_i\oplus B_i)\oplus C_{i-1} Si=(AiBi)Ci1
    • 定义第 − 1 -1 1列以包含 C in C_{\text{in}} Cin,则 G − 1 = C in G_{-1}=C_{\text{in}} G1=Cin C in = 1 C_{\text{in}}=1 Cin=1就是有进位), P − 1 = 0 P_{-1}=0 P1=0(不可能有传播进位)
    • 若从 − 1 -1 1 i − 1 i-1 i1位中生成一个进位,则第 i − 1 i-1 i1位会有进位输出,故 C i − 1 = G i − 1 : − 1 C_{i-1}=G_{i-1:-1} Ci1=Gi1:1
    • 生成的位要么在第 i − 1 i-1 i1位生成,要么从之前的位中生成并传播到第 i − 1 i-1 i1
    • 因此 S i = ( A i ⊕ B i ) ⊕ G i − 1 : − 1 S_i=(A_i\oplus B_i)\oplus G_{i-1:-1} Si=(AiBi)Gi1:1

核心问题:快速计算 G − 1 : − 1 , G 0 : − 1 , G 2 : − 1 , ⋯   , G N − 2 : − 1 G_{-1:-1},G_{0:-1},G_{2:-1},\cdots,G_{N-2:-1} G1:1,G0:1,G2:1,,GN2:1
前缀: G − 1 : − 1 , G 0 : − 1 , G 2 : − 1 , ⋯   , G N − 2 : − 1 G_{-1:-1},G_{0:-1},G_{2:-1},\cdots,G_{N-2:-1} G1:1,G0:1,G2:1,,GN2:1 P − 1 : − 1 , P 0 : − 1 , P 2 : − 1 , ⋯   , P N − 2 : − 1 P_{-1:-1},P_{0:-1},P_{2:-1},\cdots,P_{N-2:-1} P1:1,P0:1,P2:1,,PN2:1
思想:二分法(与线段树类似)
计算 G G G P P P的方法:

  • G i : j = G i : k + P i : k G k − 1 : j G_{i:j}=G_{i:k}+P_{i:k}G_{k-1:j} Gi:j=Gi:k+Pi:kGk1:j
  • P i : j = P i : k P k − 1 : j P_{i:j}=P_{i:k}P_{k-1:j} Pi:j=Pi:kPk1:j

证明
(1) 根据我们前面提到的“连锁反应”,第 j − 1 j-1 j1位的进位传播到第 i i i位需要 P j P_j Pj P i P_i Pi均为 1 1 1,故 P i : j = P i : k P k − 1 : j P_{i:j}=P_{i:k}P_{k-1:j} Pi:j=Pi:kPk1:j
(2) 在第 j j j到第 i i i位产生进位的条件是第 i i i位本身能产生进位,或第 j j j i − 1 i-1 i1位能传播进位且第 i i i位能传播进位。所以我们有递推公式 G i : j = G i + P i G i − 1 : j G_{i:j}=G_i+P_iG_{i-1:j} Gi:j=Gi+PiGi1:j。同理,条件也可以是第 j + 1 j+1 j+1到第 i i i位能产生进位,或第 j j j位能产生进位且第 j + 1 j+1 j+1到第 i i i位能传播进位,所以有 G i : j = G i : j + 1 + P i : j + 1 G j G_{i:j}=G_{i:j+1}+P_{i:j+1}G_j Gi:j=Gi:j+1+Pi:j+1Gj
下面采用数学归纳法证明 G i : j = G i : k + P i : k G k − 1 : j G_{i:j}=G_{i:k}+P_{i:k}G_{k-1:j} Gi:j=Gi:k+Pi:kGk1:j
①当 k = i k=i k=i时,注意到 G i : k = G i : i = G i G_{i:k}=G_{i:i}=G_i Gi:k=Gi:i=Gi P i : k = P i : i = P i P_{i:k}=P_{i:i}=P_i Pi:k=Pi:i=Pi,故结论显然成立。
②假设当 k = u k=u k=u时成立。则当 k = u − 1 k=u-1 k=u1时, G i : j = G i : u + P i : u G u − 1 : j = G i : u + P i : u ( G u − 1 + P u − 1 G u − 2 : j ) = ( G i : u + P i : u G u − 1 ) + P i : u P u − 1 G u − 2 : j = G i : u − 1 + P i : u − 1 G u − 2 : j = G i : k + P i : k G k − 1 : j G_{i:j}=G_{i:u}+P_{i:u}G_{u-1:j}=G_{i:u}+P_{i:u}(G_{u-1}+P_{u-1}G_{u-2:j})=(G_{i:u}+P_{i:u}G_{u-1})+P_{i:u}P_{u-1}G_{u-2:j}=G_{i:u-1}+P_{i:u-1}G_{u-2:j}=G_{i:k}+P_{i:k}G_{k-1:j} Gi:j=Gi:u+Pi:uGu1:j=Gi:u+Pi:u(Gu1+Pu1Gu2:j)=(Gi:u+Pi:uGu1)+Pi:uPu1Gu2:j=Gi:u1+Pi:u1Gu2:j=Gi:k+Pi:kGk1:j,结论成立。
综上,计算 G G G P P P的方法是正确的。

因此,我们取 k k k j j j i i i的区间中点即可完成二分计算。

图示(16位前缀加法器):
16位前缀加法器
例如, G 14 : − 1 = G 14 : 7 + P 14 : 7 G 6 : − 1 G_{14:-1}=G_{14:7}+P_{14:7}G_{6:-1} G14:1=G14:7+P14:7G6:1 P 14 : 7 = P 14 : 11 P 10 : 7 P_{14:7}=P_{14:11}P_{10:7} P14:7=P14:11P10:7。并且 G 0 : − 1 = G 0 + P 0 G − 1 G_{0:-1}=G_0+P_0G_{-1} G0:1=G0+P0G1 P 0 : − 1 = P 0 P − 1 P_{0:-1}=P_0P_{-1} P0:1=P0P1

黑色单元构成的网络成为前缀树(prefix tree)。

延迟: N N N位前缀加法器的关键路径:

  • P i P_i Pi G i G_i Gi的预计算(是同时进行的): t pg t_{\text{pg}} tpg
  • 获得前缀 G i : j G_{i:j} Gi:j P i : j P_{i:j} Pi:j log ⁡ 2 N ⋅ t pg_prefix \log_2{N}\cdot t_{\text{pg\_prefix}} log2Ntpg_prefix
  • 通过XOR门计算 S i S_i Si t XOR t_{\text{XOR}} tXOR

故总延迟 t PA = t pg + log ⁡ 2 N ⋅ t pg_prefix + t XOR t_{\text{PA}}=t_{\text{pg}}+\log_2{N}\cdot t_{\text{pg\_prefix}}+t_{\text{XOR}} tPA=tpg+log2Ntpg_prefix+tXOR,以 N N N的对数增长。这明显提高了速度,但需消耗更多的硬件资源。

四、减法器

用补码表示负数:正数 B B B的相反数是 B B B的二进制表示取反再加 1 1 1
A − B = A + B A-B=A+B AB=A+B的补码。

因此,减法器可以将输入 B B B取反,设置 C in = 1 C_{\text{in}}=1 Cin=1,与 A A A相加即可。

图示:
减法器

参考书目

《数字设计和计算机体系结构》机械工业出版社

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐