全文内容主要来自对课程《深入浅出计算机组成原理》的学习笔记。
17 | 建立数据通路(上)
指令周期(Instruction Cycle)
一条指令的执行过程:
- Fetch:取得指令
- Decode:指令译码
- Execute:执行指令
(重复以上步骤)
上述的一个循环称为指令周期(Instruction Cycle)
。
指令存在存储器
,由控制器
操作,通过 PC 寄存器
和指令寄存器
取出指令,控制器
也控制解码
过程。
指令执行则是由算术逻辑单元(ALU)
操作的,简单的无条件地址跳转则在控制器内完成。
三个 Cycle:
Instruction Cycle
:指令周期Machine Cycle
:机器周期/ CPU 周期Clock Cycle
:也就是时钟周期,机器的主频
一个指令周期由多个机器周期组成,一个机器周期由多个时钟周期(至少2个)组成。
建立数据通路
数据通路就是我们的处理器单元
一类是操作元件
:也叫组合逻辑元件
(Combinational Element),其实就是我们的 ALU
;
一类是存储元件
:也有叫状态元件
(State Element),例如寄存器。
通过数据总线链接起来进行使用,即建立数据通路。
控制器,为了循环完成指令的读取和解码,将结果信号输送给ALU。例如 CPU 有 2k+ 个指令,即有同样多的控制信号组合。
正是控制器,使得可以“编程”来实现功能,构建“存储程序型计算机”。
CPU 所需要的硬件电路
- ALU:根据输入计算结果输出;
- 寄存器:进行状态读写;
- “自动”的电路:按照固定的周期,不停地实现 PC 寄存器自增;
- “译码”的电路:对指令进行 decode。
cpu 满载
:如果操作系统调度了一个高优先级的任务,那么cpu就优先执行这个任务即满载,Idle 闲置
:如果操作系统调度了一个低优先级的idle任务,那么cpu就执行这个优先级最低的简单任务,显示为空闲状态,即假装“没事做”,有其他高优任务时可随时抢占。
idle 进程,优先级最低,仅当其他进程都阻塞时被调度器选中。idle 进程循环执行 HLT 指令,关闭 CPU 大部分功能以降低功耗,收到中断信号时 CPU 恢复正常状态。
18 | 建立数据通路(中)
组合逻辑电路(Combinational Logic Circuit):给定输入,就能得到固定的输出。
光有上述的不足够,更像机械计算机,电子计算机则需要时序逻辑电路(Sequential Logic Circuit),可解决3个问题:
- 自动运行
- 存储
- 时序协调
时钟信号的硬件
CPU 的主频是由一个
晶体振荡器
来实现的,而它生成的电路信号,就是时钟信号
。
核心:电的磁效应。
如下图:
开关A:手动控制;
开关B:自然状态是合上。
那么,开关A合上后,由于线圈通电B就会被断开,而后线圈没有磁性B又会弹回,于是又下面的时钟信号
,叫做反馈电路
:
可简化成如下方式,一个输出结果接回输入的反相器
(Inverter),也即非门
。
通过 D 触发器实现存储功能
基于反馈电路构建有记忆的电路,以实现寄存器和存储器。先看如下电路(2个或非门):
NOR | 0 | 1 |
---|---|---|
0 | 1 | 0 |
1 | 0 | 0 |
如上图和表:
- 开始R、S断开,A输入0、0,输出则1;B输入0、1,输出则0;电路稳定,Q输出0;
- 如R闭合,A输入1、0,输出则0;B输如0,0,输出则1;A输入变成1、1,输出则0;电路稳定,Q输出1;
- 如R重断开,A输入1、0,输出则0;B不变;电路稳定;但R、S和步骤1一样,Q却输出1;
- 如S闭合,B必然输出0;则Q也输出0。
上述为触发器
(Flip-Flop):当两个开关都断开的时候,最终的输出结果,取决于之前动作的输出结果,即记忆功能
。
为了实现利用上述写入数据,加入了两个与门和一个时钟信号,看下如电路:
- CLK为低电平的时候,R、S无论开关,其紧邻的与门必然输出0;
- CLK为高电平的时候,R、S状态会控制Q输出。
再将 R 和 S 用一个反相器连起来,就成为最常用的 D 型触发器
,一个输入的数据信号 D,也就是 Data:
图中 Q 表示主输出,!Q 表示 Q 的补码。可以发现:
- CLK 低电平的时候,D 输入无影响,Q 始终 0;
- CLK 高电平的时候,D 的输入会决定 Q 的结果,且信号一致。
故,一个 D 型触发器
控制一个bit读写,N 个并列可做成 N 位触发器。
因此,程序可以“存储”,而不是靠固定的线路连接或者手工拨动开关,如此便解决。
19 | 建立数据通路(下)
让计算机“自动”跑起来:时钟信号->实现计数器->PC计数器->译码器->CPU。
计数器
PC(Program Counter)寄存器
,又叫程序计数器
。
如上图所示:每过一个时钟周期,就能固定自增 1。
- 自增之后,可以取D型触发器里的值作为指令地址;
- 顺序地存放指令,就是为了能定时地不断执行新指令。
一条指令,经历程序计数,到获取指令、执行指令,需要在一个时钟周期里,否则可能会出错。
设计确保上限即耗时最长的一条 CPU 指令能完成的,我们称之为单指令周期处理器
(Single Cycle Processor)。
读写数据所需要的译码器
很多 D 型触发器可以组成一块存储空间作为内存。寻址的电路就是译码器。
先简化,两个地址选一个,成为 2-1 选择器;如下图:
- 输入0,则输出和A一致;
- 输入1,则输出和B一致。
如果输入信号有3个不同开关,则能选择$2^3$个,称为3-8译码器
。现在CPU上64位的,即有$2^{64}$个开关的译码器。
构造一个最简单的 CPU
CPU通路:
- 自动计数器,作为 PC 寄存器;
- 连一个地址译码器+内存(大量 D 型触发器);
- 计数器随着时钟主频自增,译码器获取指令的内存地址,写入指令寄存器;
- 指令译码器将指令解析成 opcode 和操作数;
- 链接 ALU 获取计算结果,写回到寄存器或者内存。
if…else…电路:
实际上不是控制电路,被拆解成一条 cmp 和一条 jmp 指令;
条件跳转指令也是在 ALU 层面执行的,“译码 - 执行 - 更新寄存器“,不需要控制器。执行一条计算机指令,其实可以拆分到很多个时钟周期,而不是必须使用单指令周期处理器的设计。
20 | 面向流水线的指令设计(上)
单指令周期处理器
CPU 指令:“取得指令(Fetch)- 指令译码(Decode)- 执行指令(Execute)
如果 CPI = 1,即 1 个时钟周期执行一个指令,叫单指令周期处理器
(Single Cycle Processor)。
但,由于指令电路复杂程度不一,实际上时间就不一样,因此就只能取最长的指令运行时间作为时钟周期,造成资源浪费。
现代处理器的流水线设计
现代的 CPU 基本都是指令流水线
(Instruction Pipeline):其实就是将不同步骤拆开来执行,同时指令间也不用等待串行。
指令流水线中的每个步骤称为流水线阶段
或者流水线级
(Pipeline Stage)。例如“取指令 - 指令译码 - ALU 计算(指令执行)- 内存访问 - 数据写回”就是5级。
虽然流水线会使得指令的时钟周期增加,但复杂指令被拆分成多个小流水线级,就可以提高 CPU 主频了。只要保障一个最复杂的流水线级的操作,在一个时钟周期内完成就好了。
现代的 ARM 或者 Intel 的 CPU,流水线级数都已经到了 14 级。
该技术下,单指令时间没变,但提升 CPU 的“吞吐率”,同时执行 5 条不同指令的不同阶段。
性能瓶颈
流水线级数并不是越高越好。每增加一级的流水线,就要多一级写入到流水线寄存器的操作。所以单纯地增加流水线级数,不仅不能提升性能,反而会有更多的 overhead 的开销。
一个 CPU 的时钟周期:可以认为是完成一条最复杂的指令拆分后流水线级的操作时间。
21 | 面向流水线的指令设计(下)
芯片的主频战争
Intel 在 2001 年推出 Pentium 4,特点就是高主频,1GHZ,设计的最高是 10GHz。但,在这过程中,使用超长的流水线。
在 Pentium 4 之前的 Pentium III ,流水线的深度是 11 级,当今一般也就 14;
而 Pentium 4 是 20 级。
流水线记住不能缩短单指令效应时间,但可以增加吞吐率。
冒险和分支预测
Pentium 4 为什么失败?
- 功耗问题。流水线深度增加,需要更高主频;
- 且电路增多,晶体管增加。于是功耗增加;
- 性能提升不一定。例如互相有依赖的code,不能分级后并行。
冒险(Hazard)问题:数据冒险、结构冒险、控制冒险。
流水线越长,这个冒险的问题就越难以解决。因为级数越高,越难使用乱序执行、分支预测等方案。
一般用 IPC
(Instruction Per Cycle)来衡量 CPU 执行指令的效率。 即 CPI(Cycle Per Instruction)的倒数。