当前位置: 首页 > article >正文

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指令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

测试与验证

测试与验证模块是比较困难的,这里一般出错首先检查在译码阶段是否得到一个正确的译码结果,其次是执行阶段,在正确译码是是否得到了译码的信息,然后依据信息,进行数据处理的时候,是否是我们预期的数据,最后检查数据是否成功写入寄存器,即可逻辑清楚的排查问题
接下来是验证阶段:
在这里插入图片描述
这里指令的作用都写的很清楚,笔者添加了两句注释,进行更详细的解释。因此寄存器的值就可以进行判断了。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

http://www.lryc.cn/news/2418439.html

相关文章:

  • 本人跑fedora 13的烦恼
  • java定时器Timer 类的使用及Timer already cancelled问题解决
  • Apache License Version 2.0中文翻译版
  • 关于Tapestry5运行时报错的解决方法。
  • TransactionScope使用(二)——msdtc不可用
  • hmailserver的反垃圾邮件功能
  • 基于python豆瓣电影爬虫数据可视化分析推荐系统(完整系统源码+数据库+详细文档+论文+详细部署教程)
  • 《黄金瞳》 台词:你人真好!
  • FreeBSD下Mount总结
  • ISA Server 2004的安装与SMTP 筛选器的使用
  • ExtJS中表格控件的使用,属性设置和数据的获取
  • 深入浅出—设计模式重要原则
  • 网页客服代码集锦
  • TC20下的内联汇编 (转)
  • 让你的Vista,Win7变成多国语言版!
  • 信托公司利用境外资金的两大途径
  • Linux中的Netlink详解
  • Dialog.dismiss()方法无效的解决方法
  • 如何利用python盗qq_一个团队为了让我帮他提高流量,竟然盗我QQ,没办法,我只好帮他用python刷了刷流量!...
  • 电脑浏览器打不开但是可以上网微信和qq可以正常登录的解决办法
  • 中央处理器 —— CPU的功能和基本结构
  • PGXZ-腾讯全功能分布式关系数据集群
  • 一次macOS的升级填坑(macOS Catalina - macOS Monterey)
  • 笛卡尔积是什么?多表查询中的消除笛卡尔积。
  • Drupal10使用composer安装module和theme
  • UEFI与 Legacy BIOS两种启动模式详解
  • 常用CHM帮助文档集锦下载
  • html 全场开场动画,HTML5 星际大战电影开场字幕动画
  • 适用于 Java 程序员的 CSP ,第 2 部分
  • LCD常见接口总结