文章目录
计组真的太难了!
本章学习是整个计组噩梦,全程高能恶心,学完后我终于理解了CPU是如何运行的,在这里先简单的过一遍CPU执行流程,注意这个流程仅仅是针对于CPU在外部拿指令然后解释该条指令并执行的过程仅此而已,不会涉及操作系统的知识
- 首先计算机一旦通电,我们的CPU师傅就开始忙了
- CU控制单元发出取指令地址的信号
- 指令地址到了CU门口,即IR寄存器
- 将IR里面的OP操作码送进CU,此时必定会经历一次译码
- CU控制单元收到译码器译码过来的指令,收到该指令后,因为一个指令会对应多个微操作,所以根据其他信号来执行组成该指令的微操作,即每一个微操作会向外发起一个信号,发到控制总线就各个部件就会进行微操作,比如该信号可以对两个寄存器开路,一个In数据一个Out数据,在整个总线中就会发现只有这两个之间可以传输数据,那就表示这个信号完成了一个微操作(一个指令可能包含多个微操作,所以往后的微操作也会因为这个CU发出的信号一起执行了,那这时候就完成了该指令)
- 完成该指令后,CU会自动向外发送一个取下一条指令地址的,所以我们CPU会不停不停地执行指令,即使没有指令了,设计者们也不会让CPU闲下来,他们设计了一个NOP操作等等,用来让CPU处于运作状态,即CU会不停的工作,但是不是向外发出控制信号。
提前注明:CU是控制单元,和CPU不一样,CU存在CPU里面。
CPU的功能与架构
CPU的组成
下面会给出王道视频里面给出的CPU大致样子,讲道理画的确实很好,但是一般初学者都看不懂,因为我们不知道里面具体是干嘛的,怎么互相配合运行,下面会以我最喜欢的提出问题方式记笔记。
上图中主要要求你学习的就是三个东西,要知道运算器和控制器的作用,然后整个取数存数是如何进行,但是我要简略讲一下所有部件是干嘛的,我最讨厌卖关子,很多视频都这样,给你甩一个知识点后又不告诉你这干嘛的,这是最恶心的。
- 运算器: 在CU控制器发出信号后,假设该指令会发生一些计算,就会送到运算器中进行运算,总而言之如果指令设计运算就给他,有的指令没有运算就不会来到这里。
- 控制器: CPU里面最难学最重要的一个部件,该部件是指令能否正确执行的关键,因为指令在译码后送进CU,CU会将收到的指令在CU内部进行一系列操作然后最后发出一个信号到控制总线,然后就会导通里面的out和in的路线,寄存器就会传输数据了。
- MAR: 地址寄存器,这里会存放PC送过来的地址,然后我们使用这个地址往存储器里面取数据或者指令,那么我们取完数据或者指令后放到哪里呢?
MDR: 数据寄存器,解决上面的问题就是将取到的东西放到MDR中,等待下一个CU控制信号发出才知道该MDR要送往CPU里面哪个地方。- IR: IR就是存放指令地址的,我们的学习指令系统的时候就有学指令长啥样,总之他肯定包含一个OP操作码,我们CPU基本上就是用这个OP操作码确定我们要执行什么操作。
- PC:程序计数器,就是用来指明你要执行的指令地址,由于每次执行之前都要进行一次取指操作,(因此在后面介绍指令流程设计的时候会围绕这个固定的操作对CPU进行优化,这里可以不用先理解),PC里面存的是指令地址,他会在CU发起取指信号的时候往MAR地址寄存器里面传输过去,记住这个就行。
- 下面三个寄存器我们计组只需要记住他是操作数就行,不建议还记住他用来存哪些特定的数据
- ACC: 存放操作数
- MQ: 存放操作数
- X: 存放操作数
ALU放在了运算器中进行解释,因为他是运算器内部结构的重要部件。
运算器
- ALU: 这家伙就是用来计算的,ALU里面有很多组合电路,常用的计算机必备计算功能都会被集成在里面,所以有很多组合电路,一开始学的时候一定要记住他这个东西是很牛的,不要很所以当然的觉得就是用来计算,好像没啥,里面的学问多的很,但是唯一要记住的是ALU可以通过CU发出的不同控制信号在ALU里面选择一个组合电路进行计算,也就是加法指令要选加法的电路进行,减法要选实现减法的电路。
那么问题来了,我们的数据是怎么送进去的?
答:一种是ALU直接与多个寄存器连线,优点是快速,但是缺点是我们寄存器一旦多起来了,路线会很多。
问题再次出现,我们这么多寄存器怎么知道我们要用哪一个?
答:有两种办法,如下图所示,使用多路选择器,可以用控制信号发出的信号来选择对应的运算,另一种是使用三态门,这里依旧是使用控制信号给出的来进行选择对应的运算操作。这两种是没啥区别的,功能上都是实现了控制选择运算操作,优点都是不会数据冲突,缺点是设计复杂,不仅路线多,刚刚也说到要根据控制信号来选择,这么多线要实现控制也不容易啊,本身ALU就很多组合电路了。
这里应该还有一个问题就是我们的在将数据送入ALU之前,需要同时将两个操作数送入,很明显我们使用寄存器直连的方式不存在不同时送进去的问题,因为上面的结构中,每一个寄存器都直接与ALU连接着,我们在导通路线之前肯定已经将数据送入了寄存器中才会收到对应的控制信号进行导通路线。既然说这个是一个问题的提出,那下面的这种总线结构会出现这个问题。
下面这张图的这种总线结构,在控制信号发出后,ALU路线打开in,寄存器路线打开out,就可以将数据送入ALU,但是!但是! 我们的总线传输数据的时候只能够有一个in和一个out路线,不能有多个in或者out,因为我们总线里面传输数据只能传一个数据,不能同时存在多个,这样我们想想,你真的非得实现这种的话,你又要筛选哪种数据对应哪个路线的,又要对数据进行隔离不可以让他们串味了,想想的话这样搞就更恶心了,所以我们总线只能够传输一种数据,回到问题根本,我们数据总线只能传送一个数据,但是我们ALU需要同时传入两个数据才能运算,这时候使用这种总线结构就做不到同时传输两个数据到ALU中,那怎么办?
答:其实这里很容易想到,就是使用缓冲,学习了存储器系统后这里应该很快就想到办法了,我们使用一个暂存寄存器,先让一个数据存入到暂存寄存器中,等到下一个数据送来另一端的时候,我们同时将暂存寄存器里面的数据也一并送进去即可。
这里再穿插一个问题:由于我们刚刚提到总线只允许传输一个数据,那我们ALU运算完后想也不想就往数据总线里面放就会随时造成冲突,因为我们想都不想总线里面有没有东西现在正在传输就扔过去肯定不行的。
答:我们再设置一个暂存寄存器,我们不要立刻将数据抛到总线上先,我们先探测一下该数据总线有没有东西现在正在传输,没有的时候再打开要存放的寄存器的in路线,打开ALU的out路线,这样就完成了一次愉快的数据传输,具体怎么探测就不用管了,再深究就是往设计CPU硬件发展了,我点到为止即可。
- 从取指操作开始执行流程过一遍:
CU发起取指信号->PC存入MAR->在存储器中拿出指令->取出的指令数据存入MDR中->MDR中的指令数据被送到IR寄存器中随时准备送入CU控制器处理->指令进入CU控制器后进行一系列操作知道了你这条指令要干嘛(这里是关键,下面我们就是围绕CU这个阶段要干嘛进行学习),然后发起对应的操作信号,发送到控制总线中,然后选择正确的寄存器进行导通,就可以进行数据传输->接下来就是根据CU发出的信号,信号控制下进行数据之间的传输或者计算或者存取数据等等,CPU如此循环往复执行这些操作。
总结:计算机中常用的是总线结构,所以这里只是通过直连寄存器引出总线结构传输的运算器。
控制器
下图也是从王道视频里面截图来的,但是缺少了一个控制总线,不然就完美了,在下面解释中要时刻记住所有信号的发出都是通过CU发信号到控制总线然后通过控制总线来导通对应的寄存器进行传输数据。
其实流程已经在运算器里面说了一遍了,说到底还是CU扮演者最重要的指挥者角色。下面再根据细节过一遍流程
::::
通电后,CPU开始运行,CU控制单元首先发出一个取指令地址信号
(执行指令之前拿到指令地址取出来,这是必不可少的)
↓
收到信号后,控制总线将打开:PC程序计数器的out线,打开MAR的in线,通过CPU内部总线将PC里面存着的指令地址放到MAR中
↓
MAR拿到指令地址后,CU这时候会知道继续发起信号,将MAR的out线打开,MDR的in线打开,其实就是将MAR存的指令地址,在存储器中通过该地址找到存放的指令(指令的样子或者说指令存储的结构位数等等都是在指令系统讲过了,这里不说明,直到我们通过地址找到指令数据就行),然后将该指令数据存放在MDR中
↓
MDR拿到真正的指令数据后,CU这时候会知道继续发起信号,将MDR的out线打开,IR的in线打开,这里就是要把MDR的指令数据送进IR寄存器,因为我们最终是要把指令送进CU里面,但是送进CU只有一条路,那就是经过IR(目前这么理解就可以,后面解释CU这个大部件的时候会再次解释为何要放一个IR寄存器)
↓
IR拿到指令数据后,在指令系统中我们学到这条指令前部分是OP,即指令操作码,用来标识这条指令是干嘛的,后半部分就是操作数的地址码,还可能有寻址方式等。回到正题,我们需要的是将OP操作码部分送入CU控制单元,然后CU拿到OP后就知道要发出什么控制信号了
↓
假设我们是一个取数指令操作,将操作数存到R寄存器中(R是我假设的,不要纠结)
↓
CU拿到OP后发送控制信号,将IR中的地址码部分送到MAR中,也就是:打开IR的out线,打开MAR的in线,这就完成了将IR中的地址码部分送过去给MAR(我们这里假设不采用间接寻址,而是直接寻址)
↓
CU这时候知道继续发送控制信号,将MAR的out线打开,将MDR的in线打开,这时候我们使用MAR地址找到了对应的操作数,将其放到MDR中
↓
MDR拿到操作数数据后,CU已经有了OP操作码,所以接下来会知道根据这条指令操作码要做什么,也就是说CU知道接下来要将数据传到哪里,这时候CU发出控制信号,将MDR的out线打开,将R寄存器的in线打开,即将MDR拿到的数据存放到R寄存器中
↓
这样就完成了一个指令的操作
↓
其实还没有完,再细节一点的话,这时候CU应该是再发起信号将PC程序计数器里面的地址去继续取出下一条指令然后存在IR中(具体解释就是:我们CU有两种设计硬布线和微程序,不管是哪种设计都会是每一条指令在执行之前先找到该指令,叫做取指阶段,我们这个阶段是必须要做的,由于每一个指令内,可以分为多个操作,我们将一个数字取出来存好,也要经历取地址取操作数存操作数等等,那我们将这些细分后,既可以类比成一个循环,我们在指令最后一个微小操作执行完后,我们接下来要干什么?是不是要进行下一条指令,那下一条指令执行前要干嘛?是不是要取指令地址?那这样的话也就是说执行完最后一步后会自动转到取指令地址操作,就加入一条指令有10个微操作,其中0往后肯定是取指操作,当我们执行到最后一条操作的时候,10%10=0,那0是不是取指?那是不是就解释了为什么我们为什么说其实再细节一点的话应该是结束指令后会进行取地址的操作,OK至此终于解释完毕)
事实上在这里应该有人会有一个疑问,我们一直说总线总线的,难到我地址和数据都往一条总线传吗,当然不是,我们会区分数据总线,控制总线,地址总线,每一个总线只能够传输一种类型的数据。
下面是将运算器与控制器拼一起的图,可以看到是通过CPU内部总线进行通讯的。
上图中想要告诉我们的主要是总线的事情,但是这里没有给出控制总线非常可惜,差一点王道视频就做到我认为的完美了,要记得这里面所有的操作都是CU发起的,通过控制总线发起,我们数据才能在CPU内部流通。
- 控制总线:在这里没有给出,但只需要知道,控制总线是CU发出来的,是CU拿到指令后,在内部进行一系列操作后发出来一个控制信号(具体的后面会学习,CU这玩意难到飞起),控制总线可以将我们寄存器之间的in路线和out路线打开,也就是图中的比如:我们控制总线在收到CU这样一条指令后需要将R0数据传入ACC中,那这时候CU信号发到控制总线,控制总线就会打开R0的out线,然后打开ACC的in线,又因为我们使用的是总线结构,这些寄存器的in和out线都是直接接在内部的数据总线上,这些in和out线只需要等待被打开就行,那这时候我们打开了两个寄存器之间的线就知道数据如何流向了,这时候就会将R0里面的数据流向ACC,ACC就有了R0里面的数据(再啰嗦一下,真的不能忘记这个,忘记了这个细节的话后面难到飞起)
- 数据总线:外部数据总线与内部数据总线不同,比如外部数据总线MDR路线时出线MDRoutE、入线MDRinE,但是内部数据总线MDR路线是出线MDRout、入线MDRin,就是少了个E,E为External外部的,区分外部数据总线之间的导通路线。
- 地址总线:这个是与外部存储器打交道的,将MAR地址存到这里然后用该地址去存储器中找到对应的数据或者指令,将其送到MDR。
指令执行过程
在上面学习过程中,有一个很不得劲的点就是,一直没有说到CU控制器里面咋搞的,怎么就取到指令就会发出微操作信号?所以在这一个知识点开始进入CU这个恶心的部件但是对于整个CPU来说最重要的一个部件。
先学习一些理论知识,即一些术语,后面一直会说到的词
- 指令周期
是指一个指令在取指令阶段开始到结束执行的这段时间叫做指令的周期
开始上难度了,从指令周期开始细分套娃
- 机器周期(将指令周期进行分阶段)
每一个指令周期内可以进行分阶段,每一个阶段对应的时间叫做机器周期。假设:下图中的取指令地址阶段,取操作数的有效地址阶段等等,这里只是举例子,我们现代计算机中采用的一般为五段阶段(取指,译码,执行,访问,写回,往后会单独介绍) - 节拍
就是在一个机器周期里面,需要多少个节拍能够完成该周期或该阶段。还有一个注意的点是,由于有些微操作很快很快,快到比我们安排的节拍还要快,那有时候就会安排到与另一个也同样很快的并且互不冲突的微操作一起在同一个节拍内执行完。(因此后面也会提到,一个微命令可能会对应多个微操作) - 微命令(重点,我将这个提前到了这里学习)
我们知道CU是通过发信号来让数据传输,换句话说就是发信号执行微操作,再换句话说就是我们一系列的指令说白了就是控制信号打开传输路线对数据传来传去(当然这里面包含了运算器运算)。OK说明白后,我们的微命令就是可以理解为一个信号,一个信号可以执行多个微操作, 换句话说就是一个微命令可以执行多个微操作。(为什么要学这个知识点,是因为我在后面 解释节拍为何由来的时候最强有力的一个证明)
有了节拍这个知识点后,我们就出现了第一个问题,就是定长节拍和不定长节拍。
解释一下节拍是干嘛的:回忆后,发现一个指令可以分多个 微命令 ,比如我要取一个操作数地址存到寄存器里面,经历取指地址,取数地址,存数这些 微命令 可以用一个个节拍表示当前机器周期内(当前阶段内)正在执行哪一个 微命令 。
还有一个记住的细节:我们还要知道处于哪一个周期阶段内,这时候就会有对应的触发器,这是设计人员定的,他定多少个阶段就有多少个触发器,假设有四个阶段,那就有四个触发器会在执行过程中将当前阶段的信号发送给CU。
问题又出现了:老子干嘛要特地用节拍来表示当前阶段的某个 微命令 ,好问题!当然是我们需要设计一个高效率的CPU需要用到,并且并发操作也需要用到,总之,设计指令执行方案都要用到,还有一个原因是,当我们知道某某指令互不干扰的时候可以同时执行,这就需要编译器的配合了,将这些微操作安排在一起并发执行。另外我们节拍在硬布线设计中用来配合CU发出一条怎样的信号去执行微操作。
- 最后需要解决我的一个问题就是:不定长这种好是好,但是我们的CU怎么知道我当前阶段执行完成了没,会不会因为你的不定长特性搞得我们的CU乱套了都?
这个问题在我脑海出现的时候让我心头一颤,但是我仔细一想,老子差点又给自己绕进去了,我们之前讲过,每一个指令在执行完成之后都会自动的执行下一条指令的取指操作,那也就是说我们当前指令没有执行完的话,在顺序执行中,是不会向有取指操作的。就算在并发的操作中,我们提前了取指操作,那也会有探测技术暂存在IR寄存器中而不会直接放入CPU。(这句话最后也体现了我们为何要设置IR而不是直接放入CU中直接执行的原因,是因为有时候需要等待暂存的动作)
- 另外一个问题在此又出现:假设我在定长的机器周期内,我指令很短,周期还没结束咋整?
那这个就根本不用担心了,我们汇编总NOP操作就是用来给CPU在空闲的时候执行的,然而这个是编译器用来填充的,我们编程人员当然不会这样无端无故加个NOP,然后也有了编译器后,编译过程中,甚至会帮你调整各种不冲突的微操作顺序,提高执行效率。
最后还有一个具体的例子,可以看到NOP仅仅取指,不执行任何操作,相当于人类的摸鱼抖腿操作,用来浪费时间的。
还有一个就是有时候会有中断周期,这个就是给中断的时间,记住还有这么一个阶段即可。 (留给中断的时间不多了哈哈)
指令流程
接下来的一系列操作在介绍CPU部件的时候顺便说了一遍流程,下面直接看图
- 取指周期
- 间址周期
- 执行周期
因为每一个指令都不一样,所以执行周期没有固定的,但是这些不同指令也导致了我们设计CU控制单元的重要部分,每一条指令都要列举出来所有的可能性,然后设计在CU控制单元中才能够发出对应的信号(这里后面设计CU控制单元会讲,那部分还是很精彩的)
- 中断周期
指令执行方案
- 单指令周期:
顺序执行,一条指令执行完成之前不会提前发起下一条或者其他指令的操作,另一个特点就是,每一条指令时间周期需要跟最长的那一个周期对齐,也就是说每一条指令周期要一样,并且要用最长的那个作为我们的周期。单指令这个名字也是由此得来,每一个指令周期都是一样的,表现出单一的特性。- 多指令周期:
顺序执行,一条指令执行完成之前不会提前发起下一条或者其他指令的操作,另一个特点就是,我们可以不用对齐周期,即可以让每一个指令周期不一样,就是不定长周期。每一个周期都可能不一样,所以叫做多指令,表现出多的特性。- 流水线:
这个方案好啊,也恶心,也难学,后面会特地学习,这里原本不想说的,但是不说的话就会很憋屈,因为上面两个都是顺序执行的方案,我们现代计算机可不兴全部只能按顺序,肯定是要并发的,所以流水线就完成了这个任务,只要发起不冲突的微操作就OK了。还有其他很多流水线方案,这里就留到后面解决。
- 总结:这个知识点是让我们了解执行周期,但是我们一开始就一直在解释整个流程如何执行,所以我们在这里应该解决的问题就是指令的分割套娃,指令执行方案。两个都很重要,最开心的事,我们知道了有流水线来实现我一直在期待的并发操作。还有就是解释了最牛皮的问题就是我们的指令执行完后自动会区下一条指令的地址,这就保证了我们是连续的执行每一条操作而不是特地设计某个硬件来通知我执行完了一个指令需要下一条指令这样,完全就是很突然的实现了循环操作一样,总之CPU在通电后就一直在忙碌状态,空闲的时候也会一直取指,所以自动循环取指是一个我认为很牛皮的点。这个让我在不完美的计算机组成中找到了一点优雅的设计点子。
数据通路
这个知识点貌似出现的很无语,因为我们在上面学习的时候这个总线结构已经出现过,也了解了一些总线结构具体执行流程,可以说这里可能就是用来让你了解知道这个叫单总线那个叫专用通路结构(多介绍了一个专用通路),简单介绍几个术语然后,这其中的执行流程其实在上面的时候都走过几遍了,这应该是给那些设计CPU的人在这里需要深入了解。
单总线结构
单总线就是我们的CPU内部在传输数据的时候我们只有一条总线可以传输,总线又只能够传输一条数据,假设你的PC设备需要实现的功能很庞大和 复杂的话这个肯定满足不了,那么既然后单那肯定有双或者多,那这是必然的,所以我们可以在设计的时候加一条总线,那么这时候就可以加快数据的传输。 不要以为单总线就是只有一条线,里面会有地址总线,控制总线,数据总线,是因为总线系统只能给到一个控制权,比如给到外部IO设备的时候,控制权当下只能是IO设备占用了,因此单总线可能在执行速度上没有那么快(当然肯定可以设计多总线)
既然我们可以有多条总线的话,我们很容易就想到,在之前的ALU需要同时传入两个操作数,需要用暂存寄存器,但是设计多条总线后,就可以一次性传入两个操作数了,就避免了需要暂存的需要,速度也快了(但是如果一般来说暂存寄存器还是有必要设计的,因为CPU有时候可能真的是保证不了两个操作数同时放入)
- 微操作
微操作终于出现了对应的定义,计组就是这样,知识点多到离谱,如果前面不用现在才学到的知识的话难以解释通,并且前面也小小解释了一下微操作。微操作其实指的就是:数据从一个地方out后,in到另一个地方,这里也再次印证了我们发起控制信号后,执行微操作,而执行的这些微操作可以是将数据传输,那么我们如何才是传输,那就是控制信号导通路线后进行双方之间的数据传输(可以是寄存器之间),所以这一个控制信号正确与准时发出就是我们这章节中要搞明白的重点。因此微操作就是通过控制信号执行的。
总线形式需要用到CU控制单元发出信号来实现对应的操作,这一点我们在开头部分一直在强调的,我们CPU能够执行就是取到指令后发出对应指令的一些控制信号才知道导通哪根线传输数据。
- 那我这时候有一个疑问,总线既然这么容易发生冲突,有没有一种方法可以将其完全杜绝掉?总不可能我们的计算机一开机后时不时就数据冲突,这甚至会导致很多的问题。
答:肯定有,下面的专用通路就是解决总线共享数据通路的问题,以后还有各种CU设计和流水线方案设计都是用来避免冲突的,让CPU能够像一个人一样思考做出决策和不停地运行下去。
专用通路结构
专用通路解决了总线结构的数据冲突问题,因为我们每一个寄存器之间都有一个路线能够直连到达,这条线对于这两个寄存器来说是单独的,所以就叫做专用通路结构
- 重点是:我们的控制信号是发到C0~n 中,每两个点之间的一条线都会对应一个C控制信号,如果我们的CU将该C控制信号导通就代表这条线就开始传输数据,即开始执行微操作,到这里说到底还是CU控制器是大佬,要干啥要听他指挥,指挥哪一个C有效就执行怎样的微操作。
- 总结:这两个结构怎么说,这个知识点好像学了又没学一样,很空虚的一个知识点,勉强理解成我们在设计CPU的时候可以根据需求设计,不仅仅限于总线结构。
硬布线控制器设计
CU入门就此开始,这东西恶心的一批,但是学懂了之后就会觉得设计者很牛批,同时这里也是慢慢深入了解CU和CPU的一个分支点,我就是从这里之后逐渐知道CPU的整个运作流程到底是怎样了。
硬布线执行流程
首先解释下面这张图
- 指令可以用汇编语言表示
- 一个指令有多个阶段
- 一个阶段内包含多个微命令(微命令其实就是一个控制信号)
- 一个微命令可以发起多个微操作
- 一个阶段内包含多个微命令(微命令其实就是一个控制信号)
- 一个指令有多个阶段
- 表示一个指令使用触发器,一个指令会包含几个阶段
这里我就要好好说一下了:触发器不用记住,我们需要知道的就是我们设计CU的时候,你安排了几个阶段就设计几个触发器,这里每一个阶段对应一个触发器,问题来了,我们安排触发器干嘛?
答:触发器在后面为发起一个对应指令的操作的控制信号有用,也就是说发起信号需要他确定当前处于哪一个周期。
又疑惑了:我确定周期干嘛?我按照顺序执行不就好了?
答:Good Question!这个问题我差点给自己玩死了,由于硬布线设计中,我们CU内部全是组合电路,恶心的一批,这些组合电路可以根据周期,还有节拍,还有操作码,还有标志,这些信息放进去后,可以找到对应该操作码的对应的那一个组合电路里面然后导通某一个操作信号,该操作信号导通后就发出去,这样就完成了发射信号操作,发出信号就代表完成了一个微命令了,提醒一下:我们微命令可以对应多个微操作。那问题就解决了,我们周期是用来辅助CU发出一个信号的。这个周期可以视为一个参数一样,我们CU相当于一个函数,函数需要对应的参数才知道要返回一个什么样的数据
- 回顾一下:(其实这里是怕我自己又忘记了T0~n是节拍的意思)
一个节拍可以对应多个微操作。
↑这句话一句惊醒梦中人,一个节拍也是CU必要的参数,在发出指令前需要知道当前运行在哪一个节拍,所以在硬布线控制器设计中,我们采用固定机器周期的话,每一个阶段都是一样的节拍个数,那这时候,我们的节拍发生器设计起来就简单,因为固定了节拍发起个数后,很容易就知道我们的指令执行完没有,是否可以进入下一条指令,比如:第一个取指阶段,当最后一个节拍完成后,我们节拍立刻返回第一个节拍,阶段进入下一个阶段,这样就完成了顺序执行,假设最后一个阶段和最后一个节拍完成后,这时候我们可以利用很多种因素,比如节拍最后一个节拍加上最后一个阶段触发器的信号组合起来就可以发起下一条指令的执行控制信号。具体来说,当微指令执行完成后,相应的控制信号会被触发,这些控制信号会传递到下一个阶段,并启动下一条指令的执行。在硬件实现中,通常会有一个控制信号发生器,它会在每个节拍结束时生成一个控制信号,这个控制信号会传递到下一个阶段,并启动下一条指令的执行。
我又问题又提出来:我们上面讲的都是固定节拍即固定一个阶段内的节拍数,那有时候我们不采用固定机器周期的话(上面解释了:机器周期就是一个阶段,阶段内有多个节拍,节拍可以叫时钟周期(CPU频率),指令周期是从取指阶段到结束阶段的整个指令执行时间),这时候我们怎么知道什么时候进入下一个阶段和什么时候结束指令?
答:需要这么设计的话,节拍发生器设计就会变得相对复杂,因为你不定长周期的话, 就意味着我们的节拍发生器需要保证最长的那一个阶段的所有时钟周期都能够发生节拍,所以我们设计的时候必须按照最长的那一个阶段的机器周期长度来设计节拍发生器的发生节拍数,同时需要一种常见的方法是使用指令计数器。在程序开始执行时,我们将指令计数器初始化为0,并在每条指令执行完成后增加1。这样,我们可以通过检查指令计数器的值来确定当前执行的指令来检测指令是否执行完成了需要进入下一条指令,另外,我们还可以使用状态寄存器来记录指令的执行状态。在每条指令执行完成后,我们将状态寄存器更新为相应的状态值,以便后续的逻辑可以检查该状态值来确定指令是否已经执行完成,同样的,需要确定下一个阶段是否开始执行,通常需要依赖状态寄存器、控制信号或者触发器等机制。需要注意的是,在不定长时钟周期下,节拍发生器的处理需要更加精确和可靠。我们可以通过一些额外的逻辑来确保节拍发生器的准确性和稳定性,例如使用分频器或滤波器等。
讲了这么多,下面给出硬布线CU控制器外部需要什么数据传入,怎么控制的。但其实在上面已经讲了CLK和几个触发器,下面继续讲明白CU在发出信号之前需要接受哪些信号。
- IR指令寄存器->操作码译码器
首先执行一个指令肯定是需要指令操作码,操作码在指令中存着(指令系统讲过了),没有指令操作码的话怎么知道你执行什么样的操作,OP操作码需要经过操作码译码器,这里就出现了一个疑问了:为啥要操作码译码器?
答:OP操作码很长,我们需要根据他的这条OP转化为一个信号位或者对应的一个数发给CU控制单元里面更容易判断是要执行哪一条指令,而不是OP这一长串操作码直接送进来,从模块化思想也能想到,需要将译码单独出去,不然的话我们需要在CU内部进行转化到对应的操作的信号,这样加重了CU的负担,设计起来也不优雅。 - CLK 节拍发生器
- 不同阶段的触发器
- 标志 : 这个目前用不上,就不解释了,执行流程中他几乎没啥存在感
- CU内部执行完成后最后发出信号
准备好所有数据后送入CU,然后CU就知道要发出什么样的控制信号了。
走一遍流程:
首先是从IR中取出OP放到译码器中
↓
确定当前执行的阶段,一开始就是取指阶段,这里假设我们不在取指,而是访问阶段
↓
节拍发生器就会根据该阶段从T0开始发出节拍,每一个节拍会让CU发出一个信号去实现一个微指令
↓
这里节拍发生器一直发出节拍
↓
突然到了最后一个节拍,那么我们的阶段触发器就进入了下一个触发器,假设进入下一个阶段为执行阶段
↓
同样这里节拍发生器从T0开始发出每一个节拍,一个节拍会让CU发出一个信号去实现一个微指令
↓
这里节拍发生器一直发出节拍
↓
突然到了最后一个节拍,假设我们的执行阶段就是最后一个阶段了,就结束了当前整个指令的执行
↓
回到第一步执行下一个指令
- 细节与疑惑:IR不是存了指令数据了吗,我们CU里面为啥还要有FE取指阶段,还要重复取?如果不是重复取的话我们FE阶段是干嘛的,那真正的取指信号怎么发出?
在硬布线设计中,我们的FE取指阶段其实是确认IR取过来译码后是否正确,有时候会发生突发情况等等,而真正发出取指信号是由CPU内部的时钟信号、复位信号以及其他控制信号共同作用而发出的,至此在硬布线中的执行流程大概是这样。
硬布线CU内部
首先解决几个问题
- CU是发出信号的,那我们怎么判断该指令是实现什么微操作呢?
这个一般人真想不到,设计师是反向操作,是通过微操作来推断你可能在执行哪一个指令,牛批真的牛批,由这个可以得出,我们上面说到过硬布线的CU里面全是组合电路。 - 知道上面的答案后,知道一个微操作可能会被多个指令在不同的节拍用到
那我们就知道如何设计CU了,如下图所示,我们要设计一个M(MAR)->MDR的微操作组合电路,意思是我们信号进来后,比如知道你需要做ADD操作,那ADD操作必然会执行到M(MAR)->MDR访存操作,所以我们指令信号进来后,触发ADD信号,然后在访存阶段即IND 触发器信号进来触发路线,T1节拍会执行访存微操作,节拍到了T1后也触发对应的路线,这些信号也就是上面学到的,这些信号一同完成触发电路,能够导通的话就会在输出端输出一个控制信号,这样就完成了一个微操作。
- 第二个问题就是:我们怎么知道哪个指令会触发这些微操作啊?难不成每一个指令我都列出来,然后将这些用到的指令所对应的微操作都集成到CU里面吗?
答:还真是,所以这就是硬布线CU恶心的地方,他需要设计出每一个微操作对应的组合电路
这些组合电路怎样才会发出信号呢?
需要节拍阶段指令信号一起,符合条件就发出对应该组合电路的微操作
指令取一次进来后,我阶段那么多,节拍更多,可以做到等我节拍执行完后进入下一个阶段吗,当我阶段全部完成后能告诉CPU我要执行下一条指令吗?
还真可以,上面说了进入下一个阶段可以通过状态寄存器,然后阶段完成后更容易了,因为阶段是我们在设计的时候就确定下来有多少个阶段的,总之到了最后一个阶段肯定有法子通知CPU去取下一条指令,然后互我们说到IR其实在FE取指阶段已经完成取指了,真实的取指其实是由CPU内部的时钟信号、复位信号以及其他控制信号共同作用而发出的,FE是确认指令。
怎么设计微操作的组合电路
说到这后其实已经很想放弃了,但是没想到真正优雅的东西在这里,还好坚持下来了。
这里开始介绍如何写出该电路公式
- 设计微操作组合电路
首先是需要一个公式,公式出来后一切都结束了,甚至可以通过软件输入一个公式直接生成电路,所以我们得到对应的即可
那这个公式是什么来的?干啥的?
这个公式是需要满足所有会用到某个微操作的指令,即我们执行流程中提到的所有参数都要用上,节拍、阶段、译码出来后的指令信号等等。
假设我们设计M(MAR)->MDR这个访存操作(记住 ,我们仅仅举例设计这个微操作而已)
- 分析每一个阶段,是否出现M(MAR)->MDR
这看出,取指和间址周期必须会出现M(MAR)->MDR,所以这两个阶段都有可能会出现,所以就要设计电路,让他有这个判断电路
下图就是两个阶段,FE是肯定一定绝对会出现访存,所以直接节拍*阶段两个信号就可以表示我们在执行M(MAR)->MDR,IND就要看哪些指令,比如NOP就不会继续间接寻址访存操作,所以这得看我们指令有多少,就列出多少,下面仅仅列出了五个会用到访存微操作而已(从这里也可以知道,我们需要用多少指令就要在每一个微操作内列出是否会用到该微操作,只要有用到就要加上式子进行判断)
执行阶段也是一样的,这个需要列出每一个指令看哪一个有用到该访存微操作,用到了就要加上他所处的阶段和哪一个上节拍,也就是我们的外部传进来的信号。
有了上面的分析后对所有指令列出所有微操作,其实已经得到了公式了,但是我们需要优化一下当信号发出后,比如:可不可以将某些速度很快的微操作安排在互不冲突的微操作的同一节拍内执行了,接下来就是在完成这样一件事,并且在最后的时候还会有十分优雅的操作出现。
下面三个原则就是我们要做的事情,这个部分其实很多都可以用编译器编译过程中进行调整,所以我就直接上图了,不作过多解释
- 取指周期
- 间址周期
- 执行周期
- 最后一步,组合逻辑设计
- FE取指周期
- IND间址周期
- EX执行周期
执行周期我们此处只列举了几个指令,实际上我们在设计硬布线控制器的时候需要将所有要用到的指令都要设计进组合电路里面,所以说真的很恶心,我们现在做的还仅仅是一个微操作,一个微操作需要考虑到所有的指令是否会发生该微操作,我真的要吐了,一点都不优雅!这里很明显感受到了计算机的不完美性。
接下来应该还有中断周期时间表。有几个阶段就要考虑几个表和组合电路。
- 整合阶段出一个电路公式
在这里我认为是最优雅的,他通过列出操作时间表(结合上述我们分析得到的各个阶段和节拍对应指令的微操作得到的表),这样的话我们只需要在这么多微操作中找到我们要设计的那一个,看他会被哪一个指令用到,并且要看这个微操作处于哪个节拍哪个阶段,这样就可以得到一个电路公式,有了电路公式就可以得到组合电路了,这里通过表来整合每一个阶段每一个微操作对应哪一个指令的事情实在是太优雅了,在这个表我们其实也可以发现,我们为何要优化,原因是我们将可以在一个节拍内一起执行的操作合并后的话我们电路中就少了一个需要另外判断的微操作了,我们只需要在满足这个,比如PC->MAR微操作的时候,发出这个控制信号,即一个控制信号的同时将1->R也一并执行了,这个操作实在是太骚了,也感慨硬件设计人员是真的牛批啊。
总结
硬布线设计中,这个CU真的是恶心,小小CU竟然需要承受这么多电路,硬布线设计这么恶心这么难,我们猜也能够猜到他肯定是用在RISC架构精简指令集,因为指令少,所以相比于复杂指令集来说是更好设计的,同时硬布线设计通常具有更高的执行速度,因为它是直接通过物理电路实现指令的执行。
在最后在学习中我会发出的疑惑:老子压根不想用硬布线设计,这设计太恶心太不完美了,也就设计人员在设计的时候让我感受到了优雅的操作,有没有一种不要搞这么多电路的,可不可以像设计程序一样一个部分一个部分的设计,然后封住?
答:肯定有,接下来要学的就是微程序控制器设计,这玩意学起来简单,设计思想和编程一样,而且因为学会硬布线控制器设计后真的是把最难啃的一块肉啃下来了,所以在这里我要感谢自己,真的太六了,这玩意压根不是人学的。
微程序控制器设计
微程序的基本理念
在开始微程序控制器设计之前,首先直视硬布线控制器的弊端,硬布线控制器是通过指令码还有需要放进硬布线控制器各种参数比如节拍发生器,阶段触发器,标志寄存器等等都会影响硬布线控制器的控制信号输出,最大的问题就是组合电路设计很复杂很困难,也很庞大。作为一名优秀的程序员,看不得这种鲁莽的设计方式,所以接下来用一种稍稍优雅的微程序设计方式。
下图中我们将以前的一个节拍内发生的微操作将其看做成一个微指令
微指令可能对应多种微操作,所以这就实现了封装思想,我们用一个微指令就可以实现微操作,那么多种微指令就组合成了一个阶段的微程序,阶段的微程序组合成一个大的微程序,这个大的微程序就代表了一条指令的整个执行流程。(简而言之:套娃)
这时候我有一个疑问:我们微指一般来说对应一条微操作,这样的封装好像跟没封装差不多一样?
答:当然不是,我们微指令是指一个控制信号,我们一个控制信号能够完成多个微操作,而我们封装好一个微指令后时不时就代表我们每次使用一个微指令的时候即发出一个控制信号完成了 微操作,那这样解释的话意义就不同了。而不是我们不封装还希望这么多微操作像编程一样使用的话会很麻烦,就好比我一个节拍要完成多个微操作,我岂不时还要特地取出多条微操作?
微程序的基本结构
在微程序控制器里面只需要几个部件,然后还只需要很少的电路线连接就能完成一个指令的控制信号发出,下面给出结构图:
- IR指令
取指后的指令就存放在这里,里面包含了OP操作码,接下来是要送进微地址形成部件的 - 微地址形成部件
这个是当OP操作码送进来后,相当于一个译码器,形成微指令地址的首地址(这里后面会用到,提前说明:我们微指令会刻录在CM控存中,所以需要用地址去取出对应的微指令发出控制信号) - 顺序逻辑
这个就有意思了,当你微地址形成部件送过来一个微地址后,这个顺序逻辑会指挥你这个微地址,我们知道取指周期是一定会执行的,所以微地址首先取CM中找0地址然后顺序执行完取指周期,然后我们当前要执行哪一个指令是需要跳转到对应指令的微程序首地址的,那怎样跳转呢?答:很简单,我们会用节拍和标志信号送进顺序逻辑,这时候在顺序逻辑里面可以直接修改微地址到对应的CM里面的微程序地址。然后接下来就传输地址了。 - CMAR
MAR是地址寄存器的意思,加了一个C表示微小MAR寄存器,也是用来存放微地址的。疑问来了:老子在顺序逻辑里面都弄好地址了还搁这另外存到CMAR几个意思?答:在这里最直接的解释就是缓冲一下,我查了很多资料问了GPT,给我的理由很深奥,但是有一个我能看得懂的就是我们在顺序逻辑中位数有存在非地址的,会有信号位,所以我们需要一个CMAR存一个十分纯洁的地址,然后再将其送去译码器了。 - 地址译码器
我真的大问号?明明我们传入的微地址CMAR已经是可以拿到微程序了,为何还要经过一个地址译码器,这是干嘛啊,直接送进CM里面找程序不就好了?
答:这里不建议看,但是如果有兴趣就看下去,没有的话真的别看。
真实的解答是这样的:我们地址译码器是形成控制信号的,我们CM控存中的一条条微指令是取出来给译码器用的,这一点先别震惊,我还了解到,CMAR可以直接将地址送到CM找到对应的微指令,然后再将这个微指令送入译码器进行译码,这种方法是可行的,但是,让译码器读取CMAR中的地址,然后译码器自己从CM中取出微指令进行译码,这种做法可以更加明确地将地址的解析(译码)和微指令的执行分开,使得整个过程更加模块化和清晰。这样做也有助于减少数据传输的复杂性,因为所有的数据都直接流向译码器,而不是在多个组件之间传递。至此,脑子顺畅多了。 - CM控制存储器
存储一条条微指令数据,数据长啥样?
长这样:| 微指令操作码 | 下地址 |
解释一下细节:我们一般来说将CM设计的首地址肯定是取指周期,因为每一条指令执行前都要拿到该指令,也就是先取指操作。往后就是不同的阶段周期,每一个周期对应一个微程序,一个微程序有多个微指令。然后指令操作码是需要送给译码器形成控制信号,然后下地址就送给顺序逻辑,下地址会通过CMAR再去译码器,为何要去译码器上面解释了,不想懂就不用管了。
有一个点我认为很优雅的是:我们每一个周期的最后一个微指令的下地址是0,0就代表回到取指阶段,那正好了,我们最后一个微指令完成就标志该指令完成了,自动回到取指阶段了,这个是真的妙哉啊。
微指令设计
既然我们的硬布线控制器里面的指令设计中可以将其中一些微操作并在一个节拍内执行,那我们微指令当然可以这么做,但是我们要知道,我们CM的微指令是取出来后是要送到译码器里面形成控制信号的,所以不要忘记控制位的设计,还有我们会将下地址送到顺序逻辑,这个点也不要忘记,总而言之送到哪里就要考虑对方的感受。
-
水平型微指令(下面会介绍操作控制码两种不同的编码)
-
直接编码
-
字段编码
-
字段间接编码
-
-
下地址设计
这里简单说一下就是我们只需要知道下地址是指明下一条微指令的地址就行,具体的了解即可。再深入了解我就要变身成为CPU了。
微程序CU设计
讲真,学到这里后,基本一看就懂了,在微程序控制器中,多了一个节拍,那就是CMDR送往CMAR的过程,在硬布线中在执行完成后会将下一条指令进行取指阶段送到IR,在这里也是,只不过送到IR后,是正常的,之后还要送进微地址形成部件然后再送进CMAR,这个又是一个节拍,记得加上(其实这是硬件设计人员考虑的)
- 设计步骤
其实前面和硬布线设计差不多,不同的是需要确定微指令格式,因为我们上面说到微指令可以有多重设计,但是用哪一个就看设计人员确定,确定微指令码点这个就是设计微指令应该对应什么控制位,其实我也不太了解,就不深究了, 这里重点学习的是微程序的整体设计思想。
两种CU设计的区别
指令流水线
这个知识点就是教你如何并发操作思想,还有就是提高CPU的执行效率。
-
顺序执行
没啥好说的,顺序执行我们上面学到的基本都是顺序执行 -
一次重叠
意思是先进来的指令执行完取指阶段,第二条刚好进来后,既然取指所用到的寄存器没人用,简单可以理解为在不冲突的情况下第二条指令可以直接开始执行,不用管第一条指令执行到哪里了,因为阶段不冲突。
-
二次重叠(理论上可以实现)
流水行性能指标
-
吞吐率
n个任务/完成n个任务用的总共时间
装入时间:开头装入n条指令的时间
排空时间:结尾所有指令逐渐退出的时间
-
加速比
使用流水线时间/顺序执行时间
-
效率
其实就是画出图后,计算执行时间的面积/总面积
影响指令流水线的因素与技术
这个叫做分阶段,这个图也有很多学问,首先每一个阶段之间都会有一个缓冲寄存器,然后我们要实现更多的并发操作就可以在每一个阶段中增加多个部件。
一般设计的时候会将每一个阶段的机器周期设置一样,这样方便设计。
但是我肯定会想到不固定的机器周期,那这时候我们就可以通过设计复杂的,为了 实现并发,在阶段内添加多几个部件。当然这时候缓冲的作用出来了,我们会想怎么实现通知下一个阶段内的部件我当前阶段执行完了,需要你进行,这时候缓冲就实现了这个效果,有数据就代表执行完,没有的话下一个阶段就会继续等待来数据才知道你当前阶段是否执行完了。
影响因素
-
结构冲突
-
数据相关
-
控制相关
总结来说就是:即使在不定长机器周期内要实现不冲突的并发操作,我们可以采用空操作等多种手段介入,那就会想,会不会影响到整个CPU的执行流程或者什么细节出现崩溃,放心吧,机率很小,因为我们可以通过编译器使得程序执行的指令得到一个很完美的执行顺序,然后在插入空操作等介入的时候也不用担心,因为我们都是为了避免冲突,避免冲突这件事情又怎么会让CPU崩溃呢?下面就给出了多发技术,让你并发个够,就问你爽不爽,越来越明白现代计算机到底是如何运行的了。
五段式流水线
这里其实在上面有提到过了五段式,这里主要是将五段式流水线执行流程,简单过一遍就行,基本学到这就啥都会了。
运算类指令
需要注意的是:我们访存一般是先去cache中找。然后这个运算类指令由于没有访存,所以我们计算机采用了五段式流水线设计的话,我们编译器会根据你的机器给你安排在访存阶段为空操作短。
LOAD指令
STORE指令
由于存指令是不用写回寄存器,我们往存储器里写就好,所以在WB阶段安排了空段。
写回PC
这一个细节需要唠一唠。
对于会修改PC的指令来说,我们需要一个写回PC操作,安排这个写回PC也是一个学问。
- 条件转移:由于写回PC的执行速度会很快,所以我们将这个可以安排在与访存阶段一起执行了。
- 无条件转移:因为我们不希望进入访存的时候其他并发的操作对 我们PC进行了修改,所以我们需要在EX阶段尽早的修改了PC,这样实现了无条件的转移。
完结撒花
请听我说:CPU真的太恶心了我去,越学越崩溃,但是有一说一硬啃下来后基本完全理解了计算机CPU怎么进行高速运作的,确实万分感慨,GPT和文心一言被我骂惨了,很多问题细节都是去找人工智障,心力疲惫,相信我日后复习看到这应该也能懂了,所以来一个不总结的总结吧,学这玩意,CPU没烧我脑子烧了。