CPU结构
(一)CPU的功能
想了解CPU的结构,首先要知道CPU的功能。然后讨论什么样的结构能够为CPU提供相应的功能。CPU由运算器和控制器组成。
1、控制器的功能
取指令,把指令从内存单元中取出
分析指令,对指令的操作码部分进行阶码,分析这条指令要完成什么功能,是指令集中哪一条指令
执行指令,CPU的控制器发出各种操作命令,由这些操作命令控制相应的部件去完成指令要求的操作,这些操作命令具有一定的先后顺序。如,要完成一个内存操作数和寄存器中数据的加法运算,首先要内存操作数从内存中取出,送入到CPU,然后完成加法操作,再把加法的结果保存在指定的寄存器中。
控制程序的输入和结果的输出。
总线的管理,对总线的控制权和使用权进行管理。
处理异常情况和特殊请求。
2、运算器的功能
实现算术运算和逻辑运算。
对上面的功能进行归纳,CPU要具备以下功能:
指令控制,操作控制,时间控制,处理中断,数据加工。
(二)CPU结构框图
首先CPU通过总线和计算机的其他部分进行通信,这些总线包括了控制总线,数据总线,地址总线。
控制总线:双向的,由CPU向各个部件发出的控制命令,方向是向外的。外部设备向CPU提出的请求,以及外部设备的状态可以通过控制总线向内告知CPU。
数据总线:双向的,CPU向外部设备或者是存储器写入数据,另外CPU还可以从外部设备,内存读入数据
地址线:单向的,都是由CPU发出,送给内存或者是外部设备的接口。
下面通过前面分析的CPU的功能来分析图中CPU的结构。
指令控制功能:把指令从内存单元中取出,需要的硬件包括PC寄存器和IR寄存器,PC寄存器指出了指令的地址。IR是指令寄存器,从内存单元中取出来的指令被放入到CPU中的IR寄存器中。所以从指令控制角度看,CPU需要有寄存器。
操作控制和时间控制:这两部分控制,需要控制单元,由控制单元对指令进行译码,译码之后,在给定的时刻给出给定的操作命令。这个由CU来做。
数据加工:我们需要ALU,由ALU完成数据的算术运算和逻辑运,当然运算过程中需要寄存器,运算的操作数和运算的结果都保存在寄存器中。
处理中断:需要中断系统的支持。
实际上,处理上图中给定的结构,还有其他的辅助电路,以及各个部分之间进行连接的互联机构。
(三)CPU的寄存器
1、用户可见的寄存器
(1) 通用寄存器:可用于存放操作数,也可以作为某种寻址方式的专用寄存器。
(2) 数据寄存器:存放操作数,满足各种数据类型的要求,如保存整数的寄存器和保存浮点数的寄存器。两个寄存器拼接存放双倍字长数据。
(3) 地址寄存器:存放地址,其尾数应当满足最大的地址范围。用于特殊的寻址方式,段基址,栈指针。
(4) 条件码寄存器:存放条件码,作为程序分支的依据。
2、用户不可见的寄存器
如:在流水线结构的计算机中,流水段之间的流水段寄存器都是用户不可见的。
3、控制和状态寄存器
(1)控制寄存器:如,要从计算机的内从中取出一条指令,PC把地址送给MAR,MAR把地址传送给主存储器,并且控制单元发出读命令,读出的指令放入MDR中,MDR指令将指令放入IR中:
这个过程中涉及到了四个寄存器。这些寄存器都是用于控制CPU操作的,所以都属于控制寄存器。其中MAR, MDR, IR是用户不可见的,PC是用户可见的。
所谓用户是否可见指的是用户在编写程序过程中是否可以读取操作这些寄存器的值。
(2)状态寄存器:反应指令执行结果的情况或者是计算机软件,硬件的状态。状态寄存器和前面讲的条件码寄存器是类似的,可以用于存放条件码。
(3)PSW寄存器,他是程序状态字的缩写,用于存放程序状态字的。所谓程序状态字,就是在中断或者是子程序调用过程中,为了能够让程序能够正确的返回断点,返回断点之后还能够接着执行给定的程序,在中断或者是转子程序之前,就要保存程序的运行现场和程序的断点。这些运行现场和断点,包括了程序运行的软件信息和硬件信息,这些信息时保存在寄存中的,有些表示程序运行状态的寄存器可以通过指令集中的指令进行读或者是写,但是有的不能通过指令集中的指令进行读或者写,因为涉及到的状态比较多,如果为每一个状态都设计一条指令进行读的话,指令集将会非常庞大,为了完成程序现场和程序断点的保存,计算机的设计者就把这些软硬件相关的寄存器集合成一个大的寄存器,这个寄存器就是程序状态字寄存器,程序状态字的长度比较长,有些程序状态字可以达到几千位。但是我们可以利用交换程序状态字的方式来完成程序现场切换,这使得程序中断过程中保存程序状态,断点,以及返回之后恢复断点的工作变得比较容易。
(四)控制单元CU和中断系统
1、 CU:产生全部指令执行时候所需要的操作命令序列,任何一条指令要在CPU上执行,CPU要对指令进行译码,根据他是什么样的指令或者要求完成什么功能,要产生完成这些功能所需要的微操作命令序列。不仅仅是要产生这些操作命令,而且要保证这些操作命令之间的先后顺序。
CU的设计方法有两种:
组合逻辑设计:硬连线逻辑实现。这种方式速度快,如精简指令集计算机他的控制器实现实现都是采用硬连线逻辑。
微程序设计:这种方式设计简单,适用于复杂功能的指令的设计,采用存储逻辑进行设计。
2、 中断系统,这个将在后面进行讲解
(五)ALU
在计算机中数的表示中有详细的介绍。
指令周期
(一)指令周期
从取出一条指令开始,一直到这条指令执行结束所需要的全部时间。
完成一条指令需要把指令从内存中取出,放入到CPU的IR寄存器,需要对指令的操作码进行分析,需要对指令寻址方式进行分析,接着就是执行指令,执行指令包括了完成指令要求的全部运算。具体划分如下:
上面这种将指令完成周期分为取值周期和执行周期,这种划分方式只是这里方便讲解的划分,对于不同的CPU,不同的指令集,不同的设计方法可以把完成一条指令分成不同的阶段。按照上面的划分方式,得到:
1、每条指令的指令周期不同
即使在同一个CPU中,不同的指令,指令周期的长度,或者指令周期中包含的机器周期个数也可能是不一样的,如:
(1) 只包括取指周期,不包括执行周期的指令,如 NOP指令表示空操作,计算机系统不需要做任何操作。
(2) 包括了取值周期和执行周期,并且取值周期和执行周期时间长度是相同的。如 ADD mem指令,这条指令表示的是从mem内存单元取出一个操作数和ACC寄存器中的数据做加法,结果保存在ACC中。取值需要访问内存将指令取出,执行也需要访问内存将操作数取出。这两个周期基本相同,因为在执行过程中,访存用时最长。
(3) 包括了取指周期和执行周期,取指周期和执行周期长度不同。如MUL men,把ACC中的内容和内存中的内容做乘法操作,结果保存在寄存器中。乘法执行周期长度较长。
2、具有间址寻址的执行周期
指令集中支持多种类型的寻址方式,特别是间址寻址方式,在寻找操作数地址过程中,也需要访问内存,在执行结果,如果我们把去操作数和执行都放入到执行周期,那么在执行周期需要多次访问,首先要取出操作地址,再次取操作数,这就导致执行周期比较长,所以在执行周期中专门添加一个间址周期。那这条指令解释周期分为三个阶段:
3、具有中断周期的指令周期
如果机器是支持中断的,那么在指令周期执行周期结束的时候,就要确认是否有中断请求,如果有中断请求,我们需要去响应中断,相应中断的过程要保存断点,要形成中断服务程序的入口地址,要关闭中断,这些操作都需要在中断周期中完成。这样一条指令完整的指令周期如下:
4、指令周期流程
5、CPU工作周期的标志
指令周期不同阶段,控制器需要做不同的操作。因此,控制器在指令周期的不同阶段需要发出不同的命令,控制器也需要知道当前处于指令周期的哪一个阶段,即使是对同一个部件进行操作,在执行周期的不同的阶段,这些操作也是不同的。
CPU访存的四种性质,如下图:
取指令:要将指令送入IR中
取地址:要将这个地址送入IR地址码部分,或者是MDR的地址码部分
取操作数:放入到CPU寄存器中
存断点:将断点信息存入内存。
尽管上面的操作都是对内存进行操作,但是在不同的指令周期要完成的操作是不一样的。为了对控制单元进行设计,我们必须要标识出当前处于指令执行的那个阶段,以便于控制器发出相应的控制操作。把数据传送到不同的位置。这些标志就是CPU工作周期的标志,我们采用触发器进行各个周期的标识:
(二)指令周期的数据流
前面已经将指令周期划分为,取指周期,间址周期,执行周期,中断周期,下面分别进行各个周期的数据流介绍。
1、取指周期数据流:
(1)PC将地址传送给MAR
(2)MAR将地址送入地址总线
(3)地址总线把地址送给存储器
(4)CU发出读命令,将控制信号送到控制总线
(5)控制总线将读控制信号送到存储器
(6)存储器执行读操作,把相应的数据送到数据总线
(7)数据通过数据总线送入MDR
(8)将MDR中的指令送入IR
(9)CU把加1之后的PC值保存到PC中,为下一条指令取指做准备。
2、间址周期数据流
一旦取指周期结束,CU便检查IR中的内容,以确定其是否有间址操作。间址周期说明当前我们要执行的这条执行采用的是间接寻址的方式,那么指令所需要的操作数的地址保存在地址的地址码部分,在上图中,指令的IR和MDR中都有,首先要取出操作数的地址,地址在存储器中,起始操作可以从IR开始,也可以从MDR开始,这里从MDR开始。
(1) 把MDR中的地址码部分送入到MAR进行内存单元访问,访问目的是要取出这条指令操作数所在内存单元的地址。
(2) MAR将地址送入地址总线
(3) 地址总线把地址送给存储器
(4) CU发出读命令,将控制信号送到控制总线
(5) 控制总线将读控制信号送到存储器
(6) 存储器执行读操作,把相应的数据送到数据总线,这时候,这个数据实际上是一个地址,是这条指令所需要的操作数所在的内存单元的地址。
(7) 数据通过数据总线送入MDR,此时MDR地址码部分保存了我们真正需要的操作数的地址。
3、 执行周期数据流:
不同的指令在执行周期操作差异是非常大的,所以这里不做介绍,后面会专门介绍这部分内容。
4、 中断周期数据流
中断周期我们要用到硬件包括PC,CU,MAR,MDR,要想知道中断周期的数据流,我们首先要知道在中断周期我们做了什么操作。中断周期所做的操作实际上包括了三部分。即:保存断点,形成中断服务程序的入口地址,硬件关中断。这里主要给出前两部分示意图:
(1) 由CU确定把程序断点保存在内存单元中的哪一个地址,所以CU给出地址,将这个地址放入到MAR中,
(2) MAR将地址送入地址总线
(3) 地址总线把地址送给存储器
(4) CU向存储器发出写命令,把命令送到控制总线
(5) 控制总线把写命令送入到存储器
(6) 保存断点,断点就是中断之后,返回到程序的执行位置,也就是中断返回之后要执行的下一条指令的地址是多少,这个地址就保存在PC中,所以保存断点就是保存当前PC的值。所以PC将值送入MDR中
(7) MDR将地址送到数据总线
(8) 数据总线送入内存。
(9) 形成中断服务程序入口地址,中断服务程序的入口地址如何形成在后面进行详细介绍,这里只需要知道中断服务程序的入口地址由CU给出。CU将这个值直接写入到PC中。后面在执行的时候就从中断服务程序开始执行,从PC开始,PC中就指出了下一条我们要执行的指令。
指令流水
(一)如何提高计算机速度
1、 提高访存速度:
高速芯片,cache,多体并行
2、 提高IO和主机之间数据传输速度:
中断, DMA, 通道, IO处理机,多总线
3、 提高运算速度
高速芯片, 改进算法, 快速进位链
提高整机处理能力,除了使用高速芯片,还可以改进系统结构,开发系统的并行性。
4、 系统的并行性
(1) 并行的概念
(2)并行性的等级
(二)指令流水原理
为了讲解方便,后面的讲解假设指令解释过程只有取值和执行两个阶段。
1、 指令串行执行
上图的执行过程是完全的串行操作,一条指令解释过程执行结果以后,再开始下一条指令的开始过程,那么实际上,如果我们再控制器实现过程中把取指部件和执行部件完全的独立开进行设计的话,那么在取指阶段只会用到取指令部件,那么执行指令阶段我们只会用到执行指令部件,这样的话,当上图中第二条指令的取指令部件运行的时候,其执行指令部件是空闲的。如果我们采用这种结果这种方式去解释一条指令的话,总有一个部件是空闲的,控制器的利用率非常低。
2、 指令的二级流水
上图中,取指和执行在时间上是重叠的,使得取指部件和执行部件都都得到了充分的利用。整个指令周期就会减半,理想情况下速度会提升一倍,但只是理想情况,下面看看影响指令流水效率加倍的因素。
3、 影响指令流水效率加倍的因素
(1) 取指时间 > 执行时间:
有些指令比较复杂,涉及到的操作比较复杂,取指时候假设指令格式是统一的,长度是一样的,那么取指时间都是相同的,那么对于复杂指令来说,他的执行时间就会比取指时间更长,如何解决这个问题呢:
如上图,在取址部件在执行指令部件之间增加指令部件缓冲区,这个缓冲区用于缓冲取指部件取回来的指令,如果取指时间小于执行之间,那么取出的指令在缓冲区中进行缓存,指令指令部件一旦空闲,就可以到缓冲区中取执行,继续执行。
(2) 条件转移指令对指令流水的影响
这里说的是条件转移,对于无条件转移对于指令流水的影响比较小,因为在译码阶段就能够知道这条指令十一条转移指令,而且能知道转移指令的地址。但是对于条件转移指令来说,只有当这条指令阶段结束以后,有了执行的结果,才能知道转移条件是否成立,也才能确定下一条指令的真正地址是多少,造成的时间损失比较大。在现在的程序中,一些用机器语言写成的程序条件转移指令还是比较多的,一般来说3-5条指令中,就有一条是条件转移指令,这类指令对流水线的效率影响是比较大的。
解决这种影响因素的方式很多,如分支预测方法,分支预测的方法也很多,如猜测法。这里就不做过多的讲解,感兴趣的可以自己去查相关资料。
4、 指令六级流水
下面看一个六级流水线的例子
上图是一个六级流水线,横轴表示时间,纵轴表示指令,这个流水线被分成了六级,这六级的功能分别是:取指令,指令译码,形成操作数地址,取操作数,执行,结果的写回,所谓结果的写回是指把运算结果写回到指令的寄存器中,或者是写回到给定的内存单元中。
上图中一共给出了9条指令,假设六级流水线每段时间都是相同的,这样的话,我们采用串行方式完成一条指令,一条指令就需要6个时间单位,9条指令就需要54个时间单位。如果采用流水线方式只是用了14个时间单位,当然是假设9条指令不冲突并且没有条件转移指令的情况下。
(三)影响指令流水线性能的因素
1、 结构相关
所谓结果相关是指,流水线中功能部件发生冲突,两条或者是两条以上的指令争用同一个功能部件,如:一条指令利用运算器计算下一条指令的地址,另外一条指令在同一个时钟周期想利用运算完成指令指定的运算,这两条指令就会发生资源冲突,见下图:
如上图中,第一条指令在第4个时钟周期需要从内存中取操作数,而此时第4条指令需要取指令,也要访问内存。两条指令在同一个时间段要访问内存,就会造成资源冲突。同样在第5个时间段,第2条指令取操作数,第五条指令要取指令。怎么解决这种资源冲突问题呢
(1) 停顿。
如,第一条指令在第四个周期要取操作数,第四条指令要取指令,都要使用内存,那么第四条指令做停顿,即第4条指令不在第四周期做取指操作,把取指操作往后推,在第5周期或第6周期或者更靠后的地方来做。
(2) 把指令存储器和数据存储器分开
取指令的时候从指令存储器中取,存储操作时候,对数据存储器进行操作。现在计算机中把指令cache和数据cache分开就是为了解决冲突问题。
(3) 指令预取技术
如果对内存访问比较快,存取周期比较短,那么取指部件可以利用空闲时间把多条指令从内存单元中取出来,放入CPU中的指令缓冲队列等待执行。
这里讲解的冲突是以内存为例,实际上其他资源进行访问也会冲突,如对寄存器访问,在同一个周期中,一条指令要读某一个寄存器,另一条指令要把结果写回到这个寄存器中。运算器也可能会发生冲突.
2、 数据相关
不同的指令,重叠进行操作,流水线分成多个段,可能有多个读段,多个写段,通常情况下前段是读,后段是写操作,那么就可能改变操作数的读写次序,就会产生数据相关,数据相关一共有三种情况:
(1) 先写后读相关(RAW)
对同一个存储单元,或者是某一个寄存器,要先完成写操作,再做读操作,称之为先写后读。
上图中给出给出了两条指令,第一条是做减法操作,结果保存在R1中, 第二条是做加法操作,加法指令的操作数要利用到上一条减法指令执行结果R1,这两条指令要正确执行,必须是第一条指令完成操作,并且将结果写到R1,第二条指令读操作数的时候,必须是第一条执行最新的指令结果,才会保证程序执行的正确性。
(2) 先读后写相关(WAR)
对同一个存储单元,或者是某一个寄存器,要先完成读操作,再做写操作,称之为先读后写。
上图中,第一条是存储指令,把R2内容存到某一个内存单元中,第二条指令做一个加法操作对R2内容进行更新,那么也要保证先读后写次序。如果次序反了,那么保存到主存储器M的数据就错了,将会是ADD执行的结果。
(3) 写后写相关(WAW)
保证对同一个寄存器写入的顺序,这个时间顺序是由程序规定的。
上面两条指令都是写指令,要保证两条指令执行结果和串行时候执行结果完全相同的。如,如果上面还有第三条指令是读取R3的值,那么程序串行读出的是第二条指令的执行结果,如果发生写错误,将会导致第三条指令读出的是第一条指令的结果。
对于数据相关问题的解决办法:
(1) 后推法(推后读/推后写)
如先写后读相关中的两条指令,当第一条指令的写还没有完成,那么第二条指令等待,一直等待第一条指令把执行结果写入到R1中后,第二条指令再进行读操作。
(2) 采用旁路技术 / 相关专用通路
上面的后推法中,必须等到第一条指令把执行结果写入到R1,第二条指令才能继续执行读取R1,这实际上在时间上是有浪费的,因为对R1中保存的数值,在第一条指令执行阶段,即完成减法操作输出到运算器的输出端的时候,实际上这个结果已经给出了,那么采用旁路技术,不需要等待这个结果写入到R1,这个结果一旦在运算器的输出端生成以后,直接把他送入到第二条指令进行运算的时候运算器的输入,这样能更好的节约时间。
3、 控制相关
之前将的两种,一种是资源造成的,第二种是数据造成的,所谓控制相关,是由转移指令引发的。如下程序:
上面程序中,如果CPX指令比较不想等,BNE指令就跳转到M,BNE必须等到CPX指令比较结果出来以后,下面的指令才知道后续指令是如何执行的。在流水线中这会造成浪费。下面结合流水线的例子分析浪费的原因。
如上图中,如果第三条指令是条件转移指令,我们知道只有等到第三条指令执行结果完成时候,才知道转移条件是否成立,如果不成立,顺序执行,如果成立,那么要给出新的指令的地址。现在看看第三条指令的执行,第三条指令在译码阶段并不知道是否会真正发生转移,第四条指令在时间段4就会进入流水线完成取指令操作,同样,第5条指令在第5时间段也会进入流水线,第6条指令在第6时间段进入流水线,第7条指令在第7时间段进入流水线,如果转移条件成立,那么在第三条指令执行结束的时候才知道真的发生了转移,转移到了第15条指令,那么中间的已经进入到流水线,并进行了指令解释过程的指令,由于发生转移,这些指令在真正串行执行过程中是不应该执行的,他们的执行结果要作废。从15条指令重新开始,取指,分析,执行等过程,中间作废的指令就是转移造成的损失。
(四)流水线性能
1、 吞吐率
单位时间内指令流水线所完成的指令的条数,或 输出结果的数量。
下面假设流水线被分成m段,每一段的执行时间都是相同的,并且等于t。
(1) 最大吞吐率:流水线满负荷运转,没有发生资源冲突,也没有发生数据的相关,没有转移指令等条件下,这条流水线能够达到的最大的吞吐率是多少。在流水线满负荷情况下,没经过一个t就会有一个指令输出,所以最大吞吐量:
(2) 实际吞吐率:一段时间内实际完成的任务数,或者是指令的条数除以完成这些指令所需要的时间。连续n条指令的实际吞吐率为:
连续处理n条指令说明一共输出了n个结果,这n个结果使用了多长时间呢,第一条指令执行完成需要m*t,之后流水线满负荷运转,并且没经过时间t就会有一条指令流出流水线。后面还有n – 1条指令,所以n – 1条指令流出需要(n - 1)*t。所以:
2、 加速比
加速比给出的是使用流水线效果如何,即:m段的流水线的速度与等功能的非流水线的速度之比。
设流水线各段时间为t,完成n条指令在m段流水线上需要的时间:
完成 n 条指令在等效的非流水线上共需时间:
所以加速比为:
3、 效率
流水线中硬件的使用率,因为在流水线中存在流水线的建立时间,建立时间指的是第一条指令进入流水一直到第一个结果从流水中输出。另外还有流水线的排空时间,排空时间是最后一条指令进入到流水线到最后一条指令输出。
从上图中可以看出,在这两个时间当中,第一个时间段,只有S1在运行,2,3,4都没有运行,第二个时间段只有S1, S2运行,3,4处于空闲,第三时间段以及排空过程中的各个事件也是如此,所有的设备不可能一直处于工作状态。这样就可以计算流水线的利用率:
(五)流水线的多发技术
在流水线的基础上,对指令的解释速度进一步提高的方法。前面讲解的是用一条流水线如何提高指令的解释速度,利用这个思路,尽心进一步的扩展,如果我们使用多条流水线,有几条指令同时进入到不同的流水线中进行解释,这样的话速度会被进一步的提高。这种方法就是超标量技术。
1、 超标量技术
每个时钟周期内,多条独立指令进入到不同的流水线中执行。
需要配置多条流水线,多个功能部件。
如上图中,有三条流水线,在每个时钟周期,可以有三条独立的指令分别进入每一个流水线执行,这样,指令的解释速度和用一条流水线相比,最高加速比可以达到三倍。利用这种方法,通常情况下可以在执行当中不去调整指令的执行顺序,指令的执行顺序在编译过程中采用优化技术,把多条可以并行执行的,独立的指令挑选出来,搭配起来,让他们同时进入到三条流水线执行,这种方法就是超标量的方法。
2、 超流水线技术
把一个时钟周期(一个流水段)进一步的细分,如:分成三段,在一个时钟周期内,这三条指令进入到流水线中,当然也不是同时进入,还是分阶段进入。那么一个功能部件可以被使用三次。如下图
图中,流水线的结构还是原来的结构,取指,译码,执行,写回,四个阶段。但是,和流水线方式不同的是,我们把每一个时钟周期均分成了三份。这种方式和把流水线进行进一步细分,如把现在的流水线细分成12段,两种方式是有区别的,在流水线的设计过程中,流水段之间是要加入锁存器的,把每一个流水段的执行结果在锁存器中进行锁存,作为下一个流水段执行的操作信号,控制信号,或者操作的数据。如果我们采用超标量这种方式的话,在流水段之间依然有锁存器,但是一个流水段有把他等分三分,这三分之间是没有锁存器的。
超流水关键技术就是不同的指令处在同一个流水段中,相互之间的信号不能叠加。现在处理器中很多处理器都采用了超流水技术。
在超流水技术中,也不在执行过程中调整指令的执行顺序,也是通过编译程序解决优化问题。
如果采用超流水技术,流水线的速度,在这个例子中,速度将提高为原来的三倍。
3、 超长指令字技术
在数字信号处理,多媒体信号处理等领域应用广泛。
由编译程序找到指令之间的并行性,这个并行性的寻找是根据计算机中执行部件的数量和种类确定的,另外指令之间也不能有相关性,编译器或者编译程序找到了这些可以并行执行的指令, 然后编译器把这些指令组合成一条长的,甚至是几百字节的指令字,这个指令字包含了多个操作码,多个操作数字段。这条指令被从计算机的内存中取出,然后多个操作码字段进行译码,由多个并行的功能部件分别去执行相应的操作,这种方式可以减少取指令的时间,因为每次取出的是十几条甚至是几十条指令。这种方法叫做超长指令字,因为指令字的长度很长。
(六)流水线的结构
1、 指令流水线结构
完成一条指令分为6段,每一段需要一个时钟周期。实际上把指令的执行过程分为多少段,每段做哪些操作,对于不同的CPU是不同的。
如果流水线不出现断流,即不产生资源冲突,不产生数据冲突,不产生控制冲突,并且指令条数足够,能够连续输入到流水线中,那么一个时钟周期就会有一个结果,从整体来说,我们如果不采用流水线技术的话,6个时钟周期才能生成一个结果,相当于指令的结束速度会提高6倍。到那时分成这些段之后,在段之间一定要加上锁存器,这个锁存器用于保存前面的流水段的结果,同时为下一段提供操作数据和操作信号。2、 运算流水线
一个运算过程如果比较复杂的话,也可以采用流水线的方式进行实现,以提高运算的速度。如浮点运算完成的功能比较复杂,运算的时间比较长,在加减法运算中,需要求阶差,对阶,尾数求和,最后进行规格化,这个时间和定点加法相比要长很多。我们也可以采用流水线的方式来做。可以将其分为对阶,尾数求和,规格化三段流水线。
为了使得流水线提高速度的方式更加有效,每一段操作的时间要尽可能相等,如果不等,每个时钟周期就要按照时间最长的那个功能段进行设计,会降低整个流水线的速度。
中断系统
(一)概述
在输入输出系统中讲到过如何使用中断完成输入和输出,实际上中断系统的作用并不局限与输入输出上,他的应用非常非常广泛,比如程序调试,或者是计算机系统中啊发生异常事件,都可以使用中断系统进行处理。
1、 引起中断的各种因素
(1) 认为设置的中断
如在程序中编入转管指令,利用转管指令访问管理程序,也就是操作系统中的程序,来帮助我们实现程序中的一些功能。
(2) 程序性事故
在程序运行过程中,或者是指令执行过程中,如发生运算的溢出,操作码不能识别,或者是非法的除法,也会引发中断,
(3) 硬件的故障
如存储器故障,电源断电等
(4) IO设备输入输出
(5) 外部事件
如:利用键盘中断键中断程序执行。
2、 中断系统需要解决的问题。
(1) 各个中断源如何向CPU提出中断请求。
(2) 提出中断请求,各个中断源都是独立提出的,那么如果有多个中断源同时提出了中断请求,如何解决
(3) 有中断源提出中断请求,CPU在什么条件下,在什么时间,以什么方式来响应中断
(4) 响应中断,中断返回以后,还要返回到别中断的程序的断点,那么断点,现场如何保存。
(5) 要中断,去执行中断服务程序,需要找到中断服务程序的入口地址,并且把地址送给PC才能够执行,那么如何寻找中断服务程序的入口地址,或者怎么形成入口地址。
(6) 如何恢复现场,如何进行返回
(7) 如果在处理中断过程中,又出现了新的中断,怎么办
后面的讲解怎样采用软件+硬件的方式解决这些问题。不同的计算机对中断系统的软硬件功能划分是不同的,主要依据要求的中断系统的速度,设计的复杂性和设计的灵活性进行折中。
(二)中断请求标记和中断判优逻辑
各个中断源如何向CPU提出中断请求?
给每个中断源设置一个触发器INTR,用这个触发器标记这个中断源是否提出了中断请求,一个中断源对应一个中断标志触发器,多个中断源就有多个中断触发器,这些个中断触发器在逻辑上就组成了中断请求标志寄存器,如下图:
每个中断请求触发器可以把他放在各个中断源接口电路中,是分散的,从逻辑上组成一个中断请求标记的寄存器。也可以把他集中在CPU中,做在CPU在中断系统内。
如果有多个中断源同时提出了中断请求,也要相应,但是最终响应哪一个?
相应对系统影响最大的,最重要的那个中断源。通常情况会把中断源根据他会产生事件的严重程度或者是重要性进行分级。这个分级就是中断源的优先级。我们会用现有的中断判优逻辑在现有的中断源中判断哪个中断源优先级最高,就响应哪个中断源的中断。我们可以用硬件和软件两种实现方法来做。
1、 硬件实现(排队器)
现在的计算机系统中,大多数判优都是硬件实现,硬件实现有两种方法。
(1) 排队器分散在各个中断源的接口电路中。如外部设备IO中断的链式排队器,把多个中断源的判优电路连接在一起,就构成了一个中断判优硬件电路。
(2) 集中在CPU内部,或者是CPU外的某一个专门负责中断判优的机构中,如下图:
上图中,从1 – 4优先级按降序排列,这个硬件电路对于第一个中断源优先级最高,如果有中断请求,输入端是1,输出端也是1。对于2,3,4他们输出为1的条件是比他们高的中断器都没有中断请求,因此,如果1有中断请求,后面的中断源都不能发出中断请求。所以排队器的输出中,只有一位是1,其他的各位都是0.
对于硬件排队器,在设计排队器的时候,要对中断源的优先级进行划分,然后设计排队器,对中断源进行排队,以相应优先级最高的中断源提出的中断请求。
2、 软件实现(程序查询)
假如现在有A,B,C三个中断,并且按优先级降序排列。程序查询过程如下:
(三)中断服务程序入口地址的寻找
1、 硬件向量法
使用硬件方式形成中断向量地址:
上面的向量地址形成部件下面连接的是排队器的输出,在排队器的输出只有一根线是高电平,其他的都是低电平。这个输出指出了目前在所有申请中断的中断源中,哪一个中断源的优先级最高,向量形成部件就能够确定应该去执行那个中断服务程序,就可以形成该服务程序的中断向量,再通过中断向量找到该中断服务程序的入口地址。中断服务程序的入口地址,有两种方法给出:
左图:在中断向量的存储单元中存放一条跳转指令,这条跳转指令中包含了中断服务程序的入口地址,使用跳转指令直接跳转到中断服务程序的入口地址。
右图:在中断向量的内存单元中保存中断服务程序的入口地址,我们要把这个入口地址取回来送入到PC中,去执行中断服务程序。
在上图中:
使用硬件的方式速度快,但是灵活性低。
2、 软件查询发
假如有8个中断源,1,2,3…8按照降序排列。软件查询法要查询中断服务程序的入口地址,实际上是通过执行中断识别程序进行查找(中断识别程序入口为M)。
M对应的指令SKP 指的是跳过下一条指令,其功能是:查询第一个中断源的额触发器D, 看触发器等于0还是等于1,如果等于1,说明中断源提出了中断请求,那么就要去执行相应的中断服务程序,如果触发器等于0,说明这个中断源没有提出中断服务请求,就跳过下一条指令,进入下一个中断源的判断,这样依次进行检测。
使用软件的方法更加灵活,如我们要改变中断源的优先级,我们就可以调整查询的次序。
(四)中断响应
1、 响应中断的条件
并不是在任何条件下,只要中断源提出中断服务请求,CPU都要立即进行相应。加入我们的CPU只能支持单重中断,所谓单重中断是指,CPU在响应某一个中断源的中断请求,并且开始执行这个中断源的中断服务程序情况下,即使有新的中断源发出中断请求,CPU也不能进行响应,为了标识CPU是否允许响应中断请求,在CPU中有一个允许中断触发器EINT,当EINT = 1时候,允许CPU响应中断请求。
2、 响应中断的时间
并不是任何时间都能够响应请求,如,一般的机器,CPU只有在指令的执行阶段结束以后,才能够响应中断请求。那么有些计算机,他的指令系统中,有些指令可能非常复杂,执行的时间比较长,为了能够及时的处理一些异常事件,那么允许CPU在执行指令的过程中进行中断响应,但是,通常情况下都必须在指令执行结束后才能响应中断。
指令执行阶段结束,CPU会发出查询信号,这个查询信号送到每一个中断源中的中断请求触发器,查询信号驱动中断请求触发器将触发器的输出端置为1,然后把中断请求信号发送到排队电路。
3、 中断隐指令
中断系统还要解决程序断点的保存,需要生成中断服务程序的入口地址,另外,对于单重中断CPU,在中断服务执行过程中,不允许有新的中断请求打断当前正在执行的中断服务程序,那么即使是在多重中断中,也不允许优先级低的中断源请求打断正在执行的优先级高德中断源的服务请求。在中断响应过程中,要解决下面三个问题
(1) 保护程序断点
因为中断服务结束后,程序要返回到断点继续执行。断点的保护有两种方法:第一,将断点,或者是中断服务返回来后要执行的指令的地址保存到特定的内存单元地址中。第二,将程序断点进栈,返回的时候将程序断点出栈。
(2) 寻找中断服务程序的入口地址
前面已经讲过了两种方法:一种是使用硬件向量法,将向量地址送给PC,PC中保存的是向量地址,这个向量地址中包含了中断服务程序的入口地址或者是一条跳转指令,这条跳转指令会跳转到中断服务程序。二是使用软件的方法,把中断识别程序他的入口地址送到M中,计算机去执行中断识别程序以找到中断服务程序的入口地址
(3) 关中断
关中断避免在单重中断机器中,在执行中断服务程序的过程中,有新的中断源打断当前中断服务程序的执行。另外,即使在多重中断CPU中,采用关中断的方式,也是为了保存程序断点,保存程序现场的过程。
关中断就是中断允许触发器值置为0,关中断的电路:
图中:
INT是中断标记触发器,表示当前CPU正在执行某一个中断。
EINT是允许中断触发器,表示在当前时间CPU是否允许响应新的中断请求。
什么时候能够响应中断:中断允许触发器的输出为1,另外要有中断请求,也就是说排队器送出的信号中至少有一个要等于1,实际上排队器的输出信号如果有中断请求,只有一根线为1,所以使用或门表示有中断请求,两者都为1,表示即允许响应中断,并且确实有中断请求提出,在这种情况下,通过S端将中断标记寄存器INT置为1。一旦中断标记寄存器被置为1,那么中断允许触发器就要被置为0。另外中断排队器输出结果还要送给向量地址形成部件,以形成向量地址,这个向量地址要送给PC,为执行中断服务程序做准备。
这个标题的隐指令指的是,上面的三个操作都是由计算机的硬件完成的,但是并不是在某一条具体指令的驱动下完成的,也就是说中断隐指令并不是计算机指令集中的一条指令,但是在执行响应中断的时候,这些硬件操作都要进行执行。
(五)保户现场和恢复现场
1、保护现场
(1)保存断点
由中断隐指令来做,中断隐指令一共完成了三个操作,保存断点,形成中断服务程序的入口地址,硬件关中断。
(2)寄存器内容
执行中断服务程序时候,要用到CPU中的一些寄存器,这些寄存器的内容也要进行保存,因为这些内容进行中断返回的时候,主程序还要用到,这些内容可以通过中断服务程序完成寄存器内容的保护。所以我们再编写中断服务程序过程中,用到的寄存器内容,在使用寄存器之前,先要把这些内容保护起来,可以保存在堆栈中,或者是内存的某个地址中。
2、恢复现场
恢复现场由中断服务程序完成,如,保护现场的时候,保存寄存器的内容可以使用push指令把寄存器内容压入对堆栈中,恢复现场时候使用出栈指令恢复现场,由此可见,中断服务程序需要包含下面几个方面的内容:
(1)保护现场:断点保存,硬件实现,使用的是计算机隐指令。保护寄存器内容,可以使用push指令实现,这种方式是将寄存器的内容保存在了堆栈中。
(2)执行其他服务程序:视不同的请求源而定。
(3)恢复线程:使用pop指令,将堆栈内容弹出
(4)中断返回:使用IRET指令
(六)多重中断
1、多重中断概念
所谓多重中断是指,CPU在执行中断服务程序的过程中,如果又有新的中断源提出了中断服务请求,并且新的中断源的优先级比正在处理的中断服务程序的优先级更高,就要进行响应。
如上图,在执行主程序的过程中,某个中断源提出了中断服务请求,主程序的执行被中断,CPU响应中断源的请求,去响应中断服务程序,再执行中断服务程序过程中,有优先级更高的中断源提出了中断服务请求,这个中断服务程序的执行会再次被中断,CPU去响应新的中断服务请求,如果执行过程中又有优先级更高的中断源提出中断请求,当前的中断服务继续被打断,去执行新的中断服务程序,执行完成之后返回。中断谁就返回谁,一步步进行中断返回。
2、实现多重中断的条件
CPU被设计成可以相应多重中单,就要允许CPU在执行某个中断服务程序的时候响应新的中断请求。
(1)CPU内部有一个允许中断触发器EINT,在中断服务过程中,如果我们想响应新的中断服务请求,EINT就要提前被打开。
(2)CPU在响应某个中断服务程序过程中,只有优先级别高的中断源才能有权中断优先级别低的中断源。如,下图:
上图:
1、 CPU执行主程序,在执行主程序的过程中,BC同时提出了中断服务请求。
2、 响应优先级高的中断源的请求,所以响应B。
3、 B执行结束后,返回主程序,因为B中断的是主程序。
4、 此时C的中断服务程序还没有被响应,中断请求标记还在。CPU只要发出查询信号,主程序的执行再次被中断,响应中断源C的请求。
5、 响应C过程中,即使D发出了中断请求,但是D优先级别小于C,CPU并不会响应D的中断请求,将继续执行C。
6、 C执行结束以后返回主程序,因为C中断的是主程序。
7、 此时D的中断请求还在,CPU再次响应中断源D中断请求,执行D的中断服务程序。
8、 D执行过程中,如果有一个比D优先级高的中断服务请求A被提出,D的中断服务就会被打断,转而执行A的中断服务程序。
9、 当A的中断服务程序执行结束以后要返回D。因为A是中断了D的执行
10、 D执行结果后返回主程序。
3、中断屏蔽技术
(1)屏蔽触发器的作用
通过设置中断屏蔽字来改变中断服务优先级,从而提高系统设计和响应的灵活性。中断屏蔽字设置的时候,要通过中断屏蔽触发器进行设置。
上图中,INTR为中断请求触发器,MASK为中断屏蔽触发器。D为完成触发器。
当D完成工作后,要 向CPU提出中断请求,这个中断请求能够提出中断的条件是,这个中断源他没有被屏蔽掉,MASK的Q端的输出应该为0,表示没有屏蔽,在D触发器输出为1,并且MASK非端输出也是1的条件下才能够向中断请求触发器在CPU查询信号的作用下使得中断请求触发器的输出为1。也就是MASK为0表示没屏蔽,Q端输出为0,Q非端输出为1,此时没有屏蔽,INTR可置为1。如果MAASK为1,说明这个中断源提出的中断请求会被屏蔽掉,此时INTR触发器不管触发器D是什么样的值,INTR输出都是0,都是低电平,也就不能通过电路想CPU发出中断请求。
中断屏蔽触发器还可以使用另外一种方法进行使用:
上图为中断优先级排队器和屏蔽触发器的结合,如果MASK = 1,表示对应的中断源的中断服务请求不会被响应,也无法提出中断请求,如果MASK = 1,则MASK的非就为0,一旦MASK非为0,则输出一定为0,则INTP为0,就无法提出中断请求。
从上面可以看出,屏蔽触发器的作用就是使得中断源无法向CPU提出中断请求,也不能参加中断优先级排队器的排队。
(2)屏蔽字
屏蔽触发器对应的值就是屏蔽字,加入现在有16个中断源,每个中断源都对应了一个屏蔽字,每一个屏蔽字表示当这个中断源他的中断服务程序在执行过程中是否允许某一个中断源提出的中断请求进入到排队器中去排队,如果对应的值为1,就表示屏蔽,如果是0,表示开放。
从上图看,如果现在运行的是1号中断源运行的中断服务程序,那再这个程序执行过程中,无论1-16号中断源的屏蔽字就为1,所以2-16号的中断请求就无法进入排队器,所以CPU也不会对其的中断请求进行响应。如果是2号运行,则2-16位为1,说2-16号中断源中断请求不能进入排队器。(3)屏蔽技术可改变处理优先等级
中断响应优先级,中断屏蔽字不能改变,可改变的是处理的优先级。响应的优先级是由硬件电路来确定的,不可改变的。处理的优先级可以通过通断屏蔽字的方式让高优先级的中断不能进入到中断排队器中去排队,当然更不能被响应。这个可以让某一个中断源的处理优先级得到提高
如上图,公有A,B,C,D四个中断源,中间一列为原屏蔽字,第三列为设置的新的屏蔽字。可以通过设置新的屏蔽字来设置处理的优先级,响应的优先级A,B,C,D是固定的,因为硬件排队器中已经给出了响应优先级的次序,不能进行修改,但是新设置的屏蔽字把排队器处理的优先级设置为了A,D,C,B。在A执行过程中,任何中断源的中断请求都不能进入到中断排队器中,在D执行过程中,只有A的中断请求可以被送到中断排队器中去排队,对于C,在执行C的中断服务程序的过程中,对应的中断屏蔽字被设置成了0110,只有A和D他们的中断服务请求可以被送到中断排队器中进行排队,B和C就算有中断请求也不能进入到中断排队器。
从上面可以看出,排队器的作用是:决定同时进入排队器的多个中断请求哪个应该被执行,二屏蔽字决定了哪个中断源的请求可以进入到排队器进行排队。
(4)屏蔽技术的其他作用
可以屏蔽某个中断源的请求
上图为一个中断服务程序完整的过程,设置屏蔽字后要提前开中断,提前开中断的目的是为了实现多重中断。恢复现场之前需要关中断,因为恢复现场,恢复中断屏蔽字过程中不允许被打断,恢复完成后再开中断和中断返回。
(5)多重中断的断点保护
要实现断点保护,可以采用多种办法。
可以将断点进栈,由中断隐指令完成。也可以把程序断点保存到特定的地址 ,如“0”地址,这个0地址不一定就是真正物理地址的0,可以是一个给定的地址,这个也是由中断隐指令完成的。
在中断周期中,如果将断点保存在了0地址,需要进行以下步骤:
如上图,首先要将地址送给MAR命令存储器进行写操作。再把断点写入MDR,然后将MDR内容存入存储器进行保存。
但是在多种中断中,如果我们每次保存都将断点PC的值保存在某一个给定的地址,如0地址,那么后面的保存就会将前面保存的内容覆盖掉。那如何保证断点的内容不被覆盖丢失呢?
如上图程序:
1、0地址用于保存断点
2、5地址保存的是跳转指令,跳转到SERVE,SERVE是中断服务程序入口地址。5是中断向量地址,由中断向量地址形成部件形成后送入PC中。
3、保存现场,将现场数据保存在SAVE这个内存单元中。这里是将ACC内容进行八寸
4、LDA 0,指令的含义是取数操作,把地址为0的内存单元中的数据取出来放入到ACC寄存器,地址为0的地址中的内容实际上就是程序断点。
5、通过STA指令把断点值保存在了RETURN单元中,这就完成了0地址或者是特定的内存单元地址内容的转存,实质上就是转存了程序的断点,通过转存把程序的断点保护起来。
6、然后开中断,执行相应的中断服务程序
7、执行完以后要进行返回,返回之前通过LDA恢复程序现场,将SAVE中的数据取出。
8、通过一条间接跳转指令,跳转包RETURN内存单元中保存的地址,这个地址就是程序的断点,也就是下一条要执行指令的地址就保存在RETURN中。