当前位置: 首页> 娱乐> 八卦 > 自己动手写CPU_step6.1_算数运算指令

自己动手写CPU_step6.1_算数运算指令

时间:2025/8/7 19:49:50来源:https://blog.csdn.net/qq_44830822/article/details/141820543 浏览次数:0次

接上篇的加减指令,本篇主要实现CLZ、CLO、SLT等指令。

CLZ:从最高位开始数0的个数直到遇到1。

        例:0x0000,0001        CLZ指令结果:31        0x8000,ffff        CLZ指令结果是0

CLZ:从高位开始数1的个数直到遇到0。

        例:0xffff,0000        CLO指令结果:16        0x0000,ffff        CLO指令结果是0


修改ID模块

主要修改部分:

end else if (op == `EXE_SPECIAL2) begin     //由FUNC字段决定,操作码为011100的指令reg1_rden   <=  1'd1;reg2_rden   <=  1'd1;reg_wb      <=  1'd1;case (func)`EXE_CLZ:   beginaluop   <=  `EXE_CLZ_FUNC;end`EXE_CLO:   beginaluop   <=  `EXE_CLO_FUNC;enddefault:    beginaluop   <=  7'd0;endendcase

整体代码:

`include "defines.v"
//译码阶段  对if_id传入的指令进行译码,分离出操作数和操作码
module id(input                           rst,input [`InstAddrBus]            pc,input [`InstDataBus]            inst,//读通用寄存器        读取        input [`RegDataBus]             reg1_data,input [`RegDataBus]             reg2_data,output reg                      reg1_rden,output reg                      reg2_rden,output reg [`RegAddrBus]        reg1_addr,  //源操作数1的地址output reg [`RegAddrBus]        reg2_addr,  //源操作数2的地址//送到ex阶段的值output reg [`RegDataBus]        reg1,       //源操作数1 32boutput reg [`RegDataBus]        reg2,       //源操作数2 32boutput reg                      reg_wb,     //写回目的寄存器标志    output reg [`RegAddrBus]        reg_wb_addr,//写回目的寄存器地址output reg [`AluOpBus]          aluop,      //操作码    //相邻指令的冲突,由EX阶段给出数据旁路input                           ex_wr_en,   //处于执行阶段的指令是否要写目的寄存器 input [`RegDataBus]             ex_wr_data, input [`RegAddrBus]             ex_wr_addr,  //相隔一条指令的冲突,由MEM阶段给出数据旁路input                           mem_wr_en,  //处于访存阶段指令是否要写目的寄存器input [`RegDataBus]             mem_wr_data,input [`RegAddrBus]             mem_wr_addr);wire [5:0] op = inst[31:26];         //从指令中获取操作码   高6位
wire [5:0] func = inst[5:0];        //从指令中获取功能号确定指令类型   低6位
wire [4:0] shmat = inst[10:6];      //部分移位位数不从寄存器取值,直接由shmat给出
reg [`RegDataBus] imm;              //立即数always @ (*) beginif (rst) beginreg1_rden       <= 1'd0;reg2_rden       <= 1'd0;reg1_addr       <= 5'd0;reg2_addr       <= 5'd0;imm             <= 32'd0;reg_wb          <= 1'd0;reg_wb_addr     <= 5'd0;aluop           <= 7'd0;end else beginreg1_rden       <= 1'd0;reg2_rden       <= 1'd0;reg1_addr       <= inst[25:21];         //默认从指令中读取操作数1地址reg2_addr       <= inst[20:16];         //默认从指令中读取操作数2地址imm             <= 32'd0; reg_wb          <= 1'd0;reg_wb_addr     <= inst[15:11];         //默认结果地址寄存器rd        aluop           <= 7'd0;                //操作类型if (op == `EXE_SPECIAL) beginreg1_rden   <= 1'd1;reg2_rden   <= 1'd1;reg_wb      <= 1'd1;case (func) `EXE_AND:   beginaluop   <=  `EXE_AND_FUNC;end            `EXE_OR:    beginaluop   <=  `EXE_OR_FUNC;end`EXE_XOR:   beginaluop   <=  `EXE_XOR_FUNC;end`EXE_NOR:   beginaluop   <=  `EXE_NOR_FUNC;end`EXE_SLLV:  beginaluop   <=  `EXE_SLL_FUNC;end`EXE_SRLV:  beginaluop   <=  `EXE_SRLV_FUNC;end`EXE_SRAV:  beginaluop   <=  `EXE_SRAV_FUNC;end`EXE_SLL:   beginreg1_rden   <=  1'd0;imm[4:0]    <=  shmat;aluop       <=  `EXE_SLL_FUNC;end`EXE_SRL:   beginreg1_rden   <=  1'd0;imm[4:0]    <=  shmat;aluop       <=  `EXE_SRL_FUNC;end`EXE_SRA:   beginreg1_rden   <=  1'd0;imm[4:0]    <=  shmat;aluop       <=  `EXE_SRA_FUNC;end`EXE_MOVN:  beginif (reg2 == 32'd0)  beginreg_wb  <=  1'b0;end else beginreg_wb  <=  1'b1;aluop   <=  `EXE_MOVN_FUNC;endend`EXE_MOVZ:  beginif (reg2 == 32'd0)  beginreg_wb  <=  1'b1;aluop   <=  `EXE_MOVZ_FUNC;end else beginreg_wb  <=  1'b0;endend`EXE_MFHI:  beginreg1_rden   <=  1'b0;reg2_rden   <=  1'b0;aluop       <=  `EXE_MFHI_FUNC;end`EXE_MFLO:  beginreg1_rden   <=  1'b0;reg2_rden   <=  1'b0;aluop       <=  `EXE_MFLO_FUNC; end`EXE_MTHI:  beginreg2_rden   <=  1'b0;reg_wb      <=  1'b0;aluop       <=  `EXE_MTHI_FUNC;end`EXE_MTLO:  beginreg2_rden   <=  1'b0;reg_wb      <=  1'b0;aluop       <=  `EXE_MTLO_FUNC;end`EXE_ADD:   beginaluop   <=  `EXE_ADD_FUNC;end`EXE_ADDU:  beginaluop   <=  `EXE_ADDU_FUNC;end`EXE_SUB:   beginaluop   <=  `EXE_SUB_FUNC;end`EXE_SUBU:  beginaluop   <=  `EXE_SUBU_FUNC;end`EXE_SLT:   beginaluop   <=  `EXE_SLT_FUNC;end`EXE_SLTU:  beginaluop   <=  `EXE_SLTU_FUNC;enddefault:    aluop       <=  7'd0;endcaseend else if (op == `EXE_SPECIAL2) begin     //由FUNC字段决定,操作码为011100的指令reg1_rden   <=  1'd1;reg2_rden   <=  1'd1;reg_wb      <=  1'd1;case (func)`EXE_CLZ:   beginaluop   <=  `EXE_CLZ_FUNC;end`EXE_CLO:   beginaluop   <=  `EXE_CLO_FUNC;enddefault:    beginaluop   <=  7'd0;endendcaseend else beginreg1_rden   <= 1'd1;  //需要读取操作数1 rs寄存器的值reg2_rden   <= 1'd0;  //不需要读取操作数2 rt寄存器值,imm         <= {16'h0, inst[15:0]};   reg_wb      <= 1'd1;reg_wb_addr <= inst[20:16];case (op)`EXE_ORI:   begin           //或指令  rs寄存器值是操作数1,imm是操作数2,结果放到rt寄存器aluop   <=  `EXE_ORI_OP;           end`EXE_ANDI:  beginaluop   <=  `EXE_ANDI_OP;end `EXE_XORI:  beginaluop   <=  `EXE_XORI_OP;end`EXE_LUI:   beginreg1_rden   <= 1'b0;aluop   <=  `EXE_LUI_OP;end`EXE_ADDI:  beginaluop   <=  `EXE_ADDI_OP;end`EXE_ADDIU: beginaluop   <=  `EXE_ADDIU_OP;end`EXE_SLTI:  beginaluop   <=  `EXE_SLTI_OP;end`EXE_SLTIU: beginaluop   <=  `EXE_SLTIU_OP;enddefault:aluop   <=  7'd0;    endcaseendend
endalways @ (*) beginif (rst) beginreg1 <= 32'd0;end else if (reg1_rden == 1'd1 && ex_wr_en == 1'd1 && ex_wr_addr == reg1_addr) begin    //执行阶段旁路reg1 <= ex_wr_data;end else if (reg1_rden == 1'd1 && mem_wr_en == 1'd1 && mem_wr_addr == reg1_addr) begin   //访存阶段旁路reg1 <= mem_wr_data;end else if (reg1_rden == 1'd1) begin   //从通用寄存器获取操作数reg1 <= reg1_data;end else if (reg1_rden == 1'd0) begin   //从指令中获取操作数reg1 <= imm;end else beginreg1 <= 32'd0;end
endalways @ (*) beginif (rst) beginreg2 <= 32'd0;end else if (reg2_rden == 1'd1 && ex_wr_en == 1'd1 && ex_wr_addr == reg2_addr) begin    //执行阶段旁路reg2 <= ex_wr_data;end else if (reg2_rden == 1'd1 && mem_wr_en == 1'd1 && mem_wr_addr == reg2_addr) begin   //访存阶段旁路reg2 <= mem_wr_data;end else if (reg2_rden == 1'd1) begin   //从通用寄存器获取操作数reg2 <= reg2_data;end else if (reg2_rden == 1'd0) begin   //从指令中获取操作数reg2 <= imm;end else beginreg2 <= 32'd0;end
endendmodule

