使用Verilog语言实现了一个五级流水线的RISC-V架构CPU。
经典的CPU五级流水线分为取指令(IF)、译码(ID)、执行(EX)、访问存储器(MEM)、写回寄存器(WB)五个阶段。采用这种流水线的划分可以提升处理器硬件资源的利用率,同一时间流水线最多可以操作五条指令,将极大地提升运算效率。
对于算术、逻辑指令来说,流程较为简单且具有代表性:在第一个时钟周期,指令从外部指令存储器送到取指模块;接着在译码阶段被判断其为何种指令并提取立即数,以及/或读取寄存器中的值;接下来将相关结果送至执行阶段,执行阶段会根据指令类型进行加减、与或等操作运算;然后信息会送至访存阶段,因为算术和逻辑指令不涉及外部存储器的读写,所以在访存阶段不对信息进行有效操作便将其在下一个周期传递至回写阶段将所得结果写回目标寄存器。
对于条件分支指令来说(例如BGE指令,将寄存器rs1和寄存器rs2的值进行比较,如果rs1的值大于等于rs2便跳转,反之则不跳转,跳转的地址为当前地址加上由立即数构成的偏移量),我们在取指阶段就提取其立即数并对其进行判断,通过立即数的最高位为1还是0(即其偏移量为正还是负)进行预测,这是因为在实际编译情况中,向后跳转的指令出现的概率更大。
这种预测方式为BTFN(Back Taken, Forward Not taken)预测,静态预测。此预测方式虽然在准确率上较复杂的动态分支预测可能有所降低,但其所使用的硬件资源以及所需要消耗的功率都非常小。我们会在下一个时钟周期对预测正确与否进行判断,如果预测正确则继续正常执行,如若预测错误则冲刷掉错误执行的指令并重新执行正确的指令。
在RISC-V文档中指出,用静态分支预测技术来代替指令槽结构体现了RISC-V简化硬件的哲学。
对于加载、存储指令,RISC-V支持字(word)、半字(half-word)以及字节(byte)的读写操作。加载、存储指令的流程与算术、逻辑指令相类似,但与之不同的是,加载、存储指令会在访存(MEM)阶段进行存储器的访问操作。相关的地址、数据及控制信号会在译码及执行阶段得到并被送到访存阶段。Load类指令在访存阶段访问数据存储器获得操作数,在回写阶段将操作数写入寄存器,完成取数;Store类指令在访存阶段将操作数写入数据存储器,完成存数。
在CPU内添加了一个ctrl模块对流水线的暂停及冲刷进行控制。ctrl模块会将各种各样的暂停信号输送到流水线各阶段。从而对流水线进行控制。
本CPU中通过构造一个csr_reg模块来储存和维护RISC-V指令架构定义的一些控制和状态寄存器(CSR),并借此来实现与CSR相关的指令。
流水线的引入大大提升了CPU的运算效率,但同时也会带来数据冲突问题。数据冲突是指不同的指令之间的操作数存在着数据相关性造成的冲突。我们的流水线结构中存在着RAW(Read-After-Write)先写后读相关性,即“后续执行的指令需要读取的源操作数寄存器索引”与“前序执行的指令需要写回的结果寄存器索引”相同造成的数据相关性。我们的CPU采用了数据前推的方法对此进行解决。举例:将处于流水线执行或访存阶段的运算结果,送到译码阶段。
哪天有时间,把源码整理一下发上去…