本系列为作者学习记录
感谢Jack-Cui视频的启发
视频地址:【计算机科学速成课】[40集全/精校] - Crash Course Computer Science
系列文章目录
前言
本文内容包括:
- 寄存器和内存-Registers and RAM
- 中央处理器-The Central Processing Unit.
- 指令和程序-Instructions & Programs
- 高级CPU设计-Advanced CPU Designs
一、寄存器和内存
考虑存储1位的数据,最基础的是AND-OR锁存器,如图所示。它能锁定一个值,因此成为锁存器。对其有2个基本操作,即读和写。
在此之上加入一些逻辑门,将SET和RESET输入端转化为输入数据端和使能端,就可再次向上抽象,形成下图所示的锁存器。当使能端无效时,无论输入数据是什么,输出都不会改变。
将多个锁存器并排放置,拓展为8位或是更高位的存储,就是寄存器。当寄存器的位数很高时,并排放置就需要设置很多输入线和输出线,因此将其转化为矩阵形式。要启用某个锁存器时,打开其对应的行线和列线即可。
为了将地址转化为行列坐标,就需要多路复用器,可以将4位输入转化为16位输出,即行列坐标。
至此我们可以把256位存储当做一个整体,包含8位地址线,数据输入线,写入使能线,读取使能线,如图所示。
然后将多个存储并排放置就可以扩展存储位数,将8个256位并排放置连接后,就可构成一个8字节的RAM。
RAM全称Random Access Memory,现在还有很多其他类型的RAM,例如SRAM,DRAM,闪存和NVRAM等。
二、中央处理器
中央处理器(CPU)负责执行程序,程序由操作组成,这些操作叫“指令(Instruction)”,CPU内有许多组件。
程序和数据一样,可以存放在内存里。程序执行需要RAM、寄存器、指令寄存器、指令地址寄存器等部件。在取址阶段,根据指令地址寄存器将RAM内对应地址的指令复制到指令寄存器内(此处指令表只有Load_A、Load_B、Store_A、ADD四个操作)。然后进入解码阶段,根据指令表由“控制单元”解码。解码后进执行阶段,根据解码的结果运行程序。
(注:图中连线非真实连线,是对所有连接线的一种抽象,这叫做“微体系架构”)
如图所示,以8位为例。在程序初始化时,所有内容都重置为0,指令地址寄存器为0,读取RAM中地址为0的数据,此处为00101110,将其存入指令寄存器内进行解码。解码将指令寄存器的内容分为两部分,前4位为操作码,后4位为操作作用的数据地址,此处操作码为0010,数据地址为1110。解码后还要对操作码进行检查,目的是识别不同的操作指令。根据指令表,0010代表载入寄存器A的指令,因此将RAM中1110(14)对应的数据载入到寄存器A中(需要打开RAM的读取使能),即00000011放入A,即完成了本次指令的执行操作,再将指令地址寄存器+1,进入下一个指令。
可将指令地址寄存器与指令寄存器以及检查电路进行更高层的抽象,抽象为“控制单元”,控制单元与RAM、寄存器、ALU等器件连接,运行程序。此外,控制单元还要连接时钟,推进CPU的操作,时钟频率决定程序运行速度,现代CPU通常可以“超频”或者“降频”。至此,可以把除RAM之外的器件抽象为CPU(图中蓝框内)。
三、指令和程序
CPU的强大之处在于它是可编程的,写入不同指令就能执行不同的任务。在执行加法时,顺序很重要,因为计算结果将会存放在后面的寄存器内。上节中的指令表只有4个操作,我们扩展一些指令,增加SUB、JUMP、JUMP_NEG、HALT。SUB是减法运算;JUMP让程序跳转到新的位置,可以改变指令运行的顺序;JUMP_NEG是一种特殊的JUMP操作,当运算结果为负时,跳转到某个指令;HALT是让程序停止的操作。 除了JUMP_NEG,还有许多其他类型的跳转,例如JUMP_IF_EQUAL(如果相等跳转),JUMP_IF_GREATER(如果更大跳转)。视频中演示了一个求余数的操作,通过不断的运行减法来实现。
目前我们的例子都是基于8位的,这8位中只有4位为操作码,也就是说至多只能表示16个操作指令。因此,现代CPU使用两种方法来扩展操作,一是用更多的位来表示指令,直接增加了指令长度;二是使用可变指令长度,例如HALT这些不需要操作内存的指令,可以将原来存放内存地址的几位也用来表示指令。在执行JUMP时,需要知道跳转位置,这个值在JUMP操作的后面得到,存放在内存里,称为“立即值(Immediate Value)”,这样设计可以使指令变为任意长度,相应的会使读取更加复杂。(原视频翻译此处有些不清晰,感谢弹幕的补充)
四、高级CPU设计
上节视频中的除法通过进行多次减法实现,十分低效,因此现代CPU直接在硬件层面设计了除法,会使ALU更大更复杂,但是运算速度会更快。之前我们提到,CPU的运算速度取决于时钟周期,在运行某条指令时,我们需要从RAM中读取数据,这个操作通常需要几个时钟周期才能完成,导致CPU空等数据,造成了大量的延迟。
解决办法就是在CPU上增加一点RAM,称为“缓存”,这样CPU从RAM读取数据时,不再是一个一个读取,而是读取一批数据(这么做的原因是局部性原理),虽然在复制数据时花的时间更久,但是在后续使用数据时减少了延迟。缓存也可以存放一些计算的中间值,当缓存内数据满了但是又需要读取新的数据时,我们需要保留这些中间值。因此缓存内每块空间有一个特殊标记——“脏位(Dirty bit)”,在从RAM读取新的数据时,如果目前缓存内的数据是“脏的”,那么会先把数据写回RAM,再载入新的数据。
另一种提升CPU性能的方法是“指令流水线(Instruction Pipelining)”,在介绍指令运行过程时,是取址、解码、执行三个阶段依次进行,实际上这三个阶段可以并行,在执行当前指令,可同时解码下个指令,读取下下个指令,就可在1个时钟周期内运行1条指令(之前需要3个时钟周期)。但是这样的操作也会带来新的问题,即假如指令之间有依赖性时,必须停止执行,避免出现问题。高端的GPU会动态排序有依赖关系的指令以最小化停止时间,叫做“乱序执行”,虽然复杂但是高效。
另外一个问题就是如果出现JUMP指令时,简单的CPU需要等待得到跳转位置后再继续运行,会造成空等而产生延迟。而高端的CPU会进行“推测执行”,将JUMP操作想象为一个岔路口,CPU会推测走哪条路的概率更大,然后提前将指令放入流水线,得到JUMP结果后,如果正确,则可以马上执行,如果错误,则清空所有指令。为了尽可能降低错误次数,开发了更加复杂的方法称为“分支预测(Branch Prediction)”,正确率可达90%以上。
理想情况下,CPU1个时钟周期完成1条指令,而“超标量处理器(Superscalar)”1个时钟周期可完成多条指令。在指令执行阶段,总会有些区域仍可能空闲,所以一次性处理多条指令效果更好。
还有一种提升CPU的方法是“多核处理器”,像是拥有很多独立的CPU,联系紧密可共享资源,可以多核合作运算。
总结
本文介绍了RAM、CPU、指令、和更高级的CPU设计。