修改EX模块

主要修改部分:

//计算clz和clo的结果,0~32
wire [31:0]  clz_res    =   reg1[31] ? 0 : reg1[30] ? 1 : reg1[29] ? 2 : reg1[28] ? 3 : reg1[27] ? 4 : reg1[26] ? 5 :reg1[25] ? 6 : reg1[24] ? 7 : reg1[23] ? 8 : reg1[22] ? 9 : reg1[21] ? 10 : reg1[20] ? 11 :reg1[19] ? 12 : reg1[18] ? 13 : reg1[17] ? 14 : reg1[16] ? 15 : reg1[15] ? 16 : reg1[14] ? 17 : reg1[13] ? 18 : reg1[12] ? 19 : reg1[11] ? 20 : reg1[10] ? 21 : reg1[9] ? 22 : reg1[8] ? 23 : reg1[7] ? 24 : reg1[6] ? 25 : reg1[5] ? 26 : reg1[4] ? 27 : reg1[3] ? 28 : reg1[2] ? 29 : reg1[1] ? 30 : reg1[0] ? 31 : 32 ;
wire [31:0] reg1_i     =    ~reg1;
wire [31:0] clo_res    =    reg1_i[31] ? 0 : reg1_i[30] ? 1 : reg1_i[29] ? 2 :reg1_i[28] ? 3 : reg1_i[27] ? 4 : reg1_i[26] ? 5 : reg1_i[25] ? 6 : reg1_i[24] ? 7 : reg1_i[23] ? 8 : reg1_i[22] ? 9 :reg1_i[21] ? 10 : reg1_i[20] ? 11 : reg1_i[19] ? 12 : reg1_i[18] ? 13 : reg1_i[17] ? 14 : reg1_i[16] ? 15 : reg1_i[15] ? 16 : reg1_i[14] ? 17 : reg1_i[13] ? 18 : reg1_i[12] ? 19 :reg1_i[11] ? 20 : reg1_i[10] ? 21 : reg1_i[9] ? 22 : reg1_i[8] ? 23 : reg1_i[7] ? 24 :reg1_i[6] ? 25 : reg1_i[5] ? 26 : reg1_i[4] ? 27 : reg1_i[3] ? 28 : reg1_i[2] ? 29 : reg1_i[1] ? 30 : reg1_i[0] ? 31 : 32 ;
 `EXE_CLZ_FUNC:                  beginreg_wb_data     <=  clz_res;end     `EXE_CLO_FUNC:                  beginreg_wb_data     <=  clo_res;end      

