实现的指令说明

这里解释有符号扩展与无符号扩展,有符号数扩展符号位。也就是1,无符号数扩展0。也就是在前面补满零或1

R-型指令

加减比较指令

add、addu、sub、sub、slt、sltu 这6条指令的格式指令码都是6’b000000,即SPECIAL类,另外第6-10bit都为0,需要依据指令中0-5bit功能码的值进一步判断是哪一种指令。

当功能码是6’b100000时,表示是add指令,加法运算
指令用法为:add rd, rs, rt
指令作用为:rd <- rs + rt,将地址为rs的通用寄存器的值,与地址为rt的通用寄存器的值进行加法运算,结果保存到地址为rd的通用寄存器中。但是有一种特殊情况:如果加法运算溢出,那么会产生溢出异常,同时不保存结果。

当功能码是6’b100001时,表示是addu指令,加法运算
指令用法为:addu rd, rs, rt
指令作用为:rd <- rs + rt,将地址为rs的通用寄存器的值,与地址为rt的通用寄存器的值进行加法运算,结果保存到地址为rd的通用寄存器中。与add指令不同之处在于addu指令不进行溢出检查,总是将结果保存到目的寄存器。

当功能码是6’b100010时,表示是sub指令,减法运算
指令用法为:sub rd, rs, rt
指令作用为:rd <- rs - rt,将地址为rs的通用寄存器的值,与地址为rt的通用寄存器的值进行减法运算,结果保存到地址为rd的通用寄存器中。但是有一种特殊情况:如果减法运算溢出,那么产生溢出异常,同时不保存结果。

当功能码是6’b100011时,表示是subu指令,减法运算
指令用法为:subu rd, rs, rt
指令作用为:rd <- rs - rt,将地址为rs的通用寄存器的值,与地址为rt的通用寄存器的值进行减法运算,结果保存到地址为rd的通用寄存器中。与sub指令不同之处在于subu指令不进行溢出检查,总是将结果保存到目的寄存器。

当功能码是6’b101010时,表示是slt指令,比较运算
指令用法为:slt rd, rs, rt
指令作用为:rd <- (rs < rt),将地址为rs的通用寄存器的值,与地址为rt的通用寄存器的值按照有符号数进行比较,如果前者小于后者,那么将1保存到地址为rd的通用寄存器中,反之,将0保存到地址为rd的通用寄存器中。

当功能码是6’b101011时,表示是sltu指令,比较运算
指令用法为:sltu rd, rs, rt
指令作用为:rd <- (rs < rt),将地址为rs的通用寄存器的值,与地址为rt的通用寄存器的值按照无符号数进行比较,如果前者小于后者,那么将1保存到地址为rd的通用寄存器中,反之,将0保存到地址为rd的通用寄存器中。

计数指令

clzclo这2条指令的格式指令码都是6’b011100,在MIPS32指令集架构中表示SPECIAL2类,另外第6-10bit都为0,需要依据指令中0-5bit功能码的值进一步判断是哪一种指令。

当功能码是6’b100000时,表示是clz指令,计数运算
指令用法为:clz rd, rs
指令作用为:rd <- coun_leading_zeros rs,对地址为rs的通用寄存器的值,从其最高位开始向最低位方向检查,直到遇到值为**“1”**的位,将该位之前“0”的个数保存到地址为rd的通用寄存器中,如果地址为rs的通用寄存器的所有位都为0(即0x00000000),那么将32保存到地址为rd的通用寄存器中。

当功能码是6’b100001时,表示是clo指令,计数运算
指令用法为:clo rd, rs
指令作用为:rd <- coun_leading_ones rs,对地址为rs的通用寄存器的值,从其最高位开始向最低位方向检查,直到遇到值为**“0”**的位,将该位之前“1”的个数保存到地址为rd的通用寄存器中,如果地址为rs的通用寄存器的所有位都为1(即0xFFFFFFFF),那么将32保存到地址为rd的通用寄存器中。

乘法指令

multu、mult、mul指令
这3条指令都是R类型指令,并且mul指令的指令码是SPECIAL2multmultu的指令码是SPECIAL

当指令码为SPECIAL2,功能码为6’b000010时,表示是mul指令,乘法运算
指令用法为:mul rd, rs, st
指令作用为:rd <- rs × rt,将地址为rs的通用寄存器的值,与地址为rt的通用寄存器的值作为有符号数相乘,乘法结果的低32bit保存到地址为rd的通用寄存器中

当指令码为SPECIAL,功能码为6’b011000时,表示是mult指令,乘法运算
指令用法为:mult rs, st
指令作用为:{hi, lo} <- rs × rt,将地址为rs的通用寄存器的值,与地址为rt的通用寄存器的值作为有符号数相乘乘法结果的低32bit保存到LO寄存器中,高32bit保存到HI寄存器中

当指令码为SPECIAL,功能码为6’b011001时,表示是multu指令,乘法运算
指令用法为:multu rs, st
指令作用为:{hi, lo} <- rs × rt,将地址为rs的通用寄存器的值,与地址为rt的通用寄存器的值作为无符号数相乘,乘法结果的低32bit保存到LO寄存器中,高32bit保存到HI寄存器中。与mult指令的区别在于multu指令执行中将操作数作为无符号数进行运算

I-型指令

addi、addiu、slti、slti指令

当指令码是6’b001000时,表示是addi指令,加法运算
指令用法为:addi rt, rs, immediate
指令作用为:rt <- rs + (sign_extended)immediate,将指令中16位立即数进行符号扩展,与地址为rs的通用寄存器的值进行加法运算,结果保存到地址为rt的通用寄存器中。但是有一个特殊情况:如果加法运算溢出,那么产生溢出异常,同时不保存结果。

当指令码是6’b001001时,表示是addiu指令,加法运算
指令用法为:addiu rt, rs, immediate
指令作用为:rt <- rs + (sign_extended)immediate,将指令中16位立即数进行符号扩展,与地址为rs的通用寄存器的值进行加法运算,结果保存到地址为rt的通用寄存器中。与addi指令的区别在于addiu指令不进行溢出检查,总是将结果保存到目的寄存器。

