【自己动手写CPU】流水线暂停机制的设计和实现+乘累加乘累减的实现

背景

乘累加,乘累减,除法指令在流水线执行阶段占用多个周期,因此需要暂停流水线,以等待这些多周期指令执行完毕。
暂停流水线有两种方法实现。
一种直观的实现方法:保持取指令地址PC的值保持不变,同时保持流水线各个阶段的寄存器不变。
OpenMIPS采用的是一种改进过的方法:假如位于流水线第n阶段的指令需要多少个时钟周期,进行请求流水线暂停。 需要保持指令地址PC值不变,同时保持流水线第n个阶段,第n个阶段之前的各个阶段的寄存器不变,而第n阶段后面的指令继续进行。例如:流水线执行阶段的指令请求流水线暂停,保持PC不变,同时保持取指、译码、执行阶段的寄存器不变,但是可以允许访存、回写阶段的指令继续运行。
为此,添加CTRL模块,接收各阶段传递过来的流水线暂停请求信号,从而控制流水线各阶段的运行。

CTRL模块

在这里插入图片描述CTRL模块输入来自ID,EX模块的请求暂停信号stallreq。该模块对暂停请求信号进行判断,然后输出流水线暂停信号stall。
对于教学版OpenMIPS而言,只有译码、执行阶段可能会有暂停请求,取指、访存阶段没有暂停请求。
为了实现流水线暂停机制而对系统结构所做的修改
在这里插入图片描述

接口描述

接口名宽度(bit)输入/输出作用
rst1输入复位信号
stallreq_from_id1输入处于译码阶段的指令是否请求流水线暂停
stallreq_from_ex1输入处于执行阶段的指令是否请求流水线暂停
stall6输出暂停流水线控制信号

代码

//ctrl.v
module ctrl(
input wire rst,
input wire stallreq_from_id,
input wire stallreq_from_ex,
output reg[5:0] stall
);
/**************************说明******************************
*****stall[0]表示取指地址PC是否保持不变,为1表示保持不变*****
*******stall[1]表示流水线取指阶段是否暂停,为1表示暂停*******
*******stall[2]表示流水线译码阶段是否暂停,为1表示暂停*******
*******stall[3]表示流水线执行阶段是否暂停,为1表示暂停*******
*******stall[4]表示流水线访存阶段是否暂停,为1表示暂停*******
*******stall[5]表示流水线回写阶段是否暂停,为1表示暂停*******
************************************************************/
always @ (*) begin
	if(rst == `RstEnable) begin
		stall <= 6'b000000;
	end else if(stallreq_from_ex == `Stop) begin
		stall <= 6'b001111;
	end else if(stallreq_from_id == `Stop) begin
		stall <= 6'b000111;
	end else begin
		stall <= 6'b000000;
	end
end
endmodule

乘累加、乘累减指令

SPECIAL=011100

31-2625-2120-1615-1110-65-0useagefunction
SPECIAL2rsrt0000000000MADD(000000)madd rs,rt{HI,LO}<-{HI,LO}+rs*rt
SPECIAL2rsrt0000000000MADDU(000001)maddu rs,rt{HI,LO}<-{HI,LO}+rs*rt
SPECIAL2rsrt0000000000MSUB(000100)msub rs,rt{HI,LO}<-{HI,LO}-rs*rt
SPECIAL2rsrt0000000000MUSBU(000101)msubu rs,rt{HI,LO}<-{HI,LO}-rs*rt

实现思路

在流水线执行阶段采用两个时钟周期完成运算,第一个时钟周期进行乘法运算,第二个时钟周期将乘法结果与HI,LO寄存器进行加/减法。
需要保存的两个信息:

  • 当前是第几个时钟周期
  • 乘法结果
    通过在EX/MEM模块中添加两个寄存器cnt,hilo,分别保存以上两个信息。
    在这里插入图片描述hilo_temp_o是乘法结果
    cnt_o代表当前是第几个时钟周期,当在一个时钟周期送回到EX模块时,此时判断当前处于乘累加、乘累减的第几个执行周期

乘累加 乘累减代码

define.v

//全局
`define RstEnable 1'b1
`define RstDisable 1'b0
`define ZeroWord 32'h00000000
`define WriteEnable 1'b1
`define WriteDisable 1'b0
`define ReadEnable 1'b1
`define ReadDisable 1'b0
`define AluOpBus 7:0
`define AluSelBus 2:0
`define InstValid 1'b0
`define InstInvalid 1'b1
`define Stop 1'b1
`define NoStop 1'b0
`define InDelaySlot 1'b1
`define NotInDelaySlot 1'b0
`define Branch 1'b1
`define NotBranch 1'b0
`define InterruptAssert 1'b1
`define InterruptNotAssert 1'b0
`define TrapAssert 1'b1
`define TrapNotAssert 1'b0
`define True_v 1'b1
`define False_v 1'b0
`define ChipEnable 1'b1
`define ChipDisable 1'b0


//指令
`define EXE_AND  6'b100100
`define EXE_OR   6'b100101
`define EXE_XOR 6'b100110
`define EXE_NOR 6'b100111
`define EXE_ANDI 6'b001100
`define EXE_ORI  6'b001101
`define EXE_XORI 6'b001110
`define EXE_LUI 6'b001111

`define EXE_SLL  6'b000000
`define EXE_SLLV  6'b000100
`define EXE_SRL  6'b000010
`define EXE_SRLV  6'b000110
`define EXE_SRA  6'b000011
`define EXE_SRAV  6'b000111
`define EXE_SYNC  6'b001111
`define EXE_PREF  6'b110011

`define EXE_MOVZ  6'b001010
`define EXE_MOVN  6'b001011
`define EXE_MFHI  6'b010000
`define EXE_MTHI  6'b010001
`define EXE_MFLO  6'b010010
`define EXE_MTLO  6'b010011

`define EXE_SLT 6'b101010
`define EXE_SLTU 6'b101011
`define EXE_SLTI 6'b001010
`define EXE_SLTIU 6'b001011
`define EXE_ADD 6'b100000
`define EXE_ADDU 6'b100001
`define EXE_SUB 6'b100010
`define EXE_SUBU 6'b100011
`define EXE_ADDI 6'b001000
`define EXE_ADDIU 6'b001001
`define EXE_CLZ 6'b100000
`define EXE_CLO 6'b100001
`define EXE_MULT 6'b011000
`define EXE_MULTU 6'b011001
`define EXE_MUL 6'b000010

`define EXE_MADD 6'b000000
`define EXE_MADDU 6'b00001

`define EXE_MSUB 6'b000100
`define EXE_MSUBU 6'b000101


`define EXE_NOP 6'b000000
`define SSNOP 32'b00000000000000000000000001000000

`define EXE_SPECIAL_INST 6'b000000
`define EXE_REGIMM_INST 6'b000001
`define EXE_SPECIAL2_INST 6'b011100

//AluOp
`define EXE_AND_OP   8'b00100100
`define EXE_OR_OP    8'b00100101
`define EXE_XOR_OP  8'b00100110
`define EXE_NOR_OP  8'b00100111
`define EXE_ANDI_OP  8'b01011001
`define EXE_ORI_OP  8'b01011010
`define EXE_XORI_OP  8'b01011011
`define EXE_LUI_OP  8'b01011100   

`define EXE_SLL_OP  8'b01111100
`define EXE_SLLV_OP  8'b00000100
`define EXE_SRL_OP  8'b00000010
`define EXE_SRLV_OP  8'b00000110
`define EXE_SRA_OP  8'b00000011
`define EXE_SRAV_OP  8'b00000111

`define EXE_MOVZ_OP  8'b00001010
`define EXE_MOVN_OP  8'b00001011
`define EXE_MFHI_OP  8'b00010000
`define EXE_MTHI_OP  8'b00010001
`define EXE_MFLO_OP  8'b00010010
`define EXE_MTLO_OP  8'b00010011

`define EXE_SLT_OP 8'b00101010
`define EXE_SLTU_OP 8'b00101011
`define EXE_SLTI_OP 8'b01010111
`define EXE_SLTIU_OP 8'b01011000
`define EXE_ADD_OP 8'b00100000
`define EXE_ADDU_OP 8'b00100001
`define EXE_SUB_OP 8'b00100010
`define EXE_SUBU_OP 8'b00100011
`define EXE_CLZ_OP 8'b10110000
`define EXE_CLO_OP 8'b10110001
`define EXE_ADDI_OP 8'b01010101
`define EXE_ADDIU_OP 8'b01010110

`define EXE_MULT_OP 8'b00011000
`define EXE_MULTU_OP 8'b00011001
`define EXE_MUL_OP 8'b10101001

`define EXE_MADD_OP 8'b10100110
`define EXE_MADDU_OP 8'b10101000
`define EXE_MSUB_OP 8'b10101010
`define EXE_MSUBU_OP 8'b10101011

`define EXE_NOP_OP    8'b00000000

//AluSel
`define EXE_RES_LOGIC 3'b001
`define EXE_RES_SHIFT 3'b010
`define EXE_RES_MOVE 3'b011	
`define EXE_RES_ARITHMETIC 3'b100
`define EXE_RES_MUL 3'b101
`define EXE_RES_NOP 3'b000


//指令存储器inst_rom
`define InstAddrBus 31:0
`define InstBus 31:0
`define InstMemNum 131071
`define InstMemNumLog2 17


//通用寄存器regfile
`define RegAddrBus 4:0
`define RegBus 31:0
`define RegWidth 32
`define DoubleRegWidth 64
`define DoubleRegBus 63:0
`define RegNum 32
`define RegNumLog2 5
`define NOPRegAddr 5'b00000


`define Stop 1'b1 //流水线暂停
`define NoStop 1'b0 //流水线继续

pc_reg.v

`include "define.v"
//PC模块:给出指令地址
/*module pc_reg(
input wire clk,//时钟信号
input wire rst,//复位信号
output reg[`InstAddrBus]pc,//[31:0]要读取的指令地址,指令地址线宽度为32
output reg ce//指令存储器使能信号
);
always @(posedge clk) 
begin
	if(rst==`RstEnable)//rst=1,复位信号有效,当输入rst为高电平时,复位信号有效
	begin
		ce<= `ChipDisable;//指令存储器禁用
	end
	else //rst=0
	begin
		ce<=`ChipEnable;//复位结束后,指令存储器使能
	end
end
always @ (posedge clk)
begin
	if(ce==`ChipDisable)//指令存储器禁用
	begin
		pc<=32'h00000000;//指令存储器禁用时,PC为0
	end
	else
	begin
		pc<=pc+4'h4;//指令存储器使能时,PC的值每时钟周期加4
	end
