增添指令 注意事项:
注意必须在每一级的WriteBack
和RegAddr
检查回写的内容和地址是否符合新增指令的期望,防止在上游产生的正确回写数据在下游被覆盖!
注意务必为新增指令添加阻塞板块Tuse
Tnew
的取值。
由于特判信号是随着指令在流水线中流动的,所以其后也要加_DEin
这样的后缀。
注意默认拓展是0拓展,要进行符号拓展需要将新增指令的特判信号传入EXTOp
。
运算类 (1) 运算 + 判断 + 回写(地址确定)
P4T1添加指令为 eam
,R型指令。具体操作为,取出 GPR[rs]
的低16位 ,作为一个有符号数 对17取模,结果必须为0~16 的某一个非负数,结果为一个16位的数。之后,若 GPR[rt]
的最高位为1,则将该运算结果零扩展至32位,否则将该运算结果一扩展 至32位。将结果存至 GPR[rd]
中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 controller wire eam = (opcode_DEin == 6'b0 && func_DEin == 6'b111110 ) ? 1 : 0 ;RegWrite_DEin = (eam || ...) ? 1 : 0 ; RegDst_DEin = (eam || ...) ? 2'b01 : ... wire isEam_DEin = (eam) ? 1 : 0 ;hazardStall wire eam = (opcode_DEin == 6'b0 && func_DEin == 6'b111110 ) ? 1 : 0 ;Tuse_RS = (eam || ...) ? 0 : ... Tuse_RT = (eam || ...) ? 0 : ... Tnew_DEin = (eam || ...) ? 1 : ... mips wire [15 :0 ] eam_low16 = A1_DEin[15 :0 ];wire [15 :0 ] eam_ans16 = (($signed (eam_low16) % $signed (16'd17 )) + $signed (16'd17 )) % $signed (16'd17 );wire [31 :0 ] eam_ans32 = (A2_DEin[31 ] == 1 ) ? {16'b0 , eam_ans16} : {16'b1 , eam_ans16};assign WriteBack_DEin = (isEam_DEin == 1 ) ? eam_ans32 : ... assign WriteBack_EMin = (isEam_DEout === 1 ) ? WriteBack_DEout : ...assign WriteBack_MWin = (MemtoReg_EMout == 1 ) ? WD : WriteBack_EMout;
(2)计算 + 回写
P5T1的指令为addoi
,行为是将16位立即数符号拓展或加上GRF[rs],将这个加和的相反数的原码存入rt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 controller wire addoi = (opcode_DEin == 6'b111110 ) ? 1 : 0 ;RegWrite_DEin = (addoi || ...) ? 1 : 0 ; ALUSrc_DEin = (addoi || ...) ? 1 : 0 ; wire isAddoi = (addoi) ? 1 : 0 ;HazardStall wire addoi = (opcode_DEin == 6'b111110 ) ? 1 : 0 ;Tuse_RS = (addoi || ...) ? 1 : ... Tuse_RT = (addoi || ...) ? `maxTuse : ... Tnew_DEin = (addoi || ...) ? 2 : ... module origin( input [31 :0 ] A_in; output [31 :0 ] A_out; ); reg [31 :0 ] hold;reg [31 :0 ] temp;integer i;always @(*) begin if (A_in > 32'h0 ) begin hold = A_in; end else if (A_in < 32'h0 ) begin temp = A_in - 32'h1 ; for (i = 0 ; i < 31 ; i = i + 1 ) begin hold[i] = !temp[i]; end hold[31 ] = 1 ; end else begin hold = 32'h0 ; end end assign A_out = hold; endmodule mips wire [31 :0 ] addoi_neg = 32'h0 - ALU_ans;wire [31 :0 ] addoi_ans; assign WriteBack_EMin = (isAddoi) ? addoi_ans : ...
跳转类 (1)判断 + 回写(地址不确定) + 跳转
P4T2添加指令为 cptl
,I型指令 (注意,这个不是J型指令 ,跳转方式和beq
类似)。具体操作为,若 GPR[rs]
的低18位的二进制表示中存在连续6个1,则将PC+8存于 GPR[rt]
中,否则将PC+8存于 GPR[31]
中。之后,把PC的值无条件修改 为PC + 4 + {sign_ext}(offset<<2 || 0^2)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 controller wire cptl = (Opcode_DEin == 6'b111110 ) ? 1 : 0 ;RegWrtie_DEin = (cptl || ...) ? 1 : 0 ; RegDst_DEin = (cptl || ...) ? 2'b10 : ... Branch_DEin = (cptl || ...) ? 1 : 0 ; Zero_DEin = (cptl || ...) ? 1 : 0 ; wire isCptl_DEin = (cptl) ? 1 : 0 ;hazardStall wire cptl = (Opcode_DEin == 6'b111110 ) ? 1 : 0 ;Tuse_RS = (cptl || ...) ? 0 : ... Tuse_RT = (cptl || ...) ? `maxTuse : ... Tnew_DEin = (cptl || ...) ? 1 : ... check( input [31 :0 ] A1_DEin, output yes ); mips assign WriteBack_DEin = (isCptl_DEin == 1 ) ? pc_FDout + 32'h8 : ...assign RegAddr_DEin = (isCptl_DEin == 1 && yes == 1 ) ? rt_DEin : ... assign WriteBack_EMin = (isCptl_DEin == 1 || ...) ? WriteBack_DEout : ...assign RegAddr_EMin = (isCptl_DEin == 1 || ...) ? RegAddr_DEout : ...assign WriteBack_MWin = (isCptl_DEin || ...) ? WriteBack_EMout : ...assign RegAddr_MWin = (isCptl_DEin || ...) ? RegAddr_EMout : ...
访存类 (1)访存 + 判断 + 回写(地址不确定)
P5T3添加指令为 lwoc
,I型指令。具体操作为,从DM中读取起始地址为 GPR{base} + {sign_ext}(offset<<2 || 0^2) 的一整个字,若该结果小于无符号32位整数 0x80000000
,则取该结果的低4位进行零扩展后作为写入 GRF
的地址,否则取指令中的 rt
为写入 GRF
的地址,写入数据均为该结果。
(acknowledgement:Lazyfish)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 controller wire lwoc = (opcode_DEin == 6'b111110 ) ? 1 : 0 ;RegWrite_DEin = (lwoc || ...) ? 1 : 0 ; RegAddr_DEin = (lwoc || ...) ? 2'b00 : ... ALUOp_DEin = (lwoc || ...) ? 2'b00 : .... MemRead_DEin = (lwoc || ...) ? 1 : 0 ; MemtoReg_DEin = (lwoc || ...) ? 1 : 0 ; wire isLwoc_DEin = (lwoc) ? 1 : 0 ;hazardStall wire lwoc = (opcode_DEin == 6'b111110 ) ? 1 : 0 ;Tuse_RS = (lwoc || ...) ? 1 : 0 ; Tuse_RT = (lwoc || ...) ? `maxTuse : ... Tnew_DEin = (lwoc || ...) ? 3 : ... mips wire smaller = (WD < 32'h80000000 ) ? 1 : 0 ;wire [4 :0 ] low4_zeroExt = {1'b0 , WD[3 :0 ]};RegAddr_MWin = (isLwoc_EMout == 1 && smaller == 1 ) ? low4_zeroExt : RegAddr_EMout;
回写地址不确定 事实上,我们可以将回写地址不确定分为两类,第一种是必须要使用存储器中数据的,如上面所说的lwoc
那样,另一种是在M级之前就能根据计算判断回写地址的。例如:
将GRF[rt]
和GRF[rs]
做加法,和是奇数写入rd
,否则写入31号寄存器。
像这样的题目,事实上,我们总能通过在D级内实现一个组合逻辑便能得到正确回写位置(这时需要将Tues_RT
和Tuse_RS
设为0,并把Tnew_DEin
设置为1)。
我们也可以把判断的事情交给E级,这样就需要处理转发正确性的问题。
如果我们在controller
中总是默认写入rd
寄存器,则我们只需要考虑实际写入31号寄存器且下一条指令正好使用到了31号寄存器的问题。
1 2 wire stall_E = (isxxx_DEout == 1 && (rt_DEin == 31 || rs_DEin== 31 )) ? 1 : 0 ;wire stall_M = (isxxx_EMout == 1 && RegAddr_MWin == 31 && (rt_DEin == 31 || rs_DEin == 31 )) ? 1 : 0 ;
原码反码与补码
原码:原码是一种简单的机器数表示法,最高位为符号位,用 0
表示正数,用 1
表示负数,其余位表示数值的绝对值。
对于十进制数 +5
,若用 8 位二进制原码表示,其原码为 00000101
。其中最高位 0
表示正数,后面的 0000101
表示数值 5
的二进制形式。
对于十进制数 -5
,8 位二进制原码为 10000101
,最高位 1
表示负数,后面同样是 5
的二进制表示形式。
反码:对于正数,其反码与原码相同;对于负数,反码是在原码的基础上,除符号位外,其余各位按位取反(即 0 变 1,1 变 0)得到的。
十进制数 +5
的 8 位二进制反码依然是 00000101
,因为正数的反码和原码一致。
十进制数 -5
的原码是 10000101
,那么它的 8 位二进制反码则是 11111010
。先保留符号位 1
,然后将原码中除符号位之外的 0000101
按位取反得到 11111010
。
补码:对于正数,其补码与原码、反码相同;对于负数,补码是在反码的基础上再加 1
得到的。
十进制数 +5
的 8 位二进制补码还是 00000101
。
十进制数 -5
的反码是 11111010
,那么它的 8 位二进制补码就是 11111011
,即反码 11111010
再加 1
得到。
减法溢出 考虑一个大的负数减去一个正数,如:
A - B = 0x80000000 - 0x00000001
减去B就等于加B的相反数的补码(按位取反再加1)-1的补码为0xffffffff。
1_10000000_00000000_00000000_00000000 + 1_11111111_11111111_11111111_11111111 = 11_01111111_11111111_11111111_11111111
temp[32] != temp[31] 溢出
PS:感谢lazyfish
提供的题目(传送门:lazyfish-lc.github.io)