在 1978 年推出的 Intel 8086 处理器奠定了现代 x86 架构的基础。与当代处理器不同,8086 是一款纯 16 位芯片,其算术逻辑单元(ALU)在单一时钟周期内完成 16 位值的算术与逻辑运算。Ken Shirriff 通过对芯片硅层的显微摄影与逆向工程,揭示了 ALU 控制电路的精密实现:微指令使用「XI」占位操作,由硬件根据机器码 opcode 替换为实际的 ALU 功能;控制逻辑依赖 5 个触发器存储操作码、3 个触发器追踪临时寄存器,并通过可编程逻辑阵列(PLA)生成 27 条控制信号;尤为特殊的是,查找表控制信号采用自举升压驱动电路,以克服 NMOS 晶体管的阈值电压损耗,确保在 16 位并行负载下仍能维持信号完整性。这些发现表明,CISC 处理器的微架构在处理复杂指令集时,需要在控制逻辑中嵌入大量的特例处理与信号路由,从而解释了为何 x86 指令集的持续成功伴随着硬件复杂度的指数级增长。
微指令架构:数据移动与 ALU 操作的解耦
8086 处理器的大多数机器指令由微码实现,每条机器指令对应一组微指令序列的执行。与直觉相反,8086 的微指令架构采用了一种独特的双域设计:左半部分负责在源操作数与目标操作数之间移动数据,而右半部分则执行第二项独立操作,这可以是跳转、子程序调用、内存读写或 ALU 运算本身。这种设计使得单条微指令能够并行完成数据准备与控制流决策,从而在有限的芯片面积内提升指令吞吐量。
对于 ALU 运算而言,微指令包含一个 5 位字段用于指定具体的 ALU 操作,以及一个 2 位字段用于标识提供输入数据的临时寄存器。这两个字段在 ALU 控制电路中扮演着核心角色,因为控制逻辑必须同时获知「执行什么运算」以及「从哪里获取操作数」。一个关键的设计特性是:微指令本身并不总是直接指定 ALU 操作,而是经常使用一个名为「XI」的伪操作占位符。当微码指定 XI 时,硬件会自动将其替换为机器指令 opcode 中编码的实际 ALU 功能。这种机制允许相同的微代码序列复用来处理 ADD、SUB、ADC、SBB、AND、OR、XOR、CMP 等八种不同的算术逻辑指令,同时也使得 INC、DEC、DAA、DAS、AAA、AAS 等指令共享同一套微代码模板。
微操作的执行遵循严格的时序约束。由于 ALU 的运算结果无法在配置微指令的同一周期内可用,8086 采用了两步式微操作序列:第一步微指令配置 ALU 的工作模式,第二步微指令才从 ALU 读取运算结果并将其存储到目标位置。这意味着控制电路必须在配置与读取之间「记住」所指定的 ALU 操作,这一需求直接催生了后文将详细讨论的触发器组与操作锁存器设计。
ALU 电路实现:查找表驱动的可配置运算单元
与早期微处理器(如 6502 为每种运算使用独立电路,或 8085 采用优化门丛的方式)不同,8086 的 ALU 采用了一种更为灵活且可扩展的架构:通过两个查找表(LUT)配合少量门电路,为每一位生成进位输出与结果位。这种设计与现代 FPGA 的查找表实现原理相似,意味着只需改变 6 条控制信号的组合,就能让同一硬件执行完全不同的运算功能。
8086 ALU 单 bit 电路的核心结构包含两个多路复用器(梯形符号),它们使用两个输入操作数的相应位作为选择信号,从 6 条控制信号中选取特定的输出值。这些输出值分别控制进位生成逻辑与进位传播逻辑,从而决定该 bit 位的运算行为。通过向 ALU 输入不同的控制信号配置,同一电路可以实现加法、减法、逻辑与、逻辑异或等各种运算。8086 在芯片上复制了 16 份该电路以支持完整的 16 位数据宽度,而控制信号则在整个 ALU 中共享。
尽管查找表架构提供了灵活性,8086 的指令集复杂性要求额外的控制信号来处理各种特殊情况。例如,比较操作(CMP)在逻辑上等同于减法,但数值结果被丢弃、仅更新状态标志位;带进位加法(ADC)与普通加法(ADD)在第 0 位的进位输入上有所不同;减法需要将进位标志取反以模拟借位行为。此外,8086 还支持按 2 递增或递减(INC2、DEC2),这要求进位信号从第 1 位注入而非第 0 位。各种移位操作同样需要特殊处理:循环移位可能包含或排除进位位,算术右移需要复制最高有效位。综合这些需求,8086 的 ALU 除了 6 条查找表控制信号外,还需要约 21 条额外的特殊场景控制信号来覆盖所有指令变体。
控制电路在硅基上的物理实现
在芯片的显微照片中,ALU 控制电路位于芯片底部、紧邻 ALU 本身的位置。控制信号从右侧进入,首先被锁存器捕获以实现时序同步;随后穿过可编程逻辑阵列(PLA),该阵列对指令进行译码并生成控制信号;最终,控制信号向左传输到 ALU 的各个控制端口。金属层被剥离后,硅层与多晶硅层的痕迹清晰可见,残留的金属走线呈现红色,为逆向工程提供了物理依据。
当微代码指定 XI 操作时,操作码的替换过程由 XI 多路复用器在锁存前完成。考虑到 8086 指令集的高度复杂性,XI 的映射逻辑并非简单直译。替换过程涉及三个来源的信号:来自专用「X」寄存器的机器指令位 5-3、来自指令寄存器的位 6,以及来自组译码 ROM(Group Decode ROM)的位 4。这种分层译码机制的设计动机在于:指令集有意让指令位的模式与 ALU 操作编码保持某种对应关系,但映射规则因指令类型而异。八种基本算术逻辑指令(ADD、SUB、OR 等)遵循直接映射,可从 8086 opcode 表中直观看出;但其他指令的映射关系则较为隐晦。此外,某些指令将操作码编码在机器指令的第一个字节,而另一些指令则将操作码放在第二个字节,这解释了为何需要 X 寄存器来暂存相关位。
为了在配置与结果读取之间保持操作的连续性,ALU 控制电路使用触发器组来「记住」配置信息。操作码的 5 位字段由五个触发器存储,这些触发器在每条新机器指令开始时复位至操作码 0,即默认的 ADD 操作。这一设计带来了一个重要的优化空间:由于复位后的默认值已经是 ADD,处理器可以在没有显式配置微指令的情况下直接执行加法,从而节省一个微周期、缩短指令执行时间。临时寄存器的选择同样依赖三个触发器(每个临时寄存器对应一个),微指令的 2 位字段被解码后激活相应的触发器,其输出连接到 ALU 以使能对应的临时寄存器。临时寄存器触发器同样在每条新指令开始时复位,默认选中临时寄存器 A。
五位操作码触发器的输出进入操作 PLA,该 PLA 由「或」层与「和」层组成,实现「积之和」形式的布尔逻辑。操作 PLA 产生 27 条输出控制信号,其中约 15 条被馈送至查找表 PLA,后者进一步生成 6 条查找表控制信号。在查找表 PLA 的最左侧,高电流驱动电路对控制信号进行放大,以驱动连接到 16 个晶体管(每个 ALU bit 一个)的查找表控制线。这种大电流驱动是必要的,因为每个控制信号需要同时充放电 16 个晶体管的栅极电容,RC 延迟不可忽视。
自举升压驱动:克服阈值电压的模拟技巧
控制信号在离开查找表 PLA 后,需要通过一个精心设计的驱动电路才能到达 ALU。这个驱动电路的实现曾让逆向工程者困惑多年:一个晶体管的栅极直接连接 +5V 电源,似乎会永远导通。然而,查阅《DRAM 电路设计》文献后确认,这种电路被称为「自举字线驱动电路」(Bootstrap Wordline Driver),其目的是使输出电压超过普通 NMOS 电路所能达到的电压水平,从而提供更优的性能。
NMOS 电路的一个根本限制是晶体管将信号拉高时,输出电压无法达到栅极电压,而是会低一个阈值电压 VTH(通常约 0.5 伏或更多)。自举电路利用电容耦合效应来克服这一限制:当输入为 +5V 且时钟为高电平时,点 A 的电压约为 4.5V,已经损失了阈值电压;随后当时钟变低、反相时钟驱动上拉晶体管导通时,由于第二个晶体管源极与漏极的电压跳变,通过电容耦合效应将其栅极电压推升到高于原始电压的水平,可能增加数伏特。高电压栅极产生完整的电源电压输出,避免了 VTH 导致的压降。那个看似「永远导通」的 +5V 晶体管起到类似二极管的作用,防止升压后的电压通过输入端回流耗散。
自举驱动电路在 8086 ALU 中的应用有两个关键原因。首先,查找表控制信号驱动的是传输晶体管,而传输晶体管会因阈值电压导致信号衰减,因此需要从尽可能高的电压开始驱动。其次,每个控制信号连接到 16 个晶体管(每个 ALU bit 一个),这是相当大的扇出负载。提升控制信号的初始电压有助于克服 RC 延迟,提高整体性能。显微照片显示了六个自举驱动电路位于查找表 PLA 的左侧,这些输出晶体管的尺寸远大于周围的其他晶体管,因为它们必须提供高电流驱动能力。
时序与复位:流水线阶段的控制信号同步
控制触发器的复位时机揭示了 8086 内部流水线的细节。触发器由芯片上的「Second Clock」信号复位。当一条新机器指令开始执行时,「First Clock」信号在指令的第一个字节上产生,「Second Clock」信号则在指令的第二个字节上产生。需要注意的是,这些信号并不一定出现在连续的时钟周期中,因为如果指令队列为空,可能需要额外的内存读取周期。
为何选择 Second Clock 而非 First Clock 来复位 ALU 状态?答案在于 8086 的小规模流水线设计:前一条微指令可能仍在 First Clock 期间完成收尾工作;直到 Second Clock,状态才稳定到可以安全复位的程度。这种设计确保了流水线相邻阶段之间的状态隔离,避免了数据冒险。
结论:CISC 复杂度的控制层面代价
8086 ALU 控制电路的逆向工程揭示了 RISC 与 CISC 设计哲学之间的根本差异。在 ARM 等 RISC 处理器中,指令译码是直接且线性的,控制逻辑相对简洁。但 8086 作为 CISC 处理器,其指令集的各种边角案例和特殊情况遍布整个控制路径:机器指令有时在第一个字节指定 ALU 操作,有时在第二个字节,有时甚至在其他位置,这催生了 X 寄存器锁存器、XI 多路复用器和组译码 ROM 的复杂协作。8086 的 ALU 还包含晦涩的 BCD 调整操作(四种类型)和七种移位变体,进一步增加了控制逻辑的复杂度。
然而,这种复杂度也带来了回报:x86 指令集至今仍是计算领域的主流架构,证明 CISC 的「复杂指令」在某些场景下确实提供了密度与效率的优势。理解 8086 ALU 控制电路的实现细节,不仅是对计算机历史的致敬,也为现代处理器设计提供了关于如何在硅基约束下平衡灵活性与性能的宝贵参考。
参考资料
- Ken Shirriff,《Notes on the Intel 8086 processor's arithmetic-logic unit》,2026 年 1 月,https://www.righto.com/2026/01/notes-on-intel-8086-processors.html
- Andrew Jenner,《8086 microcode disassembly》,https://www.reenigne.org/blog/8086-microcode-disassembled/