自己动手写CPU_step5_移动指令

移动操作指令

`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设计的正确性。

下一步计划实现算数运算指令!

  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值