2流水线处理机及其设计精讲.docx
《2流水线处理机及其设计精讲.docx》由会员分享,可在线阅读,更多相关《2流水线处理机及其设计精讲.docx(28页珍藏版)》请在冰点文库上搜索。
![2流水线处理机及其设计精讲.docx](https://file1.bingdoc.com/fileroot1/2023-5/5/bd0ae4d6-85df-453c-ae89-3e01a388c3e2/bd0ae4d6-85df-453c-ae89-3e01a388c3e21.gif)
2流水线处理机及其设计精讲
6.3.3流水线各级信号的产生
至此,我们已经描述过了所有3种指令类型的流水线操作。
下面我们来总结控制信号的产生方法。
控制信号分布在流水线处理机的各级,我们当然可以在它们要被使用的流水线级产生,例如SIMM可在EXE级产生。
但这样,除了译码将会造成本级一些延迟之外,还要使用流水线寄存器来传递必要的信息,例如指令操作码等。
我们采用的方法是,控制信号集中在ID级由译码电路产生,并使用流水线寄存器把每一个控制信号传递到它被使用的流水线级。
我们把所有的控制信号归纳在表6.4中。
表6.5列出了除了ALUOP的各控制信号的产生表达式。
表6.6列出了ALUOP控制信号的产生表达式。
由以上表达式,我们可以有如图6.29所示的控制电路。
图中使用了流水线寄存器来把控制信号传递到合适的流水线级。
我们可以看出,流水线控制部件的设计方法与非流水线单周期处理机的控制部件的设计方法非常类似,而有限状态机和微程序的设计方法显得不太适合。
表6.4流水线各级控制信号的定义
流水线级
控制信号
注释
IF级
BTAKEN
转移发生
ID级
SST
选择store(rd)
EXE级
SIMM
选择立即数
ALUOP
ALU操作码
WZ
写Z标志
MEM级
WMEM
写存储器
WB级
SLD
选择load
WREG
写寄存顺堆
表6.5控制信号的产生表达式
BTAKEN=branch+bne
+beqZ
SST=store
SIMM=andi+ori+addi+subi+load+store
WZ=andi+ori+addi+subi+and+or+add+sub
WMEM=store
SLD=load
WREG=andi+ori+addi+subi+and+or+add+sub+load
表6.6ALU控制信号的产生表达式
指令
ALUOP1
ALUOP0
操作
指令
ALUOP1
ALUOP0
操作
and
0
0
与
andi
0
0
与
or
0
1
或
ori
0
1
或
add
1
0
加
addi
1
0
加
sub
1
1
减
subi
1
1
减
load
1
0
加
store
1
0
加
ALUOPl=add+addi+sub+subi+load+store
ALUOPO=or+ori+sub+subi
图6.29流水线处理机的数据路径和控制部件
6.4结构相关及解决方法
流水线处理机的设计存在3个非常著名的问题(hazard)。
它们是
(1)结构相关问题(structuralhazard);
(2)数据相关问题(datahazard);
(3)转移相关问题(branchhazard)。
我们将在本节简要介绍第一个问题,下面两节分别介绍第二和第三个问题。
结构相关(structuraldependence)问题指的是,由于硬件资源不充足而导致流水线不畅通。
我们将举3个例子来说明结构相关问题及其相应的解决方案。
,
第一个例子,也是最典型的例子,就是设想只为流水线处理机配备一个外部公共存储器模块。
这在现代计算机系统设计中是常见的。
它的好处是程序和数据可以共享这个存储器模块,二者之间的界限是可以动态调整的。
使用分开的两个外部指令存储器模块和数据存储器模块的计算机被称为具有哈佛(Harvard)结构的计算机。
依程序类型的不同,有的需要较大的程序空间和较小的数据空间;有的则需要较小的程序空间和较大的数据空间。
在这种情况下,哈佛结构的存储器不能被灵活、充分地利用。
然而,如果只采用一个存储器模块而不采取任何其它的措施,则会出现结构相关问题,造成流水线堵塞。
图6.30示出了由于存储器端口资源的不充足而使取指令和load/store指令访问存储器的动作不能同时进行。
图6.30单个存储器模块所造成的流水线堵塞
图6.30中的存储器模块只有一套访问端口。
当load/store指令访问存储器时,必须暂停取指令操作。
当然也可以让取指令优先而暂停load/store指令的存储器访问操作。
然而,不管是哪一种情况,计算机的性能将会有很大的损失。
那么,采用一个外部存储器模块的现代计算机是如何解决这个问题的呢?
说起来很简单,如图6.31所示,在处理机内部,设置两个分开指令和数据的高速缓冲存储器(CACHE)。
把存储器中经常使用的程序段和数据分别放在指令CACHE和数据CACHE中。
访问CACHE命中时,处理机可直接从CACHE得到指令和数据,而不再访问外部存储器。
有关CACHE的原理和结构,我们将在9章详细讨论。
再举第二个例子。
把ALU指令的WB级提前一个周期。
由以前的描述可知,ALU指令在EXE级结束时已把结果放在了流水线寄存器R,在MEM级不做任何运算,只是把它传递到流水线寄存器C,以便在WB级写入寄存器堆。
我们可以把这样的数据路径加以改造,使ALU指令提前一个周期i即在通常的MEM级就把结果写入寄存器堆。
如果load指令仍用5个周期,则势必在有的周期有两个数据要同时写入寄存器堆;而有的周期则没有一个数据要写入,见图6,32(a)。
由于寄存器堆只有一个写端口,势必造成有些ALU写操作被延后。
解决这个问题的方法有3种:
第一种就是前面已经描述过的方法,即ALU指令也用5个周期;第二种是设计两个写端口的寄存器堆;第三种是改造load指令的格式,消除存储器地址计算周期。
办法是直接用一个寄存器操作数作为地址访问存储器,其指令格式形如loadrd,(fsl),即把由rsl指定的寄存器操作数拿来直接作为存储器的地址,把访问到的存储器数据写入寄存器rd,见图6.32(b)。
这样做会带来一个额外的好处,这就是store指令可以有store(rsl),rs2之类的格式,即把寄存器rsl的内容直接作为存储器地址i把寄存器rs2的内容写入存储器,从而可以省去寄存器堆前面的多路器以及相应的控制信号SST。
图6.31分开的指令CACHE和数据CACHE使流水线畅通
第三个结构相关的例子是,如果store指令格式形如storerd,(rsl,rs2),而寄存器堆只有两个读端口。
寄存器rsl和rs2的内容相加得到存储器地址,寄存器rd的内容存入存储器。
这样的store指令要求同时读出3个寄存器的内容。
如果寄存器堆只有两个读端D,肯定要堵塞流水线。
办法只有一个,设计一个带有3个读端口的寄存器堆,并在流水线处理图6.32用4个周期完成ALU指令(a),Load指令也用4个周期完成(b)机中为它配置3个流水线寄存器。
还看很多其它类型的结构相关的例子,例如浮点除法器使用迭代的方法计算商,而每次迭代用相同的电路等,我们在此不一一列举。
图6.32用4个周期完成ALU指令(a),Load指令也用4个周期完成(b)
6.5数据相关及解决方法
6.5.1数据相关问题
上一节给出的例子中,所有的指令都是不相关的,即任何——条指令的源操作数都不依赖于其它指令的计算结果。
本节讨论数据相关(datadependence)及解决方法。
现在让我们来看看下面的程序例子:
I1:
addr1,r2,r3
I2:
subr4,rl,r5
I3:
andr6,r7,r1
I4:
Orr8,r1,r9
I5:
addir10,r1,100
I1指令把寄存器r2和r3的内容相加,结果存入寄存器r1。
它下面的4条指令均与I1相关,即使用I1的结果,见图6.33给出的数据相关关系图。
从图中可见是在每个周期结束时,由时钟上升沿把数据打人寄存器。
在数据没被打人之前,任何从该寄存器读出的数据都是过时的。
图中的I2到I4的3条指令就属于这种情况,它们从r1寄存器读出的数据都是过时的,是不能使用的。
15则没关系,当它读r1寄存器时,n已把结果写入。
为了减少数据相关指令的条数,我们让写寄存器堆的操作提前半个周期,即由时钟的下降沿打入。
这样做是可行的,因为我们假定一个时钟周期是10ns,寄存器堆的访问只需
要5ns。
这样,数据相关的指令条数减至两条,见图6.34。
下面我们讨论如何在流水线处理机中处理数据相关问题。
我们将描述两种方法,一种
图6.33I1下面有3条指令不能从寄存器r1读出正确的数据
图6.34把WB提前半个周期,I1下面还有两条指不能从寄存器r1读出正确的数据
是暂停相关指令的执行(暂停流水线),直到能够正确读出寄存器操作数为止;另一种是采用专门的数据通路,直接把结果送到ALU的输入端(内部数据前推)。
在描述这两种方法时,我们使用图6.34的方案,即把写寄存器堆提前半个周期。
这时,我们只需考虑两条相关指令。
下面以ALU指令为例,分别对两种方法加以描述,然后再讨论load指令同时使用这两种方法的情况。
6.5.2暂停数据相关流水线
暂停数据相关流水线指的是当数据相关出现时,暂时停止相关指令的执行,等到相关数据能从寄存器读出,再恢复执行。
暂停流水线的两种表示方法见图6.35和图6.36。
这里涉及到两个问题:
一个是如何检测出数据相关,再一个是如何暂停流水线。
以下我们对这两个问题分别加以描述。
首先,让我们考虑数据相关如何用硬件电路检测出来。
参照本节开始给出的程序例子,I1写目的寄存器rd,I2和I3的源操作数是寄存器rsl或rs2中的数据,且它们的rsl或rs2与I1的目的寄存器号rd相等时才有可能发生数据相关,因此,硬件电路中要有比较器。
由于指令格式中源寄存器号rs2与立即数部分重叠,而立即数是不会出现相关的,因此,指令操作码必须要参与检测(区分是寄存器操作数还是立即数)。
;另外,如例子中的I1指令,rd一定作为目的寄存器号使用,即结果是要被写入目的寄存器时,后面的指令才有可能与之相关。
由于store指令也使用rd,但它不写寄存器,因此,WREG信号也应参与检测(实际上,WREG也是从操作码中得出的)。
我们把上述规则用表达式写出如下(它们是在ID级检测数据相关的表达式):
DEPEN=A_DEPEN+B_DEPEN
A_DEPEN=EXE_A_DEPEN+MEM_A_DEPEN
图6.35停止相关指令的执行(表示方法1)
图6.36停止相关指令的执行(表示方法2)
B_DEPEN=EXE_B_DEPEN+MEM_B_DEPEN
EXE_A_DEPEN=(1D_rsl==EXE_rd)(EXE_WREG==1)(1D_rsllsReg)
EXE_B_DEPEN=(1D_rs2==EXE_rd)(EXE_WREG==1)(1D_rs21sReg)
+(1D_rd==EXE_rd)(EXE_WREG==1)(store)
MEM_A_DEPEN=(1D_rsl==MEM_rd)(MEM_WREG==1)(1D_nllsReg)
MEM_B_DEPEN=(1D_rs2==MEM_rd)(MEM_WREG==1)(1D_rs2IsReg)
十(1D_rd==MEM_rd)(MEM_WREG==1)(store)
ID_rsllsReg=and十andi十or十ori十add十addi十sub十subi十load十store
ID_rs2IsReg=and十or十add十sub
由于一条指令中的两个源操作数都可能与上一条指令的目的操作数相关,因此总的数据相关DEPEN由A_DEPEN和B_DEPEN两部分组成。
A_DEPEN指的是源寄存器rsl数据相关;B_DEPEN指的是源寄存器rs2数据相关。
另外,两条指令12和13都可能与U相关。
如果是在流水线ID级检测数据相关,则对于12来讲,n处在EXE级;对于13来讲,n处在MEM级;因此,A_DEPEN包括EXE_A_DEPEN和MEM_A_DEPEN两部分。
EXE_A_DEPEN的意思是处在ID级的指令与处在EXE级的指令数据相关。
同理,MEM_A_DEPEN的意思是处在ID级的指令与处在MEM级的指令数据相关。
同样,B_DEPEN包括EXE_B_DEPEN和MEM_B_DEPEN两部分。
EXE_A_DEPEN为真的条件是I2的rsl与I1的rd相等(即ID_rsl==EXE—rd)、rsl字段是寄存器(1D—rs1IsReg)并且I1的rd确实是目的寄存器(即EXE_WREG==1)。
后一个条件是为排除store指令而加上的。
EXE—B—DEPEN与EXE—A—DEPEN类似,但rs2确实是源寄存器号(1D—rs21sReg)所包含的指令要比ID—rslIsReg少得多。
I3与I1的数据相关判断与此类似,只是把宇头EXE换成MEM。
EXE代表流水线EXE级;MEM代表流水线MEM级。
EXE_rd和MEM_rd分别是EXE级和MEM级流水线寄存器d的输出。
数据相关条件就这样得出来了。
需要注意的一点是,rs2应包含store指令的rd。
当条件满足时,要暂停流水线,因此下一步的任务是确定如何暂停流水线。
这里要特别注意的一个问题是,不能停止所有指令的流水线,而只是暂停相关指令及其后续的所有指令。
上例中,若12与n相关,只暂停12及12以下的指令,而不应把n也暂停,否则的话,处理机将永远暂停下去。
暂停流水线要做以下3件事情:
①封锁当前正在译码的指令的写控制信号;②不把从存储器取来的下一条指令打人IR;⑧不改变当前PC值。
我们使用如下的方法实现流水线的暂停:
WPC=
WIR=
ID—WZ=
Decoder—WZ
ID—WMEM=
Decoder—WMEM
ID—WREG=
Decoder—REG
图6.37给出的是实现暂停流水线的硬件电路。
暂停条件判断电路STALL的输入包括OPCODE,ID—rsl,ID—rs2,EXE—rd,EXE—WREG,MEM—rd和MEM—WREG。
输出为DEPEN。
用DEPEN信号来封锁本条指令及后续指令。
封锁本条指令的方法是把DEPEN求反,再分别和译码器的输出Decoder—WZ,Decoder—WMEM和Decoder—WREG相与,再送到ID级和EXE级之间的流水线寄存器的输入端。
只有这些写信号才改变处理机或存储器的状态,。
因而我们不必去封锁诸如ALUOP或多路器的选择信号。
封锁其后续指令的方法是禁止向IR及PC写入新的数据,即把DEPEN的非接到IR和PC的写使能端WIR和WPC。
当这两个信号为0时,禁止向IR和PC写人数据。
读者不妨试着画出它在数据相关前后的时序图。
图6.37实现暂停流水线的硬件电路
当I2与I1相关时,流水线暂停两个周期;I3与Il相关时流水线暂停一个周期。
我们
把被暂停掉的周期称作流水线“气泡”。
暂停流水线是解决流水线处理机数据相关问题的一
种方法。
使用这种方法,保证处理机能够从寄存器堆读出正确的数据。
但是,暂停流水线两个或一个周期造成了处理机性能的损失。
如果没有数据相关,处理机完全可以多执行两条或一条指令。
在由使用这种方法的处理机组成的计算机系统中,高级语言的编译器和汇编器在产生最终目标机器码时,应尽量避免出现过多的数据相关指令序列。
编译器和汇编器最初可以在相关指令之间插入nop指令,然后进行优化,用一些有意义的不相关的指令替换掉nop,以减少“气泡”的出现。
较早的MIPS处理机甚至没有数据相关的硬件检测电路,完全依靠软件优化来保证不出现指令之间的数据相关。
下一小节介绍的方法则能完全避免由于与ALU指令相关而造成的“气泡”的出现。
6.5.3提高流水线处理机的性能——内部前推
流水线数据相关问题的本质在于一条指令执行时要用到上面指令的计算结果,但这个.结果尚未被写入寄存器堆。
如果我们不拘泥于形式,非要ALU使用从寄存器堆读出的数据不可的话,流水线“气泡”问题也可以得到解决。
试想,数据相关发生在ALU计算周期,而所有的计算任务均由这一个ALU来按顺序完成。
这也就是说,ALU计算时发现与上一条或两条指令的结果发生数据相关的话,这些结果实际上已由ALU计算出来了,只是还没有写入寄存器堆。
那么,这时的结果在哪儿呢?
当然,它们还在流水线寄存器R和C中。
这就好了,把它们直接拿过来用!
为此,我们在Al。
U的两个数据输入端各加一个多路器,使R和C中的数据能被直接送到ALU的输入端,见图6.38到图6.40。
这就是所谓的内部前推(internalforwarding)技术。
图6.38内部前推——CC3周期,add指令在EXE级
见图6-38,ALU输入端A的多路器有3个输入。
无数据相关时,选择0号输入,也就是流水线寄存器A中的内容;相关时,选择2或3号输入,它们来自于流水线寄存器R或。
C。
在本节的例子中,12与n相关时,选择2号输入;13与n相关时,选择3号输入。
1号输入没有被使用,只是标在那里,在设计多路器时可以去掉该输入以使电路简化。
ALU输入端B:
的多路器有4个输入。
无数据相关时,若是寄存器操作数,:
选择0号输入:
若是立即数,选择1号输入。
2号与3号输入等同于ALU的A端多路器。
图6.39内部前推--CC4周期,add结果在R中,sub使用R的内容运算
如何产生这两个多路器的选择信号是内部前推电路的关键所在。
表6.7和表6.8列出了两个多路器选择信号的输出真值表。
注意,表6.8中的rs2包含了store指令中的rd,参见图6.39和图6.40中的电路,其中的rs2来自于寄存器堆前面多路器的输出。
表6.7ALUA端多路器选择信号的输出真值表
输入
输出
MEM_WREG
EXE_rs1==MEM_rd
WB_WREG
EXE_rs1==WB_rd
ADEPEN1
ADEPEN0
输入选择
0
x
0
X
0
0
A
1
0
0
X
0
0
A
1
1
0
X
1
0
MEM_R
0
X
1
0
0
0
A
0
X
1
1
1
1
WB_C
1
0
1
0
0
0
A
1
1
1
0
1
0
MEM_R
1
0
1
1
1
1
WB_C
1
1
1
1
1
0
MEM_R
表6.8ALUB端多路器选择信号的输出真值表
输入
输出
EXE_rs2
IsReg
MEM_WREG
EXE_rs2
==MEM_rd
WB_WREG
EXE-rs2
==WB_rd
BDEPEN1
BDEPEN0
输入选择
0
X
X
X
X
0
1
立即数
1
0
X
0
X
0
0
B
1
1
0
0
X
0
0
B
1
1
1
0
X
1
0
MEM_R
1
0
X
1
0
0
0
B
1
0
X
1
1
1
1
WB_C
1
1
0
1
0
0
0
B
1
1
1
1
0
1
0
MEM_R
1
1
0
1
1
1
1
WB_C
1
1
1
1
1
1
0
MEM_R
表6.8比表6.?
多了一项EXE—rs21sReg,这是因为指令中的立即数本身是不与上面指令的结果相关的。
我们在表6.7中忽略了EXE—rs/IsReg,这是因为除了转移指令,所有其它指令的rsl的位置均是寄存器描述符,而转移指令在EXE级是不做任何操作的。
两表中的最后一行需要特别说明一下。
例如在表6.8中,MEM_WREG=1,EXE_rs2=MEM_rd,并且WB_WREG=1,EXE_rs2=WB_rd,说明当前指令的第二个源寄存器操作数与它上面的两条指令都相关。
也就是说,上面的两条指令向同一个目的寄存器写入结果。
这种情况一般是不会出现的。
若出现,我们选择较新的结果,即MEM—R,送到ALU的B数据输入端。
图6.40内部前推——CC5周期,add结果在C中,and使用C的内容运算
图6.38到图6.扎以及表6.7和表乱8演示的是在EXE级ALU操作时才检测数据相关,有点临阵磨枪的味道。
我们完全可以在ID级完成这项工作,并把结果(多路器的选择信号ADEPEN两位和BDEPEN两位)作为控制信号打人流水线寄存器,以便轻装上阵,在进入EXE级后立即开始ALU的操作。
图6.41给出的是采用数据前推技术的流水线处理机的数据路径和控制部件的结构图。
你会发现数据相关检测电路也放在ID级来做了(表6.7。
和表6.8中所有信号的字头都用前一级的符号来代替,即MEM用EXE来代替,WB用MEM来代替)。
图6.41采用数据前推技术的流水线处理机的数据路径和控制部件的结构图
6.5.4处理1oad指令——暂停与内部前推相结合
以上讨论的都是与ALU指令相关的情况。
ALU指令在EXE级结束后,结果就出现在流水线寄存器R中,后续指令可以通过内部前推电路来直接使用它。
但是,一条指令若与l。
ad指令相关,情况就不那么简单了。
在EXE级结束后,load指令还在忙着访问存储器呢。
要在MEM级结束后,结果才出现在流水线寄存器D中,见图6.42。
这时,即使使用内部前推技术也无法消除l。
ad指令与它的下一条相关指令之间的第、一个“气泡”。
图6.42与load指令相关
怎么办?
办法有两个,一个是硬件方面不做任何努力,完全由软件来避免使用过时数
据进行计算。
当一条指令与它上面的10ad指令数据相关时,在它们中间插入两条nop指
令,然后优化,用两条不相关的指令替换两条nop指令。
替换一条是一条,替换不掉就那么放着。
第二种办法是由硬件负责检测与load指令的相关性。
没有办法消除的第——个“气泡”就留着。
为了保证操作结果的正确性,我们采用暂停流水线一个周期的方法。
第二个“气泡”用内部前推技术加以消除·。
:
我们给出—F面的用于实现l。
ad流水线暂停的控制信号的表达式。
这个信号是在ID级产生,并且使用与暂停ALU流水线类似的方法。
LOADDEPEN=EXE_A_DEPEN十EXE_B_DEPEN
EXE_A_DEPEN=(ID_rsl==EXE_rd)(EXE_SLD==1)(ID_rslIsReg)
EXE_B_DEPEN=(ID_rs2==EXE_rd)(EXE_SLD==1)(ID_s2IsReg)
+(ID_rd==EXE_rd)(EXE_SLD==1)(store)
ID_rsllsReg=and+andi+or+ori+addi+addi+sub+subi+load+store
ID_rs21sReg=and+or十add+sub
注意,以上的表达式中没有使用WREG信号,而是用了SLD。
因此在书写SLD,不能选择任意项,而应只书写WREG==1处的SLD。
或者,—一个保险的方法是把译出的1。
ad指令打人流水线寄存器,使用它而不是EXE—SLD。
图6.43带有内部前推及load相关暂停功能的流水线处理机
由于流水线已经暂停了一个周期,剩下的内部前推电路可以仿照上一小节的描述加以设计。
图6.43的电路是带有内部前推及load相关暂停功能的流水线处理机。
所有的控制信号均在m级产生,并使用流水线寄存器把它们传递到所需要的位置。
注意,封锁控制信号的与门已合并在Decoder中了。
即使流水线处理机有了load相关暂停功能,但它只保证操作结果的正确性。
如果编译器和汇编器能对程序做些优化,尽量消除与load指令的相关,就会提高流水线处理机的性能。
图6.44(a)中的第二条指令使用它上面的load指令从存储器取来的结果。
这势必造成流水