整体代码:

`include "defines.v"
//执行阶段,根据译码阶段得到的操作码和操作数进行运算,得到结果
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,从访存阶段给出旁路(上一条指令));wire [31:0] mfhi_res = mem_hi_wren_i ? mem_hi_i : wb_hi_wren_i ? wb_hi_i : hi_reg_i; 
wire [31:0] mflo_res = mem_lo_wren_i ? mem_lo_i : wb_lo_wren_i ? wb_lo_i : lo_reg_i; //需要转换成补码的指令
wire [31:0] reg2_mux   =   ((aluop==`EXE_SUB_FUNC)||(aluop==`EXE_SUBU_FUNC)||(aluop==`EXE_SLT_FUNC)) ? (~reg2+1) : reg2;
wire [31:0] res        =   reg1 + reg2_mux;    //判断加减的结果是否溢出,减法转换成加法
//overflow flag 两个正数相加得负或两个负数相加得正则溢出
wire        of         =   ((!reg1[31]&&!reg2_mux[31]&&res[31])||(reg1[31]&&reg2_mux[31]&&!res[31])); 
//计算clz和clo的结果,0~32
wire [31:0]  clz_res    =   reg1[31] ? 0 : reg1[30] ? 1 : reg1[29] ? 2 : reg1[28] ? 3 : reg1[27] ? 4 : reg1[26] ? 5 :reg1[25] ? 6 : reg1[24] ? 7 : reg1[23] ? 8 : reg1[22] ? 9 : reg1[21] ? 10 : reg1[20] ? 11 :reg1[19] ? 12 : reg1[18] ? 13 : reg1[17] ? 14 : reg1[16] ? 15 : reg1[15] ? 16 : reg1[14] ? 17 : reg1[13] ? 18 : reg1[12] ? 19 : reg1[11] ? 20 : reg1[10] ? 21 : reg1[9] ? 22 : reg1[8] ? 23 : reg1[7] ? 24 : reg1[6] ? 25 : reg1[5] ? 26 : reg1[4] ? 27 : reg1[3] ? 28 : reg1[2] ? 29 : reg1[1] ? 30 : reg1[0] ? 31 : 32 ;
wire [31:0] reg1_i     =    ~reg1;
wire [31:0] clo_res    =    reg1_i[31] ? 0 : reg1_i[30] ? 1 : reg1_i[29] ? 2 :reg1_i[28] ? 3 : reg1_i[27] ? 4 : reg1_i[26] ? 5 : reg1_i[25] ? 6 : reg1_i[24] ? 7 : reg1_i[23] ? 8 : reg1_i[22] ? 9 :reg1_i[21] ? 10 : reg1_i[20] ? 11 : reg1_i[19] ? 12 : reg1_i[18] ? 13 : reg1_i[17] ? 14 : reg1_i[16] ? 15 : reg1_i[15] ? 16 : reg1_i[14] ? 17 : reg1_i[13] ? 18 : reg1_i[12] ? 19 :reg1_i[11] ? 20 : reg1_i[10] ? 21 : reg1_i[9] ? 22 : reg1_i[8] ? 23 : reg1_i[7] ? 24 :reg1_i[6] ? 25 : reg1_i[5] ? 26 : reg1_i[4] ? 27 : reg1_i[3] ? 28 : reg1_i[2] ? 29 : reg1_i[1] ? 30 : reg1_i[0] ? 31 : 32 ;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:                 beginreg_wb_data     <=  mfhi_res;       end`EXE_MFLO_FUNC:                 beginreg_wb_data     <=  mflo_res;end`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;end`EXE_ADD_FUNC,`EXE_SUB_FUNC,`EXE_ADDI_OP:                   begin       //加法减法都是加法实现reg_wb_data     <=  res;reg_wb_o        <=  of ? 0 : 1;end`EXE_ADDU_FUNC,`EXE_SUBU_FUNC,`EXE_ADDIU_OP:                  begin       //无符号数无需判断溢出,直接截断保存reg_wb_data     <=  res;end`EXE_CLZ_FUNC:                  beginreg_wb_data     <=  clz_res;end     `EXE_CLO_FUNC:                  beginreg_wb_data     <=  clo_res;end      default:                        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

