赞
踩
开发ILP的方法可以分为两大类:(ILP指令级并行)
流水线处理机实际CPI
基本程序块:一串连续的代码除了入口和出口 以外,没有其他的分支指令和转入点
对于两条指令i(在前,下同)和j(在后,下同),如果下述条件之一成立, 则称指令j与指令i数据相关。
数据相关反映了数据的流动关系,即如何从其产生者流动到其消费者。
相关性是程序的一个特性, 是否一个相关会导致实际的冲突,是否该冲突会造成暂停, 这是流水线结构的基本特性。
解决方法:
保持相关关系但避免冲突
通过变换代码来消除相关关系
数据相关的检测:
名:指令所访问的寄存器或存储器单元的名称。
如果两条指令使用相同的名,但是它们之间并没 有数据流动,则称这两条指令存在名相关。
指令j与指令i之间的名相关有两种:
如果指令j写的名与指令i读的名相同,则称指令i和j发生了反相关
如果指令j和指令i写相同的名,则称指令i和j发生了输出相关。
名相关的两条指令之间并没有数据的传送。如果一条指令中的名改变了,并不影响另外一条指令的执行。
根据指令在流水线中要保留的读访问和写访问的顺序,可以将数据冲突 分为3种类型。
考虑两条指令i和j ,且i在j之前进入流水线, 可能发生的数据冲突有:
控制相关是指由分支指令引起的相关。
对于正确地执行程序来说,必须保持的最关键的两个属性是:数据流和 异常行为。
保持异常行为是指:无论怎么改变指令的执行顺序,都不能改变 程序中异常的发生情况。弱化为:指令执行顺序的改变不能导致程序中发生新的异常。
数据流:指数据值从其产生者指令到其消费者指令的 实际流动。
动态调度的优点:
到目前为止我们所使用流水线的最大的局限性:
为了支持乱序执行,我们将5段流水线的译码阶段再分为两个阶段:
在前述5段流水线中,是不会发生WAR冲突和WAW冲突的。但乱序执 行就使得它们可能发生了。
指令乱序完成带来的最大问题:
记分牌的目标:在没有结构冲突时,尽可能早地执行没有数据冲突的指 令,实现每个时钟周期执行一条指令。
要发挥指令乱序执行的好处,必须有多条指令同时处于执行阶段。
采用了记分牌的MIPS处理器的基本结构
每条指令的执行过程分为4段 (主要看浮点操作数,因而不考虑涉及访问内存的 阶段)
如果检测到WAR冲突,就不允许该指令将结果写到目的寄存器。这发生在以 下情况:
记分牌中记录的信息由3部分构成
约定:
进入条件:
not Busy[FU] & not Result[D];// 功能部件空闲且没有 //写后写(WAW)冲突。
计分牌内容修改:
Busy[FU]←yes; // 把当前指令的相关信息填入功能部件状态表。
Op[FU]←Op; // 记录操作码。
Fi[FU]←D; // 记录目的寄存器编号。
Fj[FU]←S1; // 记录第一个源寄存器编号。
Fk[FU]←S2; // 记录第二个源寄存器编号。
Qj[FU]←Result[S1]; // 记录将产生第一个源操作数的部件。
Qk[FU]←Result[S2]; // 记录将产生第二个源操作数的部件。
Rj[FU]←not Qj[FU]; // 置第一个源操作数是否可用的标志。
如果Qj[FU]为“no”,就表示没有操作部件要写S1,数据可用。
置Rj[FU]为“yes”。否则置Rj[FU]为“no”。
Rk[FU]←not Qk[FU];// 置第二个源操作数是否可用的标志。
Result[D]←FU; // D是当前指令的目的寄存器。功能部件FU将把结果写入D。
进入条件:
Rj[FU] & Rk[FU];// 两个源操作数都已就绪。
计分牌内容修改:
Rj[FU]←no; // 已经读走了就绪的第一个源操作数。
Rk[FU]←no; // 已经读走了就绪的第二个源操作数。
Qj[FU]←0; // 不再等待其他FU的计算结果
Qk[FU]←0;
结束条件:
功能部件操作结束。
进入条件:
任意f((Fj[f]不等于Fi[FU] or Rj[f]=no)
& (Fk[f]不等于Fi[FU] or Rk[f]=no));// 不存在WAR冲突。
计分牌内容修改:
任意f(if Qj[f]=FU then Rj[f]←yes); // 如果有指令在等待该结果(作为第一源操
作数),则将其Rj置为“yes”,表示数据可用。
任意f(if Qk[f]=FU then Rk[f]←yes); // 如果有指令在等待该结果(作为第二源操
作数),则将其Rk置为“yes”,表示数据可用。
Result(Fi[FU])←0; // 释放目的寄存器Fi[FU]。
Busy[FU]=no; // 释放功能部件FU。
核心思想:
每个保留站中保存一条已经流出并等待到本功能部件执行 的指令(相关信息)。
包括:操作码、操作数以及用于检测和解决冲突的信息:
浮点加法器有3个保留站:ADD1,ADD2,ADD3
浮点乘法器有两个保留站:MULT1,MULT
每个保留站都有一个标识字段,唯一地标识了该保留站。
使用Tomasulo算法的流水线需3段:
各符号的意义
r:分配给当前指令的保留站或者缓冲器单元编号;
rd:目标寄存器编号;
rs、rt:操作数寄存器编号
imm:符号扩展后的立即数;
RS:保留站;
result:浮点部件或load缓冲器返回的结果;
Qi:寄存器状态表;
Regs[ ]:寄存器组;
与rs对应的保留站字段:Vj,Qj
与rt对应的保留站字段:Vk,Qk
Qi、Qj、Qk的内容或者为0,或者是一个大于0的整数
指令流出
浮点运算指令 进入条件:有空闲保留站(设为r) 操作和状态表内容修改: if (Qi[rs] ≠ 0) //检测第一操作数是否就绪 { RS[r].Qj <- Qi[rs] }; //第一操作数没有就绪,进行寄存器换名,即把将产生该操作数的保留站的编号放入当前保留站的Qj。该编号是一个大于0的整数。 else { RS[r].Vj <- Regs[rs] ; //第一操作数就绪。把寄存器rs中的操作数取到当前保留站的Vj。 RS[r].Qj <- 0 //置Qj为0,表示当前保留站的Vj中的操作数就绪。 } if (Qi[rt] ≠ 0) // 检测第二操作数是否就绪 { RS[r].Qk <- Qi[rt] ; //第二操作数没有就绪,进行寄存器换名,即把将产生该操作数的保留站的编号放入当前保留站的Qk。该编号是一个大于0的整数。 } else { RS[r].Vk <- Regs[rt] ; //第二操作数就绪。把寄存器rt中的操作数取到当前保留站的Vk。 RS[r].Qk <- 0 // 置Qk为0,表示当前保留站的Vk中的操作数就绪。 } RS[r].Busy <- yes; //置当前保留站为“忙” RS[r].Op <- Op; //设置操作码 Qi[rd] <- r; // 把当前保留站的编号r放入rd所对应的寄存器状态表项,以便rd将来接收结果。 load和store指令 进入条件:缓冲器有空闲单元(设为r) 操作和状态表内容修改: if (Qi[rs] ≠ 0) // 检测第一操作数是否就绪 {RS[r].Qj <- Qi[rs] } //第一操作数没有就绪,进行寄存器换名,即把将产生该操作数的保留站的编号存入当前缓冲器单元的Qj。 else {RS[r].Vj <- Regs[rs]; // 第一操作数就绪,把寄存器rs中的操作数取到当前缓冲器单元的Vj RS[r].Qj <- 0 }; // 置Qj为0,表示当前缓冲器单元的Vj中的操作数就绪。 RS[r].Busy <- yes; // 置当前缓冲器单元为“忙” RS[r].A <- Imm; // 把符号位扩展后的偏移量放入当前缓冲器单元的A 对于load指令: Qi[rt] <- r; // 把当前缓冲器单元的编号r放入load指令的目标寄存器rt所对应的寄存器状态表项,以便rt将来接收所取的数据。 对于store指令: if (Qi[rt] ≠ 0) // 检测要存储的数据是否就绪 {RS[r].Qk <- Qi[rt] } //该数据尚未就绪,进行寄存器换名,即把将产生该数据的保留站的编号放入当前缓冲器单元的Qk。 else {RS[r].Vk <- Regs[rt]; // 该数据就绪,把它从寄存器rt取到store缓冲器单元的Vk RS[r].Qk <- 0 }; // 置Qk为0,表示当前缓冲器单元的Vk中的数据就绪。
浮点操作指令
进入条件:(RS[r].Qj = 0)且(RS[r].Qk= 0); // 两个源操作数就绪
操作和状态表内容修改:进行计算,产生结果 。
load/store指令
进入条件:(RS[r].Qj = 0)且r成为load/store缓冲队列的头部
操作和状态表内容修改:
RS[r].A <- RS[r].Vj + RS[r].A; //计算有效地址
对于load指令,在完成有效地址计算后,还要进行:
从Mem[RS[r].A]读取数据; //从存储器中读取数据
浮点运算指令和load指令 进入条件:保留站r执行结束,且CDB就绪。 操作和状态表内容修改: 任意x (if(Qi[x] = r) // 对于任何一个正在等该结果的浮点寄存器x { Regs[x] <- result; // 向该寄存器写入结果 Qi[x] <- 0 }; // 把该寄存器的状态置为数据就绪 任意x (if(RS[x].Qj = r) // 对于任何一个正在等该结果作为第一操作数的保留站x {RS[x].Vj <- result; // 向该保留站的Vj写入结果 RS[x].Qj <- 0 }; // 置Qj为0,表示该保留站的Vj中的操作数就绪 任意x (if(RS[x].Qk = r) // 对于任何一个正在等该结果作为第二操作数的保留站x {RS[x].Vk <- result; // 向该保留站的Vk写入结果 RS[x].Qk <- 0 }; // 置Qk为0,表示该保留站的Vk中的操作数就绪。 RS[r].Busy <- no; // 释放当前保留站,将之置为空闲状态。 store指令 进入条件:保留站r执行结束,且RS[r].Qk = 0 // 要存储的数据已经就绪 操作和状态表内容修改: Mem[RS[r].A] <- RS[r].Vk // 数据写入存储器,地址由store缓冲器单元的A字段给出。 RS[r].Busy <- no; //释放当前缓冲器单元,将之置为空闲状态。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。