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的通用寄存器中。
计数指令
clz,clo这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指令的指令码是SPECIAL2,mult、multu的指令码是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实现多周期处理器之——(六)简单算数操作指令的实现相关推荐
- verilog实现多周期处理器之——目录及总述
本系列博文将使用verilog语言,实现兼容MIPS32指令集架构的处理器--OpenMIPS,MIPS是典型的RSIC处理器.主要依据雷思磊老师的<自己动手写CPU>,袁春风老师主编的& ...
- verilog实现多周期处理器之——(四)逻辑,移位操作与空指令的添加
逻辑,移位操作与空指令的添加 综述 ID模块的修改 EX模块的修改 仿真验证 I-型指令 lui ori andi xori xor&nor R-型指令 or and 移位类指令 sll sr ...
- verilog实现多周期处理器之——(一)基本概念以及总体框架
本系列博文将使用verilog语言,实现兼容MIPS32指令集架构的处理器--OpenMIPS,MIPS是典型的RSIC处理器.本系列博文参考雷思磊老师的<自己动手写CPU>,袁春风老师主 ...
- verilog实现多周期处理器之——(五)移动操作(通用数据传送)指令的实现
本文参考作者 自己动手写CPU之第六阶段(1)--移动操作指令说明 自己动手写CPU之第六阶段(2)--移动操作指令实现思路 本文会添加笔者自己的思路以及理解在其中. 指令说明 这6条指令都是R类型指 ...
- verilog实现多周期处理器之——(二)第一条指令ori的实现
本博文希望对于OpenMIPS第一条指令ori加以实现并总结.会加入一些基本的理论以及博主的学习记录. 流水与五级流水 什么是流水:拆分,并行.将多条指令的执行相互重叠起来.就构成了流水,这样充分利用 ...
- verilog实现多周期处理器之——(三)数据相关问题及其解决
本文于自己动手写CPU之第五阶段--流水线数据相关问题 "相关"问题 流水线中常常有一些被称为"相关"的情况发生,它使得指令序列中下一条指令无法依照设计的时钟周 ...
- verilog实现多周期处理器之——(零)GUN工具链的安装
参考雷思磊老师得<自己动手写CPU> 这里不需要下载书中说的虚拟机,这里笔者用的是VMware.不需要破解,直接安装点击选择仅用于非商业即可使用!! 安装Ubuntu这里笔者不给出步骤. ...
- 深圳软件测试培训:软件生命周期(SDLC)的六个阶段
深圳软件测试培训:软件生命周期(SDLC)的六个阶段 1.问题的定义及规划 此阶段是软件开发方与需求方共同讨论,主要确定软件的开发目标及其可行性. 2.需求分析 在确定软件开发可行的情况下,对软件需要 ...
- xcode江湖录-第04章 风水宝地--界面生成器之StoryBoard简单示例 与 约束
第04章风水宝地--界面生成器之StoryBoard简单示例 ??如何设置转场动画?? ??如何在参与到转场动作中?? ??如何让页面跳转到自定义VC?? ??如何设置自定义跳转模式?? ??如何用S ...
最新文章
- 二叉排序树的相关操作
- 用C#或JavaScript扩展XSLT
- java:输入输出流
- Item 16: 让const成员函数做到线程安全
- CNDO-INTGRL-SS-BINTGS-斯莱特轨道指数---递推方法
- nginx lua调用redis和mongo
- 《直播疑难杂症排查系列》之一 :播放失败
- ajax如何处理服务器返回的三种数据类型
- python glob.glob() 函数
- java通过POI技术将html转成word
- Bailian2973 Skew数【进制】
- 语言 泰克示波器程序_示波器再升级,EMI测试不求人
- 系统学习机器学习之神经网络(十) --BAM网络
- Sublime Text 无法安装插件
- 分布式机器学习(下)-联邦学习
- 使用版本控制软件TortoiseSVN对程序和文档进行控制的说明
- 计算机声卡原理,什么是电脑声卡 电脑声卡的工作原理
- u盾如何在计算机上使用方法,u盾在电脑中具体使用操作过程
- 360断网急救箱 dns服务器未响应,发现DNS服务异常,用360断网急救箱修复后过一段时间又复发。怎么办?...
- gromacs 安装_安装gromacs的一些心得
热门文章
- 解决sea.js引用jQuery提示$ is not a function的问题
- fatal: could not read Username for ‘https://git.dev.tencent.com‘ 解决方法
- java中自定义异常类
- latex : 系统找不到指定文件问题解决方案
- python URLError,HTTPError 的异常处理
- coc部落冲突关联错误101解决方案
- .NET Core,.NET Framework和Xamarin有什么区别?
- 有哪些开源C ++静态分析工具? [关闭]
- 远程源已存在于“ git push”到新存储库中
- C ++ Singleton设计模式