算术操作指令的实现

在这里插入图片描述
R型的源操作数为两个寄存器
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上述区别:加减是否判断溢出、比较分有无符号数
下述比较:找0/1
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
就是加点判断逻辑,这儿略过
在这里插入图片描述
修改EX

`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,

	//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,
	
	//执行结果
	output reg[`RegAddrBus]       wd_o,
	output reg                    wreg_o,
	output reg[`RegBus]			   wdata_o,
	
	//执行阶段的指令对HI、LO寄存器的写操作请求 通用寄存器赋值给HILO
	output reg[`RegBus]           hi_o,
	output reg[`RegBus]           lo_o,
	output reg                    whilo_o		
);
	//保存逻辑运算的结果
	reg[`RegBus] logicout;			//保存逻辑运算结果
	reg[`RegBus] shiftres;			//保存移位运算结果
	reg[`RegBus] moveres;			//保存移动操作结果
	reg[`RegBus] arithmeticres;	//保存算术运算的结果
	reg[`RegBus] HI;					//保存HI最新值
	reg[`RegBus] LO;					//保存LO最新值
	
	
	
	//reg [`DoubleRegBus] hilo_temp1;
	//reg stallreq_for_madd_msub;	
	
	wire[`RegBus] reg2_i_mux;			//保存输入的第二个操作数reg2_i的补码
	wire[`RegBus] reg1_i_not;			//保存输入的第一个操作数reg1_i取反后的值
	wire[`RegBus] result_sum;			//保存加法结果
	wire ov_sum;							//保存溢出情况
	wire reg1_eq_reg2;					//第一个操作数是否等于第二个操作数
	wire reg1_lt_reg2;					//第一个操作数是否小于第二个操作数
	wire[`RegBus] opdata1_mult;		//乘法操作中的被乘数
	wire[`RegBus] opdata2_mult;		//乘法操作中的乘数
	wire[`DoubleRegBus] hilo_temp;	//临时保存乘法的结果,宽度为64位
	reg[`DoubleRegBus] mulres;			//保存乘法结果,宽度为64位
	
	
	//根据大类型判断输出
	always @ (*) begin
		wd_o <= wd_i;	//传递写目的寄存器地址
							//下述指令且溢出 则不写
		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
				wdata_o <= mulres[31:0];
			end	 	
			default:					begin
				wdata_o <= `ZeroWord;
			end
		endcase
	end	
	
	
	//根据小类型作逻辑运算
	always @ (*) 
		if(rst == `RstEnable) begin
			logicout <= `ZeroWord;
		end 
		else begin
			case (aluop_i)
				`EXE_OR_OP:			begin
					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
					logicout <= `ZeroWord;
				end
			endcase
		end    
		
	//根据小类型作移位运算
	always @ (*) 
		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	//sra和srav都是这个指令
					shiftres <= ({32{reg2_i[31]}} << (6'd32-{1'b0, reg1_i[4:0]})) 
												| reg2_i >> reg1_i[4:0];
				end
				default:				begin
					shiftres <= `ZeroWord;
				end
			endcase
		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	//将HI的值作为移动操作的结果
					moveres <= HI;
				end
				`EXE_MFLO_OP:		begin	//将LO的值作为移动操作的结果
					moveres <= LO;
				end
				`EXE_MOVZ_OP:		begin	//将reg1_i的值作为移动操作的结果
					moveres <= reg1_i;
				end
				`EXE_MOVN_OP:		begin	//将reg1_i的值作为移动操作的结果
					moveres <= reg1_i;
				end
				default : begin
				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};
		end 
		else if(wb_whilo_i == `WriteEnable) begin
			{HI,LO} <= {wb_hi_i,wb_lo_i};
		end 
		else begin
			{HI,LO} <= {hi_i,lo_i};			
		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_MULT_OP) || (aluop_i == `EXE_MULTU_OP)) begin
			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;
		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	

	//SUB和SUBU为溢出和不溢出检查的减法
	//SLT是符号位的两寄存器比较和立即数寄存器比较
	//是上述运算则变为补码,不是则本身
	assign reg2_i_mux = ((aluop_i == `EXE_SUB_OP) || (aluop_i == `EXE_SUBU_OP) ||
							  (aluop_i == `EXE_SLT_OP) ) ? (~reg2_i)+1 : reg2_i;
	
	//加法如下;减法为加补码;有符号数比较加补码就是相减
	assign result_sum = reg1_i + reg2_i_mux;										 
	
	//加减法时的溢出判断
	//A.均为正但和为负
	//B.均为负但和为正
	assign ov_sum = ((!reg1_i[31] && !reg2_i_mux[31]) && result_sum[31]) ||
						 ((reg1_i[31] && reg2_i_mux[31]) && (!result_sum[31]));  
	
	//计算操作数1是否小于2,分两种情况
	// A.为EXE_SLT_OP有符号比较运算时,分三种
	//	 A1. reg1负 reg2正 ,则1<2
	//   A2. 1正2正,result_sum为负,则1<2
	//		A3. 1负2负,result_sum为负,则1<2
	// B.无符号时,直接比较
	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);
  
   //对操作数1逐位取反
   assign reg1_i_not = ~reg1_i;	
	
	//根据不同的算术运算类型,给变量赋值
	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	//找1
					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] ? 30 : reg1_i[0] ? 31 : 32 ;
				end
				`EXE_CLO_OP:		begin	//找0
					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	
	
	//乘法运算
   //取得乘法操作的被乘数,如果是有符号乘法且被乘数是负数,那么取补码
	assign opdata1_mult = (((aluop_i == `EXE_MUL_OP) || (aluop_i == `EXE_MULT_OP))
								 && (reg1_i[31] == 1'b1)) ? (~reg1_i + 1) : reg1_i;
	
	//取得乘法操作的乘数,如果是有符号乘法且乘数是负数,那么取补码
   assign opdata2_mult = (((aluop_i == `EXE_MUL_OP) || (aluop_i == `EXE_MULT_OP))
								 && (reg2_i[31] == 1'b1)) ? (~reg2_i + 1) : reg2_i;		
	
   //得到临时乘法结果
	assign hilo_temp = opdata1_mult * opdata2_mult;																				

	//修正乘法结果 最终保存到mulres中
	//	A.有符号乘法指令mult、mul
	//	 A1.如果一正一负,那需要求补码
	//	 A2.同号 则保存过去
	// B.如果是无符号乘法multu,保存过去
	always @ (*) begin
		if(rst == `RstEnable) begin
			mulres <= {`ZeroWord,`ZeroWord};
		end 
		else if ((aluop_i == `EXE_MULT_OP) || (aluop_i == `EXE_MUL_OP))begin
			if(reg1_i[31] ^ reg2_i[31] == 1'b1) begin
				mulres <= ~hilo_temp + 1;
			end 
			else begin
				mulres <= hilo_temp;
			end
		end 
		else begin
			mulres <= hilo_temp;
		end
	end

endmodule

在这里插入图片描述
在这里插入图片描述
要进行多周期运算,所以要实现流水线暂停
在这里插入图片描述
在这里插入图片描述

`include "defines.v"

module ctrl(

	input wire							rst,

	input wire                   stallreq_from_id,
	input wire                   stallreq_from_ex,
	
	output reg[5:0]              stall       
	
);

	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

在这里插入图片描述
对应修改PC_REG模块

`include "defines.v"

module pc_reg(

	input  wire					clk,
	input  wire					rst,
	//来自控制模块的信息
	input wire[5:0]               stall,
	
	output reg [`InstAddrBus]	pc,	//InstAddrBus为宏定义 31:0 代表ROM的地址总线宽度 输出给ROM的addr
	output reg                 ce		//给到ROM的使能
	
);
	//如果复位,则使能信号ce拉0,否则置1
	always @ (posedge clk) begin
		if (rst == `RstEnable) begin	//复位信号有效 1'b1
			ce <= `ChipDisable;
		end 
		else begin
			ce <= `ChipEnable;
		end
	end
	
	//如果使能信号为低,则指令地址拉0;使能有效,则指令地址自加4'h4
	always @ (posedge clk) begin
		if (ce == `ChipDisable) begin	//芯片禁止宏定义 1'b0
			pc <= 32'h00000000;
		end 
		else if(stall[0] == `NoStop)begin
	 		pc <= pc + 4'h4;	//自加4 字节寻址
		end
	end
	
endmodule

修改IF/ID模块

`include "defines.v"

module if_id(
	input wire clk,
	input wire rst,
	input wire [`InstBus] 		if_inst,	//InstBus为ROM数据宽度,同样为31:0
	input wire[`InstAddrBus]	if_pc  ,	//ROM地址总线宽度 31:0
	
	//来自控制模块的信息	
	input wire[5:0]            stall,	
	
	output reg [`InstBus] 		id_inst,
	output reg[`InstAddrBus]   id_pc  
	
);

	//复位有效 输出为32位的0;否则则寄存传递来的取指指令地址和指令
	//1.当stall[1]为stop,stall[2]为nostop,表示取指阶段暂停,译码阶段继续
	//  使用空指令作为下一个周期进入译码阶段的指令
	//2.当stall[1]为nostop,取指阶段继续,取得的指令进入译码阶段
	//3.其余情况,保持不变
	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
			
endmodule

在这里插入图片描述
修改ID/EX

`include "defines.v"

module id_ex(

	input wire					  clk,
	input wire					  rst,

	
	//从译码阶段传递的信息
	input wire[`AluOpBus]         id_aluop,	//[7:0]
	input wire[`AluSelBus]        id_alusel,	//[2:0]
	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
	
);
	//打一拍传过去
	//1.当stall[2]为stop,stall[3]为nostop,表示译码阶段暂停,执行阶段继续
	//  使用空指令作为下一个周期进入执行阶段的指令
	//2.当stall[2]为nostop,译码阶段继续,译码指令进入执行阶段
	//3.其余情况,保持不变
	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
	end
	
endmodule

EX后续修改;修改EX/MEM

`include "defines.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,      //NEW
	input wire[`RegBus]          ex_lo,      //NEW
	input wire                   ex_whilo, 	 //NEW
	
	//送到访存阶段的信息
	output reg[`RegAddrBus]      mem_wd,
	output reg                   mem_wreg,
	output reg[`RegBus]			  mem_wdata,
	output reg[`RegBus]          mem_hi,     //NEW
	output reg[`RegBus]          mem_lo,     //NEW
	output reg                   mem_whilo	 //NEW
	
	
);
	//1.当stall[3]为stop,stall[4]为nostop,表示执行阶段暂停,访存阶段继续
	//  使用空指令作为下一个周期进入访存阶段的指令
	//2.当stall[3]为nostop,执行阶段继续,执行后的指令进入访存阶段
	//3.其余情况,保持不变
	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  
	end       
			
endmodule

修改MEM/WB

`include "defines.v"

module mem_wb(

	input	wire				  			clk,
	input wire				      	rst,
	
  //来自控制模块的信息
	input wire[5:0]               stall,
	
	//来自访存阶段的信息	
	input wire[`RegAddrBus]       mem_wd,
	input wire                    mem_wreg,
	input wire[`RegBus]			 	mem_wdata,
	input wire[`RegBus]           mem_hi,
	input wire[`RegBus]           mem_lo,
	input wire                    mem_whilo,	

	//送到回写阶段的信息
	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		       
	
);
	//1.当stall[4]为stop,stall[5]为nostop,表示访存阶段暂停,回写阶段继续
	//  使用空指令作为下一个周期进入回写阶段的指令
	//2.当stall[4]为nostop,访存阶段继续,访存后的指令进入回写阶段
	//3.其余情况,保持不变
	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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实现思路是重点
在这里插入图片描述
修改ID译码模块

						`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	

在这里插入图片描述
修改EX

`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,

	//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,			//乘累加累减的周期数
	
	//执行结果
	output reg[`RegAddrBus]       wd_o,
	output reg                    wreg_o,
	output reg[`RegBus]			   wdata_o,
	
	//执行阶段的指令对HI、LO寄存器的写操作请求 通用寄存器赋值给HILO
	output reg[`RegBus]           hi_o,
	output reg[`RegBus]           lo_o,
	output reg                    whilo_o,

	output reg[`DoubleRegBus]     hilo_temp_o,	//乘累加累减的乘结果
	output reg[1:0]               cnt_o,      	//乘累加累减的周期数
	
	output reg							stallreq 		//给到CTRL的暂停信号
);
	//保存逻辑运算的结果
	reg[`RegBus] logicout;			//保存逻辑运算结果
	reg[`RegBus] shiftres;			//保存移位运算结果
	reg[`RegBus] moveres;			//保存移动操作结果
	reg[`RegBus] arithmeticres;	//保存算术运算的结果
	reg[`RegBus] HI;					//保存HI最新值
	reg[`RegBus] LO;					//保存LO最新值
	
	
	reg [`DoubleRegBus] hilo_temp1;	//乘累加累减第2阶段
	reg stallreq_for_madd_msub;	
	
	wire[`RegBus] reg2_i_mux;			//保存输入的第二个操作数reg2_i的补码
	wire[`RegBus] reg1_i_not;			//保存输入的第一个操作数reg1_i取反后的值
	wire[`RegBus] result_sum;			//保存加法结果
	wire ov_sum;							//保存溢出情况
	wire reg1_eq_reg2;					//第一个操作数是否等于第二个操作数
	wire reg1_lt_reg2;					//第一个操作数是否小于第二个操作数
	wire[`RegBus] opdata1_mult;		//乘法操作中的被乘数
	wire[`RegBus] opdata2_mult;		//乘法操作中的乘数
	wire[`DoubleRegBus] hilo_temp;	//临时保存乘法的结果,宽度为64位
	reg[`DoubleRegBus] mulres;			//保存乘法结果,宽度为64位
	
	
	//根据大类型判断输出
	always @ (*) begin
		wd_o <= wd_i;	//传递写目的寄存器地址
							//下述指令且溢出 则不写
		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
				wdata_o <= mulres[31:0];
			end	 	
			default:					begin
				wdata_o <= `ZeroWord;
			end
		endcase
	end	
	
	
	//根据小类型作逻辑运算
	always @ (*) 
		if(rst == `RstEnable) begin
			logicout <= `ZeroWord;
		end 
		else begin
			case (aluop_i)
				`EXE_OR_OP:			begin
					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
					logicout <= `ZeroWord;
				end
			endcase
		end    
		
	//根据小类型作移位运算
	always @ (*) 
		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	//sra和srav都是这个指令
					shiftres <= ({32{reg2_i[31]}} << (6'd32-{1'b0, reg1_i[4:0]})) 
												| reg2_i >> reg1_i[4:0];
				end
				default:				begin
					shiftres <= `ZeroWord;
				end
			endcase
		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	//将HI的值作为移动操作的结果
					moveres <= HI;
				end
				`EXE_MFLO_OP:		begin	//将LO的值作为移动操作的结果
					moveres <= LO;
				end
				`EXE_MOVZ_OP:		begin	//将reg1_i的值作为移动操作的结果
					moveres <= reg1_i;
				end
				`EXE_MOVN_OP:		begin	//将reg1_i的值作为移动操作的结果
					moveres <= reg1_i;
				end
				default : begin
				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};
		end 
		else if(wb_whilo_i == `WriteEnable) begin
			{HI,LO} <= {wb_hi_i,wb_lo_i};
		end 
		else begin
			{HI,LO} <= {hi_i,lo_i};			
		end
	end	

	//HILO
	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
			whilo_o <= `WriteEnable;
			hi_o <= mulres[63:32];
			lo_o <= mulres[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 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_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	

	//SUB和SUBU为溢出和不溢出检查的减法
	//SLT是符号位的两寄存器比较和立即数寄存器比较
	//是上述运算则变为补码,不是则本身
	assign reg2_i_mux = ((aluop_i == `EXE_SUB_OP) || (aluop_i == `EXE_SUBU_OP) ||
							  (aluop_i == `EXE_SLT_OP) ) ? (~reg2_i)+1 : reg2_i;
	
	//加法如下;减法为加补码;有符号数比较加补码就是相减
	assign result_sum = reg1_i + reg2_i_mux;										 
	
	//加减法时的溢出判断
	//A.均为正但和为负
	//B.均为负但和为正
	assign ov_sum = ((!reg1_i[31] && !reg2_i_mux[31]) && result_sum[31]) ||
						 ((reg1_i[31] && reg2_i_mux[31]) && (!result_sum[31]));  
	
	//计算操作数1是否小于2,分两种情况
	// A.为EXE_SLT_OP有符号比较运算时,分三种
	//	 A1. reg1负 reg2正 ,则1<2
	//   A2. 1正2正,result_sum为负,则1<2
	//		A3. 1负2负,result_sum为负,则1<2
	// B.无符号时,直接比较
	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);
  
   //对操作数1逐位取反
   assign reg1_i_not = ~reg1_i;	
	
	//根据不同的算术运算类型,给变量赋值
	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	//找1
					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] ? 30 : reg1_i[0] ? 31 : 32 ;
				end
				`EXE_CLO_OP:		begin	//找0
					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	
	
	//乘法运算
   //取得乘法操作的被乘数,如果是有符号乘法且被乘数是负数,那么取补码
	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;
	
	//取得乘法操作的乘数,如果是有符号乘法且乘数是负数,那么取补码
   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;		
	
   //得到临时乘法结果
	assign hilo_temp = opdata1_mult * opdata2_mult;	

	//修正乘法结果 最终保存到mulres中
	//	A.有符号乘法指令mult、mul
	//	 A1.如果一正一负,那需要求补码
	//	 A2.同号 则保存过去
	// B.如果是无符号乘法multu,保存过去
	always @ (*) begin
		if(rst == `RstEnable) begin
			mulres <= {`ZeroWord,`ZeroWord};
		end 
		else if ((aluop_i == `EXE_MULT_OP) || (aluop_i == `EXE_MUL_OP) ||
				  (aluop_i == `EXE_MADD_OP) || (aluop_i == `EXE_MSUB_OP))begin
			if(reg1_i[31] ^ reg2_i[31] == 1'b1) begin
				mulres <= ~hilo_temp + 1;
			end 
			else begin
				mulres <= hilo_temp;
			end
		end 
		else begin
			mulres <= hilo_temp;
		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		//执行阶段第1个时钟周期
						hilo_temp_o <= mulres;
						cnt_o <= 2'b01;
						stallreq_for_madd_msub <= `Stop;
						hilo_temp1 <= {`ZeroWord,`ZeroWord};
					end 
					else if(cnt_i == 2'b01) begin	//执行阶段第2个时钟周期 在下一拍才会回来
						hilo_temp_o <= {`ZeroWord,`ZeroWord};						
						cnt_o <= 2'b10;				//设为10而不是00,防止重复进行
						hilo_temp1 <= hilo_temp_i + {HI,LO};
						stallreq_for_madd_msub <= `NoStop;
					end
				end
				`EXE_MSUB_OP, `EXE_MSUBU_OP:		begin
					if(cnt_i == 2'b00) begin		//执行阶段第1个时钟周期
						hilo_temp_o <=  ~mulres + 1 ;
						cnt_o <= 2'b01;
						stallreq_for_madd_msub <= `Stop;
					end 
					else if(cnt_i == 2'b01)begin	//执行阶段第2个时钟周期
						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	
	
	//暂停流水线
	//目前如此
   always @ (*) begin
     stallreq = stallreq_for_madd_msub;
   end
	
	
endmodule

在这里插入图片描述
在这里插入图片描述
EX/MEM见上一部分,已修改完毕
在这里插入图片描述
在这里插入图片描述
除法器已流水线实现过
在这里插入图片描述
7-16的图是重点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

`include "defines.v"

module div(

	input	wire							clk,
	input wire							rst,
	
	input wire                    signed_div_i,	//是否是有符号除法
	input wire[31:0]              opdata1_i,		//被除数
	input wire[31:0]		   		opdata2_i,		//除法
	input wire                    start_i,			//是否开始除法运算
	input wire                    annul_i,			//是否取消除法运算
	
	output reg[63:0]              result_o,		//除法运算结果
	output reg			            ready_o			//除法运算是否结束
);

	wire[32:0] div_temp;
	reg [5:0]  cnt;										//记录试商法记录了几轮,32时表示结束
	reg [64:0] dividend;
	reg [1:0]  state;
	reg [31:0] divisor;	 
	reg [31:0] temp_op1;
	reg [31:0] temp_op2;
	
	//dividend的低32位保存被除数、中间结果,第k次迭代结束的时候dividend[k:0]
	//保存的就是当前中间结果,dividend[31:k+1]保存的是被除数中还没有参与运算
	//的数据,dividend高32位是每次迭代的被减数,minuend-n运算结果保存在div_temp	
	assign div_temp = {1'b0,dividend[63:32]} - {1'b0,divisor};

	always @ (posedge clk) begin
		if (rst == `RstEnable) begin
			state    <= `DivFree;
			ready_o  <= `DivResultNotReady;
			result_o <= {`ZeroWord,`ZeroWord};
		end 
		else begin
			case (state)
				//分三种情况
				//1.开始除法运算,但除数为0,进入DivByZero
				//2.开始除法运算,且除数不为0,进入DivOn,初始化cnt为0,如果是有符号除法且被除数或除数为负
				//  则先取补码。除数保存到divisor中,被除数的最高位保存到dividend第32位,进行第一次迭代
				//3.没有开始除法运算,保持ready_o为DivResultNotReady和result_o为0
				`DivFree:			begin               
					if(start_i == `DivStart && annul_i == 1'b0) begin
						if(opdata2_i == `ZeroWord) begin		//除数为0
							state <= `DivByZero;
						end 
						else begin
							state <= `DivOn;
							cnt <= 6'b000000;
							if(signed_div_i == 1'b1 && opdata1_i[31] == 1'b1 ) begin	//有符号数 先变为正
								temp_op1 = ~opdata1_i + 1;
							end 
							else begin
								temp_op1 = opdata1_i;
							end
							if(signed_div_i == 1'b1 && opdata2_i[31] == 1'b1 ) begin
								temp_op2 = ~opdata2_i + 1;
							end 
							else begin
								temp_op2 = opdata2_i;
							end
							dividend <= {`ZeroWord,`ZeroWord};
							dividend[32:1] <= temp_op1;	//首先把被除数给到低位
							divisor <= temp_op2;				//除数
						end
					end 
					else begin
							ready_o <= `DivResultNotReady;
							result_o <= {`ZeroWord,`ZeroWord};
					end          	
				end
				
				//
				`DivByZero:		begin               //DivByZero状态
					dividend <= {`ZeroWord,`ZeroWord};
					state <= `DivEnd;		 		
				end
				
				//DivOn
				//分三种情况
				//1.如果输入信号annul_i为1,表示处理器取消除法运算,DIV直接回到DivFree
				//2.如果annul_i为0且cnt不为32,表示试商法还没结束,此时如果减法结果div_temp
				//  为负,则此次迭代结果为0,如果为正,则迭代结果为1,dividend最低位保存每次
				//  的迭代结果,同时保持DivOn,且cnt+1
				//3.cnt32表示结束,如果为有符号除法,且一正一负,则结果取补码,商保存在dividend
				//  低32位,余数在高32位
				`DivOn:				begin               //DivOn状态
					if(annul_i == 1'b0) begin
						if(cnt != 6'b100000) begin					
							//如果div_temp[32]为1,表示结果为负
							//将dividend左移一位,将被除数中还没有参与运算的最高位
							//加入到下一次迭代的被减数中,同时将0追加到中间结果
							if(div_temp[32] == 1'b1) begin
								dividend <= {dividend[63:0] , 1'b0};
							end 
							else begin
							//如果为0,将减法的结果与被除数还没有参与运算的最高位
							//加入到下一次迭代的被减数中,同时将1追加到中间结果
								dividend <= {div_temp[31:0] , dividend[31:0] , 1'b1};
							end
							cnt <= cnt + 1;
						end 
						else begin	//试商法结束
							if((signed_div_i == 1'b1) && ((opdata1_i[31] ^ opdata2_i[31]) == 1'b1)) begin
								dividend[31:0] <= (~dividend[31:0] + 1);	//商求补码
							end
							if((signed_div_i == 1'b1) && ((opdata1_i[31] ^ dividend[64]) == 1'b1)) begin              
								dividend[64:33] <= (~dividend[64:33] + 1);//余数求补码
							end
							state <= `DivEnd;
							cnt <= 6'b000000;            	
						end
					end 
					else begin
						state <= `DivFree;
					end	
				end
				
				//设置输出信号ready_o,等待EX送来divStop信号,送来后回到divfree
				`DivEnd:			begin               //DivEnd状态
					result_o <= {dividend[64:33], dividend[31:0]};  
					ready_o <= `DivResultReady;
					if(start_i == `DivStop) begin
						state <= `DivFree;
						ready_o <= `DivResultNotReady;
						result_o <= {`ZeroWord,`ZeroWord};       	
					end		  	
				end
		  endcase
		end
	end

endmodule

修改ID
在这里插入图片描述
修改EX
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值