仿真测试1

34010001        ORI         赋值reg1为0000,0001
3c02ffff            LUI          赋值reg2为ffff,0000
70201820        CLZ        clz(reg1) => reg3         结果应该是31 
70402021        CLO        clo(reg2) => reg4        结果应该是16

由仿真结果可知,这两条指令执行正确。


下面是SLT指令的实现

SLT指令:(rs < rt) => rd(1/0)        如果rs小于rt寄存器的值,将1写入rd否则写0。

修改ID模块

这部分和之前类似,只给出部分代码

        if (op == `EXE_SPECIAL) begincase (func) `EXE_SLT:   beginaluop   <=  `EXE_SLT_FUNC;end`EXE_SLTU:  beginaluop   <=  `EXE_SLTU_FUNC;endelse begincase (op)`EXE_SLTI:  beginaluop   <=  `EXE_SLTI_OP;end`EXE_SLTIU: beginaluop   <=  `EXE_SLTIU_OP;end

修改EX模块

//计算reg1 < reg2的补码比较
assign reg1_lt_reg2 = ((aluop == `EXE_SLT_FUNC) || (aluop == `EXE_SLTI_OP)) ? ((reg1[31] && !reg2[31]) || (!reg1[31] && !reg2[31] && res[31]) || (reg1[31] && reg2[31] && res[31])) : (reg1 < reg2);case (aluop) `EXE_SLT_FUNC,`EXE_SLTU_FUNC,`EXE_SLTI_OP,`EXE_SLTIU_OP:     beginreg_wb_data     <=  reg1_lt_reg2;end

整体ID模块和EX模块

`include "defines.v"
//译码阶段  对if_id传入的指令进行译码,分离出操作数和操作码
module id(input                           rst,input [`InstAddrBus]            pc,input [`InstDataBus]            inst,//读通用寄存器        读取        input [`RegDataBus]             reg1_data,input [`RegDataBus]             reg2_data,output reg                      reg1_rden,output reg                      reg2_rden,output reg [`RegAddrBus]        reg1_addr,  //源操作数1的地址output reg [`RegAddrBus]        reg2_addr,  //源操作数2的地址//送到ex阶段的值output reg [`RegDataBus]        reg1,       //源操作数1 32boutput reg [`RegDataBus]        reg2,       //源操作数2 32boutput reg                      reg_wb,     //写回目的寄存器标志    output reg [`RegAddrBus]        reg_wb_addr,//写回目的寄存器地址output reg [`AluOpBus]          aluop,      //操作码    //相邻指令的冲突,由EX阶段给出数据旁路input                           ex_wr_en,   //处于执行阶段的指令是否要写目的寄存器 input [`RegDataBus]             ex_wr_data, input [`RegAddrBus]             ex_wr_addr,  //相隔一条指令的冲突,由MEM阶段给出数据旁路input                           mem_wr_en,  //处于访存阶段指令是否要写目的寄存器input [`RegDataBus]             mem_wr_data,input [`RegAddrBus]             mem_wr_addr);wire [5:0] op = inst[31:26];         //从指令中获取操作码   高6位
wire [5:0] func = inst[5:0];        //从指令中获取功能号确定指令类型   低6位
wire [4:0] shmat = inst[10:6];      //部分移位位数不从寄存器取值,直接由shmat给出
reg [`RegDataBus] imm;              //立即数always @ (*) beginif (rst) beginreg1_rden       <= 1'd0;reg2_rden       <= 1'd0;reg1_addr       <= 5'd0;reg2_addr       <= 5'd0;imm             <= 32'd0;reg_wb          <= 1'd0;reg_wb_addr     <= 5'd0;aluop           <= 7'd0;end else beginreg1_rden       <= 1'd0;reg2_rden       <= 1'd0;reg1_addr       <= inst[25:21];         //默认从指令中读取操作数1地址reg2_addr       <= inst[20:16];         //默认从指令中读取操作数2地址imm             <= 32'd0; reg_wb          <= 1'd0;reg_wb_addr     <= inst[15:11];         //默认结果地址寄存器rd        aluop           <= 7'd0;                //操作类型if (op == `EXE_SPECIAL) beginreg1_rden   <= 1'd1;reg2_rden   <= 1'd1;reg_wb      <= 1'd1;case (func) `EXE_AND:   beginaluop   <=  `EXE_AND_FUNC;end            `EXE_OR:    beginaluop   <=  `EXE_OR_FUNC;end`EXE_XOR:   beginaluop   <=  `EXE_XOR_FUNC;end`EXE_NOR:   beginaluop   <=  `EXE_NOR_FUNC;end`EXE_SLLV:  beginaluop   <=  `EXE_SLL_FUNC;end`EXE_SRLV:  beginaluop   <=  `EXE_SRLV_FUNC;end`EXE_SRAV:  beginaluop   <=  `EXE_SRAV_FUNC;end`EXE_SLL:   beginreg1_rden   <=  1'd0;imm[4:0]    <=  shmat;aluop       <=  `EXE_SLL_FUNC;end`EXE_SRL:   beginreg1_rden   <=  1'd0;imm[4:0]    <=  shmat;aluop       <=  `EXE_SRL_FUNC;end`EXE_SRA:   beginreg1_rden   <=  1'd0;imm[4:0]    <=  shmat;aluop       <=  `EXE_SRA_FUNC;end`EXE_MOVN:  beginif (reg2 == 32'd0)  beginreg_wb  <=  1'b0;end else beginreg_wb  <=  1'b1;aluop   <=  `EXE_MOVN_FUNC;endend`EXE_MOVZ:  beginif (reg2 == 32'd0)  beginreg_wb  <=  1'b1;aluop   <=  `EXE_MOVZ_FUNC;end else beginreg_wb  <=  1'b0;endend`EXE_MFHI:  beginreg1_rden   <=  1'b0;reg2_rden   <=  1'b0;aluop       <=  `EXE_MFHI_FUNC;end`EXE_MFLO:  beginreg1_rden   <=  1'b0;reg2_rden   <=  1'b0;aluop       <=  `EXE_MFLO_FUNC; end`EXE_MTHI:  beginreg2_rden   <=  1'b0;reg_wb      <=  1'b0;aluop       <=  `EXE_MTHI_FUNC;end`EXE_MTLO:  beginreg2_rden   <=  1'b0;reg_wb      <=  1'b0;aluop       <=  `EXE_MTLO_FUNC;end`EXE_ADD:   beginaluop   <=  `EXE_ADD_FUNC;end`EXE_ADDU:  beginaluop   <=  `EXE_ADDU_FUNC;end`EXE_SUB:   beginaluop   <=  `EXE_SUB_FUNC;end`EXE_SUBU:  beginaluop   <=  `EXE_SUBU_FUNC;end`EXE_SLT:   beginaluop   <=  `EXE_SLT_FUNC;end`EXE_SLTU:  beginaluop   <=  `EXE_SLTU_FUNC;enddefault:    aluop       <=  7'd0;endcaseend else if (op == `EXE_SPECIAL2) begin     //由FUNC字段决定,操作码为011100的指令reg1_rden   <=  1'd1;reg2_rden   <=  1'd1;reg_wb      <=  1'd1;case (func)`EXE_CLZ:   beginaluop   <=  `EXE_CLZ_FUNC;end`EXE_CLO:   beginaluop   <=  `EXE_CLO_FUNC;enddefault:    beginaluop   <=  7'd0;endendcaseend else beginreg1_rden   <= 1'd1;  //需要读取操作数1 rs寄存器的值reg2_rden   <= 1'd0;  //不需要读取操作数2 rt寄存器值,imm         <= {16'h0, inst[15:0]};   reg_wb      <= 1'd1;reg_wb_addr <= inst[20:16];case (op)`EXE_ORI:   begin           //或指令  rs寄存器值是操作数1,imm是操作数2,结果放到rt寄存器aluop   <=  `EXE_ORI_OP;           end`EXE_ANDI:  beginaluop   <=  `EXE_ANDI_OP;end `EXE_XORI:  beginaluop   <=  `EXE_XORI_OP;end`EXE_LUI:   beginreg1_rden   <= 1'b0;aluop   <=  `EXE_LUI_OP;end`EXE_ADDI:  beginaluop   <=  `EXE_ADDI_OP;end`EXE_ADDIU: beginaluop   <=  `EXE_ADDIU_OP;end`EXE_SLTI:  beginaluop   <=  `EXE_SLTI_OP;end`EXE_SLTIU: beginaluop   <=  `EXE_SLTIU_OP;enddefault:aluop   <=  7'd0;    endcaseendend
endalways @ (*) beginif (rst) beginreg1 <= 32'd0;end else if (reg1_rden == 1'd1 && ex_wr_en == 1'd1 && ex_wr_addr == reg1_addr) begin    //执行阶段旁路reg1 <= ex_wr_data;end else if (reg1_rden == 1'd1 && mem_wr_en == 1'd1 && mem_wr_addr == reg1_addr) begin   //访存阶段旁路reg1 <= mem_wr_data;end else if (reg1_rden == 1'd1) begin   //从通用寄存器获取操作数reg1 <= reg1_data;end else if (reg1_rden == 1'd0) begin   //从指令中获取操作数reg1 <= imm;end else beginreg1 <= 32'd0;end
endalways @ (*) beginif (rst) beginreg2 <= 32'd0;end else if (reg2_rden == 1'd1 && ex_wr_en == 1'd1 && ex_wr_addr == reg2_addr) begin    //执行阶段旁路reg2 <= ex_wr_data;end else if (reg2_rden == 1'd1 && mem_wr_en == 1'd1 && mem_wr_addr == reg2_addr) begin   //访存阶段旁路reg2 <= mem_wr_data;end else if (reg2_rden == 1'd1) begin   //从通用寄存器获取操作数reg2 <= reg2_data;end else if (reg2_rden == 1'd0) begin   //从指令中获取操作数reg2 <= imm;end else beginreg2 <= 32'd0;end
endendmodule
`include "defines.v"
//执行阶段,根据译码阶段得到的操作码和操作数进行运算,得到结果
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,从访存阶段给出旁路(上一条指令));wire [31:0] mfhi_res = mem_hi_wren_i ? mem_hi_i : wb_hi_wren_i ? wb_hi_i : hi_reg_i; 
wire [31:0] mflo_res = mem_lo_wren_i ? mem_lo_i : wb_lo_wren_i ? wb_lo_i : lo_reg_i; //需要转换成补码的指令
wire [31:0] reg2_mux   =   ((aluop==`EXE_SUB_FUNC)||(aluop==`EXE_SUBU_FUNC)||(aluop==`EXE_SLT_FUNC)) ? (~reg2+1) : reg2;
wire [31:0] res        =   reg1 + reg2_mux;    //判断加减的结果是否溢出,减法转换成加法
//overflow flag 两个正数相加得负或两个负数相加得正则溢出
wire        of         =   ((!reg1[31]&&!reg2_mux[31]&&res[31])||(reg1[31]&&reg2_mux[31]&&!res[31])); 
//计算clz和clo的结果,0~32
wire [31:0]  clz_res    =   reg1[31] ? 0 : reg1[30] ? 1 : reg1[29] ? 2 : reg1[28] ? 3 : reg1[27] ? 4 : reg1[26] ? 5 :reg1[25] ? 6 : reg1[24] ? 7 : reg1[23] ? 8 : reg1[22] ? 9 : reg1[21] ? 10 : reg1[20] ? 11 :reg1[19] ? 12 : reg1[18] ? 13 : reg1[17] ? 14 : reg1[16] ? 15 : reg1[15] ? 16 : reg1[14] ? 17 : reg1[13] ? 18 : reg1[12] ? 19 : reg1[11] ? 20 : reg1[10] ? 21 : reg1[9] ? 22 : reg1[8] ? 23 : reg1[7] ? 24 : reg1[6] ? 25 : reg1[5] ? 26 : reg1[4] ? 27 : reg1[3] ? 28 : reg1[2] ? 29 : reg1[1] ? 30 : reg1[0] ? 31 : 32 ;
wire [31:0] reg1_i     =    ~reg1;
wire [31:0] clo_res    =    reg1_i[31] ? 0 : reg1_i[30] ? 1 : reg1_i[29] ? 2 :reg1_i[28] ? 3 : reg1_i[27] ? 4 : reg1_i[26] ? 5 : reg1_i[25] ? 6 : reg1_i[24] ? 7 : reg1_i[23] ? 8 : reg1_i[22] ? 9 :reg1_i[21] ? 10 : reg1_i[20] ? 11 : reg1_i[19] ? 12 : reg1_i[18] ? 13 : reg1_i[17] ? 14 : reg1_i[16] ? 15 : reg1_i[15] ? 16 : reg1_i[14] ? 17 : reg1_i[13] ? 18 : reg1_i[12] ? 19 :reg1_i[11] ? 20 : reg1_i[10] ? 21 : reg1_i[9] ? 22 : reg1_i[8] ? 23 : reg1_i[7] ? 24 :reg1_i[6] ? 25 : reg1_i[5] ? 26 : reg1_i[4] ? 27 : reg1_i[3] ? 28 : reg1_i[2] ? 29 : reg1_i[1] ? 30 : reg1_i[0] ? 31 : 32 ;
//计算reg1 < reg2的补码比较
assign reg1_lt_reg2 = ((aluop == `EXE_SLT_FUNC) || (aluop == `EXE_SLTI_OP)) ? ((reg1[31] && !reg2[31]) || (!reg1[31] && !reg2[31] && res[31]) || (reg1[31] && reg2[31] && res[31])) : (reg1 < reg2);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:                 beginreg_wb_data     <=  mfhi_res;       end`EXE_MFLO_FUNC:                 beginreg_wb_data     <=  mflo_res;end`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;end`EXE_ADD_FUNC,`EXE_SUB_FUNC,`EXE_ADDI_OP:                   begin       //加法减法都是加法实现reg_wb_data     <=  res;reg_wb_o        <=  of ? 0 : 1;end`EXE_ADDU_FUNC,`EXE_SUBU_FUNC,`EXE_ADDIU_OP:                  begin       //无符号数无需判断溢出,直接截断保存reg_wb_data     <=  res;end`EXE_CLZ_FUNC:                  beginreg_wb_data     <=  clz_res;end     `EXE_CLO_FUNC:                  beginreg_wb_data     <=  clo_res;end      `EXE_SLT_FUNC,`EXE_SLTU_FUNC,`EXE_SLTI_OP,`EXE_SLTIU_OP:     beginreg_wb_data     <=  reg1_lt_reg2;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

仿真测试

34010001        给reg1赋值0000,0001
3c02ffff            给reg2赋值ffff,0000
0022182b       SLTU      (reg1 < reg2) => reg3        无符号
0022182a       SLT        (reg1 < reg2) => reg3        有符号
2844ffff           SLTI       (reg2 < 0000ffff) => reg4    有符号
2c44ffff           SLTIU     (reg2 < 0000ffff) => reg4    无符号 

由仿真结果可知,符合我们预期结果,这四条比较指令也没问题。

下一步实现乘除指令!

关键字:自己动手写CPU_step6.1_算数运算指令

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: