加法器与算术电路:从半加器到 ALU
更新时间:2026-09-10。本文是「芯片设计 · 入门层」数字逻辑基础第五篇。依赖 数制与编码 的补码知识,用到 异或门 与 MUX。
本文要回答的问题
- 半加器和全加器差在哪?真值表与
sum/carry表达式? - 行波进位为什么慢?进位链如何成为关键路径?
- 超前进位(carry lookahead)怎么"一口气"算出所有进位,代价是什么?
- 有了补码,为什么加法器加一点异或控制就能当减法器用?
- 比较器、ALU 怎么在加法器外搭出来?零/进位/溢出标志怎么来?
一、半加器与全加器
两个 1 位二进制数 A、B 相加,结果可能是两位:本位和 sum 与向高位的进位 carry。半加器(half adder):
sum = A ⊕ B (异或:不同才为 1)
carry = A · B (与:都为 1 才产生进位)它叫"半"加器,因为它没法接收从低位来的进位。多位加法中,第 i 位实际要加三个量:Aᵢ、Bᵢ 和低位进位 cᵢ。**全加器(full adder)**多一个进位输入 cᵢ:
sumᵢ = Aᵢ ⊕ Bᵢ ⊕ cᵢ
cᵢ₊₁ = Aᵢ·Bᵢ + (Aᵢ ⊕ Bᵢ)·cᵢ可验证:三个 1 位相加为 3(二进制 11)时 sum=1、进位=1;为 2(10)时 sum=0、进位=1,完全正确。一个全加器可用两个半加器加一个或门搭成。
二、行波进位加法器(RCA):简单但慢
把 n 个全加器首尾相连——第 i 级的进位输出接第 i+1 级的进位输入——就得到 n 位行波进位加法器(Ripple-Carry Adder, RCA),最低位进位输入 c₀ 接 0:
A0 B0 A1 B1 A2 B2 A(n-1) B(n-1)
| | | | | | | |
[FA0]c1→[FA1]c2→[FA2]c3→ ... →[FA n-1]→ c_n
sum0 sum1 sum2 sum(n-1)问题显而易见:最高位的和 sumₙ₋₁ 必须等进位从第 0 级逐级传过来才能稳定。若一级全加器进位延迟是 t,n 位 RCA 的最坏延迟约为 n·t——延迟随位宽线性增长。这条贯穿全加器的进位链,往往就是 32/64 位数据通路里最长的组合路径之一,直接压住时钟频率。它是理解"关键路径"最具体的例子(见 时序为什么不满足)。
三、超前进位(CLA):用面积换速度
能不能不等待进位逐级" ripple ",直接从原始输入把每一级进位算出来?**超前进位加法器(Carry Lookahead Adder, CLA)**定义两个信号:
- 生成(Generate)
Gᵢ = Aᵢ·Bᵢ:这一位无论有无进位输入,都一定产生进位; - 传播(Propagate)
Pᵢ = Aᵢ ⊕ Bᵢ:这一位在有进位输入时,会把进位传到下一级。
于是进位递推式可展开为:
c₁ = G₀ + P₀·c₀
c₂ = G₁ + P₁·G₀ + P₁·P₀·c₀
c₃ = G₂ + P₂·G₁ + P₂·P₁·G₀ + P₂·P₁·P₀·c₀
…所有 G、P 只依赖 A、B,输入一到就能并行算出;各级进位也都表达成 G、P、c₀ 的组合,几级门延迟即可全部得到,不再逐级等待。和位则 sumᵢ = Pᵢ ⊕ cᵢ。代价是:进位展开式随位宽变长、扇入和连线急剧增加,面积(门数)增大、高位驱动变重,所以纯 CLA 一般只做 4 位一组,组间再用分层 CLA(组生成/组传播)级联。
这就是一次典型的 PPA 权衡:RCA 省面积但慢(O(n) 延迟),CLA 快(接近 O(log n))但费面积。真实工艺库还有介于两者之间的结构——进位选择(carry-select,分块各算"进位 0/1"两套结果再用 MUX 选)、进位跳过(carry-skip,块内 ripple、块间用传播信号快速旁路),以及综合器自带的加法器生成器,会按你的时序/面积约束自动选结构。工程师通常只写 sum <= a + b,选哪种加法器是综合器在约束下决定的(见 逻辑综合)。
四、加减法器:补码让减法复用加法器
数制与编码讲过:负数用补码,−B = ~B + 1(按位取反再加一)。因此 A − B = A + (~B) + 1。只需在加法器每个 B 输入端加一个受"减法控制" sub 驱动的异或门,并让 sub 同时充当最低位进位 c₀:
sub=0(加法):Bᵢ XOR 0 = Bᵢ,c₀=0,正常算 A+B;sub=1(减法):Bᵢ XOR 1 = ~Bᵢ,c₀=1,正好算 A+~B+1 = A−B。
// 可综合的 n 位加减法器:综合器映射为前面讲的某种加法器结构
assign {cout, result} = sub ? ({1'b0, a} + {1'b0, ~b} + 1'b1)
: ({1'b0, a} + {1'b0, b});
// 更常见写法:assign sum = sub ? (a - b) : (a + b);溢出(overflow)判定:无符号数看进位/借位 cₙ;有符号补码看最高有效位的进位输入与输出是否不同,即 V = cₙ XOR cₙ₋₁,等价于"两个同号数相加结果变号"。标志位如零标志 Z = (result == 0)(可把所有结果位或非得到)、负标志 N = result 的符号位、进位 C = cₙ,都能用简单组合逻辑从结果引出。
五、比较器与 ALU
n 位比较器判断 A、B 大小,可逐位从高到低比较(高位一旦分出大小即决定结果),也可巧妙复用减法器:算 A−B,由零标志判相等、由符号/借位/溢出标志综合判 A<B、A>B。
**ALU(算术逻辑单元)**就是在同一个加减法器之外,再并排放若干功能单元(AND、OR、XOR、移位、比较等),用一个 MUX 按操作码 op 选择把哪个单元的结果送出:
always @(*) begin
case (op)
3'b000: y = a + b; // ADD(加法器)
3'b001: y = a - b; // SUB(同一加法器 + 取反)
3'b010: y = a & b; // AND
3'b011: y = a | b; // OR
3'b100: y = a ^ b; // XOR
default: y = a + b;
endcase
flag_z = (y == 0); // 零标志
flag_n = y[W-1]; // 负/符号标志
end这正是 RISC-V 入门 里每条 add/sub/and/or 指令在数据通路中真正流过的组合逻辑;PC 自增、地址计算、FIFO 读写指针(FIFO 深度计算)、循环计数也全是加法器。
六、一位全加器的门级细抠:sum 与进位为何延迟不同
全加器两条输出路径的门数不一样,理解这个不对称就理解了加法器时序的根源:
cᵢ₊₁ = AᵢBᵢ + (Aᵢ⊕Bᵢ)cᵢ只用与-或两级(或一个 AOI 复合门),进位从输入到输出很短;sumᵢ = Aᵢ⊕Bᵢ⊕cᵢ要两级异或,而异或在 CMOS 里本身就要 4 个 NAND(或传输管加恢复反相器),sum 路径天然比 carry 路径慢。
行波进位之所以"逐级 ripple",靠的正是每一级的进位路径短——第 i 级 cᵢ 一到,cᵢ₊₁ 很快就能产生,而 sum 则等进位稳定后由最后一级 XOR 给出。超前进位进一步把整条进位链替换成并行逻辑,本质是"用更长的扇入和更多连线,换掉 n 级串联"。这也是为什么工艺库里的加法器 cell(ADDF/FADD)和综合器生成的数据通路,都会把进位输入/输出放在版图上紧邻、甚至直接 abutment(拼接)成一行进位链——金属连线越短,进位越快。
再看一位的真值表能验证两条式子:
| A | B | cᵢ | sum | cₒ | 说明 |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | |
| 0 | 0 | 1 | 1 | 0 | 只有进位 |
| 0 | 1 | 0 | 1 | 0 | |
| 0 | 1 | 1 | 0 | 1 | 传播:carry 穿过 |
| 1 | 0 | 0 | 1 | 0 | |
| 1 | 0 | 1 | 0 | 1 | 传播 |
| 1 | 1 | 0 | 0 | 1 | 生成:无 carry 也有 carry |
| 1 | 1 | 1 | 1 | 1 | 生成 + 传播 |
"生成(A=B=1)/传播(A≠1·B≠... 即 A⊕B=1)/删除(A=B=0)"三态,正是下一节 G/P 信号与更高级前缀加法器的语言。
七、进位结构全景:RCA / CLA / carry-select / 前缀加法器
不同加法器是"进位怎么算"这一个问题的不同答案,谱系可以这样摆:
| 结构 | 进位怎么来 | 延迟量级 | 面积 | 典型用途 |
|---|---|---|---|---|
| 行波 RCA | 一级传一级 | O(n) | 最小 | 窄位宽、低功耗、不赶时序 |
| 超前进位 CLA(分块) | 块内 G/P 并行、块间分层 | O(n/块宽 + 层数) | 中 | 通用数据通路 |
| 进位选择 carry-select | 每块按 carry-in=0/1 各算一套,到了再 MUX 选 | O(√n)~O(n/2) 折中 | 大(近双倍块逻辑) | 关键路径上的中段 |
| 进位跳过 carry-skip | 块内 ripple,块传播信号让 carry 旁路整块 | 最坏仍 O(n),平均快 | 省 | 低功耗折中 |
| 前缀加法器(Kogge-Stone/Brent-Kung/Sklansky) | 用并行前缀网络一次算出所有 G/P | O(log n) | 大、布线密 | 高速 CPU/GPU 宽加法 |
前缀加法器是现代高性能设计的顶点:把每级"生成/传播"看作可结合的算子(黑圆点合并相邻两段的 G/P),用 log₂(n) 层前缀网络(Kogge-Stone 扇出大但层级少、Brent-Kung 布线省但层级多、Sklansky 扇入大)在几级门延迟内算出全部进位,代价是大量交叉连线和面积。这也是一次教科书式的 PPA 谱系:从 RCA 的 O(n) 延迟/最小面积,到前缀的 O(log n) 延迟/大面积,中间用 carry-select/skip 插值。
工程上你几乎不手工选:写 assign {c,s}=a+b;,综合器根据 SDC 里的时序压力、输入到达时间、负载,在加法器生成器里选结构并做位宽分区(低位 ripple、高位前缀的混合也常见)。你能做的是:①给足明确的时序约束让它知道该花面积;②避免在加法结果后再串长组合(比较、移位、深 MUX),把预算留给进位链。
八、补码加减之外:标志位、比较与零检测的电路
ALU 的状态标志几乎都能从加法器结果和进位直接引出,值得逐一过一遍电路来源:
- 零标志 Z:结果所有位 NOR(约简或非),
Z = ~(|result);位宽很宽时搭成树降延迟。 - 负/符号标志 N:直接是有符号结果最高位
result[n-1]。 - 进位 C:无符号加的进位/减法的借位(注意减法时借位常需取反 cₙ,取决于 ISA 约定,ARM/x86 各自规定不同)。
- 溢出 V:
cₙ ⊕ cₙ₋₁(最高位进位进出相异),或等价地s[n-1] ⊕ a[n-1] ⊕ b_eff[n-1] ⊕ cₙ...的符号判断,综合成几个门。
比较器除了复用减法器,也可直接做门级:相等比较是"逐位同或再 AND 成树"((a⊕b) 每一位为 0 才等);大小比较则从最高位向下做"前缀优先"——第一位不相等的 bit 直接决定大小,低位全无关。宽比较器的高位优先链和优先级编码器是同构问题,也能并行前缀化。这解释了为什么 Verilog 里 a == b、a < b 在宽位宽时同样可能是关键路径:它们背后是树/链,不是免费的。
加减法器的边界细节:①带使能的累加器 acc <= acc + x 综合成加法器 + 保持寄存器,比每次读改写省总线;②自增 pc+4 这类"加常数"用特化的增量器(一半输入是常数,大量门可消掉),比通用加法器省得多,所以 RISC-V 里 PC 自增是独立小加法器、ALU 是另一个;③饱和加减在普通加法器后加一段溢出检测 + 结果选择 MUX(溢出时输出 MIN/MAX),DSP/像素处理常见。
九、定点乘法/移位入门:加法器如何拼成乘法器
算术不止加减。无符号乘法 P = A × B 最朴素的实现就是移位—累加:对 B 的每一位,若为 1 则把部分积 A<<i 累加(部分积即 AND 阵列:A·Bᵢ),结构类似笔算竖式:
A2B0 A1B0 A0B0
A2B1 A1B1 A0B1
A2B2 A1B2 A0B2 ← 每行是一组 AND(部分积)
------------------ 逐列用全加器阵列压缩
P5..P0直接把 n 行部分积用 n 级加法器累加是 O(n) 延迟;现代乘法器用 Wallace/Dadda 树:先用全加器(3:2 压缩器,3 个输入变 1 个和 +1 个进位)和半加器(2:2)把部分积阵列逐层压缩成只剩两行,最后用一个快速加法器(CLA/前缀)合并,延迟降到 O(log n)。有符号(Baugh-Wooley,把补码负权项搬到末尾再加校正)和定点 Q 格式(小数位相加、结果位宽翻倍、截位/舍入,见 数制与编码 的定点一节)都建立在这套部分积+压缩结构上。
移位在硬件上几乎免费:固定位数的移位/循环就是连线重排(b = a << 3 只是把线错位接,0 个门);只有移位量可变的桶形移位器(barrel shifter)才需要 log₂(n) 层 MUX。这解释了 RTL 里常量移位可随意写、而 a >>> shamt(shamt 是变量)会综合出一块实打实的 MUX 阵列、也可能进关键路径。DSP 单元(FPGA 的 DSP48、ASIC 的 MAC)正是把"乘法器 + 加法器 + 累加寄存器"做成硬宏,省功耗又快,滤波器/神经网络大量复用(见 AI 加速器入门)。
十、算术在真实模块里的三个高频形态
加法器看似基础,但它在工程里反复以几种固定形态出现,认出它们能直接猜到电路与时序问题。
形态一:累加器(accumulator)与数字积分。 sum <= sum + x 每个时钟沿把新样本累加进同一个寄存器,FIR/IIR 滤波器、能量统计、移动平均、PWM 调宽都靠它。两个要点:①部分和位宽必须留保护位,n 个 W 位有符号数相加理论要 W+⌈log₂n⌉ 位,否则中间溢出,最终再右移/舍入回 W 位;②反馈环 加法器→寄存器→加法器 是一条"回自己"的路径,无法靠流水线把它切短(切了就改变累加语义),所以累加器里的加法器常常是真正的频率瓶颈,要么用快速进位结构,要么降采样后再累加。
形态二:指针/地址的模 2ⁿ 回卷。 FIFO 读写指针、PC 自增、DMA 地址都是 ptr <= ptr + STEP,且靠位宽自然回卷(n 位指针加到 2ⁿ−1 后回 0)。这里有个妙处:模 2ⁿ 加法不用额外逻辑,进位直接丢掉即可;空满判断则靠多跑一位(n+1 位指针,最高位区分"追上一圈")配合格雷码跨时钟域(见 异步 FIFO、FIFO 深度计算)。看懂补码回卷是理解这些电路的前提。
形态三:乘加 MAC 与常数乘。 DSP 里最高频的运算是 acc = acc + a*b,FPGA 直接有硬 MAC(DSP48),ASIC 用乘法器+加法器树;而乘常数几乎从不用通用乘法器——乘 10 = (x<<3)+(x<<1),综合器把常数乘法优化成若干移位和加法(shift-add),常数选得好(如用加减组合、CSD 编码)能把加法数减到很少。同理除以常数 2 的幂是移位(有符号要用算术移位并处理舍入),但除以任意变量则昂贵得多。
除法与开方是算术里的"慢动作":没有像加法那样的短进位结构,常用恢复/不恢复余数法、SRT(逐位迭代,每拍出 1 位商)或 Newton-Raphson 迭代(用乘法收敛倒数),因此除法器往往是多周期、流水线或直接调软模件(编译器把 / 转成库函数调用)。RTL 里一旦出现变量除法/取模,要立刻意识到它可能综合出一大块多周期逻辑,时序和面积都要单独评估,能避免就避免(地址对齐用位掩码、分频用计数器比较而不是 %)。
十一、工程落点:算术在关键路径上怎么办
当加法器(尤其后面还串着比较、移位、MUX)落在关键路径、压不住目标频率时,常用三招,都是本系列后续的主题:
- 流水线(pipelining):把宽加法拆成两段、中间插一级寄存器,让单周期路径变短、用吞吐换延迟(见 微架构入门)。
- 进位结构升级/retiming:让综合器用 CLA/carry-select,或把寄存器在加法器前后自动重排(retiming,见 逻辑综合)。
- 预先计算:如进位选择、分支目标地址提前算等面积换速度手段。
读 STA 报告时,加法器进位链、宽比较器、宽 MUX 是高频关键路径,理解本篇就能看懂报告里那一长串 full_adder/genblk/.../CO 单元在干什么(见 STA 报告解读)。
十二、饱和运算、累加位宽与 RTL 算术自检清单
工程算术与教科书算术最大的差别是:位宽是有限的资源,溢出是要设计的行为而不是异常。三个高频话题收束本篇。
饱和(saturating)运算:音视频、DSP 里算超范围时不希望回卷(127+1 变成 −128 会是刺耳爆音),而是"顶格"——正数溢出钳到最大值、负数钳到最小值。电路上先按足位宽算(8 位加法用 9 位),再用溢出标志选择:y = ovf ? (sum[8]? 8'h80 : 8'h7f) : sum[7:0],多一级 MUX 而已。它和补码回卷是两种刻意选择,写代码前要先知道规格要哪一种。
累加位宽估算:n 个 W 位无符号数累加,结果最大为 n·(2^W−1),需要 W+⌈log₂n⌉ 位;有符号数还要再加一个符号位的余量。滤波器、直方图统计、DMA 长度累加最容易在"中间拍"溢出——最终结果没超,但某一步部分和超了再被截回去。规则是中间保全部精度、最后一级才舍入/截取;截取时正数直接丢低位(向零取整方向有偏),要求无偏时用"四舍五入"(加 0.5ULP 即补 (1<<(k-1)) 再截)或随机舍入。
自检清单:① 表达式左右位宽是否匹配,+1 写的是 1'd1 还是会被推断成 32 位;② 有符号数是否显式 signed,混合 signed/unsigned 在 Verilog 里会被整体当无符号;③ 比较 cnt == N-1 的常量位宽是否覆盖计数器范围;④ 变量除法/取模是否会综合出多周期大电路,能否用移位/掩码代替;⑤ 回卷是想要的(指针、模 2ⁿ)还是必须防的(幅度、计数);⑥ 多周期算术(乘/除)是否已在时序约束里声明 multicycle,还是应该切流水;⑦ 复位值与初始部分和是否明确。把这七条在每个算术模块过一遍,能挡掉数据通路里大半的疑难 bug(更多位宽与符号陷阱见 数制与编码 的 Verilog 清单)。
十三、浮点数加法器一瞥:为什么对阶让算术变复杂
本篇讲的都是定点整数,而 CPU/FPU 里的浮点(IEEE 754)运算值得用一小节建立印象,理解"为什么浮点单元那么大、那么慢"。浮点数由符号、阶码、尾数组成,两数相加不能直接加尾数,要先对阶:把小阶数的尾数右移,直到两者阶码相同——右移会丢掉低位精度,所以规定总是小阶向大阶看齐。对阶后做尾数加法(含隐含前导 1),再规格化(结果左规或右规使前导位回到标准位置)、舍入(处理被对阶挤掉的位,有就近偶、向零、向上、向下四种模式)、最后判异常(溢出→无穷、下溢→零或次正规数、NaN)。乘法相对直接(阶码相加、尾数相乘),除法和开方则靠迭代。
对硬件设计者的意义有三点:①浮点数据通路中,对阶移位器(大桶形移位器)、前导零计数(决定左规位数,常专门用 LZC 优先编码器)、宽加法器和舍入逻辑串成一条很长的关键路径,所以 FPU 普遍多级流水、面积是整数 ALU 的许多倍;②浮点结合律不成立(舍入每步发生,(a+b)+c ≠ a+(b+c)),仿真和验证要考虑容差比较而不是判等;③资源敏感场景(嵌入式、DSP 阵列)尽量用定点 Q 格式(见 数制与编码),把小数点位置交给程序员,硬件只留整数运算。看懂整数加法器的进位结构,再看 FPU 数据手册里的流水级划分,就不会觉得浮点神秘——它只是"加法器 + 移位器 + 计数器 + 状态控制"按浮点算法的规定串起来而已。
十四、本篇复盘:从一个全加器到数据通路的判断力
回头看,整章的展开其实是一个不断"暴露限制、再解决限制"的链条,复盘这条链条比记住每种加法器名字更重要。单个全加器的瓶颈在进位输出:它比和输出少一级门,但当全加器逐个串联时,进位要爬过所有位,行波加法器的延迟于是随位宽线性增长。超前进位用"生成/传播"两个局部信号把进位链改写成前缀计算,把线性变对数,但代价是扇入和面积膨胀;进位选择用"两套预置结果 + 最后选择"买速度,本质是空间换时间;前缀加法器(Kogge–Stone、Brent–Kung 一族)则是在扇出、线长、级数之间做不同取舍的同一家族——没有绝对最优,只有在给定工艺、频率、面积约束下的合适点。
工程判断力正建立在这种"知道代价在哪"的认知上:写 a+b 时知道背后是一棵进位树、它在 32 位时大致多深、会不会顶到关键路径;做累加器时知道反馈环无法靠插流水线缩短、要换进位结构或降速;看到时序报告里的长路径穿过加法器时,能在"切流水、换前缀、限定位宽、改成多周期"之间选手段;写除法/取模时立刻警觉综合结果;定数据格式时知道什么时候整数够、什么时候必须上定点、什么时候才值得付浮点的代价。这些判断不需要你手算门延迟,只需要你始终记得:算术运算在硬件里不是一个符号,而是一棵有深度、有面积、有功耗的具体电路。带着这个意识进入 微架构 和时序收敛的学习,ALU、MAC、地址生成、流水线分级这些主题都会变成同一张图的不同局部。
与现有篇目的衔接
- 前置:数制与编码(补码)、逻辑门与 CMOS、编码器/译码器/MUX
- 后续时序部件:锁存器与触发器、寄存器/计数器
- 进位/延迟为何影响频率:时序为什么不满足、静态时序分析入门、STA 报告解读
- 用加法器的真实模块:FIFO 深度计算与设计、微架构入门、RISC-V 入门
总结
一句话总结:半加器算 sum=A⊕B、carry=A·B,全加器再多接一个低位进位 cin(sum=A⊕B⊕c、cout=A·B+(A⊕B)·c);把全加器串成行波进位加法器最省面积,但进位要逐级传播、延迟随位宽 O(n) 增长,进位链常成为数据通路关键路径;超前进位用 generate/propagate 信号把各级进位表达成原始输入的并行组合、几级门即可得到,用面积换速度(实际还有进位选择/跳过等折中,具体结构由综合器按时序约束选择);凭借补码 −B=~B+1,给每个 B 输入加一个受减法标志控制的异或门、并让该标志充当 c₀,同一套加法器就能兼做减法,溢出看最高位进位进出是否相异、零/负标志由结果简单引出;再把加减、与或、异或、比较等单元经 MUX 按操作码选择,就拼成了 ALU——而当算术落在关键路径上时,靠流水线、更快的进位结构与 retiming 来收敛时序。