当指令码是6’b001010时,表示是slti指令,比较运算
指令用法为:slti rt, rs, immediate
指令作用为:rt <- (rs < (sign_extended)immediate),将指令中16位立即数进行符号扩展,与地址为rs的通用寄存器的值按照有符号数比较,如果前者大于后者,那么将1保存到地址为rt的通用寄存器中,反之,将0保存到地址为rt的通用寄存器中。

当指令码是6’b001011时,表示是sltiu指令,比较运算
指令用法为:sltiu rt, rs, immediate
指令作用为:rt<- (rs < (sign_extended)immediate),将指令中16位立即数进行符号扩展,与地址为rs的通用寄存器的值按照无符号数比较,如果前者大于后者,那么将1保存到地址为rt的通用寄存器中,反之,将0保存到地址为rt的通用寄存器中。

模块修改思路

(1)修改流水线译码阶段的ID模块,添加对上述简单算术操作指令的译码,给出运算类型alusel_o、运算子类型aluop_o、要写入的目的寄存器地址wd_o等信息,同时根据需要读取地址为rs、rt的通用寄存器的值。
(2)修改流水线执行阶段的EX模块,依据传入的信息,进行运算,得到运算结果,确定最终要写目的寄存器的信息(包含:是否写、写入的目的寄存器地址、写入的值),并将这些信息传递到访存阶段。
(3)上述信息会一直传递到回写阶段,最后修改目的寄存器。

ID模块的修改

这里先进行一个简单的总结
其实代码写到这里就可以大概看出来对于译码阶段的处理了。首先得到一个指令。然后这里首先会指令人为进行化分。这里目前为止分为以下几个内容,我们填写指令的时候,只需要找到其对应的位置,设置好读写端口,即可译码成功,算是比较简单的模块,而且形式固定。

