4.1 执行单元、总线接口单元与取指队列
第 3 章介绍了组合电路和时序电路。Intel 8086 把这些概念连接成一颗完整的 16 位微处理器:组合电路负责运算与译码,寄存器保存状态,时钟协调状态变化。
本节先不学习具体指令,而是认识处理器的组织方式:哪个部分执行运算,哪个部分与存储器通信,以及六字节队列为何能让二者部分并行工作。
两个协作单元
通常把 8086 解释为两个相互协作的单元:
- 执行单元(Execution Unit,EU)对指令字节进行译码、控制执行、完成算术与逻辑运算,并更新通用寄存器和标志。
- 总线接口单元(Bus Interface Unit,BIU)形成地址、预取指令字节、传送存储器或 I/O 数据,并维护指令预取队列。
这种划分用于理解芯片的组织,并不表示芯片中有两个独立处理器。一条读取存储器的指令需要两个单元协作:EU 判断指令需要什么,BIU 完成芯片外部的总线传送。
上图划分的是职责,并没有画出每一根物理连线。BIU 会使用段信息与指令指针取得指令字节;4.2 节将系统介绍这些程序员可见的寄存器。
实验 1——把职责分配给正确单元
把每项工作分配给 EU 或 BIU。实时变化的单元面板会帮助你理解:译码、算术、地址形成和总线控制并不是同一种工作。
检查理解
为什么要在 EU 请求之前取指?
许多指令的译码和执行需要时间。在其中一些时刻,外部总线原本会处于空闲状态。BIU 利用可用的总线时间预取(prefetch)后续指令字节,再把它们放入先进先出队列。
8086 指令队列最多保存 6 个字节。“六字节”不等于“六条指令”:一条 8086 指令可能只有一个字节,也可能包含多个字节。EU 会取走当前指令所需数量的字节。
预取形成一种简单的工作重叠:
1. 总线空闲且队列未满时,BIU 预取后续指令字节。
2. EU 从队列取出已有字节并执行当前指令。
3. EU 在芯片内部工作时,BIU 可能同时补充队列。
这是早期的流水思想,但并不是后来处理器中的深度指令流水线。只有在资源需求不冲突时,两个单元才能重叠工作。
重叠何时失去作用
队列可以缓冲取指速度与执行速度的差异,却无法消除所有等待:
| 事件 | 对队列的影响 | 对 EU 的影响 |
|---|---|---|
| 队列已满 | BIU 停止预取 | EU 可以继续执行 |
| EU 消耗速度超过 BIU 补充速度 | 队列变空 | EU 等待指令字节 |
| 传送存储器或 I/O 操作数 | 总线用于传数据,预取暂停 | 若 EU 需要结果,它也要等待 |
| 已执行跳转、调用、返回或中断 | 旧字节不再属于正确路径,因此被丢弃 | 从新的指令流重新开始 |
清空队列(queue flush)是保证正确性的必要动作。假设条件跳转后的字节已经进入队列,而跳转最终成立,这些字节属于未跳转路径;如果继续执行它们,程序就会走错路径。
不要把该队列理解成高速缓存。它容量很小、只保存连续指令流,不能保留任意存储器块供反复使用,而且控制流变化会丢弃其中失效的内容。
实验 2——让队列在压力下保持有效
调节教学模型中的预取速度、EU 消耗速度、数据总线压力与转移时刻。尝试避免断粮、主动制造断粮,并观察一次转移究竟丢弃多少已预取字节。模型中的时隙只用于展现因果关系,不对应固定数量的真实 8086 时钟周期。
检查理解
分析处理器组织的可靠方法
面对每个事件时,依次问三个问题:
1. 谁做决定? 指令译码与 ALU 控制属于 EU。
2. 谁跨越芯片边界? 存储器和 I/O 总线操作属于 BIU。
3. 已经预取的字节会怎样? 它们按先进先出顺序保留,除非新的指令流使它们失效。
下一节将打开这些工作的程序员可见状态:通用寄存器、指针和变址寄存器、段寄存器、指令指针以及 FLAGS。