end
endmodule*/
//`include "defines.v"

module pc_reg(

	input	wire										clk,
	input wire										rst,
	input wire[5:0] stall,//来自控制模块CTRL
	
	output reg[`InstAddrBus]			pc,
	output reg                    ce
	
);


always @ (posedge clk) begin
		if (rst == `RstEnable) begin
			ce <= `ChipDisable;
		end else begin
			ce <= `ChipEnable;
		end
	end


	always @ (posedge clk) begin
		if (ce == `ChipDisable) begin
			pc <= 32'h00000000;
		end else if(stall[0] == `NoStop)begin	
		//end else begin
	 		pc <= pc + 4'h4;
		end
	end
	
	/*always @ (posedge clk) begin
		if (rst == `RstEnable) begin
			ce <= `ChipDisable;
		end else begin
			ce <= `ChipEnable;
		end
	end*/

endmodule

inst_rom.v

`include "define.v"
//inst_rom.v
module inst_rom(
input wire ce,
input wire[`InstAddrBus] addr,
output reg[`InstBus] inst
);
//定义一个数组,大小是InstMemNum,元素宽度是InstBus
reg[`InstBus] inst_mem[0:`InstMemNum-1]; 
//使用文件inst_rom.data 初始化指令存储器
initial $readmemh ("F:/test_2/inst_rom_7_2.data",inst_mem);
always @ (*)
begin
if(ce == `ChipDisable)//当复位信号无效时,依据输入的地址,给出指令存储器ROM中对应的元素
begin
	inst <= `ZeroWord;
end
else begin
	inst <= inst_mem[addr[`InstMemNumLog2+1:2]];
end
end
endmodule

if_id.v

