首发于CPU设计

关于流水线的三种冒险

流水线在一种情况下,在下一个时钟周期中的下一条指令无法执行。这种情况下被称为流水线的冒险,共分为三种冒险:1.结构冒险 2.数据冒险 3.控制冒险

  1. 数据冒险:当指令在流水线中重叠执行时,后面的指令需要用到前面的指令的执行结果,而前面的指令尚未写回导致的冲突,称为数据冒险(也称为数据相关性)。
  2. 结构冒险:当一条指令需要的硬件部件还在为之前的指令工作,而无法为这条指令提供服务,那就导致了结构冒险。(这里结构是指硬件当中的某个部件、也称为资源冲突)。
  3. 控制冒险:如果现在想要执行哪条指令,是由之前指令的运行结果决定,而现在那条之前指令的结果还没产生,就导致了控制冒险(实际上就是riscv的跳转指令引起的,跳转指令要经过2个周期后才会出现跳转结果)。

一、结构冒险

1. 同时读写存储器

示例1:如果指令和数据放在同一个存储器中,则不能同时读存储器

同时读存储器

解决方案1:我们有一个方便又简便的方法,即流水线停顿(stall),产生空泡(bubble)。

流水线停顿

虽然流水线停顿能用来解决各种冒险,但它的效率低下,应尽量避免。

解决方案2:在存储器中设置单独的指令高速缓存和数据高速缓存。(要强调的在计算机中主存储器也就是内存是统一存放指令和数据的,这也是冯诺依曼结构的要求,只是在CPU当中的一级高速缓存会采用指令和数据分别存放的方式)

采用dcache和icache

2. 同时读写寄存器

示例2:如果读寄存器和写寄存器同时发生,如何处理?

读写寄存器冲突

解决方案:前半个周期写,后半个周期读,并且设置独立的读写端口。

相对来说寄存器堆的读写速度比较快, 我们假设读或者写寄存器的延迟为100ps,而其他部件比如说ALU的延迟就就比较大,视为200ps, 那么我们就可以在前半个时钟周期用于完成寄存器堆的写,后半个时钟周期用来完成读操作,并且在寄存器堆上设置独立的读写口。这样就可以在一个时钟周期内同时完成了读和写的操作。

要设计一个新的处理器,结构冒险仍然是我们优先要考虑并解决的问题。但结构冒险在设计处理器时就考虑并解决好了,我们在使用时就不必考虑。

二、数据冒险

1. 发生数据相关性的三种情况:

(1)RAW(read after write):又称先写后读相关性。比如下面指令序列,如果第二条指令,在第一条指令写x5之前,第二条指令先读x5,就会引起逻辑错误。

add x5, x4, x6

add x4, x5, x2

(2)WAW(write after write):又称先写后写相关性。比如下面指令序列,如果第二条指令,在第一条指令写x5之前,第二条指令先写x5寄存器,就会引起逻辑错误。

add x5, x4, x6

add x5, x3, x2

(3)WAR(write after read):又称先读后写相关性。比如下面的指令序列,第一条指令会读取x4,第二条指令会写x4。在流水线中,如果第二条指令比第一条指令先写x4,则第一条指令就会读出错误的值。

add x5, x4, x6

add x4, x3, x2

2. 数据相关性在流水线的表现:

(1) 如果处理器核是是顺序派遣,顺序写回的微架构,在指令派遣时候就已经从通用寄存器数组中读取了源操作数。后续执行的指令写回regfile的操作不可能影响到前面指令的读取,所以不可能发生WAR相关性造成的数据冲突。

(2) 正在派遣的指令处在流水线的第二级,不管之前派遣的指令是单周期指令还是多周期指令,在5级流水线中还需要经过3个周期才能写回数据,而下一个周期后马上就要读取上一次写指令的数据。因此正在派遣的指令可能会产生前序相关的RAW相关性

(3) 假设正在派遣的指令处在流水线的第二级,之前派遣的指令是单周期指令,按照顺序写会则前序指令肯定已经完成了执行且将结果写回了Regfile。再依次写会第二条指令,因此正在派遣的指令不可能会发生WAW数据冲突。但是假设之前派遣的指令是多周期指令(长指令),由于指令需要多个周期才能写回结果。则后一条正在派遣的指令可能会产生前序相关的WAW相关性。

3. 检查数据相关性

为了解决RAW和WAW的数据相关性,可以采用outstanding instruction track fifo(OITF)模块,在流水线的派遣时(Dispatch)点,每一次派遣一次指令,则会在OITF中分配一个表项(Entry),在这个表项中存储该指令的目的寄存器索引。在流水线的写回(Write-back)点,每次按照顺序写回一个指令之后,将会将指令在OITF中的表项清除。

每条指令派遣时,都会将本指令的源操作数和目的操作数寄存器索引和OITF中的各个表项进行比对,从而判断本指令是否与已经被派遣出,尚未写回的指令产生RAW和WAW相关性。如果产生相关性,则stall住当前指令的派遣。当连续将指令指令写入OITF,如果OITF是full,则仍要stall住管线,等待OITF释放空间后,再写入并派遣。

