tiny riscv - ex执行模块和写回

145 阅读3分钟

ex

	//from id_ex
	input wire[31:0] inst_i,	
	input wire[31:0] inst_addr_i,
	input wire[31:0] op1_i,
	input wire[31:0] op2_i,
	input wire[4:0]  rd_addr_i,
	input wire 		 rd_wen_i,
	//to regs   写使能,寄存器组地址,写数据
        
	output reg[4:0] rd_addr_o,
	output reg[31:0]rd_data_o,
	output reg 	    rd_wen_o
);

wire[6:0] opcode; 
	wire[4:0] rd; 
	wire[2:0] func3; 
	wire[4:0] rs1;
	wire[4:0] rs2;
	wire[6:0] func7;
	wire[11:0]imm;
	
	assign opcode = inst_i[6:0];
	assign rd 	  = inst_i[11:7];
	assign func3  = inst_i[14:12];
	assign rs1 	  = inst_i[19:15];
	assign rs2 	  = inst_i[24:20];
	assign func7  = inst_i[31:25];
	assign imm    = inst_i[31:20];


// 执行 I型指令
always @(*)begin
		// 判断操作码
		case(opcode)
			`INST_TYPE_I:begin
                                //根据func3判断指令
				case(func3)
					`INST_ADDI:begin
                                                //进行操作数相加。ADDI指令
						rd_data_o = op1_i + op2_i;
						rd_addr_o = rd_addr_i;
						rd_wen_o  = 1'b1;
					end
                                       
                                `INST_TYPE_R_M:begin
				case(func3)
					`INST_ADD_SUB:begin
						if(func7 == 7'b000_0000)begin//add
							rd_data_o = op1_i + op2_i;
							rd_addr_o = rd_addr_i;
							rd_wen_o  = 1'b1;
						end
						else begin
							rd_data_o = op2_i - op1_i;
							rd_addr_o = rd_addr_i;
							rd_wen_o  = 1'b1; 								
						end
					end
					default:begin
						rd_data_o = 32'b0;
						rd_addr_o = 5'b0;
						rd_wen_o  = 1'b0;					
					end
				endcase
			end			        
                                        
                                        
                         
					default:begin
                                                //默认为0,目的寄存器地址为5位。
						rd_data_o = 32'b0;
						rd_addr_o = 5'b0;
						rd_wen_o  = 1'b0;
					end						
				endcase
                            // 默认值输出    
                           default:begin
				rd_data_o = 32'b0;
				rd_addr_o = 5'b0;
				rd_wen_o  = 1'b0;				
			end
                        endcase
			end	










endmodule

regs

进行访存和回写,在一个周期所以此处为时序逻辑

	input wire clk,
	input wire rst,
	//from id   通过id模块的地址
	input wire[4:0] reg1_raddr_i,
	input wire[4:0] reg2_raddr_i,
	
	//to id   给id模块数据
	output reg[31:0] reg1_rdata_o,
	output reg[31:0] reg2_rdata_o,
	
	//from ex  通过ex模块的地址
	input wire[4:0] reg_waddr_i,
	input wire[31:0]reg_wdata_i,
	input 	reg_wen

);
	reg[31:0] regs[0:31];
	integer i;

// 通过rs1,rs2寄存器地址,进行取数据。
	always @(*)begin
		if(rst == 1'b0)
			reg1_rdata_o <= 32'b0;
		else if(reg1_raddr_i == 5'b0)
			reg1_rdata_o <= 32'b0;
                //解决指令相关性冲突,读端口等于写端口,把写的数据给读的数据。      
		else if(reg_wen && reg1_raddr_i == reg_waddr_i)
			reg1_rdata_o <= reg_wdata_i;
		else
			reg1_rdata_o <= regs[reg1_raddr_i];
	end
	
	always @(*)begin
		if(rst == 1'b0)
			reg2_rdata_o <= 32'b0;
		else if(reg2_raddr_i == 5'b0)
			reg2_rdata_o <= 32'b0;
		else if(reg_wen && reg2_raddr_i == reg_waddr_i)
			reg2_rdata_o <= reg_wdata_i;
		else
			reg2_rdata_o <= regs[reg2_raddr_i];
	end
      
   // 对寄存器组进行32位初始化     
      always@(posedge clk)
          if(rst == 1'b0)
                //第一个不用管,通用寄存器组中
                for(i =0;i<31;i=i+1)begin
                    regs[i] <= 32'b0;
                end
          
          else if(reg_wen && reg_waddr_i != 5'b0)begin
			regs[reg_waddr_i] <= reg_wdata_i;
		end
            
   endmodule
        

加入R型指令

根据操作码判断为R型指令 根据func3 判断为ADD_SUB,指令。

根据fun7判断为ADD 或者SUB,fun7全为0,表示ADD。

R型和I型指令的编码方式不同。

ADDI为加立即数

ADD为加寄存器

SUB 为rs2 - rs1

指令相关性冲突 关于reg通用寄存器的读写冲突。

image.png

指令2需要取寄存器7的值,而指令1的值没有回写完成,此处存在问题。所以在指令2译码时,如果读端口地址和写端口地址相等,直接将写入数据给读出的数据。

复盘

执行:

根据if_ex传送的操作数等,ex执行模块。通过操作码判断为I型指令,然后根据func3判断为ADDI指令,然后进行操作数相加得到结果。得到输出data,以及寄存器组地址,以及写使能信号。下来将计算结果要写入寄存器组。完成写回操作。

写回:

此处需要写数据到寄存器组,所以对regs进行完善,通过ex得到寄存器地址和寄存器地址的数据,对regs寄存器进行写入数据。 对于流水线带来的指令相关性冲突,即第二条指令读的时候,第一条指令写没有完成,这个时候,我们需要直接读取写入的值。

并且写回的时候不能对地址为0的目的寄存器进行写入,直接在写回的时候加以设置。

这个过程叫做访存和回写。

根据操作码判断为R型指令 根据func3 判断为ADD_SUB,指令。

根据fun7判断为ADD指令。

image.png