当英特尔在 1985 年 10 月发布 80386 处理器时,这颗芯片不仅标志着 x86 架构首次迈入 32 位时代,更在算术运算性能上实现了质的飞跃。从 8086 的 16 位乘法需要 120 至 130 个时钟周期,到 386 的 32 位乘法最快仅需 9 个周期,这种性能跃升的背后是一套精心设计的乘除法单元微架构。理解这套「每周期 1 位」的迭代算法,不仅有助于我们把握现代 CPU 执行单元的演进脉络,也为 FPGA 实现和嵌入式系统设计提供了经典参考。
设计理念:主 ALU 复用与迭代计算
80386 的乘除法单元并非采用独立的乘法器阵列,而是巧妙复用了处理器的主 ALU 进行迭代运算。这一设计选择源于 1985 年硅片面积和功耗的严格约束 —— 在那个晶体管数量以万计而非以亿计的年代,每一平方毫米都弥足珍贵。英特尔工程师的思路是:用最小的额外硬件实现可接受的算术性能,通过微码控制将复杂的乘除运算分解为一系列简单的「加 / 减 - 移位」基本操作,每个周期推进 1 位计算结果。
这套架构依赖五个关键的内部寄存器协同工作。MULTMP 寄存器存放被乘数(或除数),TMPB 寄存器存放乘数(或除数),SIGMA 作为累加器参与加减运算并最终保存余数,DIVTMP 专门存放被除数的低 32 位,而 RESULT 寄存器则在除法运算中累积商的结果。循环计数器 COUNTR 控制迭代次数,对于 32 位运算初始值为 31。这种寄存器组织方式与 x86 指令集的 8 位、16 位、32 位三种操作数宽度保持一致 —— 微码只需调整 COUNTR 的初始值,就能复用同一套硬件处理不同宽度的运算。
乘法实现:add-and-shift 与 early-out 优化
80386 的乘法算法并非教科书上常见的 Booth 编码,而是更为直接的「加法 - 移位」算法,本质上类似于小学竖式乘法的二进制版本。算法流程可以概括为:每次迭代检查乘数当前最低位,若为 1 则将被乘数加到累加器,随后将整个乘积右移一位。右移而非左移的设计简化了硬件电路,因为移位操作可以自然地将处理过的乘数位「移出」,同时将新的结果位「移入」。
这段描述的伪代码揭示了 386 乘法的完整逻辑。首先将 COUNTR 设为操作数宽度减一作为循环上界。在循环体内,若 TMPB 的最低位为 1,则将 MULTMP 加到 SIGMA;随后对 {SIGMA, TMPB} 整体执行一次右移(有符号乘法使用算术移位);每轮结束时 COUNTR 减一,若递减至零则退出循环。关键的优化在于第 6 行的「early-out」判断:当剩余的乘数位全部为 0(无符号乘法)或全部为 1(有符号乘法)时,无论后续如何移位都不会再触发加法操作,因此可以立即终止循环。Early-out 机制将 32 位乘法的典型周期数从最坏情况的 38 个压缩到最优情况的 9 个,取决于操作数中连续零或连续一的长度。
微码序列精确实现了这一算法。以MUL r指令为例,微码首先将目标寄存器复制到 MULTMP 作为被乘数,同时将 EAX/AX/AL 的内容复制到 TMPB 作为乘数,COUNTR 被设置为操作数宽度减一。核心的迭代循环由RPT指令驱动,重复执行 SIGMA 与 TMPB 的组合移位操作,并在每个周期根据 TMPB [0] 决定是否将 MULTMP 加到 SIGMA。循环结束后,微码执行最终的移位调整和符号修正(有符号乘法需检查乘数符号并调整结果),最终将乘积的低半部写入 EAX,高半部写入 EDX/DX/AH。
除法实现:non-restoring 算法与符号处理
除法运算在硬件实现上比乘法更为复杂,因为每一步商位的确定都依赖于上一步余数的符号。80386 采用经典的「非恢复除法」(non-restoring division)算法,该算法的核心思想是:在每轮迭代中,首先将余数左移一位,然后根据余数的符号决定下一步是加除数还是减除数。若余数为负,则加除数使其恢复;若余数为正,则减除数。这种「加 - 减交替」的模式使得算法无需在每轮结束时将余数恢复到正区间,从而减少了加法器的使用次数。
除法的伪代码展示了这一过程。每次迭代将 {SIGMA, DIVTMP} 整体左移一位,随后根据 SIGMA 的符号决定是加 TMPB 还是减 TMPB,商的结果位被写入 RESULT 寄存器。循环结束后,若最终的余数为负(非恢复算法可能产生负余数),则需要加一次除数进行修正。与乘法不同,除法没有 early-out 优化 —— 每轮迭代都必须执行,因为商位的确定具有依赖性,无法提前预判后续位的模式。
有符号除法 IDIV 的实现揭示了为何其周期数比无符号 DIV 多出 5 个。IDIV 必须处理操作数符号,这要求额外的预处理和后处理步骤。预处理阶段将 dividend 和 divisor 都转换为绝对值,同时保存各自的符号标志。主除法循环执行无符号运算,后处理阶段则根据之前的符号标志修正商和余数的符号。余数的符号必须与被除数保持一致,这增加了额外的判断和可能的调整操作。这 5 个周期的额外开销正是符号处理逻辑的代价。
工程权衡:从 386 到现代处理器的演进
80386 的「每周期 1 位」迭代方案在 1985 年是工程上的合理选择。彼时的硅工艺限制使得在单一芯片上集成专用乘法器阵列既不经济也非必要 —— 主 ALU 的复用虽然降低了运算的并行度,但显著节省了晶体管预算,同时仍然提供了可接受的算术性能。对于现代 FPGA 实现和嵌入式系统设计,这套微架构依然是值得参考的范例:它展示了如何在资源受限的环境中通过巧妙的算法设计和微码控制实现复杂运算。
现代 x86 处理器的乘除法单元早已今非昔比。从 1993 年的 Pentonia 开始,专用乘法器阵列逐步取代了迭代算法;到 21 世纪初的 Core 2 时代,Booth 编码配合 Wallace 树阵列已经能够在 3 至 4 个周期内完成 64 位有符号乘法。除法运算的加速相对困难,因为其本质上的串行依赖性限制了并行度,但 radix-4 和 radix-16 除法器通过每周期计算 2 至 4 个商位,将 32 位除法的延迟从 386 时代的 38 个周期压缩到现代处理器的 13 至 19 个周期。80386 的「每周期 1 位」方案,如今已成为处理器设计课程中的历史案例,提醒我们每一代架构演进都是当时工艺水平与性能需求的权衡产物。
参考资料:本文关于 80386 微码序列和内部寄存器组织的技术细节,来源于对 80386 程序员参考手册的分析以及 nand2mario 对 386 乘除法单元的逆向工程研究。