蜂鸟E203的交付与写回机制
在经典的五级流水线模型中并没有交付的概念,在这里交付(Commit)指的是该指令不再是预测执行(Speculative)状态,而是被判定为可以真正地在处理器中被执行
交付的反义词就是“取消”(Cancel),表示该指令最后被判定为需要取消
如果处理器流水线需要将没有交付的后续指令全部取消时,就会导致“流水线冲刷”的产生,下面依次来介绍
交付与流水线冲刷
通常情况下交付都是顺序判定,理论上只有前一条指令完成交付之后才会轮到后一条指令交付
以下因素会映像指令交付:
中断、异常、分支预测指令
它们往往会导致流水线冲刷,将后续所有预取的指令都取消掉
条件码
在一些指令集架构(比如典型的ARM架构)中,对于每条指令,只有其条件码满足条件为真才能真正交付,否则就会被取消,这里的取消只是取消它自己,并不会产生流水线冲刷
根据处理器性能不同,可以选择低性能的一个周期交付一条指令或者高性能的一个周期交付多条指令;并且交付的位置可以有所不同,常见的方案如下:
执行阶段交付
在执行阶段将分支预测指令的结果解析完成并进行交付
写回阶段交付
由于有些指令需要多个周期的执行以后才能写回,并且可能产生错误异常,所以有些微架构将交付放在写回阶段
重排序交付队列(Re-Order Commit Queue)
对于高性能的超标量处理器而言,往往是乱序执行乱序写回,在写回阶段往往使用ROB或纯物理寄存器的方式,同时会配备一个较深的重排序交付队列来缓存乱序执行的指令信息,并对其进行按序交付
对于RISC-V而言,指令没有条件码、所有运算指令都不会产生异常这两个固有特点大幅简化了交付的硬件实现
在RISC-V的处理器核中只需要处理
- 分支预测指令错误预测造成的后续指令流取消
- 中断和异常造成的后续指令流取消
蜂鸟E203的交付实现
蜂鸟E203处理器将交付安排在执行阶段,并且可以保证:只要前序的指令没有发生分支预测错误、中断或异常就可以判定该指令能够被成功交付。对于分支预测错误的指令自身和遭遇了中断或者异常的指令自身而言,仍然是属于成功交付的指令,因为他们自身已经被真正执行并对处理器状态真正地产生了影响
对于分支预测指令,蜂鸟E203使用IFU中进行预测的分支指令为带条件跳转指令类型,有以下几个条件:
beq:两个整数操作数相等则跳转
bne:两个整数不相等则跳转
blt:有符号数小于则跳转
bltu:无符号数大于则跳转
bge:有符号数大于则跳转
bgeu:无符号数大于则跳转
这些跳转条件的决定由ALU完成,所以具体的跳转操作是在ALU之后完成的
相关代码位于rtl/e203/core/e203_exu_alu_bjp.v
ALU在计算出结果后会发送给交付模块,交付模块根据预测结果和真实结果进行判断,如果预测和真实的结果相符则预测成功,不会进行流水线冲刷,否则就进行流水线冲刷
相关模块位于rtl/e203/core/e203_exu_commit.v和e203_exu_branchslv.v(这是e203_exu_commit的子模块,用于对分支预测指令 的结果进行判断)
对于多周期的长指令,交付同样在执行阶段完成,但是写回则需要在后续的周期内进行,并且对于特殊的长指令在写回时产生的错误异常会被当作异步异常进行处理,所以说并不会引起不必要的流水线冲刷
写回与写回仲裁
蜂鸟E203采用了因地制宜的混合策略,兼顾了面积最小化的原则和较好的性能,核心思想就是“分类讨论”:将指令划分为单周期指令和长指令,将长指令的交付和写回分开,使得即使执行了多周期长指令,仍然不会阻塞流水线。
写回部分主要由最终写回仲裁、长指令写回仲裁和OITF组成
最终写回仲裁
位于rtl/e203/core/e203_exu_wbck.v
E203具有两级写回仲裁模块,第一个就是最终写回仲裁模块(Final Write-Back Arbitration,FWBA)
该模块主要用于仲裁所有来自ALU的单周期指令的写回和所有来自长指令写回仲裁模块的长指令的写回
也就是说FWBA用于所有指令最终写回的判断
仲裁采用优先级仲裁的方式,由于长指令的写回比正在协会的ALU指令在程序流中处于更早的位置,长指令就具有更高的写回优先级,这就导致了以下情形发生:如果在长指令完成执行准备写回时,有单周期指令正在写回,它会被“打断”(指的是在上一条写回完成后不会继续写回下一条单周期指令,而是会转而写回长指令),长指令得到写回;如果在没有长指令写回的空闲周期,来自ALU的单周期指令则可以随便写回,这也就意味着在程序流中处于更迟位置的单周期指令可以比更早位置的长指令先写回寄存器组。这就是的蜂鸟E203处理器具有乱序写回的能力
代码片段如下:
1 | module e203_exu_wbck( |
长指令写回仲裁与OITF
E203具有两级写回仲裁模块,第二个就是长指令写回仲裁模块(Long-Pipes Instructions Write-Back Arbitration,LPIWBA)
OITF和长指令写回仲裁模块协同合作完成所有长指令的写回操作,长指令写回仲裁主要用于仲裁不同长指令之间的写回,因为这些指令来自不同执行单元、执行的周期数不同、执行的顺序不同、写回的地方不一样,就需要记录这些指令的先后关系,这就用到了OITF
OITF在之前的执行部分已经介绍过,它本质上是一个记录还未写回但是已经在执行的长指令的FIFO。每个被派遣的长指令都会在OITF中分配一个表项(Entry),这个表项的FIFO指针就作为这个长指令的ITAG,长指令不管被派遣到任何运算单元都会携带这个ITAG,同时写回时也要带着相同的ITAG
OITF的深度就决定了能够派遣的滞外(Outstanding,也就是OITF中的“O”)长指令的个数。为了硬件实现的简洁,蜂鸟E203采用严格按照OITF的顺序写回到方法——OITF的读指针会指向最先进入此FIFO的表项,通过使用此读指针作为长指令写回仲裁的选择参考,就可以保证不同长指令的写回顺序和派遣顺序严格一致。
每次长指令写回仲裁模块成功写回一个长指令后,对应地OITF表项就被从FIFO中退出了
由于有些长指令可能发生执行错误,因此需要产生异常——长指令写回仲裁模块会和交付模块产生接口触发异常,如果长指令产生异常,则不会真正写回,而是在接口部分就被丢弃。
有关FWBA的代码部分见上面的源码
长指令写回仲裁和OITF部分的源码位于rtl/e203/core/e203_exu_disp.v、rtl/e203/core/e203_exu_oitf.v、rtl/e203/core/e203_exu_longwbck.v三个文件
这里直接复制粘贴了全部源码,推荐系统地看一下三个文件的代码来更好地理解实现思路
1 | /* e203_exu_disp */ |
综上所述,蜂鸟E203的执行结构是一种混合的策略:
- 单周期指令:顺序发射、顺序执行、顺序写回
- 长指令:顺序发射、乱序执行、顺序写回
- 所有指令混杂:顺序发射、乱序执行、乱序写回
在其中最核心的思想就是取得“更高的性能-面积比”,这套解决思路还是比较巧妙的