流水线的数据相关和冲突

实验目的

(1)理解流水线数据相关;

(2)掌握流水线数据冲突的解决方法。

实验原理

数据相关引发流水线数据冲突的场景:产生结果的指令(“写者”)尚未将处理后的结果写回到通用寄存器堆中,而需要这个结果的指令(“读者”)已经在译码阶段了,此刻“读者”指令从通用寄存器堆中读出的值是旧值而非新值。

如何避免产生这种错误呢?下面将讨论解决数据相关引发流水线冲突的检测和两种解决办法。

流水线数据相关的检测

根据五级流水线的CPU微结构,判断或是检测数据相关的条件具体可以描述为:处于译码阶段的指令需要获取源寄存器值,如果这些源寄存器中的任何一个地址(寄存器号)与当前时刻处于执行阶段、访存阶段或是写回阶段的需要写入的目的寄存器的地址相同,则表明处于译码阶段的指令与当前时刻处于执行阶段、访存阶段或是写回阶段的指令存在数据相关。

但是在检测数据相关时还要考虑以下情况。第一,参与检测的指令到底有没有寄存器的源操作数,比如I型指令中,只有一个寄存器的源操作数,再比如U型和J型指令压根就没有寄存器的源操作数,那么在考虑这类指令的数据相关时,不需要将和寄存器无关的源操作数考虑进去。第二,指令中有寄存器的源操作数,但是寄存器号为0,那么也不会相关,因为0号寄存器的值恒为0。第三,处于执行阶段、访存阶段或是写回阶段的指令是否需要写结果寄存器,比如分支指令不需要写寄存器,其“目的寄存器号”实际是立即数的一部分。

用停顿法处理数据冲突

解决数据冲突一种直观的解决思路是:让需要这个结果的“读者”指令在取指和译码阶段一直等待,直到产生结果的“写者”指令将结果写入到通用寄存器堆中,才可以进入到流水线的下一级的执行阶段。这种方法称为停顿(stall),也称作阻塞。

RISCV_Pipeline_6e_停顿法
图 1. 停顿法处理数据冲突

因为取指令阶段NextPC的值随着PC的变化而变化,译码阶段的其他值也是根据译码阶段的指令的变化而变化,所以停顿的基本思想就是保持这两个数值保持不变,简单来说就是保持从PC寄存器取出的pc值和从译码阶段的流水线寄存器PR1取出的指令值不变。具体实现方法可以利用寄存器的使能信号,当检测到冲突时阻止PC和PR1这两个寄存器更新数据,如图 1所示stall信号。当取指令和译码阶段停顿时,其他阶段的数据仍然继续在流水线中向前传递,当相关的数据写入目的寄存器后,数据相关消失,随即撤销停顿信号,流水线恢复正常。根据相关的寄存器是处于执行阶段、访存阶段还是写回阶段,停顿的周期数分别是3、2、1。

当发生数据冲突时,从寄存器堆读出的寄存器值是错误的,如果该寄存器值传递到执行阶段,ALU的运算结果也将是错误的,最终会将错误的结果写入目的寄存器。为了避免将错误的结果写入目的寄存器,在停顿的同时需要清零译码—执行的流水线寄存器PR2(包括控制信号的流水线寄存器),如图 1所示flush信号。

用前递法解决数据冲突

在遇到数据相关的指令时可以观察到一个现象,那就是需要这个结果的指令(“读者”)在译码阶段等待的过程中,前面产生结果的指令(“写者”)其实已经在执行阶段产生了结果,只不过还没有写入到寄存器堆中。

比如这两条指令:

    add x5,x0,4
    add x6,x5,3

当“add x5,x0,4”处于执行阶段时,“add x6,x5,3”此时处于译码阶段,且该指令要获取寄存器堆中地址为5的值,此时就可以在执行阶段的ALU的结果输出处到译码阶段的寄存器堆读出结果的生成处添加一条数据通路,来把执行阶段对地址x5的处理结果送到译码阶段。这种方法称作前递或转发(forwarding),也称作旁路(bypassing)。同样的,按照这种思想,还可以依次添加从访存阶段、写回阶段到译码阶段的前递通路。增加前递的数据通路如图 2所示。

前递法解决数据冲突
图 2. 前递法解决数据冲突

再来看一个特殊例子,对于以下的指令序列:

    add x4,x1,x1
    add x4,x2,x2
    add x4,x3,x3
    sub x6,x5,x4

当“add x4,x1,x1”处于写回阶段时,“add x4,x2,x2”处于访存阶段,“add x4,x3,x3”处于执行阶段,“sub x6,x5,x4”处于译码阶段,那么处于译码阶段的“sub x6,x5,x4”应该选择哪个前递通路传来的数据呢?很显然,应该选择离它最近的执行阶段前递过来的结果。所以,选择哪个前递通路传来的结果,不仅要看读寄存器堆的源数据的地址与转发过来的结果的地址是否一致,还要考虑不同流水段之间的关系。

再来看这样一个指令序列:

    lw x19,8(x0)
    addi x20,x19,3

假设此时的“lw x19,8(x0)”指令位于执行(EX)阶段,那么“addi x20,x19,3”就位于译码阶段,此时产生了数据相关,虽然执行阶段有到译码阶段的前递通路,但是lw指令在执行阶段尚未读出存储器中的数据,要到访存(MEM)阶段才能读出要写入目的寄存器x19的数据。所以此时需要等待一个周期之后才能从MEM阶段前递,解决跟在lw指令后面的指令与之产生数据冲突。

实验任务

1. 前递数据通路设计

上面介绍了流水线数据冲突的检测和解决办法,并且以23条指令的数据通路为例给出了具体方案。实验任务是在前面实验自己设计的27条指令的单周期数据通路的基础上,设计支持27条指令的流水线数据通路并且能够解决数据冲突。具体要求如下。

(1)用前递法解决流水线数据冲突。

(2)对于由上一条load指令引起的数据冲突需要停顿一个周期再前递。

2. 虚拟面板设计

根据自己设计的数据通路,绘图作为虚拟面板的背景;添加必要的观察数据和信号,同时在设计代码中添加相应的观察变量。

建议在之前的23条指令流水线虚拟面板基础上替换数据通路背景图,减少添加观察数据和观察信号的工作量。替换背景图通常会引起观察变量以及信息流显示(绿线)的错位;观察变量可拖动到新的位置,但是实验平台目前还没有交互式定义信息流绿线的功能。为避免错位后影响外观,附件材料中提供了不包含绿线的23条指令流水线虚拟面板文件,可在此虚拟面板的基础上替换自己修改的背景图。

为了减少绘制背景图的工作量,附件材料还提供了前面23条指令的流水线数据通路图,可使用开源软件diagrams.net(又名draw.io)修改。diagrams.net有Web版和桌面版, 可从https://www.diagrams.net/ 进入Web版或下载桌面版。背景图需导出为PNG格式,导出菜单中选择最后一项“高级”,可以设置DPI,原虚拟面板背景图的DPI是150dpi。背景图的文件大小不宜过大,否则会影响虚拟面板的加载速度,超过1MB实验平台无法加载。

3. 验证

在实验平台调试验证自己的设计。