移动操作指令
`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) beginif (rst) beginHI <= 32'd0;end else if (hi_wren_i) beginHI <= hi_reg_i;end else beginHI <= HI;endendalways @ (posedge clk) beginif (rst) beginLO <= 32'd0;end else if (lo_wren_i) beginLO <= lo_reg_i;end else beginLO <= LO;endendalways @ (*) beginif (rst) beginhi_reg_o <= 32'd0;end else if (hi_wren_i) beginhi_reg_o <= hi_reg_i;end else beginhi_reg_o <= 32'd0;endendalways @ (*) beginif (rst) beginlo_reg_o <= 32'd0;end else if (lo_wren_i) beginlo_reg_o <= lo_reg_i;end else beginlo_reg_o <= 32'd0;endendendmodule
该模块的输入与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 @ (*) beginif (rst) beginreg_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 beginreg_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: beginreg_wb_data <= reg1 | reg2;end`EXE_ANDI_OP,`EXE_AND_FUNC: beginreg_wb_data <= reg1 & reg2;end`EXE_XORI_OP,`EXE_XOR_FUNC: beginreg_wb_data <= reg1 ^ reg2;end`EXE_LUI_OP: beginreg_wb_data <= {reg2[15:0],reg2[31:16]};end`EXE_NOR_FUNC: beginreg_wb_data <= ~(reg1 | reg2);end`EXE_SLL_FUNC,`EXE_SLLV_FUNC: beginreg_wb_data <= reg2 << reg1[4:0];end`EXE_SRL_FUNC,`EXE_SRLV_FUNC: beginreg_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: beginreg_wb_data <= reg1;end`EXE_MFHI_FUNC: beginif (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 beginreg_wb_data <= hi_reg_i; //正常读取HI寄存器endend`EXE_MFLO_FUNC: beginif (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;endend`EXE_MTHI_FUNC: beginhi_wren <= 1'b1;hi_reg_o <= reg1;lo_reg_o <= lo_reg_i;end`EXE_MTLO_FUNC: beginlo_wren <= 1'b1;lo_reg_o <= reg1;hi_reg_o <= hi_reg_i;enddefault: beginreg_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;endendcaseendendendmodule
模块修改
在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设计的正确性。
下一步计划实现算数运算指令!