站长工具seo排名查询,制作相册的软件,wordpress超链接,免费行情软件下载入口逻辑#xff0c;移位操作与空指令的添加 综述ID模块的修改EX模块的修改仿真验证I-型指令luioriandixorixornor R-型指令orand 移位类指令sllsrlsrasllvsrlvsravssnop、nop 综述 
文章引于自己动手写CPU之第五阶段#xff08;3#xff09;——MIPS指令集中的逻辑、移位与… 逻辑移位操作与空指令的添加 综述ID模块的修改EX模块的修改仿真验证I-型指令luioriandixorixornor R-型指令orand 移位类指令sllsrlsrasllvsrlvsravssnop、nop   综述 
文章引于自己动手写CPU之第五阶段3——MIPS指令集中的逻辑、移位与空指令 指令描述参考与博文[BOOK]感谢博主的分享  由于那个指令具有不同的指令格式但一共就只有三种因此这里将其进行区分进行译码以及执行相关代码需要进行修改。这里笔者直接加入的指令具体的指令作用在书中或者博文中都很明确有需要可以自行翻阅查看。 照例笔者会将自己对于代码的理解以及相关的必要知识记录总结在对应的目录下 
ID模块的修改 
首先是要确定指令的类型这里对对应的指令进行区分使用的方式是首先判断op然后若是op下有对应的指令依次同理进行判断。注意这里有一个例外情况就是使用inst_i[31:21]进行判断这里是单独分开进行判断的。具体见代码。由于具体的指令使用的寄存器数量不同对于每一个数据的处理也是不同的具体可以在书中找到对应的操作然后对应代码中查看。  
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 	 	1b0				;reg2_read_o 	 	1b0				;reg1_addr_o 	 	NOPRegAddr			;reg2_addr_o			NOPRegAddr			;imm 			 	32h0				;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 	 	1b0				;reg2_read_o 	 	1b0				;	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) 5b0000_0:begincase(op3)EXE_OR:beginwreg_o 		 WriteEnable;aluop_o 	 EXE_OR_OP;alusel_o 	 EXE_RES_LOGIC;reg1_read_o  1b1;reg2_read_o  1b1;instvalid 	 InstValid;endEXE_AND:beginwreg_o 		 WriteEnable;aluop_o 	 EXE_AND_OP;alusel_o 	 EXE_RES_LOGIC;reg1_read_o  1b1;reg2_read_o  1b1;instvalid 	 InstValid;endEXE_XOR:beginwreg_o 		 WriteEnable;aluop_o 	 EXE_XOR_OP;alusel_o 	 EXE_RES_LOGIC;reg1_read_o  1b1;reg2_read_o  1b1;instvalid 	 InstValid;endEXE_NOR:beginwreg_o 		 WriteEnable;aluop_o 	 EXE_NOR_OP;alusel_o 	 EXE_RES_LOGIC;reg1_read_o  1b1;reg2_read_o  1b1;instvalid 	 InstValid;endEXE_SLLV:beginwreg_o 		 WriteEnable;aluop_o 	 EXE_SLL_OP;alusel_o 	 EXE_RES_SHIFT;reg1_read_o  1b1;reg2_read_o  1b1;instvalid 	 InstValid;endEXE_SRLV:beginwreg_o 		 WriteEnable;aluop_o 	 EXE_SRL_OP;alusel_o 	 EXE_RES_SHIFT;reg1_read_o  1b1;reg2_read_o  1b1;instvalid 	 InstValid;endEXE_SRAV:beginwreg_o 		 WriteEnable;aluop_o 	 EXE_SRA_OP;alusel_o 	 EXE_RES_SHIFT;reg1_read_o  1b1;reg2_read_o  1b1;instvalid 	 InstValid;endEXE_SYNC:beginwreg_o 		 WriteEnable;aluop_o 	 EXE_NOP_OP;alusel_o 	 EXE_RES_SHIFT;reg1_read_o  1b0;reg2_read_o  1b1;instvalid 	 InstValid;enddefault:beginendendcaseenddefault:beginendendcaseendEXE_ORI:		//判断op的值是进行opi指令begin						wreg_o 		 WriteEnable 		;					//ori 指令需要将结果写入目的寄存器所以wreg_o 为 WriteEnable						aluop_o 	 EXE_OR_OP 	 		; 					//运算的子类型是逻辑“或”运算						alusel_o 	 EXE_RES_LOGIC 		;					//运算类型是逻辑运算reg1_read_o  1b1					;					//需要通过Regfile的读端口1读取寄存器reg2_read_o  1b0					;					//不需要通过Regfile的读端口2读取寄存器imm 		 {16h0,inst_i[15:0]} ;					//指令执行需要的立即数						wd_o 		 inst_i[20:16] 		;					//执行指令要写入的目的寄存器地址						instvalid 	 InstValid 			; 					//ori指令是有效的endEXE_ANDI:beginwreg_o 		 WriteEnable 		;aluop_o 	 EXE_AND_OP 	 		; alusel_o 	 EXE_RES_LOGIC 		;reg1_read_o  1b1					;reg2_read_o  1b0					;imm 		 {16h0,inst_i[15:0]} ;wd_o 		 inst_i[20:16] 		;instvalid 	 InstValid 			; endEXE_XORI:beginwreg_o 		 WriteEnable 		;aluop_o 	 EXE_XOR_OP 	 		; alusel_o 	 EXE_RES_LOGIC 		;reg1_read_o  1b1					;reg2_read_o  1b0					;imm 		 {16h0,inst_i[15:0]} ;wd_o 		 inst_i[20:16] 		;instvalid 	 InstValid 			; endEXE_LUI:beginwreg_o 		 WriteEnable 		;aluop_o 	 EXE_OR_OP 	 		; alusel_o 	 EXE_RES_LOGIC 		;reg1_read_o  1b1					;reg2_read_o  1b0					;imm 		 {inst_i[15:0],16h0 } ;wd_o 		 inst_i[20:16] 		;instvalid 	 InstValid 			; endEXE_PREF:beginwreg_o 		 WriteDisable 		;aluop_o 	 EXE_NOP_OP 	 		; alusel_o 	 EXE_RES_NOP 		;reg1_read_o  1b0					;reg2_read_o  1b0					;instvalid 	 InstValid 			; enddefault: beginendendcaseif(inst_i[31:21]  11b0000_0000_000) begin if(op3  EXE_SLL)beginwreg_o  	 		WriteEnable 	;aluop_o 	 		EXE_SLL_OP 	;alusel_o 	 		EXE_RES_SHIFT 	;reg1_read_o  		1b0 			;reg2_read_o  		1b1			;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  		1b0 			;reg2_read_o  		1b1			;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  		1b0 			;reg2_read_o  		1b1			;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  1b1)  (ex_wreg_i  1b1)  (ex_wd_i  reg1_addr_o) )reg1_o  ex_wdata_i;	else//对于要是目的寄存器我们要读取的寄存器其实是最终访存要写入的寄存器那么访存的数据就直接作为源操作数进行处理if((reg1_read_o  1b1)  (mem_wreg_i  1b1)  (mem_wd_i  reg1_addr_o) )reg1_o  mem_wdata_i ;else if(reg1_read_o  1b1)reg1_o  reg1_data_i;else if(reg1_read_o  1b0)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  1b1)  (ex_wreg_i  1b1)  (ex_wd_i  reg2_addr_o) )reg2_o  ex_wdata_i;	else//对于要是目的寄存器我们要读取的寄存器其实是最终访存要写入的寄存器那么访存的数据就直接作为源操作数进行处理if((reg2_read_o  1b1)  (mem_wreg_i  1b1)  (mem_wd_i  reg2_addr_o) )reg2_o  mem_wdata_i ;else if(reg2_read_o  1b1)reg2_o  reg2_data_i;else if(reg2_read_o  1b0)reg2_o  imm;				//立即数else reg2_o  ZeroWord;
end
endmoduleEX模块的修改 
执行阶段对于不同数以及不同的信息进行处理。新建寄存器用于保存移位寄存器的结果根据指令类型对象对相应的数据进行输出。 
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		);
//保存逻辑运算的结果
reg [RegBus]	logicout	;//保存位移运算结果
reg [RegBus] 	shiftres 	;// 移动操作的结果
reg [RegBus] 	moveres 	;/*********************************************************************************
***************	依据aluop_i指示的运算子类型进行运算
*********************************************************************************/always  (*)
beginif( rst  RstEnable)logicout  ZeroWord;else begincase(aluop_i)EXE_OR_OP:beginlogicout  reg1_i | reg2_i;endEXE_AND_OP:beginlogicout  reg1_i  reg2_i;endEXE_NOR_OP:  		//逻辑或与非beginlogicout  ~(reg1_i | reg2_i);endEXE_XOR_OP:beginlogicout  reg1_i ^ reg2_i;enddefault:logicout  ZeroWord;endcase;end
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]} }  (6d32 - {1b0,reg1_i[4:0] } ) ) | reg2_i  reg1_i[4:0];default:beginshiftres  ZeroWord;end endcase
end/*********************************************************************************
***************	第二阶段依据alusel_i指示的运算类型确定wdata_o的值
*********************************************************************************/
always  (*)
beginwd_o  wd_i;				//wd_o等于wd_i要写入的寄存器地址wreg_o  wreg_i;			//wreg_o等于wreg_i,表示是否要写入目的寄存器case(alusel_i)EXE_RES_LOGIC:beginwdata_o  logicout;		//wdata_o中存放逻辑运算运算结果endEXE_RES_SHIFT:beginwdata_o  shiftres;		//wdata_o中存放位移运算运算结果endEXE_RES_MOVE:beginwdata_o  moveres;			//指令为EXE_RES_MOVEenddefault:wdata_o  ZeroWord;endcase
end
endmodule仿真验证 
测试逻辑操作  首先分析测试数据。OpenMIPS将指令转换为ori指令来执行。lui指令使用立即数扩展后的立即数做或运算。得到0x01010000 之后进行两次或运算将数据分别写入寄存器1与寄存器2中。 接下来是使用寄存器中的数据进行或运算将结果送入1寄存器。 然后使用立即数与寄存器中的数据进行与运算结果送入寄存器3在使用寄存器1与寄存器3中的数据相与结果送寄存器1然后使用寄寄存器1中的数据与立即数相异或之后分别使用寄存器中的数据进行两次运算即可。  
I-型指令 lui lui: 把立即数加载到寄存器高位。ori 
逐位逻辑操作指令  
andi 
逐位逻辑操作指令 
xori 
逐位逻辑操作指令 异或运算指令使用方法为xori rt, rs, immediate 指令作用为rt - rs XOR zero_extended(immediate)。将地址为rs的通用寄存器的值。与指令中马上数进行零扩展后的值进行逻辑“异或”运算运算结果保存到地址为rt的通用寄存器中。 
xornor 
逐位逻辑操作指令 
R-型指令 or 
逐位逻辑操作指令 
and 
逐位逻辑操作指令 得到的数据与书中预期一致。 
测试移位操作与空指令 这里笔者不一一分析这里指出一个问题是在第六行的空指令加上会多一个时钟周期不加会少一个时钟周期。自己看仿真图即可理解。这里依据给出的源文件期间分析应该有五个时钟周期的延时。 简单分析在复位后第五个时钟上升沿寄存器2开始有数据然后更新之后的四个时钟周期对寄存器1,58进行操作因此加上其本身一个维持五个时钟周期。书中是维持了四个时钟周期是不对的。   不加时候的仿真截图  加上之后的  这里书中给出的是四个时钟周期的延时。这里上边已经进行了分析。   
移位类指令 sll 
当功能码是6’b000000表示是sll指令逻辑左移 指令使用方法为sll rd, rt, sa 指令作用为rd - rt  sa (logic)将地址为rt的通用寄存器的值向左移sa位。空出来的位置使用0填充。结果保存到地址为rd的通用寄存器中。srl 
当功能码是6’b000010表示是srl指令。逻辑右移 指令使用方法为srl rd, rt, sa 指令作用为rd - rt  sa (logic)将地址为rt的通用寄存器的值向右移sa位空出来的位置使用0填充结果保存到地址为rd的通用寄存器中。sra 
当功能码是6’b000011。表示是sra指令算术右移 指令使用方法为sra rd, rt, sa 指令作用为rd - rt  sa (arithmetic)将地址为rt的通用寄存器的值向右移sa位。空出来的位置使用rt[31]的值填充结果保存到地址为rd的通用寄存器中。sllv 
当功能码是6’b000100。表示是sllv指令逻辑左移 指令使用方法为sllv rd, rt, rs 指令作用为rd - rt  rs[4:0](logic)。将地址为rt的通用寄存器的值。向左移位空出来的位置使用0填充结果保存到地址为rd的通用寄存器中。移位位数由地址为rs的寄存器值的0-4bit确定。srlv 
当功能码是6’b000110表示是srlv指令。逻辑右移 指令使用方法为srlv rd, rt, rs 指令作用为rd - rt  rs[4:0](logic)将地址为rt的通用寄存器的值向右移位空出来的位置使用0填充。结果保存到地址为rd的通用寄存器中。移位位数由地址为rs的寄存器值的0-4bit确定。 
srav 
当功能码是6’b000111表示是srav指令算术右移 指令使用方法为srav rd, rt, rs 指令作用为rd - rt  rs[4:0](arithmetic)将地址为rt的通用寄存器的值向右移位。空出来的位置使用rt[31]填充结果保存到地址为rd的通用寄存器中。移位位数由地址为rs的寄存器值的0-4bit确定。 总结来说。这六条移位操作指令能够分为两种情况sllv、srav、srlv这3条指令的助记符最后有“v”。表示移位位数是通过寄存器的值确定的sll、sra、srl这3条指令的助记符最后没有“v”表示移位位数就是指令中6-10bit的sa的值。ssnop、nop 
空操作 nop:相当于 sll zero,zero,o ssnop: equals sll zero,zero,1. 这个指令不得与其它指令同时发送这样就保证了其运行要花费至少一个时钟周期。这在简单的流水线的CPU上无关紧要但在复杂些的实现上对于实现强制的延时很有用。  另外MIPS32指令集架构中还定义了sync、pref这2条指令当中sync指令用于保证载入、存储操作的顺序对于OpenMIPS而言是严格依照指令顺序运行的载入、存储操作也是依照顺序进行的所以能够将sync指令当作nop指令处理在这里将其归纳为空指令。pref指令用于缓存预取OpenMIPS没有实现缓存所以也能够将pref指令当作nop指令处理此处也将其归纳为空指令。