OITF

4. 解决数据相关性

示例1:一条指令需要使用之前指令的结果,但是结果还没有写回。

写后读相关性

软件解决方案:插入nop指令

软件解决

但这种方法有个很大的问题,首先,插入nop指令的个数与流水线的结构相关,例如在5级流水线上正确运行的程序,在8级流水线上就不能正确运行。其次,我们希望对软件屏蔽硬件尽可能多的细节。

那么既然两条nop指令就能解决的问题,我们可以尝试在硬件上完成相同的工作。

解决方案1:流水线停顿,增加气泡

流水线停顿

解决方案2:数据前递(Forwarding)

t0在EX阶段就被计算出,所以可将它送到下一条指令ALU的输入,而不需要添加气泡。

数据前移技术

在电路上的实现如下:在过600ps后,t0的值会被保存到EX/MEM这个流水线寄存器中,与此同时,加法指令正在执行,它需要将t0的值传到ALU的输入,显然它直接从t0寄存器读的值不是最新的,最新的在访存阶段的连线上,我们从硬件连线上把这个信号引回来,作为ALU的输入端。是否使用前递的信号,我们需要根据是否出现数据冒险,来控制一个二路选择器。

当然加法指令,也有可能是第二个源操作数使用t0的值,所以前递信号也要连接到第二个输入端,同样这里也要添加二路选择器。

前移结构

这样的方式就被成为前递。它还有个名称叫作旁路。那从根本上来说,前递和旁路指的都是这件事情。只不过是观察和描述的角度不同而已。前递是从指令执行顺序的角度来描述的,而旁路则是从电路的结构角度来描述。 本来前一条指令应该将运行的结果写入到寄存器堆,然后再交给后一条指令使用,而我们现在搭建来一条新堆通路,相当于绕过了寄存器堆,直接进行了数据堆传递,所以从硬件时限的角度来看,这是一个旁路。那这就是前递和旁路的关系。

那我们进一步来看,其实不仅仅在这个点可以建立旁路,我们在下一个流水级也可以建立旁路。

示例2:从MEM/WB阶段前递到ALU的情况

MEM

所以,再添加一条旁路

增加写回回路

示例三:访存指令出现数据冒险

访存指令出现数据冒险

这个单纯的前递也无法解决(前递的箭头方向正向下或者右下)

解决方案:流水线停顿+数据前递

流水线停顿+数据前递

三、控制冒险

1. 消除控制冒险

示例:尚未确定是否发生分支,如何进行下一次取指

未确定是否发生分支

解决方案1:流水线停顿,添加气泡

流水线停顿

前面说过添加气泡效率很低,并不是一种较好的方法。我们可以从一下两方面考虑:

解决2:假设分支不发生

例如,假设经过beq指令分支不发生,最坏情况是其实分支总是发生,所以执行两条错误的lw、sw指令,又执行两条正确的指令,这样导致50%的性能浪费。

假设分支不发生

这也是因为转移指令本身和流水线的模式是冲突的,因为转移指令会改变指令的流向, 而流水线则希望能够依次地取回指令,将流水线填满。那如果这种情况是非常罕见的,也许我们还可以容忍,但实际上转移指令是非常常用的指令。

2. 缩短分支延迟

转移指令的分类:

  • 直接转移:j target和beq rs,rt,imm两种
  • 间接转移:jr t0

无条件直接跳转(j target)

这种情况跳转是确定发生的,且跳转地址在取指阶段就能得到,所以流水线不停顿。

这条指令的编码当中,带有一个26位的立即数,这个数就是要转移的目标地址的主体部分, 但是我们的目标地址应该是32位的,所以还差6位,在差的6位当中,低两位我们用0补上,因为目标地址肯定是四字节对齐的,地址的低两位肯定是0,然后还缺4位,我们通过当前的PC寄存器计算而得。先将PC寄存器的内容加4,得到的这个32位数,取其高4位,和26位地址以及最低的两位的0连接起来,构成了一个32位的数,这就是转移的目标地址。这些工作和取指可在一个时钟周期内完成。

无条件直接跳转

无条件间接跳转(jr rs)

在译码阶段得出跳转地址,流水线需停顿一个周期。

无条件间接跳转

条件跳转(beq rs,rt,imm16)

不做优化,需要根据EX阶段的结果,判断是否跳转,需要等待2个周期。

条件跳转

而实际上,比较两个数是否相等是十分简单的,只需在译码阶段对寄存器的两个输出进行比较,这样流水线停顿周期缩减为1个周期。

译码阶段比较

3. 延迟转移技术

就是调整指令的顺序,将一定会执行的指令放在分支指令后面,这样流水线不停顿。注意,不能改变这段代码原来的意义。

例如:可以将xor指令放到beq后面,经过xor指令后,beq不用等待正好可以执行,但是不能将addi或subi放到beq后面,因为beq指令需要这两个。

延迟转移技术

对于控制冒险,还可以使用分支预测进行高效的解决。

编辑于 2023-05-30 18:16・IP 属地上海