case (op)EXE_SPECIAL_INST :case(op2)5'b0000_0:case(op3)`EXE_OR:------endcase endcase `EXE_ORI:    ------`EXE_SPECIAL2_INST:
endcase
if(inst_i[31:21] == 11'b0000_0000_000) if(op3 == `EXE_SLL)------
else ------

这里是修改后的代码:

`timescale 1ns / 1ps
`include "defines.v"
module id(input     wire                        rst             ,input  wire    [`InstAddrBus]     pc_i            ,input  wire    [`InstBus]         inst_i          ,//读取regfile的值input     wire    [`InstBus]         reg1_data_i     ,input  wire    [`InstBus]         reg2_data_i     ,//输出到regfile的信息output  reg                         reg1_read_o     ,output     reg                         reg2_read_o     ,output     reg     [`RegAddrBus]      reg1_addr_o     ,output     reg     [`RegAddrBus]      reg2_addr_o     ,//送到执行阶段的信息output  reg     [`AluOpBus]            aluop_o         ,output     reg     [`AluSelBus]       alusel_o        ,output     reg     [`RegBus]          reg1_o          ,   //送到执行阶段的源操作数output     reg     [`RegBus]          reg2_o          ,   //送到执行阶段的源操作数output     reg     [`RegAddrBus]      wd_o            ,output     reg                         wreg_o          ,
//处于执行阶段的指令运行的运算结果input     wire    [`RegAddrBus]      ex_wd_i         ,       //执行阶段的目的寄存器地址input     wire                        ex_wreg_i       ,       //是否要写入数据标志 input   wire    [`RegBus]          ex_wdata_i      ,       //执行阶段送到访存的数据//处于访存阶段的指令运行的运算结果input  wire  [`RegAddrBus]      mem_wd_i        ,input  wire                        mem_wreg_i      ,input  wire    [`RegBus]          mem_wdata_i     );
//取得指令的指令码,功能码
//用于ori指令只需要判断21-31 bit的值,即可判断是否是ori指令
wire [5:0] op   = inst_i[31:26]    ;           //操作指令码
wire [4:0] op2  = inst_i[10:6 ]    ;           //由位移指令使用,定义位移位数
wire [5:0] op3  = inst_i[ 5: 0] ;          //功能码
wire [4:0] op4  = inst_i[20:16] ;          //目标寄存器码//保存指令执行需要的立即数
reg [`RegBus]  imm;//指示指令是否有效
reg instvalid;/*********************************************************************************
*************************** 第一阶段:对指令进行译码 ***************************
*********************************************************************************/always @ (*)
beginif(rst == `RstEnable)beginaluop_o       <=  `EXE_NOP_OP            ;alusel_o       <=  `EXE_RES_NOP       ;wd_o           <=  `NOPRegAddr            ;wreg_o             <=  `WriteDisable      ;instvalid      <=  `InstValid             ;reg1_read_o    <=  1'b0               ;reg2_read_o    <=  1'b0               ;reg1_addr_o    <=  `NOPRegAddr            ;reg2_addr_o        <=  `NOPRegAddr            ;imm            <=  32'h0              ;endelse beginaluop_o       <=  `EXE_NOP_OP            ;alusel_o       <=  `EXE_RES_NOP       ;wd_o           <=  inst_i[15:11]       ;wreg_o             <=  `WriteDisable      ;instvalid      <=  `InstInValid       ;reg1_read_o    <=  1'b0               ;reg2_read_o    <=  1'b0               ;   reg1_addr_o     <=  inst_i[25:21]       ;//默认通过Regfile读取端口1的寄存器地址reg2_addr_o        <=  inst_i[20:16]       ;//默认通过Regfile读取端口2的寄存器地址   imm             <=  `ZeroWord          ;case(op)`EXE_SPECIAL_INST :begincase(op2) 5'b0000_0:begincase(op3)`EXE_OR:beginwreg_o       <= `WriteEnable;aluop_o    <= `EXE_OR_OP;alusel_o     <= `EXE_RES_LOGIC;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid  <= `InstValid;end`EXE_AND:beginwreg_o         <= `WriteEnable;aluop_o    <= `EXE_AND_OP;alusel_o    <= `EXE_RES_LOGIC;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid  <= `InstValid;end`EXE_XOR:beginwreg_o         <= `WriteEnable;aluop_o    <= `EXE_XOR_OP;alusel_o    <= `EXE_RES_LOGIC;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid  <= `InstValid;end`EXE_NOR:beginwreg_o         <= `WriteEnable;aluop_o    <= `EXE_NOR_OP;alusel_o    <= `EXE_RES_LOGIC;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid  <= `InstValid;end`EXE_SLLV:beginwreg_o        <= `WriteEnable;aluop_o    <= `EXE_SLL_OP;alusel_o    <= `EXE_RES_SHIFT;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid  <= `InstValid;end`EXE_SRLV:beginwreg_o        <= `WriteEnable;aluop_o    <= `EXE_SRL_OP;alusel_o    <= `EXE_RES_SHIFT;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid  <= `InstValid;end`EXE_SRAV:beginwreg_o        <= `WriteEnable;aluop_o    <= `EXE_SRA_OP;alusel_o    <= `EXE_RES_SHIFT;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid  <= `InstValid;end`EXE_SYNC:beginwreg_o        <= `WriteEnable;aluop_o    <= `EXE_NOP_OP;alusel_o    <= `EXE_RES_SHIFT;reg1_read_o <= 1'b0;reg2_read_o <= 1'b1;instvalid  <= `InstValid;end`EXE_MFHI:beginwreg_o        <= `WriteEnable;aluop_o    <= `EXE_MFHI_OP;alusel_o   <= `EXE_RES_MOVE;reg1_read_o <= 1'b0;reg2_read_o <= 1'b0;instvalid   <= `InstValid;end`EXE_MFLO:beginwreg_o        <= `WriteEnable;aluop_o    <= `EXE_MFLO_OP;alusel_o   <= `EXE_RES_MOVE;reg1_read_o <= 1'b0;reg2_read_o <= 1'b0;instvalid   <= `InstValid;end`EXE_MTHI:beginwreg_o        <= `WriteDisable;aluop_o   <= `EXE_MTHI_OP;reg1_read_o <= 1'b1;reg2_read_o <= 1'b0;instvalid    <= `InstValid;end`EXE_MTLO:beginwreg_o        <= `WriteDisable;aluop_o   <= `EXE_MTLO_OP;reg1_read_o <= 1'b1;reg2_read_o <= 1'b0;instvalid    <= `InstValid;end`EXE_MOVN:beginaluop_o   <= `EXE_MOVN_OP;alusel_o   <= `EXE_RES_MOVE;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid   <= `InstValid;if(reg2_o != `ZeroWord)wreg_o <= `WriteEnable;else wreg_o <= `WriteDisable;end`EXE_MOVZ:beginaluop_o    <= `EXE_MOVZ_OP;alusel_o   <= `EXE_RES_MOVE;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid   <= `InstValid;if(reg2_o == `ZeroWord)wreg_o <= `WriteEnable;else wreg_o <= `WriteDisable;end`EXE_SLT:beginwreg_o         <= `WriteEnable;aluop_o    <= `EXE_SLT_OP;alusel_o    <= `EXE_RES_ARITHMETIC;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid     <= `InstValid;end`EXE_SLTU:beginwreg_o        <= `WriteEnable;aluop_o    <= `EXE_SLTU_OP;alusel_o   <= `EXE_RES_ARITHMETIC;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid     <= `InstValid;end`EXE_ADD:beginwreg_o         <= `WriteEnable;aluop_o    <= `EXE_ADD_OP;alusel_o    <= `EXE_RES_ARITHMETIC;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid     <= `InstValid;end`EXE_ADDU:beginwreg_o        <= `WriteEnable;aluop_o    <= `EXE_ADDU_OP;alusel_o   <= `EXE_RES_ARITHMETIC;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid     <= `InstValid;end`EXE_SUB:beginwreg_o         <= `WriteEnable;aluop_o    <= `EXE_SUB_OP;alusel_o    <= `EXE_RES_ARITHMETIC;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid     <= `InstValid;end`EXE_SUBU:beginwreg_o        <= `WriteEnable;aluop_o    <= `EXE_SUBU_OP;alusel_o   <= `EXE_RES_ARITHMETIC;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid     <= `InstValid;end`EXE_MULT:beginwreg_o        <= `WriteDisable;aluop_o   <= `EXE_MULT_OP;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid    <= `InstValid;end`EXE_MULTU:beginwreg_o       <= `WriteDisable;aluop_o   <= `EXE_MULTU_OP;reg1_read_o <= 1'b1;reg2_read_o <= 1'b1;instvalid   <= `InstValid;enddefault:beginendendcaseenddefault:beginendendcaseend`EXE_ORI:        //判断op的值是进行opi指令begin                       wreg_o      <= `WriteEnable        ;                   //ori 指令需要将结果写入目的寄存器,所以wreg_o 为 WriteEnable                      aluop_o     <= `EXE_OR_OP          ;                   //运算的子类型是逻辑“或”运算                        alusel_o    <= `EXE_RES_LOGIC      ;                   //运算类型是逻辑运算reg1_read_o <= 1'b1                 ;                   //需要通过Regfile的读端口1读取寄存器reg2_read_o <= 1'b0                 ;                   //不需要通过Regfile的读端口2读取寄存器imm         <= {16'h0,inst_i[15:0]} ;                  //指令执行需要的立即数                        wd_o        <= inst_i[20:16]        ;                   //执行指令要写入的目的寄存器地址                       instvalid   <= `InstValid          ;                   //ori指令是有效的end`EXE_ANDI:beginwreg_o        <= `WriteEnable        ;aluop_o    <= `EXE_AND_OP             ; alusel_o  <= `EXE_RES_LOGIC      ;reg1_read_o <= 1'b1                   ;reg2_read_o <= 1'b0                   ;imm        <= {16'h0,inst_i[15:0]} ;wd_o      <= inst_i[20:16]        ;instvalid  <= `InstValid          ; end`EXE_XORI:beginwreg_o         <= `WriteEnable        ;aluop_o    <= `EXE_XOR_OP             ; alusel_o  <= `EXE_RES_LOGIC      ;reg1_read_o <= 1'b1                   ;reg2_read_o <= 1'b0                   ;imm        <= {16'h0,inst_i[15:0]} ;wd_o      <= inst_i[20:16]        ;instvalid  <= `InstValid          ; end`EXE_LUI:beginwreg_o      <= `WriteEnable        ;aluop_o    <= `EXE_OR_OP          ; alusel_o  <= `EXE_RES_LOGIC      ;reg1_read_o <= 1'b1                   ;reg2_read_o <= 1'b0                   ;imm        <= {inst_i[15:0],16'h0 } ;wd_o         <= inst_i[20:16]        ;instvalid  <= `InstValid          ; end`EXE_PREF:beginwreg_o         <= `WriteDisable       ;aluop_o    <= `EXE_NOP_OP             ; alusel_o  <= `EXE_RES_NOP        ;reg1_read_o <= 1'b0                   ;reg2_read_o <= 1'b0                   ;instvalid  <= `InstValid          ; end`EXE_SLTI:beginwreg_o         <= `WriteEnable                        ;aluop_o    <= `EXE_SLT_OP                         ; alusel_o  <= `EXE_RES_ARITHMETIC                     ;reg1_read_o <= 1'b1                                   ;reg2_read_o <= 1'b0                                   ;imm        <= {{16{inst_i[15]}},inst_i[15:0] }     ;wd_o       <= inst_i[20:16]                        ;instvalid  <= `InstValid                          ; end`EXE_SLTIU:beginwreg_o        <= `WriteEnable                        ;aluop_o    <= `EXE_SLTU_OP                        ; alusel_o  <= `EXE_RES_ARITHMETIC                     ;reg1_read_o <= 1'b1                                   ;reg2_read_o <= 1'b0                                   ;imm        <= {{16{inst_i[15]}},inst_i[15:0] }     ;wd_o       <= inst_i[20:16]                        ;instvalid  <= `InstValid                          ;end`EXE_ADDI:beginwreg_o      <= `WriteEnable                        ;aluop_o    <= `EXE_ADDI_OP                            ; alusel_o  <= `EXE_RES_ARITHMETIC                     ;reg1_read_o <= 1'b1                                   ;reg2_read_o <= 1'b0                                   ;imm        <= {{16{inst_i[15]}},inst_i[15:0] }     ;wd_o       <= inst_i[20:16]                        ;instvalid  <= `InstValid                          ;end`EXE_ADDIU:beginwreg_o         <= `WriteEnable                        ;aluop_o    <= `EXE_ADDIU_OP                       ; alusel_o  <= `EXE_RES_ARITHMETIC                     ;reg1_read_o <= 1'b1                                   ;reg2_read_o <= 1'b0                                   ;imm        <= {{16{inst_i[15]}},inst_i[15:0] }     ;wd_o       <= inst_i[20:16]                        ;instvalid  <= `InstValid                          ;end`EXE_SPECIAL2_INST:begincase(op3)`EXE_CLZ:beginwreg_o         <= `WriteEnable                        ;aluop_o    <= `EXE_CLZ_OP                             ; alusel_o  <= `EXE_RES_ARITHMETIC                     ;reg1_read_o <= 1'b1                                   ;reg2_read_o <= 1'b0                                   ;instvalid  <= `InstValid                          ;end`EXE_CLO:beginwreg_o       <= `WriteEnable                        ;aluop_o    <= `EXE_CLO_OP                             ; alusel_o  <= `EXE_RES_ARITHMETIC                     ;reg1_read_o <= 1'b1                                   ;reg2_read_o <= 1'b0                                   ;instvalid  <= `InstValid                          ;end`EXE_MUL:beginwreg_o       <= `WriteEnable                        ;aluop_o    <= `EXE_MUL_OP                             ; alusel_o  <= `EXE_RES_MUL                            ;reg1_read_o <= 1'b1                                   ;reg2_read_o <= 1'b1                                   ;instvalid  <= `InstValid                          ;enddefault:;endcaseenddefault:beginendendcaseif(inst_i[31:21] == 11'b0000_0000_000) begin if(op3 == `EXE_SLL)beginwreg_o     <=      `WriteEnable   ;aluop_o    <=      `EXE_SLL_OP    ;alusel_o   <=      `EXE_RES_SHIFT     ;reg1_read_o <=         1'b0           ;reg2_read_o <=         1'b1           ;imm[4:0]   <=      inst_i[10:6]    ;wd_o       <=      inst_i[15:11]   ;instvalid  <=      `InstValid         ;endelse if(op3 == `EXE_SRL)beginwreg_o      <=      `WriteEnable   ;aluop_o    <=      `EXE_SRL_OP    ;alusel_o   <=      `EXE_RES_SHIFT     ;reg1_read_o <=         1'b0           ;reg2_read_o <=         1'b1           ;imm[4:0]   <=      inst_i[10:6]    ;wd_o       <=      inst_i[15:11]   ;instvalid  <=      `InstValid         ;endelse if(op3 == `EXE_SRA)     beginwreg_o     <=      `WriteEnable   ;aluop_o    <=      `EXE_SRA_OP    ;alusel_o   <=      `EXE_RES_SHIFT     ;reg1_read_o <=         1'b0           ;reg2_read_o <=         1'b1           ;imm[4:0]   <=      inst_i[10:6]    ;wd_o       <=      inst_i[15:11]   ;instvalid  <=      `InstValid         ;endendend
end/*********************************************************************************
*************************** 第二阶段:确定进行运算的源操作数1***************************
*********************************************************************************/
//regfile读端口1的输出值
always @ (*)
beginif(rst == `RstEnable)reg1_o <= `ZeroWord;else // 对于源操作数,若是目前端口的读取得寄存器数据地址是  执行阶段的要写入的目的寄存器  ,那么直接将执行的结果作为reg1_o的值。//这个数相当于是要写入的数据if((reg1_read_o == 1'b1) && (ex_wreg_i == 1'b1) && (ex_wd_i == reg1_addr_o) )reg1_o <= ex_wdata_i;  else//对于要是目的寄存器,我们要读取的寄存器其实是最终访存要写入的寄存器,那么访存的数据就直接作为源操作数进行处理if((reg1_read_o == 1'b1) && (mem_wreg_i == 1'b1) && (mem_wd_i == reg1_addr_o) )reg1_o <= mem_wdata_i ;else if(reg1_read_o == 1'b1)reg1_o <= reg1_data_i;else if(reg1_read_o == 1'b0)reg1_o <= imm;              //立即数else reg1_o <= `ZeroWord;
end/*********************************************************************************
*************************** 第三阶段:确定进行运算的源操作数2***************************
*********************************************************************************/
//regfile读端口2的输出值
always @ (*)
beginif(rst == `RstEnable)reg2_o <= `ZeroWord;else // 对于源操作数,若是目前端口的读取得寄存器数据地址是  执行阶段的要写入的目的寄存器  ,那么直接将执行的结果作为reg2_o的值。//这个数相当于是要写入的数据if((reg2_read_o == 1'b1) && (ex_wreg_i == 1'b1) && (ex_wd_i == reg2_addr_o) )reg2_o <= ex_wdata_i;  else//对于要是目的寄存器,我们要读取的寄存器其实是最终访存要写入的寄存器,那么访存的数据就直接作为源操作数进行处理if((reg2_read_o == 1'b1) && (mem_wreg_i == 1'b1) && (mem_wd_i == reg2_addr_o) )reg2_o <= mem_wdata_i ;else if(reg2_read_o == 1'b1)reg2_o <= reg2_data_i;else if(reg2_read_o == 1'b0)reg2_o <= imm;              //立即数else reg2_o <= `ZeroWord;
end
endmodule

EX模块的修改

在执行阶段也进行一点小小的总结,这里的执行阶段其实就是计算机组成原理里的ALU,逻辑运算单元,因此这里其实处理的就是对译码到的信息进行处理,具体处理的方式是依据得到的信息,对数据进行计算,对寄存器进行读取(从执行阶段得到读写数据信息),其次就是进行运算!!!
这里的运算是核心功能,之前的运算比较简单,这里新加的运算,例如无符号有符号数的加减等等,都需要处理好,以及溢出等等的判断。
具体在代码中有详细的说明,后期有时间单独总结。下面是修改后的代码。

`timescale 1ns / 1ps
`include "defines.v"
module ex(input         wire                    rst         ,//译码送至执行阶段的数据信息input       wire    [`AluOpBus]        aluop_i     ,input      wire    [`AluSelBus]   alusel_i    ,input      wire    [`RegBus]      reg1_i      ,input      wire    [`RegBus]      reg2_i      ,input      wire    [`RegAddrBus]  wd_i        ,input      wire                    wreg_i      ,//执行结果output       reg     [`RegAddrBus]  wd_o        ,output         reg                     wreg_o      ,output         reg     [`RegBus]      wdata_o     ,//HILO模块给出的HI,LO寄存器的值input      wire    [`RegBus]      hi_i        ,input      wire    [`RegBus]      lo_i        ,//回写阶段的指令是否要写HI,LO,用于检测HI,LO寄存器带来的数据相关问题input         wire    [`RegBus]      wb_hi_i     ,input      wire    [`RegBus]      wb_lo_i     ,input      wire                    wb_whilo_i  ,//访存阶段的指令是否要写HI,LO,用于检测HI,LO寄存器带来的数据相关问题input         wire    [`RegBus]      mem_hi_i    ,input      wire    [`RegBus]      mem_lo_i    ,input      wire                    mem_whilo_i ,//处于执行阶段的指令对HI,LO寄存器的写操作请求output        reg     [`RegBus]      hi_o        ,output         reg     [`RegBus]      lo_o        ,output         reg                     whilo_o );
//保存逻辑运算的结果
reg [`RegBus]  logicout    ;//保存位移运算结果
reg [`RegBus]  shiftres    ;// 移动操作的结果
reg [`RegBus]  moveres     ;// 保存HI寄存器的最新值
reg [`RegBus]  HI          ;// 保存LO寄存器的最新值
reg [`RegBus]  LO          ;//define some new variables
wire                            ov_sum              ;   //保存溢出情况
wire                            reg1_eq_reg2        ;   //第一个操作数是否等于第二个操作数
wire                            reg1_lt_reg2        ;   //第一个操作数是否小于第二个操作数
reg     [`RegBus]              arithmeticres       ;   //保存算数运算的结果
wire    [`RegBus]              reg2_i_mux          ;   //保存输入的第二个操作数reg2_i的补码
wire    [`RegBus]              reg1_i_not          ;   //保存输入的第一个操作数reg1_i取反后的值
wire    [`RegBus]              result_sum          ;   //保存加法的结果
wire    [`RegBus]              opdata1_mult        ;   //乘法操作中的被乘数
wire    [`RegBus]              opdata2_mult        ;   //乘法操作中的乘数
wire    [`DoubleRegBus]        hilo_temp           ;   //临时保存乘法的结果,宽度为64位
reg     [`DoubleRegBus]            mulres              ;   //保存乘法的结果,宽度为64位/*********************************************************************************
*************** 依据aluop_i指示的运算子类型进行运算,
*********************************************************************************/always @ (*)
beginif( rst == `RstEnable)logicout <= `ZeroWord;else begincase(aluop_i)`EXE_OR_OP:beginlogicout <= reg1_i | reg2_i;end`EXE_AND_OP:beginlogicout <= reg1_i & reg2_i;end`EXE_NOR_OP:          //逻辑或与非beginlogicout <= ~(reg1_i | reg2_i);end`EXE_XOR_OP:beginlogicout <= reg1_i ^ reg2_i;enddefault:logicout <= `ZeroWord;endcaseend
endalways @ (*)
beginif(rst == `RstEnable)shiftres <= `ZeroWord;else case(aluop_i)`EXE_SLL_OP:     //逻辑左移shiftres <= reg2_i << reg1_i[4:0];`EXE_SRL_OP:shiftres <= reg2_i >> reg1_i[4:0];`EXE_SRA_OP:shiftres <= ( {32{ reg2_i[31]} } << (6'd32 - {1'b0,reg1_i[4:0] } ) ) | reg2_i >> reg1_i[4:0];default:beginshiftres <= `ZeroWord;end endcase
end/*********************************************************************************
*************** 第一阶段,得到最新的HI,LO寄存器的值,此处要解决数据相关问题
*********************************************************************************/
always @ (*)
beginif(rst == `RstEnable){HI,LO} <= {`ZeroWord,`ZeroWord};else if(mem_whilo_i == `WriteEnable){HI,LO} <= {mem_hi_i,mem_lo_i};      //访存阶段的指令要写入HI,LO寄存器中elseif(wb_whilo_i == `WriteEnable)   {HI,LO} <= {wb_hi_i,wb_lo_i};       //回写阶段的指令要写入HI,LO寄存器中else{HI,LO} <= {hi_i,lo_i};
end
/*********************************************************************************
*************** 第二阶段,MFHI,MFLO,MOVE,MOVZ指令
*********************************************************************************/
always @ (*)
beginif(rst == `RstEnable)moveres <= `ZeroWord;                 else                begin               moveres <= `ZeroWord;case(aluop_i)`EXE_MFHI_OP:moveres <= HI;         //如果是MFHI指令,HI的值就会作为移动操作的结果`EXE_MFLO_OP:moveres <= LO;          //如果是MFLO指令,LO的值就会作为移动操作的结果                                  `EXE_MOVZ_OP:moveres <= reg1_i;        //如果是MOVZ指令,reg1_i的值就会作为移动操作的结果      `EXE_MOVN_OP:moveres <= reg1_i;        //如果是MOVN指令,reg1_i的值就会作为移动操作的结果      default:;endcaseend
end
/*********************************************************************************
*************** 第三阶段:依据alusel_i指示的运算类型,确定wdata_o的值
*********************************************************************************///always @ (*)
//begin
//  wd_o <= wd_i;               //wd_o等于wd_i,要写入的寄存器地址
//  wreg_o <= wreg_i;           //wreg_o等于wreg_i,表示是否要写入目的寄存器
//  case(alusel_i)
//      `EXE_RES_LOGIC:
//          begin
//              wdata_o <= logicout;        //wdata_o中存放逻辑运算运算结果
//          end
//      `EXE_RES_SHIFT:
//          begin
//              wdata_o <= shiftres;        //wdata_o中存放位移运算运算结果
//          end
//      `EXE_RES_MOVE:
//          begin
//              wdata_o <= moveres;         //指令为EXE_RES_MOVE
//          end
//      default:
//          wdata_o <= `ZeroWord;
//  endcase
//end
/*********************************************************************************
*********   第四阶段:如果是MTHI,MTLO指令,那么需要给出的whilo_o,hi_o,lo_i的值
*********************************************************************************/
//always @ (*)
//begin
//  if(rst == `RstEnable)
//      begin
//          whilo_o <= `WriteDisable;
//          hi_o <= `ZeroWord;
//          lo_o <= `ZeroWord;
//      end
//  else
//      if(aluop_i == `EXE_MTHI_OP)
//          begin
//              whilo_o <= `WriteEnable;
//              hi_o <= reg1_i;
//              lo_o <= LO;
//          end
//      else
//          if(aluop_i == `EXE_MTLO_OP)
//              begin
//                  whilo_o <= `WriteEnable ;
//                  hi_o <= HI;
//                  lo_o <= reg1_i;
//              end
//          else
//              begin
//                  whilo_o <= `WriteDisable ;
//                  hi_o <= `ZeroWord;
//                  lo_o <= `ZeroWord;
//              end
//end/*********************************************************************************
*************** 第一段,计算以下5个变量的值
*********************************************************************************/
// (1)如果是减法或者有符号的比较运算,那么reg2_i_mux 等于第二个操作数reg2_i的补码,否则reg2_i_mux等于reg2_i
assign reg2_i_mux = ((aluop_i == `EXE_SUB_OP) || (aluop_i == `EXE_SUBU) || (aluop_i == `EXE_SLT_OP)) ? (~reg2_i) + 1 : reg2_i ;
// (2)分三种情况,
//  A:如果是加法运算,此时的reg2_i_mux就是第二个操作数reg2_i,因此result_sum进行的是加法运算的结果
//  B:如果是减法运算,此时的reg2_i_mux就是第二个操作数reg2_i的补码,因此result_sum进行的是减法运算的结果
//  C:如果是进行的有符号比较运算,此时的reg2_i_mux就是第二个操作数reg2_i的补码,因此result_sum进行的是减法运算的结果
//    可通过判断减法的结果是否小于0,进而判断第一个操作数reg1_i是否小于第二个操作reg2_i
assign result_sum = reg1_i + reg2_i_mux;
// (3)计算结果是否溢出,加法指令(add 和 addi ),减法指令(sub) 执行的时候,需要判断是否溢出,满足以下任意情况怎判断溢出
//  A:reg1_i为正数,reg2_i_mux为正数,但两者之和为负数
//  B:reg1_i为负数,reg2_i_mux为负数,但两者之和为正数
assign ov_sum = ((!reg1_i[31] && !reg2_i_mux[31]) && result_sum[31]) || ((reg1_i[31]) && reg2_i_mux[31] && (!result_sum[31]));
// (4)计算操作数1是否小于操作数2,分别有两种情况
//  A:aluop_i为EXE_SLT_OP表示有符号比较运算,此时分三种
//      A1:reg1_i为负数,reg2_i为正数,前者reg1_i小于后者reg2_i
//      A2:reg1_i为正数,reg2_i为正数,并且reg1_i减去reg2_i的值小于0
//          即result_sum为负数,此时有reg1_i小于reg2_i
//      A3:reg1_i为负数,reg2_i为负数,并且reg1_i减去reg2_i的值小于0
//          即result_sum为负数,此时有reg1_i小于reg2_i
//  B:无符号数比较的时候,直接使用比较运算符比较reg1_i于reg2_i
assign reg1_lt_reg2 = ((aluop_i == `EXE_SLT_OP) ) ? ((reg1_i[31] && !reg2_i[31]) || (!reg1_i[31] && !reg2_i[31] && result_sum[31]) || (reg1_i[31] && reg2_i[31] && result_sum[31]) ) : (reg1_i < reg2_i);
// (5)对操作数进行取反,幅值给reg1_i_not
assign reg1_i_not = ~reg1_i;/*********************************************************************************
*************** 第二段,依据不同的算术运算类型,给出arithmeeticres变量幅值
*********************************************************************************/
always @ (*)
beginif(rst == `RstEnable)arithmeticres <= `ZeroWord;else begincase(aluop_i)`EXE_SLT_OP,`EXE_SLTU_OP:beginarithmeticres <= reg1_lt_reg2;      //比较运算end`EXE_ADD_OP,`EXE_ADDU_OP,`EXE_ADDI_OP,`EXE_ADDIU_OP:beginarithmeticres <= result_sum;      //加法运算end`EXE_SUB_OP,`EXE_SUBU_OP:beginarithmeticres <= result_sum;       //减法运算end`EXE_CLZ_OP:  beginarithmeticres <= 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 ;end`EXE_CLO_OP:beginarithmeticres <= reg1_i_not[31] ? 0    : reg1_i_not[30] ? 1    :reg1_i_not[29]    ? 2    : reg1_i_not[28] ? 3    :reg1_i_not[27] ? 4    : reg1_i_not[26] ? 5    :reg1_i_not[25]   ? 6    : reg1_i_not[24] ? 7    :reg1_i_not[23] ? 8    : reg1_i_not[22] ? 9    :reg1_i_not[21]   ? 10   : reg1_i_not[20] ? 11   :reg1_i_not[19] ? 12   : reg1_i_not[18] ? 13   :reg1_i_not[17]   ? 14   : reg1_i_not[16] ? 15   :reg1_i_not[15] ? 16   : reg1_i_not[14] ? 17   :reg1_i_not[13]   ? 18   : reg1_i_not[12] ? 19   :reg1_i_not[11] ? 20   : reg1_i_not[10] ? 21   :reg1_i_not[ 9]   ? 22   : reg1_i_not[ 8] ? 23   :reg1_i_not[ 7] ? 24   : reg1_i_not[ 6] ? 25   :reg1_i_not[ 5]   ? 26   : reg1_i_not[ 4] ? 27   :reg1_i_not[ 3] ? 28   : reg1_i_not[ 2] ? 29   :reg1_i_not[ 1]   ? 30   : reg1_i_not[ 0] ? 31   : 32 ;enddefault:arithmeticres <= `ZeroWord;endcaseend
end/*********************************************************************************
*************** 第三段,进行乘法运算
*********************************************************************************/
// (1)取得乘法运算的被乘数,如果是有符号乘法且乘法被乘数是负数,那么取补码
assign opdata1_mult = ((aluop_i == `EXE_MUL_OP) || (aluop_i == `EXE_MULT_OP))&& (reg1_i[31] == 1'b1) ? (~reg1_i + 1) : reg1_i;
// (2)取得乘法运算的乘数,如果是有符号的乘法且乘数是负数,那么取补码
assign opdata2_mult = (((aluop_i == `EXE_MUL_OP) || (aluop_i == `EXE_MULT_OP))&& (reg2_i[31] == 1'b1)) ? (~reg2_i + 1) : reg2_i;
// (3) 得到临时乘法的结果,保存在变量hilo_temp中
assign hilo_temp = opdata1_mult * opdata2_mult;
// (4)对临时乘法结果进行修正,最终的乘法结果保存在变量mulres中,主要有两点:
//  A:对于有符号的乘法指令mult,mul,需要修正临时乘法的结果,如下:
//      A1:如果乘数与被乘数两者一正一负,那么需要对临时乘法结果hilo_temp
//         求补码,作为最终的结果付变量给mulres
//      A2:如果乘数与被乘数两者符号相同,临时乘法结果hilo_temp的结果可以直接使用
//  B:如果是无符号乘法指令multu,那么直接取hilo_temp作为最终结果的赋值给变量mulres
always @ (*) beginif(rst == `RstEnable) mulres <= {`ZeroWord,`ZeroWord};else if ((aluop_i == `EXE_MULT_OP) || (aluop_i == `EXE_MUL_OP))beginif(reg1_i[31] ^ reg2_i[31] == 1'b1) mulres <= ~hilo_temp + 1;elsemulres <= hilo_temp;end else mulres <= hilo_temp;
end
/*********************************************************************************
*************** 第四段,确定要写入的寄存器的数据
*********************************************************************************/
always @ (*)
beginbeginwd_o <= wd_i;             //wd_o等于wd_i,要写入的寄存器地址//wreg_o等于wreg_i,表示是否要写入目的寄存器if(( (aluop_i == `EXE_ADD_OP) || (aluop_i == `EXE_ADDI_OP) || (aluop_i == `EXE_SUB_OP)) && (ov_sum == 1'b1))wreg_o <= `WriteDisable;else wreg_o <= wreg_i;endcase(alusel_i)`EXE_RES_LOGIC:beginwdata_o <= logicout;     //wdata_o中存放逻辑运算运算结果end`EXE_RES_SHIFT:beginwdata_o <= shiftres;        //wdata_o中存放位移运算运算结果end`EXE_RES_MOVE:beginwdata_o <= moveres;          //指令为EXE_RES_MOVEend`EXE_RES_ARITHMETIC:beginwdata_o <= arithmeticres;end`EXE_RES_MUL:beginwdata_o <= mulres[31:0];enddefault:wdata_o <= `ZeroWord;endcase
end
/*********************************************************************************
*************** 第五段,对寄存器HI,LO进行操作的信息
*********************************************************************************/always @ (*)
beginif(rst == `RstEnable)beginwhilo_o <= `WriteDisable;hi_o <= `ZeroWord;lo_o <= `ZeroWord;      end else if((aluop_i == `EXE_MULT_OP ) || (aluop_i == `EXE_MULTU_OP))beginwhilo_o <= `WriteEnable;hi_o <= mulres[63:32];lo_o <= mulres[31:0];endelse if(aluop_i == `EXE_MTLO_OP)  beginwhilo_o <= `WriteEnable;hi_o <= reg1_i;lo_o <= LO;endelse if(aluop_i == `EXE_MTLO_OP) beginwhilo_o <= `WriteEnable ;hi_o <= HI;lo_o <= reg1_i;endelse     beginwhilo_o <= `WriteDisable ;hi_o <= `ZeroWord;lo_o <= `ZeroWord;end
endendmodule

测试与验证

测试与验证模块是比较困难的,这里一般出错首先检查在译码阶段是否得到一个正确的译码结果,其次是执行阶段,在正确译码是是否得到了译码的信息,然后依据信息,进行数据处理的时候,是否是我们预期的数据,最后检查数据是否成功写入寄存器,即可逻辑清楚的排查问题
接下来是验证阶段:

这里指令的作用都写的很清楚,笔者添加了两句注释,进行更详细的解释。因此寄存器的值就可以进行判断了。










verilog实现多周期处理器之——(六)简单算数操作指令的实现相关推荐

  1. verilog实现多周期处理器之——目录及总述

    本系列博文将使用verilog语言,实现兼容MIPS32指令集架构的处理器--OpenMIPS,MIPS是典型的RSIC处理器.主要依据雷思磊老师的<自己动手写CPU>,袁春风老师主编的& ...

  2. verilog实现多周期处理器之——(四)逻辑,移位操作与空指令的添加

    逻辑,移位操作与空指令的添加 综述 ID模块的修改 EX模块的修改 仿真验证 I-型指令 lui ori andi xori xor&nor R-型指令 or and 移位类指令 sll sr ...

  3. verilog实现多周期处理器之——(一)基本概念以及总体框架

    本系列博文将使用verilog语言,实现兼容MIPS32指令集架构的处理器--OpenMIPS,MIPS是典型的RSIC处理器.本系列博文参考雷思磊老师的<自己动手写CPU>,袁春风老师主 ...

  4. verilog实现多周期处理器之——(五)移动操作(通用数据传送)指令的实现

    本文参考作者 自己动手写CPU之第六阶段(1)--移动操作指令说明 自己动手写CPU之第六阶段(2)--移动操作指令实现思路 本文会添加笔者自己的思路以及理解在其中. 指令说明 这6条指令都是R类型指 ...

  5. verilog实现多周期处理器之——(二)第一条指令ori的实现

    本博文希望对于OpenMIPS第一条指令ori加以实现并总结.会加入一些基本的理论以及博主的学习记录. 流水与五级流水 什么是流水:拆分,并行.将多条指令的执行相互重叠起来.就构成了流水,这样充分利用 ...

  6. verilog实现多周期处理器之——(三)数据相关问题及其解决

    本文于自己动手写CPU之第五阶段--流水线数据相关问题 "相关"问题 流水线中常常有一些被称为"相关"的情况发生,它使得指令序列中下一条指令无法依照设计的时钟周 ...

  7. verilog实现多周期处理器之——(零)GUN工具链的安装

    参考雷思磊老师得<自己动手写CPU> 这里不需要下载书中说的虚拟机,这里笔者用的是VMware.不需要破解,直接安装点击选择仅用于非商业即可使用!! 安装Ubuntu这里笔者不给出步骤. ...

  8. 深圳软件测试培训:软件生命周期(SDLC)的六个阶段

    深圳软件测试培训:软件生命周期(SDLC)的六个阶段 1.问题的定义及规划 此阶段是软件开发方与需求方共同讨论,主要确定软件的开发目标及其可行性. 2.需求分析 在确定软件开发可行的情况下,对软件需要 ...

  9. xcode江湖录-第04章 风水宝地--界面生成器之StoryBoard简单示例 与 约束

    第04章风水宝地--界面生成器之StoryBoard简单示例 ??如何设置转场动画?? ??如何在参与到转场动作中?? ??如何让页面跳转到自定义VC?? ??如何设置自定义跳转模式?? ??如何用S ...

最新文章

  1. 二叉排序树的相关操作
  2. 用C#或JavaScript扩展XSLT
  3. java:输入输出流
  4. Item 16: 让const成员函数做到线程安全
  5. CNDO-INTGRL-SS-BINTGS-斯莱特轨道指数---递推方法
  6. nginx lua调用redis和mongo
  7. 《直播疑难杂症排查系列》之一 :播放失败
  8. ajax如何处理服务器返回的三种数据类型
  9. python glob.glob() 函数
  10. java通过POI技术将html转成word
  11. Bailian2973 Skew数【进制】
  12. 语言 泰克示波器程序_示波器再升级,EMI测试不求人
  13. 系统学习机器学习之神经网络(十) --BAM网络
  14. Sublime Text 无法安装插件
  15. 分布式机器学习(下)-联邦学习
  16. 使用版本控制软件TortoiseSVN对程序和文档进行控制的说明
  17. 计算机声卡原理,什么是电脑声卡 电脑声卡的工作原理
  18. u盾如何在计算机上使用方法,u盾在电脑中具体使用操作过程
  19. 360断网急救箱 dns服务器未响应,发现DNS服务异常,用360断网急救箱修复后过一段时间又复发。怎么办?...
  20. gromacs 安装_安装gromacs的一些心得

热门文章

  1. 解决sea.js引用jQuery提示$ is not a function的问题
  2. fatal: could not read Username for ‘https://git.dev.tencent.com‘ 解决方法
  3. java中自定义异常类
  4. latex : 系统找不到指定文件问题解决方案
  5. python URLError,HTTPError 的异常处理
  6. coc部落冲突关联错误101解决方案
  7. .NET Core,.NET Framework和Xamarin有什么区别?
  8. 有哪些开源C ++静态分析工具? [关闭]
  9. 远程源已存在于“ git push”到新存储库中
  10. C ++ Singleton设计模式