少女祈祷中...

指令周期和指令流水线

指令周期

指令周期:处理单个指令的时间,简单分为取址周期和执行周期

状态图:

并非所有指令的周期都一样

带中断的指令周期

如果中断允许,那么会在执行指令结束后,检查有无中断及处理中断

状态图:

间址周期

间接寻址,在取操作数时需要额外的存储器访问

间址周期:把间接地址的读取看成一个额外的指令子空间

状态图:

取操作数发生了两次:根据地址取有效地址,再根据有效地址取操作数

CPU的任务

  • 取指令
  • 解释指令
  • 取数据
  • 处理数据
  • 写数据

CPU需求:寄存器:

  • 1个存储地址寄存器:MAR
  • 1个存储缓冲寄存器:MBR/存储数据寄存器:MDR(这两者并没有太大的区别)
  • 一个程序计数器:PC
  • 一个指令寄存器:IR

数据流:取值周期

1、控制器下达指令:取址周期的开始
2、通过MAR讲PC中的地址传入地址总线,同时控制器通过控制线通知存储器地址就绪,然后存储器读取地址。
3、存储器通过数据总线讲数据发送给MBR。如果是异步总线,存储器提供反馈
4、指令取回来后,PC+“1”(往后一个地址)

数据流:间址周期

1、将MBR中的地址引用送入MAR,MAR将地址传入地址总线,控制器通过控制总线通知存储器取地址
2、存储器通过数据总线将有效地址发送给MBR

数据流:中断周期

1、处理中断前,将下一条指令放到MBR中,再放到数据总线上
2、控制器将下一条指令的取值地址通过MAR放到地址总线上
3、控制器通知存储器获得数据,存储器从地址线获得地址,存储器从数据线获得数据,并将数据写入到获得的地址

指令流水线

一条指令的处理过程被分成若干个阶段,每个阶段由相应的功能部件完成

两阶段方法:将指令处理分成两个阶段:取指令和执行指令

  • 在当前指令的执行期间取下一条指令

存在的问题:

  • 执行时间一般要长于取指时间
  • 主存访问冲突(同时有多个指令要访问主存)
  • 条件分支指令使得待取的下一条指令的地址是未知的(也许是按顺序执行,也许会跳转到其他的指令处)

六阶段方法:

  • 取指令(FI)
  • 译码指令(DI)
  • 计算操作数(CO)(计算每个原操作数的有效地址)
  • 取操作数(FO)
  • 执行指令(EI)
  • 写操作数(WO)

各个阶段所需要的时间几乎是相等的

存在的问题:

  • 不是所有指令都包含6个阶段(LOAD指令不需要WO阶段)
  • 不是所有的阶段都能并行完成(FI/FO/WO都涉及到存储器访问)
  • 六个阶段不全是相等时间时会在各个流水阶段设计某种等待
  • 条件转移指令使得若干指令的读取变为无效
  • 中断:提前处理到一半的过程都需要被清除

超流水线

  • 将六级流水线细分为更多的阶段,增加流水线的深度
  • 提升时钟频率,从而提高指令吞吐率(但是可能会导致指令的级数增加)

流水线性能

假设:

  • tit_i:流水线第i段的电路延迟时间
  • tmt_m:最大段延迟(通过耗时最长段的延迟)
  • kk:指令流水线段数
  • dd:锁存延时(数据和信号从上一段送到下一段所需的段间锁存接收时间)

可以得出:

  • 周期时间(进行完一次指令中的一段的时间):t=max[ti]+d=tm+dt = max[t_i] + d = t_m + d
  • k阶段流水线执行完n条指令的时间:Tk,n=[k+(n1)]tT_{k,n} = [k + (n - 1)]t(理想情况)
  • 加速比(不使用流水线与使用流水线情况下的执行n条指令所画时间之比):Sk=T1,nTk,n=nkt[k+(n1)]t=11+n1k>1S_k = \frac{T_{1,n}}{T_{k,n}} = \frac{nkt}{[k+(n-1)]t} = \frac{1}{1+\frac{n-1}{k}} > 1

并非流水线中的阶段数越多,执行速度越快(每个阶段都存在锁存延时等开销,同时控制逻辑数量也会急剧增加)

超标量流水线

具有两条或两条以上并行工作的流水线结构

单周期->标量流水线:时间并行性的优化,主要是对现有硬件的分段

标量流水线->超标量流水线:空间并行性的优化, 需成倍增加硬件资源

冒险

在某些情况下,指令流水线会阻塞或停顿(stall),导致后续指令无法正确执行

类型:

  • 结构冒险(不同指令同时使用相同的硬件资源,比如访存)
  • 数据冒险(有些数据要等前序计算完成)
  • 控制冒险(比如条件转移)

结构冒险

已进入流水线的不同指令在同一时刻访问相同的硬件资源

解决方法:

1、在指令之间插入空泡(等待一个时钟周期再开始执行下一条指令)

2、使用不同用途的多个存储器(例如:指令和数据放在不同的Cache中)(分开访问不同存储器)

3、同一个存储器提供分时处理(例如:寄存器时钟上升沿写,时钟下降沿读)

数据冒险

未生成指令所需要的数据(例如:一条指令需要使用之前指令的运算结果,但是结果还没有写回)

解决方法:

1、插入nop指令(软件)(表示这个指令什么都不做,来确保需要用到的数据在之前的指令更新后再取出)

2、插入bubble(硬件)(也是什么都不做,但是会导致效率低下,流水线失去意义)

3、前递(forwarding) / 旁路(bypassing)(分别表示直接从一个执行单元将数据传递给另一个执行单元、以及传递给需要这个数据的指令)(无法解决:一条指令需要使用之前指令的访存结果)

4、交换指令顺序(将一些与之前的操作没有关联的指令先行执行,来增加有关联的指令之间的时间间隔,避免冲突)

控制冒险

指令的执行顺序被更改,导致先前流水线执行的指令作废

解决方法:

1、取多条指令(多个指令流(使用两个指令流),预取分支目标(取分支指令之后的指令的同时也取分支目标处的指令),循环缓冲器(维护一个小高速存储器,含有n条最近顺序取来的指令))

2、分支预测(静态预测(规则不变,如预测绝不跳转或总是跳转),或动态预测(规则变化,如预测发生/不发生转移、以及转移历史表))

  • 发生/不发生切换

仅在连续两次发生错误时改变状态

  • 转移历史表(记录过去遇到的分支时的情况,每次进行分支时查询该表)

3、提前判断

  • 直接无条件转移:例如 j Target,在取指阶段即可获得转移目标地址(流水线不停顿)
  • 间接无条件转移:例如 jr $r1,在译码阶段才能获得转移目标地址(流水线停顿1周期)
  • 直接有条件转移:例如 beq $r1, $r2, Target,在执行阶段才能获得转移目标地址(流水线停顿2周期),在寄存器堆输出端增加额外的比较电路(流水线停顿1周期)

4、交换指令顺序(提前执行判断指令,将与判断指令无关的在判断之前的指令放到判断指令后面)