`include "define.v"
module if_id(

	input	wire										clk,
	input wire										rst,
	

	input wire[`InstAddrBus]			if_pc,
	input wire[`InstBus]          if_inst,
	input wire[5:0] stall, //来自控制模块的信息
	output reg[`InstAddrBus]      id_pc,
	output reg[`InstBus]          id_inst  
	
);
/**************************说明******************************
*****stall[0]表示取指地址PC是否保持不变,为1表示保持不变*****
*******stall[1]表示流水线取指阶段是否暂停,为1表示暂停*******
*******stall[2]表示流水线译码阶段是否暂停,为1表示暂停*******
*******stall[3]表示流水线执行阶段是否暂停,为1表示暂停*******
*******stall[4]表示流水线访存阶段是否暂停,为1表示暂停*******
*******stall[5]表示流水线回写阶段是否暂停,为1表示暂停*******
(1)stall[1]为Stop,stall[5]为NoStop,表示取指阶段暂停,译码阶段
继续,所以使用空指令作为下一个周期进入译码阶段的指令
(2)stall[1]为NoStop,取指阶段继续,取得的指令进入译码阶段
(3)其余情况下,保持译码阶段的寄存器id_pc,id_inst不变
************************************************************/

	always @ (posedge clk) begin
		if (rst == `RstEnable) begin
			id_pc <= `ZeroWord;
			id_inst <= `ZeroWord;
		end else if(stall[1] == `Stop && stall[2] == `NoStop)begin
			id_pc <= `ZeroWord;
			id_inst <= `ZeroWord;
		end else if(stall[1] == `NoStop)begin
			id_pc <= if_pc;
			id_inst <= if_inst;
		end
		/*end else if(stall[4] == `Stop && stall[5] == `NoStop)begin
			wb_wd <= `NOPRegAddr;
			wb_wreg <= `WriteDisable;
			wb_wdata <= `ZeroWord;
			wb_hilo <= `ZeroWord;
			wb_hi <= `ZeroWord;
			wb_lo <= `ZeroWord;
			wb_whilo <= `WriteDisable;
		end else if(stall[4] == `NoStop) begin
			wb_wd <= mem_wd;
			wb_wreg <= mem_wreg;
			wb_wdata <= mem_wdata;
			wb_hi <= mem_hi;
			wb_lo <= mem_lo;
			wb_whilo <= mem_whilo;
		end
	 /* end else begin
		  id_pc <= if_pc;
		  id_inst <= if_inst;
		end*/
	end

endmodule

id.v

`include "define.v"
module id(

	input wire										rst,
	input wire[`InstAddrBus]			pc_i,
	input wire[`InstBus]          inst_i,

	input wire[`RegBus]           reg1_data_i,
	input wire[`RegBus]           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 ex_wreg_i,
  	input wire[`RegBus] ex_wdata_i,
	input wire[`RegAddrBus] ex_wd_i,
	
	//处于访存阶段的指令的运算结果
	input wire mem_wreg_i,
	input wire[`RegBus] mem_wdata_i,
	input wire[`RegAddrBus] mem_wd_i,
	output wire stallreq
);

  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;
  
  assign stallreq = `NoStop;//在实现加载、存储指令时会给该信号赋值
  
 
	always @ (*) begin	
		if (rst == `RstEnable) begin
			aluop_o <= `EXE_NOP_OP;
			alusel_o <= `EXE_RES_NOP;//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;			
	  end else begin //初始化
			aluop_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];//rs
			reg2_addr_o <= inst_i[20:16];//rt
			imm <= `ZeroWord;			
		case (op)//指令码
		  	`EXE_SPECIAL_INST: begin //指令码是SPECIAL
		  		case(op2)//功能码
		  			5'b00000: begin
		  				case(op3) //依据功能码判断是哪一个指令
		  					`EXE_OR: begin //or R型指令 rs|rt -> rd
		  						wreg_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:begin
		  						wreg_o <= `WriteEnable;
		  						aluop_o <= `EXE_AND_OP;//R rs&rt ->rd
		  						alusel_o <=  `EXE_RES_LOGIC;
		  						reg1_read_o <= 1'b1;
		  						reg2_read_o <= 1'b1;
		  						instvalid <= `InstValid;
		  					end
		  					`EXE_XOR:begin
		  						wreg_o <= `WriteEnable;
		  						aluop_o <= `EXE_XOR_OP;// R rs^rt ->rd
		  						alusel_o <= `EXE_RES_LOGIC;
		  						reg1_read_o <= 1'b1;
		  						reg2_read_o <= 1'b1;
		  						instvalid <= `InstValid;
		  					end
		  					`EXE_NOR:begin
		  						wreg_o <= `WriteEnable;
		  						aluop_o <= `EXE_NOR_OP;// R rs~|rt ->rd
		  						alusel_o <= `EXE_RES_LOGIC;
		  						reg1_read_o <= 1'b1;
		  						reg2_read_o <= 1'b1;
		  						instvalid <= `InstValid;
		  					end
		  					`EXE_SLLV:begin
		  						wreg_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:begin
		  						wreg_o <= `WriteEnable;
		  						aluop_o <= `EXE_SRLV_OP;
		  						alusel_o <= `EXE_RES_SHIFT;
		  						reg1_read_o <= 1'b1;
		  						reg2_read_o <= 1'b1;
		  						instvalid <= `InstValid;
		  					end
		  					`EXE_SRAV:begin
		  						wreg_o <= `WriteEnable;
		  						aluop_o <= `EXE_SRAV_OP;
		  						alusel_o <= `EXE_RES_SHIFT;
		  						reg1_read_o <= 1'b1;
		  						reg2_read_o <= 1'b1;
		  						instvalid <= `InstValid;
		  					end
		  					`EXE_SYNC:begin
		  						wreg_o <= `WriteEnable;
		  						aluop_o <= `EXE_NOP_OP;
		  						alusel_o <= `EXE_RES_NOP;
		  						reg1_read_o <= 1'b0;
		  						reg2_read_o <= 1'b1;
		  						instvalid <= `InstValid;
		  					end
								`EXE_MFHI:begin//将特殊寄存器hi的值赋给地址为rd的寄存器
									wreg_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:begin
									wreg_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:begin//hi<-rs 写特殊寄存器
                	wreg_o <= `WriteEnable;
                	aluop_o <= `EXE_MTHI_OP;
                	alusel_o <= `EXE_RES_MOVE;
                	reg1_read_o <= 1'b1;
                	reg2_read_o <= 1'b0;
                	instvalid <= `InstValid;
                end
								`EXE_MTLO:begin //lo<-rs 写特殊寄存器
                	wreg_o <= `WriteEnable;
                	aluop_o <= `EXE_MTLO_OP;
                	reg1_read_o <= 1'b1;//rs
                	reg2_read_o <= 1'b0;
                	instvalid <= `InstValid;
                end
								`EXE_MOVN:begin//判断rt寄存器的值 如果不为0 将rs的值赋给rd 反之rd值不变
                	//wreg_o <= `WriteEnable;
                	aluop_o <= `EXE_MOVN_OP;
                	alusel_o <= `EXE_RES_MOVE;
                	reg1_read_o <= 1'b1;
                	reg2_read_o <= 1'b1;
                	instvalid <= `InstValid;
                	//reg2_o的值就是地址为rt的寄存器的值
                	if(reg2_o != `ZeroWord)begin
                		wreg_o <= `WriteEnable;
                	end else begin
                		wreg_o <= `WriteDisable;
                	end
                end
								`EXE_MOVZ:begin //判断rt寄存器的值 如果是0 将rs的值赋给rd 反之rd值不变
                	aluop_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)begin
                		wreg_o <= `WriteEnable;
                	end else begin
                		wreg_o <= `WriteDisable;
                	end 
                end
								`EXE_SLT:begin//slt指令 rd<-(rs<rt)
                	wreg_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:begin //sltu指令
                	wreg_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:begin//rd<-rs+rt
                	wreg_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:begin
                	wreg_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:begin
                	wreg_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:begin
                	wreg_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:begin
                	wreg_o <= `WriteEnable;
                	aluop_o <= `EXE_MULT_OP;
                	reg1_read_o <= 1'b1;
                	reg2_read_o <= 1'b1;
                	instvalid <= `InstValid;
                end
								`EXE_MULTU:begin
                	wreg_o <= `WriteEnable;
                	aluop_o <= `EXE_MULTU_OP;
                	reg1_read_o <= 1'b1;
                	reg2_read_o <= 1'b1;
                	instvalid <= `InstValid;
                end 	
		  					default:begin
							//aluop_o <= `EXE_NOP_OP;
							//alusel_o <= `EXE_RES_OP;
							//wd_o <= `NOPRegAddr;
							//wreg_o <= `WriteDisable;
							//instvalid <= `InstValid;
							//reg1_read_o <= `ReadDisable;
							//reg2_read_o <= `ReadDisable;
							//reg1_addr_o <= inst_i[25:21];
							//reg2_addr_o <= inst_i[20:16];
		  					end
		  				endcase//op3
		  			end	// 5'b00000
					default:begin
				end
				endcase		// op2	
			end	
		  	`EXE_ORI:begin                        //ORI指令
		  		wreg_o <= `WriteEnable;		
		  		aluop_o <= `EXE_OR_OP;
		  		alusel_o <= `EXE_RES_LOGIC; 
		  		reg1_read_o <= 1'b1;
		  		reg2_read_o <= 1'b0;	  	
					imm <= {16'h0, inst_i[15:0]};	//立即数0扩展
					wd_o <= inst_i[20:16]; // 读取rt地址
					instvalid <= `InstValid;	
		  	end 
		  	`EXE_ANDI:begin //andi
		  		wreg_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];//rt
		  		instvalid = `InstValid;
		  	end
		  	`EXE_XORI:begin//xori
		  		wreg_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:begin//lui
		  		wreg_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:	begin//pref
		  		wreg_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:begin //slti   rt <- (rs < (sign_extended)immediate)
		  		wreg_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:begin
		  		wreg_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:begin
		  		wreg_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:begin
		  		wreg_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:begin//(op)
				case(op3)
					`EXE_CLZ:begin
		  				wreg_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:begin
		  				wreg_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:begin
		  				wreg_o <= `WriteEnable;
		  				aluop_o <= `EXE_MUL_OP;
		  				alusel_o <= `EXE_RES_MUL;
		  				reg1_read_o <= 1'b1;
		  				reg2_read_o <= 1'b1;
		  				instvalid <= `InstValid;
		  			end
					`EXE_MADD:begin 
		  				wreg_o <= `WriteDisable;
		  				aluop_o <= `EXE_MADD_OP;
		  				alusel_o <= `EXE_RES_MUL;
		  				reg1_read_o <= 1'b1;
		  				reg2_read_o <= 1'b1;
		  				instvalid <= `InstValid;
		  			end
					`EXE_MADDU:begin
		  				wreg_o <= `WriteDisable;
		  				aluop_o <= `EXE_MADDU_OP;
		  			  alusel_o <= `EXE_RES_MUL;
		  				reg1_read_o <= 1'b1;
		  				reg2_read_o <= 1'b1;
		  				instvalid <= `InstValid;
		  			end
					`EXE_MSUB:begin
		  				wreg_o <= `WriteDisable;
		  				aluop_o <= `EXE_MSUB_OP;
		  				alusel_o <= `EXE_RES_MUL;
		  				reg1_read_o <= 1'b1;
		  				reg2_read_o <= 1'b1;
		  				instvalid <= `InstValid;
		  			end
					`EXE_MSUBU:begin
		  				wreg_o <= `WriteDisable;
		  				aluop_o <= `EXE_MSUBU_OP;
		  				alusel_o <= `EXE_RES_MUL;
		  				reg1_read_o <= 1'b1;
		  				reg2_read_o <= 1'b1;
		  				instvalid <= `InstValid;
		  			end
					default:begin
					 end
				endcase //EXE_SPECIAL_INST2 case
			end				 
		    default:begin
		    end
		endcase		  //case op		
		if(inst_i[31:21] == 11'b00000000000)begin //sll,srl,sra
		  	if(op3 == `EXE_SLL) begin
		  		wreg_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;
		  	end else if(op3 == `EXE_SRL)begin
		  		wreg_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;
		  	end else if(op3 == `EXE_SRA) begin
		  		wreg_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;
		  	end
//endcase
		  	//end
		end       //if
	end 
end        //always
	
