失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > verilog实现多周期处理器之——(六)简单算数操作指令的实现

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

时间:2023-07-11 06:56:55

相关推荐

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

实现的指令说明

这里解释有符号扩展与无符号扩展,有符号数扩展符号位。也就是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指令beginwreg_o <= `WriteEnable ;//ori 指令需要将结果写入目的寄存器,所以wreg_o 为 WriteEnablealuop_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 ;endendendend/************************************************************************************************************第二阶段:确定进行运算的源操作数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;endendmodule

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 variableswire 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;endcaseendendalways @ (*)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 endcaseend/************************************************************************************************第一阶段,得到最新的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:;endcaseendend/************************************************************************************************第三阶段:依据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_iassign 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_iassign 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_iassign 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_notassign 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;endcaseendend/************************************************************************************************第三段,进行乘法运算*********************************************************************************/// (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作为最终结果的赋值给变量mulresalways @ (*) 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;endcaseend/************************************************************************************************第五段,对寄存器HI,LO进行操作的信息*********************************************************************************/always @ (*)beginif(rst == `RstEnable)beginwhilo_o <= `WriteDisable;hi_o <= `ZeroWord;lo_o <= `ZeroWord;endelse 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;endendendmodule

测试与验证

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

接下来是验证阶段:

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

如果觉得《verilog实现多周期处理器之——(六)简单算数操作指令的实现》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。