移动操作指令
`define EXE_MOVN 6'b001011 //不等于0转移 if rt != 0 then rs -> rd `define EXE_MOVZ 6'b001010 //等于0转移 if rt == 0 then rs -> rd `define EXE_MFHI 6'b010000 // hi -> rd `define EXE_MFLO 6'b010010 // lo -> rd `define EXE_MTHI 6'b010001 // rs -> hi `define EXE_MTLO 6'b010011 // rs -> lo
这些指令都是R型指令,即指令类型由FUNC字段决定,而OP操作码字段全0。
上面的MOVN和MOVZ指令和前篇的R型指令基本一致,只是多了判断条件。
下面的四条指令都涉及HILO寄存器,该寄存器用于保存乘法运算和除法运算的结果。
HILO寄存器实现
//特殊寄存器组,用于存储乘法的高32位和低32位或除法的商和余数 module hilo_reg( input clk, input rst, //写端口 input hi_wren_i, input [`RegDataBus] hi_reg_i, input lo_wren_i, input [`RegDataBus] lo_reg_i, output reg [`RegDataBus] hi_reg_o, output reg [`RegDataBus] lo_reg_o ); reg [`RegDataBus] HI; reg [`RegDataBus] LO; always @ (posedge clk) begin if (rst) begin HI <= 32'd0; end else if (hi_wren_i) begin HI <= hi_reg_i; end else begin HI <= HI; end end always @ (posedge clk) begin if (rst) begin LO <= 32'd0; end else if (lo_wren_i) begin LO <= lo_reg_i; end else begin LO <= LO; end end always @ (*) begin if (rst) begin hi_reg_o <= 32'd0; end else if (hi_wren_i) begin hi_reg_o <= hi_reg_i; end else begin hi_reg_o <= 32'd0; end end always @ (*) begin if (rst) begin lo_reg_o <= 32'd0; end else if (lo_wren_i) begin lo_reg_o <= lo_reg_i; end else begin lo_reg_o <= 32'd0; end end endmodule
该模块的输入与MEM_WB模块相连,输出接到EX模块。
数据相关问题
新加入的指令可能产生数据冲突:
mthi reg1 => hi IF ID EX MEM WB
mthi reg2 => hi IF ID EX MEM WB
mthi reg3 => hi IF ID EX MEM WB
mfhi hi => rd IF ID EX MEM WB 如果安正常的执行顺序,mfhi指令的EX阶段使用的hi寄存器值是第一条指令的结果,但是,这和我们实际需要的结果不同,我们需要的hi寄存器值应该是上一条指令产生的结果。所以,这里存在数据冲突,其实和之前译码部分的冲突是一个道理,这里我们也还是使用旁路技术解决这个冲突。即如果需要HILO寄存器的值,先判断有没有MEM阶段来的值,再判断有没有WB阶段来的值,如果都没有的话就是正常获取HILO寄存器的值,也即没有冲突。
EX阶段实现
//执行阶段,根据译码阶段得到的操作码和操作数进行运算,得到结果 module ex( input rst, input [`AluOpBus] aluop, input [`RegDataBus] reg1, input [`RegDataBus] reg2, input reg_wb_i, input [`RegAddrBus] reg_wb_addr_i, output reg reg_wb_o, output reg [`RegAddrBus] reg_wb_addr_o, output reg [`RegDataBus] reg_wb_data, //写回数据到目的寄存器 //HILO寄存器 input [`RegDataBus] hi_reg_i, //读取HI寄存器数据 input [`RegDataBus] lo_reg_i, //读取LO寄存器数据 output reg [`RegDataBus] hi_reg_o, //写入HI寄存器数据 output reg [`RegDataBus] lo_reg_o, //写入LO寄存器数据 output reg hi_wren, //HI寄存器写使能 output reg lo_wren, //LO寄存器写使能 // HILO寄存器旁路 input [`RegDataBus] wb_hi_i, input [`RegDataBus] wb_lo_i, input wb_hi_wren_i, //有指令写HI,从写回阶段给出旁路(隔一条指令) input wb_lo_wren_i, //有指令写LO,从写回阶段给出旁路(隔一条指令) input [`RegDataBus] mem_hi_i, input [`RegDataBus] mem_lo_i, input mem_hi_wren_i, //有指令写HI,从访存阶段给出旁路(上一条指令) input mem_lo_wren_i //有指令写LO,从访存阶段给出旁路(上一条指令) ); always @ (*) begin if (rst) begin reg_wb_o <= 1'd0; reg_wb_addr_o <= 5'd0; reg_wb_data <= 32'd0; hi_reg_o <= 32'd0; lo_reg_o <= 32'd0; hi_wren <= 1'b0; lo_wren <= 1'b0; end else begin reg_wb_o <= reg_wb_i; reg_wb_addr_o <= reg_wb_addr_i; reg_wb_data <= 32'd0; hi_wren <= 1'b0; lo_wren <= 1'b0; hi_reg_o <= 32'd0; lo_reg_o <= 32'd0; case (aluop) `EXE_ORI_OP,`EXE_OR_FUNC: begin reg_wb_data <= reg1 | reg2; end `EXE_ANDI_OP,`EXE_AND_FUNC: begin reg_wb_data <= reg1 & reg2; end `EXE_XORI_OP,`EXE_XOR_FUNC: begin reg_wb_data <= reg1 ^ reg2; end `EXE_LUI_OP: begin reg_wb_data <= {reg2[15:0],reg2[31:16]}; end `EXE_NOR_FUNC: begin reg_wb_data <= ~(reg1 | reg2); end `EXE_SLL_FUNC,`EXE_SLLV_FUNC: begin reg_wb_data <= reg2 << reg1[4:0]; end `EXE_SRL_FUNC,`EXE_SRLV_FUNC: begin reg_wb_data <= reg2 >> reg1[4:0]; end `EXE_SRA_FUNC,`EXE_SRAV_FUNC: begin //算术移位也可以直接使用>>> reg_wb_data <= ({32{reg2[31]}} << (6'd32 - {1'b0,reg1[4:0]})) | reg2 >> reg1[4:0]; end `EXE_MOVN_FUNC,`EXE_MOVZ_FUNC: begin reg_wb_data <= reg1; end `EXE_MFHI_FUNC: begin if (mem_hi_wren_i) begin //访存阶段数据旁路 reg_wb_data <= mem_hi_i; end else if (wb_hi_wren_i) begin //写回阶段数据旁路 reg_wb_data <= wb_hi_i; end else begin reg_wb_data <= hi_reg_i; //正常读取HI寄存器 end end `EXE_MFLO_FUNC: begin if (mem_lo_wren_i) begin //旁路 reg_wb_data <= mem_lo_i; end else if (wb_lo_wren_i) begin //旁路 reg_wb_data <= wb_lo_i; end else begin //正常读取LO寄存器 reg_wb_data <= lo_reg_i; end end `EXE_MTHI_FUNC: begin hi_wren <= 1'b1; hi_reg_o <= reg1; lo_reg_o <= lo_reg_i; end `EXE_MTLO_FUNC: begin lo_wren <= 1'b1; lo_reg_o <= reg1; hi_reg_o <= hi_reg_i; end default: begin reg_wb_o <= 1'd0; reg_wb_addr_o <= 5'd0; reg_wb_data <= 32'd0; hi_reg_o <= 32'd0; lo_reg_o <= 32'd0; hi_wren <= 1'b0; lo_wren <= 1'b0; end endcase end end endmodule
模块修改
在EX_MEM、MEM、MEM_WB模块中添加由对应的HILO寄存器的值和写使能信号传导至HILO寄存器。最后修改顶层模块,将MEM阶段、WB阶段以及HILO寄存器的输出值都连接到EX的输入即可。
仿真结果
本次仿真使用了16条指令:
3c010000:LUI 将立即数低位扩展至reg1 reg1:00000000
3c02ffff: LUI 将立即数低位扩展至reg2 reg2:ffff0000
3c030505:LUI 将立即数低位扩展至reg3 reg3:05050000
3c040000:LUI 将立即数低位扩展至reg4 reg4:00000000
0041200a:MOVZ if (reg1==0) reg4 = reg2 reg4:ffff0000
0061200b:MOVN if (reg1!=0) reg4 = reg3 reg4:ffff0000
0062200b:MOVN if (reg2!=0) reg4 = reg3 reg4:05050000
0043200a:MOVZ if (reg3!=0) reg4 = reg2 reg4:05050000
00000011:MTHI reg0(全0) => hi hi:00000000
00400011:MTHI reg2 => hi hi:ffff0000
00600011:MTHI reg3 => hi hi:05050000
00002010:MFHI hi => reg4 reg4:05050000
00600013:MTLO reg3 => lo lo:05050000
00400013:MTLO reg2 => lo lo:ffff0000
00200013:MTLO reg1 => lo lo:00000000
00002012:MFLO lo => reg4 reg4:00000000上述指令可以测试是否消除数据相关。
仿真结果可知,所有指令的运行结果都与预期的一致。
验证了CPU设计的正确性。
下一步计划实现算数运算指令!