Contents

简易流水线分析

流水线原理

最早的处理器采取的是单周期的模式,处理器中所有的部件都用来处理一条指令,即便该指令暂时用不到某一个部件,该部件也会干等,不会处理其他事情。这对处理器资源来说是一种极大的浪费,我们想做到的是让每一个部件在每一时刻都能干点活,不能让他闲着(充分压榨机器的劳动力)。随着RISC指令集的问世,各个部件的功能逐渐明确,不同部件之间界限也越来越清晰,指令的执行的流程也变得更加统一,人们提出了流水线技术,尽可能地让所有的部件在任何时刻都能处于工作状态。

流水线是一种ILP技术(指令级并行),这意味着在一个核中有多个指令在同时执行,不同部件执行的不同的指令。下面是解释流水线的一张图,第一条指令在第一个cycle进入IF部件,第二个cycle时,第一条指令完成IF进入ID,此时第二条指令进入IF部件,IF部件一直处于运算之中。如果指令数量足够多,最终所有的部件都会处于工作之中,不同的部件处理的是不同的指令。

https://s2.loli.net/2022/10/02/fPb8xsgMZ29FNSr.png

流水线看似很美好,但是在一个顺序流水的处理器中,还是会有各种各样的问题使得流水线不能每个cycle都执行一条新的指令,有时候需要必要的等待以保证程序的正确执行。

流水中有三大问题(hazard),分别是结构冲突、数据冲突和控制冲突,这3个冲突会使流水线不得不停下来等待,而等待过程中部分部件会开始干等,降低执行效率。这三个问题的解决也是提升流水线效率的关键。

结构冲突是指在同一个cycle有两条指令需要用到同一个部件,如果该类型部件只有一个,只能停掉整条流水线,处理完一条指令,然后放开流水线,处理下一条指令,当然也可以多设置几个该类型的部件,这样就不用暂停流水线了,直接用不同的部件处理。

数据冲突是一个比较核心的问题,也是比较难处理的问题,人们为此提出了很多解决该冲突的方法,也是我们关注的核心。在顺序的流水线中(指令的顺序就是指令完成的顺序,前面的指令比后面的指令先进入流水线,比后面的指令先完成),我们遇到的冲突主要是RAW冲突(read after write),前面一条指令向一个寄存器写入一个数,然后后面的指令正好要读这个寄存器的数,但是前一条指令还没写完,后面的指令可能会读到错误的数。在乱序的流水中还可能出现WAW(write after write) 和 WAR(write after read)冲突,分别对应两条写指令写统一个寄存器,但是前面的指令最后完成和先读后写,但是读指令在写指令之后完成。

后面会简单分析如何在流水线上进行分析和解决这些冲突情况。

流水线分析

一个部件等,整个流水线都要等

首先我要们做的一件事是识别一个流水级,以及他具体在电路中是如何实现的。

我们写的verilog代码一般被成为RTL设计,RTL是指Register Transfer Level,一般现在的逻辑电路都是由寄存器和组合逻辑组成的,一个和寄存器接一个组合逻辑,这样连接构成的。

https://s2.loli.net/2022/10/02/bTL6OElHwqhCgGI.png

我们的一个流水级其实和RTL差不多,首先在头上有一个寄存器,用于存储本cycle要处理的数据,后面的组合逻辑用于处理本周期的数据(保存在头部的寄存器中),组合逻辑连接的是下一个流水级的寄存器,在下一个周期会将本周期的处理结果传输过去。

https://s2.loli.net/2022/10/02/gVGL3QueASjoiOH.png

由此我们了解了一个流水级的构成,一个在前面的寄存器,用于存放待处理的数据,和后面的组合电路用于处理数据。当然这只是很粗略的模型,比如后面的组合逻辑部分有时我们也会放置寄存器用于存储的中间结果。总之,前面一个存储待处理数据的电路,后面一个处理数据的电路,同时我们要求在处理完数据之前,存储的数据不能进行改变。

建立好模型之后,我们可以进行时间的分析了,我们假设流水级头部的寄存器都是在上升沿更新。如下图所示,我们通常用一个方块表示一个周期,在方块中写上字表示该条指令正在个部件中被处理。

https://www.cs.uaf.edu/2011/spring/cs641/proj1/acornachione/im2.png

这种形式的表示比较侧重与指令的执行情况,我们得竖着看,才能看到同一时刻各个部件的情况。

通常情况下,流水级寄存器的数据在方块一开始就更新,在方块的最后得到处理好的数据,然后在下一个周期送到下一个流水级的寄存器中。

明白了这个流程,我们就可以简要分析流水线停顿的过程了。流水线停顿只是逻辑上的停顿,实现上各个处理器中各个部件都是独立的,我们得对不同部件采取不同的操作才能形成流水线停顿的效果。比如在五级流水线中,我们要暂停ID流水级,如下图所示(胡体系结构的无旁路设计的流水线)

https://s2.loli.net/2022/10/02/Gl347Pb5VjptIrT.png

第二条指令暂停了ID流水级,那么此时对于ID来说,我们只要关闭ID的寄存器即可,这样ID要处理的数据一直没有发生变化,也就相当于一直停在这一个流水级了,对于ID前的一个流水IF来说,如果ID的寄存器关闭了,那么IF的处理结果永远无法送到,如果此时IF仍保持更新,就会丢失数据,所以IF也要被迫停顿,对于流水线来说,中间一个环节stall,该环节和前面的所有的流水级都要stall。对于ID后面的EX来说,下一个周期仍是要更新的,如果直接把ID处理好的数据送过去,结果应该是要出错的,我们也不能把EX的寄存器直接关了,因为这样永远无法解除stall,所以最后的策略是把一个特殊的数据送到EX的寄存器中,比如全为0,让EX阶段处理空数据,相当于在排空流水线,所以ID stall的时候EX啥的其实也stall了,不过在这个图中看的不是很明显(用时空图会明显一些)

小结一下,stall的时候,stall流水级之前一般关掉寄存器,跟着一起stall,stall流水级之后的输入空数据,排空流水。

顺序流水线

主要讨论一下异常处理问题

乱序流水线

流水线与模拟器

des