CPU设计实战:Loongarch版 实践任务9:前递技术解决相关引发的冲突

在实践任务9中要求我们减少阻塞,那么如何加入数据前递通路呢?

首先让我们了解一下什么是数据前递。

继续分析前面所举的例子,可以发现第2条指令位于译码阶段的时候,虽然它所需要的第1条指令的结果还不在寄存器中,但是这个值已经在流水线的执行阶段计算出来了,那么何必非要等着这个值沿着流水线一级一级送下去写入寄存器后再从寄存器中读出呢?直接把这个值取过来用不也是可行的吗?顺着这个思路就产生了流水线前递(Forwarding)技术。其具体实现是在流水线中读取指令源操作数的地方通过多路选择器直接把前面指令的运算结果作为后面指令的输入。考虑到加法指令在执行级就完成了运算,因此能够设计一条通路,将这个结果前递至读寄存器的地方,即有一条从执行级到译码级的前递通路。除此之外,还可以依次添加从访存级、写回级到译码级的前递通路。

通过前面对于指令相关的分析,我们需要在处理器中加入阻塞流水线的控制逻辑以及前递通路。

为了解决数据相关,加入了寄存器相关判断逻辑,收集当前流水线中处于执行、访存及写回级的最多3条指令的目的寄存器信息,与译码级的源寄存器比较,并根据比较结果决定是否阻塞译码级R1;为了解决控制相关,加入了译码级和执行级能够修改PC级有效位的通路;为了解决结构相关,加入了译码级到PC级的阻塞控制逻辑;为了支持前递,加入了从执行级、访存级到译码级的数据通路,并使用寄存器相关判断逻辑来控制如何前递。可以看出,大多数机制都加在了前两级流水线上。       

这是阻塞技术测试所用时间,下面是前递技术所用时间

可以看到,对于同一个测试文件exp9的话,使用前递技术是阻塞技术所用时间的46%

增加接口

ID
input  [110:0]forwarding
EX
    output [36:0]EX_to_ID_reg_result
MEM
    output [36:0]MEM_to_ID_reg_result
WB
    output [36:0]WB_to_ID_reg_result
mycpu——top
wire [36:0]EX_to_ID_reg_result;
wire [36:0]MEM_to_ID_reg_result;
wire [36:0]WB_to_ID_reg_result;
wire [110:0]forwarding;
assign forwarding = {EX_to_ID_reg_result,   //110:106EXreg
                                            //105:74EXdata
                     MEM_to_ID_reg_result,  //73:69MEMreg
                                            //68:37MEMdata
                     WB_to_ID_reg_result    //36:32WBreg
                                            //31:0 WBdata
                       };

之后就是代码逻辑的修改,要判断rj,rk,rd该使用哪个值,并且还要考虑优先级的问题,比如译码级时源操作数寄存器号与访存级别和执行级都相同,那么使用哪一级的值呢?

wire [2:0]forward_priority;//EX is 1st MEM is sec WB is 3rd
assign forward_priority[0] = (rd == forwarding[110:106] & same_rd) || (rj == forwarding[110:106] & same_rj) || (rk == forwarding[110:106] & same_rk);
assign forward_priority[1] = (rd == forwarding[73:69]   & same_rd) || (rj == forwarding[73:69]   & same_rj) || (rk == forwarding[73:69]   & same_rk);
assign forward_priority[2] = (rd == forwarding[36:32]   & same_rd) || (rj == forwarding[36:32]   & same_rj) || (rk == forwarding[36:32]   & same_rk);

assign rj_value = same_rj ?   (forward_priority[0] && (rj == forwarding[110:106])? forwarding[105:74] :
                              (forward_priority[1]  && (rj == forwarding[73:69])? forwarding[68:37]  : forwarding[31:0])
                                                      ) : rf_rdata1;
assign rkd_value = same_rd ? (forward_priority[0] && (rd == forwarding[110:106])? forwarding[105:74] :
                                          (forward_priority[1] && (rd == forwarding[73:69])? forwarding[68:37]  : forwarding[31:0])) : 
                   same_rk ? (forward_priority[0] && (rk == forwarding[110:106])? forwarding[105:74] :
                                          (forward_priority[1] && (rk == forwarding[73:69])? forwarding[68:37]  : forwarding[31:0])) :rf_rdata2;

最后还要考虑到阻塞控制的问题,是否还需要阻塞呢?答案是肯定的,比如指令序列“LW r2,0x0(r1);ADDU r4,r3,r2”,当LW处于执行级的时候,ADDU处于译码级,那么下一拍显然ADDU不能到达执行级,只有当LW通过访存级到译码级的数据通路之后ADDU才可以到达执行级。所以lw指令在译码级时需要阻塞一拍,当它到访存级才可以加载正确的值。所以也要考虑ready_go的值如何设置,当然现在block只有一个指令需要考虑,随着指令的增多,一定要考虑ready_go信号如何调整。

更为具体的技术方案以及错误请参考汪文祥工程师等人写的CPU设计实战。


做到后面在实现的时候发现时序超了,查看时序报告发现引入了一个延迟超长的组合逻辑路径,再回去看了看书,发现书上也有此问题解决方法(做的时候没好好读书啊呜呜呜),“分析发现矛盾在于转移指令进行前递,如果不是直接把ALU的组合逻辑输出结果前递给译码级的转移指令,而是将保存下来的ALU(在访存级缓存结果中)传递过去,也能消除关键路径。”按此方法修改便可以减小延迟,但是要注意什么情况下需要将转移指令阻塞一拍。


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值