3.6 流水线逻辑控制

发布时间 2023-06-03 12:28:17作者: C~A
加载/使用冒险:在一条从内存中读出一个值的指令和一条使用该值的指令之间,流水线必须暂停一个周期。
处理ret:流水线必须暂停直到ret指令到达写回阶段。
预测错误的分支:在分支逻辑发现不应该选择分支之前,分支目标处的几条指令已经进入流水线了。必须取消这些指令,并从跳转指令后面的那条指令开始取指。
异常:当一条指令导致异常,我们想要禁止后面的指令更新程序员可见的状态,并且在异常指令到达写回阶段时,停止执行。
我们先浏览每种情况所期望的行为,然后再设计处理这些情况的控制逻辑。
特殊情况的处理
对加载/使用冒险所期望的流水线操作。只有mrmovq和popq指令会从内存中读数据。当这两条指令中的任一条处于执行阶段,并且需要该目的寄存器的指令正处在译码阶段时,我们要将第二条指令阻塞在译码阶段,并在下一个周期往执行阶段中插入一个气泡。此后,转发逻辑会解决这个数据冒险。可以将流水线寄存器D保持为固定状态,从而将一个指令阻塞在译码阶段。这样做还可以保证流水线寄存器E保持为固定状态,由此下一条指令会被再取一次。总之,实现这个流水线流需要发现冒险的情况,保持流水线寄存器F和D固定不变,并且在执行阶段中插入气泡。
 
对ret的实际处理过程,每个周期,取指阶段从指令内存中读出一条指令,对ret指令来说,PC的新值被预测成valP,也就是ret下一条指令的地址,但是我们并不期望运行ret后面的指令,要运行call之后的指令,就要在取指阶段暂停三个周期,直到访存阶段取出call后面的指令开始。
 
当分支预测错误发生时,当跳转指令到达执行阶段时就可以检测到预测错误。然后在下一个时钟周期,控制逻辑就会在译码和执行段插入气泡,取消两条不正确的已取指令。在同一个时钟周期,流水线将正确的指令读取到取指阶段。
 
对于导致异常的指令,我们必须使流水线化的实现符合期望的ISA行为,也就是在前面所有的指令结束前,后面的指令不能影响程序的状态。一些因素会使得想达到这些效果比较麻烦:1)异常在程序执行的两个不同阶段(取指和访存)被发现的,2)程序状态在三个不同阶段(执行、访存和写回)被更新。
在我们的阶段设计中,每个流水线寄存器中会包含一个状态码stat,随着每条指令经过流水线阶段,它会记录指令的状态。当异常发生时,我们将这个信息作为指令状态的一部分记录下来,并且继续取指、译码和执行指令,就好像什么都没有出错似的。当异常指令到达访存阶段时,我们会采取措施防止后面的指令修改程序员可见的状态:1)禁止执行阶段中的指令设置条件码,2)向内存阶段中插入气泡,以禁止向数据内存中写入,3)当写回阶段中有异常指令时,暂停写回阶段,因而暂停了流水线。
流水线控制机制
流水线控制逻辑能将指令阻塞在流水线寄存器中或是往流水线中插入一个气泡。假设每个流水线寄存器有两个控制输入:暂停和气泡。这两个输入都为0,使得寄存器加载它的输入作为新的状态。暂停信号1,气泡信号0时,禁止更新状态。暂停信号0,气泡信号0时,寄存器状态会设置成某个固定的复位配置,得到一个等效于nop指令的状态。将暂停气泡都为1看成出错。