/*       数据前推
给reg1_o赋值过程增加了两种情况
1:如果Regfile模块读端口1要读取的寄存器就是执行阶段要写的目的寄存器,那么直接把执行阶段的结果ex_wdata_i作为reg1_o的值
2:如果Regfile模块读端口1要读取的寄存器就是访存阶段要写的目的寄存器,那么直接把访存阶段的结果mem_wdata_i作为reg1_o的值*/
	always @ (*) begin
		if(rst == `RstEnable) begin
			reg1_o <= `ZeroWord;		
		end else if((reg1_read_o == 1'b1) && (ex_wreg_i == 1'b1) 
								&& (ex_wd_i == reg1_addr_o)) begin
			reg1_o <= ex_wdata_i; 
		end else if((reg1_read_o == 1'b1) && (mem_wreg_i == 1'b1) 
								&& (mem_wd_i == reg1_addr_o)) begin
			reg1_o <= mem_wdata_i; 			
	  end else if(reg1_read_o == 1'b1) begin
	  	reg1_o <= reg1_data_i;
	  end else if(reg1_read_o == 1'b0) begin
	  	reg1_o <= imm;
	  end else begin
	    reg1_o <= `ZeroWord;
	  end
	end
	
	always @ (*) begin
		if(rst == `RstEnable) begin
			reg2_o <= `ZeroWord;
		end else if((reg2_read_o == 1'b1) && (ex_wreg_i == 1'b1) 
								&& (ex_wd_i == reg2_addr_o)) begin
			reg2_o <= ex_wdata_i; 
		end else if((reg2_read_o == 1'b1) && (mem_wreg_i == 1'b1) 
								&& (mem_wd_i == reg2_addr_o)) begin
			reg2_o <= mem_wdata_i;			
	  end else if(reg2_read_o == 1'b1) begin
	  	reg2_o <= reg2_data_i;
	  end else if(reg2_read_o == 1'b0) begin
	  	reg2_o <= imm;
	  end else begin
	    reg2_o <= `ZeroWord;
	  end
	end
endmodule
//2021.01.18对译码模块添加移动操作指令的操作*/

regfile.v

`include "define.v"
//回写阶段的实现实际上是在Regfile模块中实现的
//Regfile模块 寄存器堆
module regfile(
input wire clk,//时钟信号
input wire rst,//复位信号,高电平有效
//写端口
input wire we,//写使能信号
input wire[`RegAddrBus] waddr,//要写入的寄存器地址
input wire[`RegBus] wdata,//要写入的数据
//读端口1
input wire re1,//第一个读寄存器端口读使能信号
input wire[`RegAddrBus] raddr1,//第一个读寄存器端口要读取的寄存器的地址
output reg[`RegBus] rdata1,//第一个读寄存器端口输出寄存器的值
//读端口2
input wire re2,//第二个读寄存器端口读使能信号
input wire[`RegAddrBus] raddr2,//第二个读寄存器端口要读取的寄存器地址
output reg[`RegBus] rdata2//第二个读寄存器端口要输出的寄存器的值
);
/*******第一段: 定义32个32位寄存器*******/
/*******第二段:写操作******************/
/*******第三段:读端口1的读操作********/
/*******第四段:读端口2的读操作*******/



//第一段:定义32个32位寄存器

reg[`RegBus] regs[0:`RegNum-1];

//第二段:实现写寄存器操作
always @ (posedge clk)
begin
	if(rst==`RstDisable)//rst==1,复位信号无效
	begin
		if((we==`WriteEnable)&&(waddr!=`RegNumLog2'h0))//写使能信号we有效 且 写操作目的寄存器不等于0的情况下
		begin//MIPS32架构规定 $0的值只能为0 所以不需要写入	
			regs[waddr]<=wdata;//将写输入数据保存到目的寄存器
		end
	end
end


//第三段:读端口1的读操作 实现第一个读寄存器端口
always @(*) 
begin 
	if(rst==`RstEnable)//当复位信号有效时
	begin
		rdata1 <= `ZeroWord;//第一个读寄存器端口的输出始终为0
	end
	else if(raddr1==`RegNumLog2'h0)//当复位信号无效时 如果读取的是$0 
	begin
		rdata1<=`ZeroWord;//直接给出0
	end
	else if((raddr1==waddr)&&(we==`WriteEnable)&&(re1==`ReadEnable))//如果第一个读寄存器端口要读取的目标寄存器与要写入的目的寄存器是同一个
	begin
		rdata1<=wdata;//直接将要写入的值作为第一个寄存器端口的输出
	end
	else if(re1==`ReadEnable)//上述情况都不满足时 
	begin
		rdata1<=regs[raddr1];//给出第一个读寄存器端口要读取的目标寄存器地址对应寄存器的值
	end 
	else//当第一个寄存器端口不能使用时
	begin
		rdata1<=`ZeroWord;//直接输出0
	end
end
//第四段 读端口2的操作 实现第二个寄存器端口 具体过程和第三段相似 注意地址的变化
always @ (*)
begin
	if(rst==`RstEnable)
	begin
		rdata2<=`ZeroWord;
	end
	else if(raddr2==`RegNumLog2'h0)
	begin
		rdata2<=`ZeroWord;
	end
	else if((raddr2==waddr)&&(we==`WriteEnable)&&(re2==`ReadEnable))
	begin
		rdata2<=wdata;
	end
	else if(re2==`ReadEnable)
	begin
		rdata2<=regs[raddr2];
	end
	else
	begin
		rdata2<=`ZeroWord;
	end
end
endmodule

id_ex.v

`include "define.v"
/*//将译码阶段取得的运算类型、源操作数、要写的目的寄存器等结果在下一个时钟递到流水线执行阶段
module id_ex(
input wire clk,//复位信号
input wire rst,//时钟信号
//从译码阶段传递过来的信息
input wire[`AluOpBus]  id_aluop,//译码阶段指令要执行的运算的子类型
input wire[`AluSelBus]  id_alusel,//译码阶段要执行的运算的类型
input wire[`RegBus]  id_reg1,//译码阶段指令要进行的运算的源操作数1
input wire[`RegBus]  id_reg2,//译码阶段指令要执行的运算的源操作数2
input wire[`RegAddrBus]   id_wd,//译码阶段的指令要写入的目的寄存器的地址
input wire  id_wreg,//译码阶段的指令是否有要写入的目的寄存器
//传递到执行阶段的信息
output reg[`AluOpBus]  ex_aluop,//执行阶段的指令要进行的运算的子类型
output reg[`AluSelBus]  ex_alusel,//执行阶段要进行的类型
output reg[`RegBus]  ex_reg1,//执行阶段的指令要进行运算的源操作数1
output reg[`RegBus]  ex_reg2,//执行阶段的指令要进行运算的源操作数2
output reg[`RegAddrBus]  ex_wd,//执行阶段的指令要写入的目的寄存器地址
output reg   ex_wreg //执行阶段的指令是否要写入的目的寄存器
);

always @(posedge clk)
begin
	if(rst == `RstEnable)//复位有效
	begin
		ex_aluop <= `EXE_NOP_OP;
		ex_alusel <= `EXE_RES_NOP;
		ex_reg1 <= `ZeroWord;
		ex_reg2 <= `ZeroWord;
		ex_wd <= `NOPRegAddr;
		ex_wreg <= `WriteDisable;
	end
else 
begin
	ex_aluop <= id_aluop;
	ex_alusel <= id_alusel;
	ex_reg1 <= id_reg1;
	ex_reg2 <= id_reg2;
	ex_wd <= id_wd;
	ex_wreg <= id_wreg;
end
end
endmodule
//在时钟周期的上升沿,将译码阶段的结果传递到执行阶段*/
module id_ex(

	input	wire										clk,
	input wire										rst,

	
	//从译码阶段传递的信息
	input wire[`AluOpBus]         id_aluop,
	input wire[`AluSelBus]        id_alusel,
	input wire[`RegBus]           id_reg1,
	input wire[`RegBus]           id_reg2,
	input wire[`RegAddrBus]       id_wd,
	input wire                    id_wreg,
	
	//来自控制模块的信息
	input wire[5:0] stall	,
	
	//传递到执行阶段的信息
	output reg[`AluOpBus]         ex_aluop,
	output reg[`AluSelBus]        ex_alusel,
	output reg[`RegBus]           ex_reg1,
	output reg[`RegBus]           ex_reg2,
	output reg[`RegAddrBus]       ex_wd,
	output reg                    ex_wreg
	
);


	always @ (posedge clk) begin
		if (rst == `RstEnable) begin
			ex_aluop <= `EXE_NOP_OP;
			ex_alusel <= `EXE_RES_NOP;
			ex_reg1 <= `ZeroWord;
			ex_reg2 <= `ZeroWord;
			ex_wd <= `NOPRegAddr;
			ex_wreg <= `WriteDisable;
		end else if(stall[2] == `Stop && stall[3] == `NoStop)begin
			ex_aluop <= `EXE_NOP_OP;
			ex_alusel <= `EXE_RES_NOP;
			ex_reg1 <= `ZeroWord;
			ex_reg2 <= `ZeroWord;
			ex_wd <= `NOPRegAddr;
			ex_wreg <= `WriteDisable;
		end else if(stall[2] == `NoStop)begin
			ex_aluop <= id_aluop;
			ex_alusel <= id_alusel;
			ex_reg1 <= id_reg1;
			ex_reg2 <= id_reg2;
			ex_wd <= id_wd;
			ex_wreg <= id_wreg;
		/*end else begin		
			ex_aluop <= id_aluop;
			ex_alusel <= id_alusel;
			ex_reg1 <= id_reg1;
			ex_reg2 <= id_reg2;
			ex_wd <= id_wd;
			ex_wreg <= id_wreg;		
		end*/
	end
	end
	
endmodule
	

ex.v

`include "define.v"
//ex.v 执行模块
module ex(
//译码阶段送到执行阶段的信息
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,
input wire rst,
//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,

//增加的输入端口
input wire[`DoubleRegBus] hilo_temp_i,//保存乘法结果
input wire[1:0] cnt_i,//处于执行阶段的第几个周期
//处于执行阶段指令对LO,HI寄存器的写操作请求
output reg[`RegBus] hi_o,
output reg[`RegBus] lo_o,
output reg whilo_o,

//执行的结果
output reg[`RegAddrBus] wd_o,
output reg wreg_o,
output reg[`RegBus] wdata_o,
output reg stallreq,

output reg[`DoubleRegBus] hilo_temp_o,
output reg[1:0] cnt_o

);
//保存逻辑运算的结果
reg[`RegBus] logicout;
//保存移位运算的结果
reg[`RegBus] shiftres;
//保存移动操作的结果
reg[`RegBus] moveres;
//保存HI,LO寄存器的最新值
reg[`RegBus] HI;
reg[`RegBus] LO;
/***********************第七章新定义一些变量***********************/
wire ov_sum;//保存溢出情况
wire reg1_eq_reg2;//第一个操作数是否等于第二个操作数
wire reg1_lt_reg2;//第一个操作数是否小于第二个操作数
reg[`RegBus] arithmeticres;//保存算术运算的结果
reg[`DoubleRegBus] mulres;//保存乘法运算的结果
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] hilo_temp1;
reg stallreq_for_madd_msub;

/*******************************************************************
**            第一段:依据aluop_i指示的运算子类型进行运算          **
*******************************************************************/
always @ (*) 
begin//1
	if(rst == `RstEnable)
	begin//2
		logicout <= `ZeroWord;
	end//2
	else
	begin//3
		case(aluop_i)//4
			`EXE_OR_OP:begin//5 逻辑或
				logicout <= reg1_i|reg2_i;
			end 
			`EXE_AND_OP: begin //逻辑与
				logicout <= reg1_i&reg2_i;
			end
			`EXE_NOR_OP:begin //逻辑或非
				logicout <= ~(reg1_i|reg2_i);
			end
			`EXE_XOR_OP:begin //逻辑异或
				logicout <= reg1_i^reg2_i;
			end
			default:begin//6
				logicout <= `ZeroWord;
			end//6
		endcase//4
	end//3
end//1
always @ (*) begin
	if(rst == `RstEnable)begin
		shiftres <= `ZeroWord;
	end else begin
		case(aluop_i)
			`EXE_SLL_OP:begin //逻辑左移
				shiftres <= reg2_i << reg1_i[4:0];
			end
			`EXE_SRL_OP:begin //逻辑右移
				shiftres <= reg2_i >> reg1_i[4:0];
			end
			`EXE_SRA_OP:begin//算术右移1
				shiftres <= ({32{reg2_i[31]}}<<(6'd32-{1'b0,reg1_i[4:0]})) |/*rt向右移sa位*/ reg2_i>>reg1_i[4:0];
			end
			default:begin
			 shiftres <= `ZeroWord;
		end
	endcase
end
end
	
/******************************************************************
****第三段:得到最新的HI,LO寄存器的值,此处要解决数据相关的问题****
******************************************************************/
always @ (*)begin
	if(rst == `RstEnable) begin
		{HI,LO} <= {`ZeroWord,`ZeroWord};
	end else if(mem_whilo_i == `WriteEnable)begin
		{HI,LO} <= {mem_hi_i,mem_lo_i};//访存阶段的指令要写HI,LO寄存器
	end else if(wb_whilo_i == `WriteEnable)begin
		{HI,LO} <= {wb_hi_i,wb_lo_i};//回写阶段的指令要写HI,LO寄存器
end
end
/*******************************************************************
******************第四段:MFHI,MFLO,MOVN,MOVZ指令********************
*******************************************************************/
always @ (*) begin
	if(rst == `RstEnable) begin
		moveres <= `ZeroWord;
	end else begin
		moveres <= `ZeroWord;
		case(aluop_i)
			`EXE_MFHI_OP:begin
				//rd<-hi
				moveres <= HI;//HI的值是移动操作的结果
			end
			`EXE_MFLO_OP:begin
				//rd<-lo
				moveres <= LO;
			end
			`EXE_MOVN_OP:begin
				//rd<-rs
				moveres <= reg1_i;
			end
			`EXE_MOVZ_OP:begin
				//rd<-rs
				moveres <= reg1_i;
			end
			default:begin
			end
		endcase
	end
end
/***************************************************************
*******如果是MTHI,MTLO指令,需要给出whilo_o,hi_o,lo_o的值*******
***************************************************************/                 
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;//写HI寄存器所以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
//end
//endmodule
/*************************************************************
******************第五段:计算以下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_OP)||(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为负数,但是两者之和为正数*/
//这个我不理解 艹    2022.3.10理解了
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表示有符号比较运算
1.reg1_i为负数、reg2_i为正数,显然reg1_i小于reg2_i
2.reg1_i为正数、reg2_i为正数,并且reg1_i减去reg2_i的值小于0(result_sum为负)此时reg1_i小于reg2_i
3.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)对操作数1逐位取反 赋值给reg1_i_not
assign reg1_i_not = ~reg1_i;
/*****************************************************************
*****第六段:依据不同的算术运算类型,给arithmeticres变量赋值*******
*****************************************************************/
always @ (*) begin
	if(rst == `RstEnable)begin
		arithmeticres <= `ZeroWord;
	end else begin
		case(aluop_i) //选择运算类型
			`EXE_SLT_OP,`EXE_SLTU_OP:begin
				arithmeticres <= reg1_lt_reg2;//比较运算
			end
			`EXE_ADD_OP,`EXE_ADDU_OP,`EXE_ADDI_OP,`EXE_ADDIU_OP:begin
				arithmeticres <= result_sum;//加法运算
			end
			`EXE_SUB_OP,`EXE_SUBU_OP:begin
				arithmeticres <= result_sum;//减法运算
			end
			`EXE_CLZ_OP:begin //计数运算clz 
				arithmeticres <= 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]?20:reg1_i[0]?31:32;
												end
			`EXE_CLO_OP:begin //计数运算clo
				arithmeticres <= (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);
												end
												default:begin
													arithmeticres <= `ZeroWord;
												end
											endcase
										end
									end 
/*****************************************************************
************************第七段:进行乘法运算***********************
*****************************************************************/
/*(1)取得乘法运算的被乘数 指令 madd,msub都是有符号乘法,如果第一个操作数reg1_i是负数
那么取reg1_i的补码为被乘数,反之直接使用reg1_i作为被乘数 如果是有符号乘法且被乘数是负数 那么取补码*/
assign opdata1_mult = (((aluop_i == `EXE_MUL_OP)||(aluop_i == `EXE_MULT_OP)||(aluop_i == `EXE_MADD_OP)||(aluop_i == `EXE_MSUB_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)||(aluop_i == `EXE_MADD_OP)||(aluop_i == `EXE_MSUB_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的值就作为最终的乘法结果,赋给变量mulres
B:如果是无符号乘法指令multu,那么hilo_temp的值就作为最终的乘法结果,赋给变量mulres
*/
always @ (*) begin//1
	if(rst == `RstEnable) begin//2
		mulres <= {`ZeroWord,`ZeroWord};
	end/*2*/ else if((aluop_i == `EXE_MULT_OP)||(aluop_i == `EXE_MUL_OP)||(aluop_i == `EXE_MADD_OP)||(aluop_i == `EXE_MSUB_OP))begin//3
		if(reg1_i[31]^reg2_i[31] == 1'b1)begin//4 被乘数和乘数一正一负
			mulres <= ~hilo_temp+1;
		end/*4*/ else begin//5 被乘数和乘数同号
			mulres <= hilo_temp;
		end//5
		end/*3*/ else begin//6 无符号乘法
			mulres <= hilo_temp;
		end//6
	end//1
/*****************************************************************
****************第八段:确定要写入目的寄存器的数据*****************
*****************************************************************/
always @ (*)begin
	wd_o <= wd_i;
	//如果是add,addi,sub,subi指令,且发生溢出,那么设置wreg_o为WriteEnable 表示不写目的寄存器
	if(((aluop_i == `EXE_ADD_OP)||(aluop_i == `EXE_ADDI_OP)||(aluop_i == `EXE_SUB_OP))&&(ov_sum == 1'b1))begin
		wreg_o <= `WriteDisable;
	end else begin
		wreg_o <= wreg_i;
	end
	case(alusel_i)
		`EXE_RES_LOGIC:begin
			wdata_o <= logicout;
		end
		`EXE_RES_SHIFT:begin
			wdata_o <= shiftres;
		end
		`EXE_RES_MOVE:begin
			wdata_o <= moveres;
		end
		`EXE_RES_ARITHMETIC:begin//除乘法外的简单算术操作指令
			wdata_o <= arithmeticres;
		end
		`EXE_RES_MUL:begin//乘法指令mul
			wdata_o <= mulres[31:0];
		end
		default:begin
			wdata_o <= `ZeroWord;
		end
	endcase
end//always
/****************************************************************
****************第九段:确定(修改)对HI,LO寄存器的操作信息***************
****************************************************************/
always @ (*)begin
	if(rst == `RstEnable) begin
		whilo_o <= `WriteDisable;
		hi_o <= `ZeroWord;
		lo_o <= `ZeroWord;
	end else if((aluop_i == `EXE_MULT_OP)||(aluop_i == `EXE_MULTU_OP))begin //mult,multu指令
		whilo_o <= `WriteEnable;
		hi_o <= mulres[63:32];
		lo_o <= mulres[31:0];
	end else if(aluop_i == `EXE_MTHI_OP)begin
		whilo_o <= `WriteEnable;
		hi_o <= reg1_i;
		lo_o <= LO;//写HI寄存器所以LO保持不变
	end else if(aluop_i == `EXE_MTLO_OP)begin
		whilo_o <= `WriteEnable;
		hi_o <= HI;
		lo_o <= reg1_i;
	end else if((aluop_i == `EXE_MSUB_OP)||(aluop_i == `EXE_MSUBU_OP))begin
		whilo_o <= `WriteEnable;
		hi_o <= hilo_temp1[63:32];
		lo_o <= hilo_temp1[31:0];
	end else if((aluop_i == `EXE_MADD_OP)||(aluop_i == `EXE_MADDU_OP))begin
		whilo_o <= `WriteEnable;
		hi_o <= hilo_temp1[63:32];
		lo_o <= hilo_temp1[31:0];
	end else begin
		whilo_o <= `WriteDisable;
		hi_o <= `ZeroWord;
		lo_o <= `ZeroWord;
	end
end
/****************************************************************
********************第十段:乘累加、乘累减************************
****************************************************************/
//MADD MADDU MSUB MSUBU指令
always @ (*) begin
	if(rst == `RstEnable) begin
		hilo_temp_o <= {`ZeroWord,`ZeroWord};
		cnt_o <= 2'b00;
		stallreq_for_madd_msub <= `NoStop;
	end else begin
		case(aluop_i)
			`EXE_MADD_OP,`EXE_MADDU_OP:begin
				if(cnt_i == 2'b00) begin //执行第一个时钟周期
					hilo_temp_o <= mulres;//此时将乘法结果mulres通过接口hilo_temp_o输出到EX/MEM模块 以便在下一个时钟周期使用
					cnt_o <= 2'b01;
					hilo_temp1 <= {`ZeroWord,`ZeroWord};
					stallreq_for_madd_msub <= `Stop;//乘累加指令请求流水线暂停
					
				end else if(cnt_i == 2'b01) begin//执行第二个时钟周期
					hilo_temp_o <= {`ZeroWord,`ZeroWord};
					cnt_o <= 2'b10;
					hilo_temp1 <= hilo_temp_i+{HI,LO}; //hilo_temp_i是上一个时钟周期得到的乘法结果
					stallreq_for_madd_msub <= `NoStop;//乘累加指令执行结束 不再请求流水线暂停
				end
			end
			`EXE_MSUB_OP,`EXE_MSUBU_OP:begin
				if(cnt_i == 2'b00) begin
					hilo_temp_o <= ~mulres+1;
					cnt_o <= 2'b01;
					stallreq_for_madd_msub <= `Stop;
				end else if(cnt_i == 2'b01) begin
					hilo_temp_o <= {`ZeroWord,`ZeroWord};
					cnt_o <= 2'b10;
					hilo_temp1 <= hilo_temp_i +{HI,LO};
					stallreq_for_madd_msub <= `NoStop;
				end
			end
			default:begin
				hilo_temp_o <= {`ZeroWord,`ZeroWord};
				cnt_o <= 2'b00;
				stallreq_for_madd_msub <= `NoStop;
			end
		endcase
	end
end
/****************************************************************
*********************第十一段:暂停流水线*************************
****************************************************************/
//目前只有乘累加和乘累减指令会导致流水线暂停,所以stallreq=stallreq_for_madd_msub
always @ (*) begin
	stallreq = stallreq_for_madd_msub;
end
endmodule

ex_mem.v

`include "define.v"
module ex_mem(

	input	wire										clk,
	input wire										rst,
	
	//来自控制模块的信息
	input wire[5:0] stall,
	
	
	//来自执行阶段的信息	
	input wire[`RegAddrBus]       ex_wd,
	input wire                    ex_wreg,
	input wire[`RegBus]					 ex_wdata, 	
	input wire[`RegBus]           ex_hi,
	input wire[`RegBus]           ex_lo,
	input wire                    ex_whilo, 	

	
	//增加的输入接口
	input wire[`DoubleRegBus] hilo_i,
	input wire[1:0] cnt_i,
	//input wire[5:0] stall,
	//送到访存阶段的信息
	output reg[`RegAddrBus]      mem_wd,
	output reg                   mem_wreg,
	output reg[`RegBus]					 mem_wdata,
	output reg[`RegBus]          mem_hi,
	output reg[`RegBus]          mem_lo,
	output reg                   mem_whilo	,
	
	//增加的输出端口
	output reg[`DoubleRegBus] hilo_o,
	output reg[1:0] cnt_o
	
	
);

/*在流水线执行阶段暂停的时候,将输入信号hilo_i通过输出接口hilo_o送出,
输入信号cnt_i通过输出接口cnt_o送出。其余时刻,hilo_o为0,cnt_o也为0。*/
	always @ (posedge clk) begin
		if(rst == `RstEnable) begin
			mem_wd <= `NOPRegAddr;
			mem_wreg <= `WriteDisable;
		  mem_wdata <= `ZeroWord;	
		  mem_hi <= `ZeroWord;
		  mem_lo <= `ZeroWord;
		  mem_whilo <= `WriteDisable;	
		  hilo_o <= {`ZeroWord,`ZeroWord};
		  cnt_o <= 2'b00;	  
		end else if(stall[3] == `Stop && stall[4] == `NoStop)begin
			mem_wd <= `NOPRegAddr;
			mem_wreg <= `WriteDisable;
			mem_wdata <= `ZeroWord;
			mem_hi <= `ZeroWord;
			mem_lo <= `ZeroWord;
			mem_whilo <= `WriteDisable;
			hilo_o <= hilo_i;
			cnt_o <= cnt_i;
		end else if(stall[3] == `NoStop)begin
			mem_wd <= ex_wd;
			mem_wreg <= ex_wreg;
			mem_wdata <= ex_wdata;	
			mem_hi <= ex_hi;
			mem_lo <= ex_lo;
			mem_whilo <= ex_whilo;	
			hilo_o <= {`ZeroWord,`ZeroWord};
			cnt_o <= 2'b00;
		end else begin
			hilo_o <= hilo_i;
			cnt_o <= cnt_i;				
		end    //if
	end      //always
			

endmodule

mem.v

`include "define.v"
//mem.v 访存
//将输入的执行阶段的结果直接作为输出
module mem(
input wire rst,
input wire clk,
//来自执行阶段的信息
input wire[`RegAddrBus] wd_i,
input wire wreg_i,
input wire[`RegBus] wdata_i,
input wire[`RegBus] hi_i,
input wire[`RegBus] lo_i,
input wire whilo_i,
//访存阶段的结果
output reg[`RegAddrBus] wd_o,
output reg wreg_o,
output reg[`RegBus] wdata_o,
output reg[`RegBus] hi_o,
output reg[`RegBus] lo_o,
output reg whilo_o
);

always @ (*)
begin
	if(rst == `RstEnable)
	begin
	wd_o <= `NOPRegAddr;
	wreg_o <= `WriteDisable;
	wdata_o <= `ZeroWord;
	hi_o <= `ZeroWord;
	lo_o <= `ZeroWord;
	whilo_o <= `WriteDisable;
end
else 
begin
	wd_o <= wd_i;
	wreg_o <= wreg_i;
	wdata_o <= wdata_i;
	hi_o <= hi_i;
	lo_o <= lo_i;
	whilo_o <= whilo_i;
end
end
endmodule

mem_wb.v

`include "define.v"
//mem_wb.v
//将访存阶段的运算结果在下一个时钟传递到回写阶段
module mem_wb(
input wire clk,
input wire rst,
//访存阶段的结果
input wire[`RegAddrBus] mem_wd,
input wire[`RegBus] mem_wdata,
input wire mem_wreg,
input wire[`RegBus] mem_hi,
input wire[`RegBus] mem_lo,
input wire mem_whilo,

input wire[5:0] stall,
//送到回写阶段信息
output reg[`RegAddrBus] wb_wd,
output reg wb_wreg,
output reg[`RegBus] wb_wdata,
output reg[`RegBus] wb_hi,
output reg[`RegBus] wb_lo,
output reg wb_whilo
);

always @ (posedge clk)
begin
	if(rst==`RstEnable)
	begin
		wb_wd <= `NOPRegAddr;
		wb_wreg <= `WriteDisable;
		wb_wdata <= `ZeroWord;
		wb_hi <= `ZeroWord;
		wb_lo <= `ZeroWord;
		wb_whilo <= `WriteDisable;
	end
	else if(stall[4] == `Stop && stall[5] == `NoStop)
	begin
		wb_wd <= `NOPRegAddr;
		wb_wreg <= `WriteDisable;
		wb_wdata <= `ZeroWord;
		wb_hi <= `ZeroWord;
		wb_lo <= `ZeroWord;
		wb_whilo <= `WriteDisable;
	end else if(stall[4] == `NoStop)begin
		wb_wd <= mem_wd;
		wb_wreg <= mem_wreg;
		wb_wdata <= mem_wdata;
		wb_hi <= mem_hi;
		wb_lo <= mem_lo;
		wb_whilo <= mem_whilo;
	end
end
endmodule

ctrl.v

//ctrl.v
`include "define.v"
module ctrl(
input wire rst,
input wire stallreq_from_id,
input wire stallreq_from_ex,
output reg[5:0] stall
);
/**************************说明******************************
*****stall[0]表示取指地址PC是否保持不变,为1表示保持不变*****
*******stall[1]表示流水线取指阶段是否暂停,为1表示暂停*******
*******stall[2]表示流水线译码阶段是否暂停,为1表示暂停*******
*******stall[3]表示流水线执行阶段是否暂停,为1表示暂停*******
*******stall[4]表示流水线访存阶段是否暂停,为1表示暂停*******
*******stall[5]表示流水线回写阶段是否暂停,为1表示暂停*******
************************************************************/
always @ (*) begin
	if(rst == `RstEnable) begin
		stall <= 6'b000000;
	end else if(stallreq_from_ex == `Stop) begin
		stall <= 6'b001111;
	end else if(stallreq_from_id == `Stop) begin
		stall <= 6'b000111;
	end else begin
		stall <= 6'b000000;
	end
end
endmodule

hilo_reg.v

`include "define.v"
//hilo_reg.v
module hilo_reg(
input clk,
input rst,
//写端口
input wire we,//写使能端口
input wire[`RegBus] hi_i,
input wire[`RegBus] lo_i,

//读端口
output reg[`RegBus] hi_o,
output reg[`RegBus] lo_o
);
always @ (posedge clk) begin
	if(rst == `RstEnable)begin	
	  hi_o <= `ZeroWord;
	  lo_o <= `ZeroWord;
	end else if(we == `WriteEnable)begin
		hi_o <= hi_i;
		lo_o <= lo_i;
	end
end
endmodule

顶层模块 例化部分在每一章增加指令时,先将例化写清楚,再去修改id,ex等

openmips.v

`include "define.v"
module openmips(

	input	wire										clk,
	input wire										rst,
	
 
	input wire[`RegBus]           rom_data_i,
	output wire[`RegBus]           rom_addr_o,
	output wire                    rom_ce_o
	
);

	wire[`InstAddrBus] pc;
	wire[`InstAddrBus] id_pc_i;
	wire[`InstBus] id_inst_i;
	
	//连接译码阶段ID模块的输出与ID/EX模块的输入
	wire[`AluOpBus] id_aluop_o;
	wire[`AluSelBus] id_alusel_o;
	wire[`RegBus] id_reg1_o;
	wire[`RegBus] id_reg2_o;
	wire id_wreg_o;
	wire[`RegAddrBus] id_wd_o;
	
	//连接ID/EX模块的输出与执行阶段EX模块的输入
	wire[`AluOpBus] ex_aluop_i;
	wire[`AluSelBus] ex_alusel_i;
	wire[`RegBus] ex_reg1_i;
	wire[`RegBus] ex_reg2_i;
	wire ex_wreg_i;
	wire[`RegAddrBus] ex_wd_i;
	
	//连接执行阶段EX模块的输出与EX/MEM模块的输入
	wire ex_wreg_o;
	wire[`RegAddrBus] ex_wd_o;
	wire[`RegBus] ex_wdata_o;
	wire[`RegBus] ex_hi_o;
	wire[`RegBus] ex_lo_o;
	wire ex_whilo_o;

	//连接EX/MEM模块的输出与访存阶段MEM模块的输入
	wire mem_wreg_i;
	wire[`RegAddrBus] mem_wd_i;
	wire[`RegBus] mem_wdata_i;
	wire[`RegBus] mem_hi_i;
	wire[`RegBus] mem_lo_i;
	wire mem_whilo_i;

	//连接访存阶段MEM模块的输出与MEM/WB模块的输入
	wire mem_wreg_o;
	wire[`RegAddrBus] mem_wd_o;
	wire[`RegBus] mem_wdata_o;
	wire[`RegBus] mem_hi_o;
	wire[`RegBus] mem_lo_o;
	wire mem_whilo_o;
	
	//连接MEM/WB模块的输出与回写阶段的输入	
	wire wb_wreg_i;
	wire[`RegAddrBus] wb_wd_i;
	wire[`RegBus] wb_wdata_i;
	wire[`RegBus] wb_hi_i;
	wire[`RegBus] wb_lo_i;
	wire wb_whilo_i;
	
	//连接译码阶段ID模块与通用寄存器Regfile模块
  wire reg1_read;
  wire reg2_read;
  wire[`RegBus] reg1_data;
  wire[`RegBus] reg2_data;
  wire[`RegAddrBus] reg1_addr;
  wire[`RegAddrBus] reg2_addr;
  //连接执行阶段与hilo模块的输出 读取HI,LO寄存器
  wire[`RegBus] hi;
  wire[`RegBus] lo;
//连接执行阶段与ex_reg模块 用于多周期的MADD MADDU MSUB MSUBU指令
  wire[`DoubleRegBus] hilo_temp_o;
  wire[1:0] cnt_o;
  
  wire[`DoubleRegBus] hilo_temp_i;
  wire[1:0] cnt_i;
  
  wire[5:0] stall;
  wire stallreq_from_id;
  wire stallreq_from_ex;
  
  //pc_reg例化
	pc_reg pc_reg0(
		.clk(clk),
		.rst(rst),
		.stall(stall),
		.pc(pc),
		.ce(rom_ce_o)		
			
	);
	
  assign rom_addr_o = pc;

  //IF/ID模块例化
	if_id if_id0(
		.clk(clk),
		.rst(rst),
		.if_pc(pc),
		.stall(stall),
		.if_inst(rom_data_i),
		.id_pc(id_pc_i),
		.id_inst(id_inst_i)      	
	);
	
	//译码阶段ID模块
	id id0(
		.rst(rst),
		.pc_i(id_pc_i),
		.inst_i(id_inst_i),

		.reg1_data_i(reg1_data),
		.reg2_data_i(reg2_data),

	  //处于执行阶段的指令要写入的目的寄存器信息
		.ex_wreg_i(ex_wreg_o),
		.ex_wdata_i(ex_wdata_o),
		.ex_wd_i(ex_wd_o),

	  //处于访存阶段的指令要写入的目的寄存器信息
		.mem_wreg_i(mem_wreg_o),
		.mem_wdata_i(mem_wdata_o),
		.mem_wd_i(mem_wd_o),

		//送到regfile的信息
		.reg1_read_o(reg1_read),
		.reg2_read_o(reg2_read), 	  

		.reg1_addr_o(reg1_addr),
		.reg2_addr_o(reg2_addr), 
	  
		//送到ID/EX模块的信息
		.aluop_o(id_aluop_o),
		.alusel_o(id_alusel_o),
		.reg1_o(id_reg1_o),
		.reg2_o(id_reg2_o),
		.wd_o(id_wd_o),
		.wreg_o(id_wreg_o),
		
		.stallreq(stallreq_from_id)
	);

  //通用寄存器Regfile例化
	regfile regfile1(
		.clk (clk),
		.rst (rst),
		.we	(wb_wreg_i),
		.waddr (wb_wd_i),
		.wdata (wb_wdata_i),
		.re1 (reg1_read),
		.raddr1 (reg1_addr),
		.rdata1 (reg1_data),
		.re2 (reg2_read),
		.raddr2 (reg2_addr),
		.rdata2 (reg2_data)
	);

	//ID/EX模块
	id_ex id_ex0(
		.clk(clk),
		.rst(rst),
		
		.stall(stall),
		
		//从译码阶段ID模块传递的信息
		.id_aluop(id_aluop_o),
		.id_alusel(id_alusel_o),
		.id_reg1(id_reg1_o),
		.id_reg2(id_reg2_o),
		.id_wd(id_wd_o),
		.id_wreg(id_wreg_o),
	
		//传递到执行阶段EX模块的信息
		.ex_aluop(ex_aluop_i),
		.ex_alusel(ex_alusel_i),
		.ex_reg1(ex_reg1_i),
		.ex_reg2(ex_reg2_i),
		.ex_wd(ex_wd_i),
		.ex_wreg(ex_wreg_i)
	);		
	
	//EX模块
	ex ex0(
		.rst(rst),
	
		//送到执行阶段EX模块的信息
		.aluop_i(ex_aluop_i),
		.alusel_i(ex_alusel_i),
		.reg1_i(ex_reg1_i),
		.reg2_i(ex_reg2_i),
		.wd_i(ex_wd_i),
		.wreg_i(ex_wreg_i),
		.hi_i(hi),
		.lo_i(lo),
		.wb_hi_i(wb_hi_i),
		.wb_lo_i(wb_lo_i),
		.wb_whilo_i(wb_whilo_i),
		.mem_hi_i(mem_hi_o),
		.mem_lo_i(mem_lo_o),
		.mem_whilo_i(mem_whilo_o),
		.hilo_temp_i(hilo_temp_i),
		.cnt_i(cnt_i),
	  
	  //EX模块的输出到EX/MEM模块信息
		.wd_o(ex_wd_o),
		.wreg_o(ex_wreg_o),
		.wdata_o(ex_wdata_o),
		.hi_o(ex_hi_o),
		.lo_o(ex_lo_o),
		.whilo_o(ex_whilo_o),
		.hilo_temp_o(hilo_temp_o),
		.cnt_o(cnt_o),
		.stallreq(stallreq_from_ex)
	);

  //EX/MEM模块
  ex_mem ex_mem0(
		.clk(clk),
		.rst(rst),
		
		.stall(stall),
	  
		//来自执行阶段EX模块的信息	
		.ex_wd(ex_wd_o),
		.ex_wreg(ex_wreg_o),
		.ex_wdata(ex_wdata_o),
		.ex_hi(ex_hi_o),
		.ex_lo(ex_lo_o),
		.ex_whilo(ex_whilo_o),
		
		.hilo_i(hilo_temp_o),
		.cnt_i(cnt_o),
	

		//送到访存阶段MEM模块的信息
		.mem_wd(mem_wd_i),
		.mem_wreg(mem_wreg_i),
		.mem_wdata(mem_wdata_i),
		.mem_hi(mem_hi_i),
		.mem_lo(mem_lo_i),
		.mem_whilo(mem_whilo_i),
		
		.hilo_o(hilo_temp_i),
		.cnt_o(cnt_i)

						       	
	);
	
  //MEM模块例化
	mem mem0(
		.rst(rst),
	
		//来自EX/MEM模块的信息	
		.wd_i(mem_wd_i),
		.wreg_i(mem_wreg_i),
		.wdata_i(mem_wdata_i),
		.hi_i(mem_hi_i),
		.lo_i(mem_lo_i),
		.whilo_i(mem_whilo_i),
	  
		//送到MEM/WB模块的信息
		.wd_o(mem_wd_o),
		.wreg_o(mem_wreg_o),
		.wdata_o(mem_wdata_o),
		.hi_o(mem_hi_o),
		.lo_o(mem_lo_o),
		.whilo_o(mem_whilo_o)
	);

  //MEM/WB模块
	mem_wb mem_wb0(
		.clk(clk),
		.rst(rst),
		
		.stall(stall),

		//来自访存阶段MEM模块的信息	
		.mem_wd(mem_wd_o),
		.mem_wreg(mem_wreg_o),
		.mem_wdata(mem_wdata_o),
		.mem_hi(mem_hi_o),
		.mem_lo(mem_lo_o),
		.mem_whilo(mem_whilo_o),
	
		//送到回写阶段的信息
		.wb_wd(wb_wd_i),
		.wb_wreg(wb_wreg_i),
		.wb_wdata(wb_wdata_i),
		.wb_hi(wb_hi_i),
		.wb_lo(wb_lo_i),
		.wb_whilo(wb_whilo_i)
									       	
	);
	hilo_reg hilo_reg0(
	.clk(clk),
	.rst(rst),
	//写端口
	.we(wb_whilo_i),
	.hi_i(wb_hi_i),
	.lo_i(wb_lo_i),
	//读端口1
	.hi_o(hi),
	.lo_o(lo)
	);
	
	ctrl ctrl0(
	.rst(rst),
	.stallreq_from_id(stallreq_from_id),
	.stallreq_from_ex(stallreq_from_ex),
	.stall(stall)
	);
endmodule      
                                                                                                                                            

openmips_min_sopc.v

`include "define.v"
//openmips_min_sopc.v
module openmips_min_sopc(
input wire clk,
input wire rst
);
//连接指令存储器
wire[`InstAddrBus] inst_addr;
wire[`InstBus] inst;
wire rom_ce;
//例化处理器
openmips openmips0(
	.clk(clk),	.rst(rst),
	.rom_addr_o(inst_addr),	.rom_data_i(inst),
	.rom_ce_o(rom_ce)
);
//例化指令存储器
inst_rom inst_rom0(
	.ce(rom_ce),	.inst(inst), .addr(inst_addr)
);
endmodule
/*module openmips_min_sopc(

	input	wire										clk,
	input wire										rst
	
);

  //连接指令存储器
  wire[`InstAddrBus] inst_addr;
  wire[`InstBus] inst;
  wire rom_ce;
 

 openmips openmips0(
		.clk(clk),
		.rst(rst),
	
		.rom_addr_o(inst_addr),
		.rom_data_i(inst),
		.rom_ce_o(rom_ce)
	
	);
	
	inst_rom inst_rom0(
		.ce(rom_ce),
		.addr(inst_addr),
		.inst(inst)	
	);


endmodule*/

openmips_min_sopc_tb.v

`include "define.v"
//·*//建立TestBench文件
//时间单位是1ns,精度是1ps
`timescale 1ns/1ps
module openmips_min_sopc_tb();
reg CLOCK_50;
reg rst;
//每隔10ns,CLOCK_50信号翻转一次,所以下一个时钟周期是20ns,对应50MHz
initial begin
	CLOCK_50 = 1'b0;
	forever #10 CLOCK_50 = ~CLOCK_50;
end
//最初时刻,复位信号有效,在第195ns复位信号无效 最小SOPC开始运行
//运行1000ns后,暂停仿真
initial begin
	rst = `RstEnable;
	#195 rst = `RstDisable;
	#1000 $stop;
end
//例化最小SOPC
openmips_min_sopc openmips_min_sopc0(
.clk(CLOCK_50),
.rst(rst)
);
endmodule
/*`timescale 1ns/1ps

module openmips_min_sopc_tb();

  reg     CLOCK_50;
  reg     rst;
  
       
  initial begin
    CLOCK_50 = 1'b0;
    forever #10 CLOCK_50 = ~CLOCK_50;
  end
      
  initial begin
    rst = `RstEnable;
    #195 rst= `RstDisable;
    #1000 $stop;
  end
       
  openmips_min_sopc openmips_min_sopc0(
		.clk(CLOCK_50),
		.rst(rst)	
	);

endmodule*/

inst_rom.data

3401ffff
00010c00
3421fffb
34020006
00220018
70220000
70220001
70220004
70220005

inst_rom.S

   .org 0x0
   .set noat
   .global _start
_start:
   ori  $1,$0,0xffff                  
   sll  $1,$1,16
   ori  $1,$1,0xfffb           # $1 = -5 为寄存器$1赋初值
   ori  $2,$0,6                # $2 = 6 为寄存器$2赋初值

   mult $1,$2                  # hi = 0xffffffff
                               # lo = 0xffffffe2

   madd $1,$2                  # hi = 0xffffffff
                               # lo = 0xffffffc4

   maddu $1,$2                 # hi = 0x5
                               # lo = 0xffffffa6

   msub $1,$2                  # hi = 0x5
                               # lo = 0xffffffc4   

   msubu $1,$2                 # hi = 0xffffffff
                               # lo = 0xffffffe2   

仿真结果

对照上述汇编指令,观察相应寄存器进行验证
在这里插入图片描述
在这里插入图片描述
当出现错误时,找到错误的信号其对应的模块和与之相关的模块,信号,进行修改。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值