P5课上三题有明显得规律,第一题一般只涉及ALU的修改,第二题是b类型的跳转指令,第三题稍微难一点,涉及访存以及对转发阻塞逻辑的修改。下面我们结合题目详细说明一下解题思路和步骤。
CCC
opcode = 忘了
tmp1 = GPR[rs]的前导零 |
Answer:在ALU把tmp1和tmp2的值计算出来,并通过ALUOp控制ALU输出不同的结果。在CTRL模块新增指令,使得该指令写入GPR[rd],并且写入数据为ALU输出结果。在Stall模块中Tnew和Tuse无脑跟add保持一致即可。
BLONEZ?(应该吧)
opcode = 111111
tmp = GPR[rs]前导1 |
这个是考场题目,但是比较简单,大家感兴趣可以看一看,我们用一道推荐题来说明这类型题目的解决方法。
BLZTAL
opcode = 100111
看题:
Answer:对于b型指令,我们要在D_CMP模块中判断b_jump,如果是条件链接(像这道题),我们就要将D_CMP模块中的b_jump指令随着流水线一直流水下去,从而满足每一级的CTRL判断GRFA3sel和GRFWDsel信号的需求;如果是无条件链接(像BLONEZ),那CTRL直接生成控制信号写入31号寄存器,写入值是PC + 8即可。在STALL模块Tuse和Tnew与Beq保持一致。
对于链接类指令Tuse,Tnew参照jal就行
特别注意一下PC的变化基数是D_PC还是F_PC。
还有一种情况可能会涉及清空延迟槽的指令,像这样:

为什么要清空延迟槽呢,我理解就是不想执行跳转类指令的下一条指令了,把他用Flush清除掉。具体实现需要在D_CMP增加一个接口Flush_checkassign Flush_check = (CMPOp == `CMP_BONALL && !B_jump);,在mips中将这一接口与D_reg_flush**相连,assign D_reg_flush = (!stall && D_flush_check);值得注意的是,我们要保证此时流水线不是阻塞状态.如果是阻塞状态我们就等到它阻塞结束再清空,想想为什么呢?因为如果流水线阻塞在D级的话,D级放的其实是跳转类指令,相当于是把自己送走了**,后面的流水级就丢失了这条指令。
LBGET
opcode = 110101

Answer:这是写入地址可选择类condition: GPR[rt] <- RD; else: GPR[rs] <- RD,我们只有在DM完成计算才能知道要写入哪个寄存器,那么D,E,(M)就都没GRFA3的视野,因此难以判断冲突条件是否成立。此时我们有两个选择:暴力阻塞;条件阻塞。
暴力阻塞:只要D级指令要访问GPR,并且访问地址非0,并且Tuse < Tnew,我们就阻塞。对应代码实现
assign E_stall_rs = ((E_lbget ? 1 : (E_GRFA3 == D_rs)) && D_rs != 0) && (rs_Tuse < E_Tnew); |
条件阻塞:D级指令要访问GPR,并且访问地址非0,并且访问地址和当前级流水线的 rs 或 rt 冲突,并且 Tuse < Tnew,我们阻塞。对应代码实现。
assign E_stall_rs = ((E_lbget ? (D_rs == E_rt || D_rs == E_rs) : (E_GRFA3 == D_rs)) && D_rs != 0) && (rs_Tuse < E_Tnew); |
以上是两种阻塞方式,暴力阻塞可能会超时,条件阻塞也可能比测评机要求的周期少(可能是跑的太快了😥)。接着还有两种译码位置,M级译码和W级译码
W级译码:对于判断条件Condition,我们不在DM进行计算,而是在顶层模块mips中进行判断,DM只需要更改DMOp从而输出判断需要的数据即可。这里你可能会发现,如果我要写入的数据来自DM并且和我判断需要的数据冲突了怎么办呢,那就只好再给DM开一个端口往后传判断需要的数据了。在W级判断出condition后传到w_ctrl,对W_GRFA3判断。显然,在这种情况下,D,E,M级都没有GRFA3的视野所以E_Tnew = 3,M_Tnew = 2,并且条件阻塞写法如下:
//Stall |
这样就可以得到正确的运算结果,但是不难看出,这并不是最优解,我们在M级其实就可以拿到GRFA3,这样就可以删除M级冲突判断的不确定性,从而减少阻塞的发生,提高CPU性能。
至于为什么会提到这种在W级译码的方式,21级的很多学长似乎采用了这种做法,优点是当condition判断需要的数据和DM输出的数据相同时,可以对电路做最小的改动实现功能,缺点也很明显运行效率较低并且当condition判断需要的数据和DM输出的数据不相同时需要新增接口。对了,笔者在测评机提交这种方法时出现了一个测试点TLE,所以这种方法大家听个思路,考场慎用吧。
M级译码:对于判断条件condition,我们在DM计算出并且随流水线流到W级,这样M级流水线寄存器就可以较早地拿到GRFA3。
assign M_GRFa3 = (M_GRFA3_sel == `GRFA3rt) ? M_rt : |
在Stall模块中因为要判断M_GRFA3,所以需要将condition信号传入Stall
module STALL ( |
M_GRFA3已知,所以E_Tnew = 2 ,M_Tnew = 1,并且条件阻塞判断如下:
//Stall |
注意,关于GRFA3的译码,如果按照笔者个人的习惯,会将condition传入CTRL模块完成GRFA3_Sel译码,但是我们发现在M级,
m_ctrl(.Condition(condition),.DMOp(DMop)) ; |
ctrl和dm在交互,这就导致了输出信号会是XXXXXXXX,所以我们选择在外部译码。虽然我也不知道这么干为什么不行。。。
CCS
这个是考试题
high_ones = memword[31:16] 1 的个数 |
这道题目和上一题的区别在于GPR[high_ones]直到M级才知道,因此冲突判断直接无脑置1即可
assign E_stall_rs = ((E_lbget ? 1 : (E_GRFA3 == D_rs)) && D_rs != 0) && (rs_Tuse < E_Tnew); |
剩下的操作跟LBGET一样,大家依葫芦画瓢应该问题不大。
写在最后
笔者的个人仓库里有P5_pro,实现了BLZTAL,LBGET,BONALL,大家如果真的对自己的bug毫无想法可以参考一下哦😋。
Lyrics Sharing
你是我的眼 |