中央处理器CPU(The Central Processing Unit)
1.前言
前面几章的知识铺垫,我们这章终于来到了对处理器的学习。
我们本章要搞清楚处理器的基本结构,以及如何与内存之间协同工作。
我们之前已经做了一个算术逻辑单元(ALU),输入两位二进制数,它会执行计算。
我们还做了两种内存:
寄存器,很小的一块内存,可以存一定位数的值。我们还做出了RAM,RAM是一块更大的内存,能读写更多的数据。
现在是时候把这些组件放在一起了,组件一个计算机心脏——CPU。
2.制作CPU
所有的程序,其实都是读写加运算的过程,如果是一些基础的数学运算指令,比如加/减,CPU会让ALU直接进行数学运算,还有可能是内存指令,CPU会和内存通信,然后读/写值,CPU由很多个组件组成,我们一边说,一边建,我们把重点放在功能,而不是在意一根根线具体怎么连,当我们一根线连接两个组件时,这些线只是所有必须连线的一个抽象,这种高层次的抽象视角叫“微体系架构”
我们首先把前面一章讲RAM内存抽象图拿出来
为了保持简单,我们假设它只有16个位置,每个位置存8位。
再来四个寄存器,我们分别叫它们寄存器A,B,C,D。寄存器用来存临时数据,和操作指令码。
我们的数据和指令代码都是以二进制存在内存里的,CPU的指令有很多,但是我们下面的流程只会用到四个指令,如图表格为四个指令表,我们给CPU支持的所有指令分配一个ID:
我们用寄存器的前四位存“操作指令代码”,简称“操作码”(opcode),后面四位地址代表数据来自哪里,数据来源可以是寄存器,也可以是RAM内存地址。
我们还需要两个寄存器
1.一个寄存器追踪程序运行到哪一步了,我们叫它“指令地址寄存器”
2.另一个寄存器存当前将要执行的指令,叫做“指令寄存器”
3.运行一个简单的程序
当计算机启动时,所有寄存器从0开始,为了举例,我们在RAM里面放了一个简单的程序,我们一步一步过一遍。下图为模拟计算器刚启动时CPU的状态:
1.取指令阶段:
CPU的第一阶段叫“取指令阶段”,负责拿到指令,首先,将“指令地址寄存器”连到RAM,寄存器的地址为0,因此RAM返回地址为0的值00101110,然后将00101110复制到“指令寄存器”里
2.解码阶段
指令拿到之后,要弄清是什么指令,才能执行(execute),这个阶段是“解码阶段”。我们看下图中的指令寄存器的代码,我们对应前面图片的代码指令表,代码“00101110”的前4位是0010是的LOAD A指令,该指令的意思是,把RAM的值放入寄存器A,代码的后四位是1110是RAM的地址,转换成十进制是14
我们上面所说的“解码阶段”具体是怎么什么样子的呢?它也是由“控制单元”进行解码的,解码的控制单元也是由许多个逻辑门组成的,如图为“LOAD A”指令的逻辑门,这个逻辑门可以检查该命令是不是LOAD A指令:
3.执行阶段
解码完成后我们根据命令开始执行,我们会根据指令把1110(十进制14)对应的地址的值放入寄存器A,RAM拿到的值是00000011,十进制的3
我们把所有的线连接起来,如图:
4.执行结束
通过上面三个步骤之后,我们的指令命令也就完成了,我们可以关闭所有线路,去拿下一条指令,我们最后还要把“指令地址寄存器”+1,“执行阶段”就到此结束,如图:
我们刚刚执行的LOAD A命令只是众多CPU命令的一种,不同的指令由不同的逻辑电路解码,这些逻辑门电路会配置CPU的对应组件来执行对应的操作,要具体分析这些电路太繁琐了,所以我还是用抽象法,把整个控制单元抽象成为一个整体组件,如图所示:
5.继续走完整个程序
控制单元指挥CPU不断的进行着
“取指令—>解码—>执行”
一次执行周期完成后,我们的“指令地址寄存器”+1,然后执行“取指令”开始,执行下一个周期,我们再来模拟运行一个周期,我们依然从取指令开始,“指令地址寄存器”现在的值是1,所以RAM返回地址1里面的值为00011111
然后我们到“解码”阶段,0001是LOAD B命令,它的意思是把指定地址的RAM值复制到寄存器B里面
这次内存地址是1111,十进制的15
最后执行阶段,我们把00001110存到寄存器B,最后一件事是“指令地址寄存器”+1。
我们又完成了一个周期循环,我们继续执行下一条指令,取出指令1000是ADD 指令,这次后面的四位0100不是代表RAM地址,而分别代表两个的寄存器的地址,第一个地址01代表寄存器A的地址,第二个地址00代表寄存器B的地址,所以一整段的代码的意思是代表把寄存器B的值加到寄存器A里,最后,把结果覆盖到寄存器A,为了能执行这个ADD指令,我们还需要整合前面几张讲到的ALU,“控制单元”负责选择正确的寄存器作为输入,ALU负责执行运算操作
ALU不能直接把运算结果直接存入寄存器,因为这样新的值会进入ALU,不断和自己相加,因此,控制单元用一个自己的寄存器暂时保存结果,ALU结束关闭后,才能把正确的值写入寄存器,如图运算结果存到了寄存器A
和之前一样,最后一件事是把指令地址+1,如图所示:
我们读取最后一条指令:01001101
这个0100指令,STORE A指令对应的意思是把寄存器A值写入RAM内存
唯一和之前步骤不同的是先前我们要读内存,所以READ ENABLE的连线转态为1,现在我们要写入内存,WRITE ENABLE的状态为1,最后成功把求和结果存入地址13的RAM,如图:
4.CPU的“节拍器”
经过四次的“取指令—>解码—>执行”,我们已经完成了一个简单程序的运行:“从内存中读取两个值,相加,然后把结果放回内存”,但上面我们是人工切换CPU的状态去实现“取指令—>解码—>执行”,真实运行的计算机肯定不能靠人为的来管理CPU的运行节奏的,它是通过“时钟”(clock)来管理CPU的运行节奏的,“时钟”以精确的时间间隔,触发电信号,控制单元会用这个信号,来推进CPU的内部操作,就像节拍器一样。
但是节奏不能太快,因为就算是电也需要一定的时间去传输,CPU“取指令—>解码—>执行”的速度叫“时钟速度”,单位是赫兹,赫兹是用来表示频率的单位,1赫兹代表一秒一个周期,假如你读完现前我讲的那4条指令花了六分钟,所以我的时钟速度大概是0.03赫兹,正常人能达到的较快运行速度,最多也就是1赫兹(一秒一次)
世界上第一块单芯片CPU是“英特尔4004”,发布与1971年的4位CPU
这是这块“英特尔4004”的微架构,是不是和我们现前画的CPU很像:
这块CPU的时钟速度是740千赫兹,每秒七十多万次,这好像很快,但是其实和我们如今的处理器相比不值一提,我们如今的手机电脑都是几千兆赫兹
有人可能听说过计算机超频,意思是修改计算机的时钟速度从而加快CPU的运行速度,芯片制造商基本都会给CPU留一点余地,可以接受一点超频,但是过高的CPU频率会加速处理器的使用寿命,严重的甚至会使使CPU过热烧毁。除了超频还有与之对应的降频,很多时候计算机都可能是低负载的运行着,没必要让计算机全速运行,适当的降频可以增加CPU的使用寿命,还可以省电,所以现在有很多处理器都是根据需求,或快或慢的调整时钟速度,这叫“动态调整频率”。
加上时钟后,我们的CPU才算完整,CPU是一个独立的组件,CPU和RAM直接通过“地址线”,“数据线”,“允许读/写线”进行通讯
5.总结
相信这是很多人第一次从内存和CPU的角度去理解程序运行机制,虽然我们今天学习的是一个极简化版的CPU,但是我们提到的很多机制,自然存在于现代计算机里,相信理解这一套运行机制后会对我们在代码编程的程序设计和程序理解中会有很多帮助。