这是我们之前已经构建的单周期的处理器,我们是可以正确地运行一些MIPS的指令的,
而MIPS指令的执行,可以分为这样5个步骤,第一步是取指,
第二步是译码,第三步是执行,第四步是访存,第五步是回写。
那我们还是结合数据通路图来看一看这样的步骤。
相比刚才的结构图,我们做了一点小小的变化,也就把IFU内部的结构进行适度的展开,
对照这个图,取指的阶段,就是用PC的值去访问指令存储器,从而得到指令的编码,
同时还需要生成PC的更新值。在译码阶段,不仅需要把指令编码进行分解,
而且还需要从寄存器堆当中读出所需的寄存器的值。第三步执行,主要在
ALU当中完成,对于算术逻辑运算指令,就是完成对应的运算,
而对于访存指令,则是计算出访存的地址。第四步是访存,对于load指令
是从数据存储器当中读出对应的数据,而对于store指令, 则是将数据送到数据存储器当中去,
而其他指令在这一步没有实质的操作。最后一步是写回,对于要改写
寄存器的指令,在这一步会将数据写入到寄存器堆当中指定的位置,
我们要注意的是,虽然分成了这五步,但只是为了便于描述而已,
所有的信号都必须要在这条指令执行的过程中保持稳定,例如从PC寄存器送到指令存储器的
这个地址信号,如果在指令执行完成前,它就发生了改变,那指令存储器送出的指令编码 也就会发生改变,
从而造成寄存器堆选取了不同编号的寄存器, 送出了不同的值,ALU也可能执行了不同的操作,
那这条指令就可能执行错误了,所以对于单周期处理器来说, 这一条指令执行的过程中,所有的信号都是必须要保持稳定的。
而我们要进行流水线的改造的话, 我们同样也会发现,这不同阶段所用到的硬件资源,基本上是相互独立的。
如果我们能把指令存储器输出的指令编码事先保存下来,
那我们就可以提前更新PC寄存器的值,并用这新的值去指令存储器当中取出一个新的指令,
而在取新指令的同时,刚才取出的那条指令的编码就会被分解成不同位域,
而寄存器堆也会根据输入送出对应寄存器的内容,
所以跟刚才的流水线原理的分析类似,如果我们想把这些硬件资源充分地利用起来,
我们就需要把它拆分成若干个阶段。那在这个电路的结构上, 要进行拆分,我们就在每一个阶段之间添加上寄存器,
这就被称为流水线寄存器。
这些寄存器用于保存前一个阶段要向后一个阶段传送的所有的信息。
我们还是以取指到译码的这个阶段为例, 我们将指令存储器的输出接到一个寄存器上,
那当一个时钟上升沿来临的时候,指令存储器输出的指令编码就会被保存到这个寄存器当中,
那么在这个上升沿之后,指令存储器的地址输入如果发生改变, 随之影响的指令存储器的输出,也不会被存到这个寄存器当中去,
所以在这个时候,我们可以用新的PC来访问这个指令存储器。
从而得到下一条指令的二进制编码,而在这个同时,前一条指令的编码
已经在这个流水线寄存器的输出上,并且经过相应的电路,切分成不同的位域,
那其中有一个位域就会通过rs连到了寄存器堆,并且 选中对应的寄存器,把其中的内容放到busA这根信号上,
而这根信号也会被接到一个流水线的寄存器上。
那么当下一个时钟上升沿来临的时候,
当前这条指令所需要的rs寄存器的值,就会被保存到这个流水线寄存器当中,
与此同时,下一条指令的二进制编码也会保存到这个流水线寄存器中。那么在很短的 Clock-to-U
时间之后,译码阶段所看到的指令的编码就已经变成第二条指令了。
所以很快,寄存器堆得到的rs的寄存器编号也发生了改变,但是这没有关系,
第一条指令所需的寄存器的值已经保存到了这个流水线寄存器当中,
而且在这个时候,也应该会被送到了ALU的输入端, 所以这样通过添加流水线寄存器,我们就先从大体上
把这个单周期处理器改造成了一个流水线的处理器。
那我们对这个流水线的处理器进行一些简单的性能分析, 比如说我们要执行这么三条指令,
我们把时间轴画出来,从0时刻开始,每一格是200ps, 那如果是在单周期的处理器上,执行一条指令需要
这样五步,也就是取指、译码、执行、访存和写回。
假设每个阶段都正好需要200个皮秒, 那执行完这条指令,就总共过去了1000ps,
然后我们才可以执行第二条指令,又用去1000ps, 然后是执行第三条指令,那这样每条指令都需要花1000ps的时间,
那这个单周期处理器,它的时钟周期就需要被设置为1000ps,
从外界看来,这个处理器每1000ps可以完成一条指令。
而对于流水线处理器,我们同样来执行这三条指令。
先看第一条指令,那要完成这条指令所需要的步骤是一样的,
同样也需要这五步,也同样需要花1000ps的时间。
但是不同在于,在过去200ps之后,当第一条指令完成了取指阶段,而进入到译码
阶段的时候,实际上取指部件已经空闲下来,我们就可以开始第二条指令的取址工作,
也就是说第二条指令在此时,已经开始执行了。
同样,再过了200ps,第一条指令完成了译码,进入到了执行阶段,
这样第二条指令也正好完成了取指,可以进入译码阶段, 而此时,第三条指令的取指也可以开始了。
这样对一个流水线处理器,虽然一条指令总共也是需要花 1000ps,但是每200ps就可以开始执行一条指令,
而且当流水线填满之后,每200ps也就可以完成一条指令,
所以对于这样一个流水线处理器,它的时钟周期可以设为200ps,
因此,这个处理器的主频就是刚才这个单周期处理器的5倍。
当然这只是理想情况,现实中的性能提升幅度并没有这么大,
其中一个原因就是这些新插入的流水线寄存器,它自身也会带来一些新的延迟,
我们假设这些寄存器的延迟是50ps,那我们再来看一看这个处理器的性能有什么样的变化。
这是刚才没有考虑流水线寄存器延迟的情况下分析的
性能表现,那如果我们加上流水线寄存器的延迟,
同样还是执行这几条指令,那就需要每隔250ps 才可以开始一条新的指令,
所以时钟周期应该设为250ps,而且对于每条指令 本身来说,需要花1250ps才能够完成。
在这一点上,是比刚才在单周期处理器还要更慢一些的。
因此对于流水线处理器来说,因为各个处理部件可以并行 工作,从而可以使得整个程序的执行时间缩短,
但是流水线并不会缩短单条指令的执行时间,相反,还会增加这个时间。
因此,采用流水线的方式,实际上是提高了指令的吞吐率,
从而从整体上缩短了程序的执行时间,提高了系统的性能。