tiny riscv - id译码模块和一些复盘

171 阅读3分钟

id译码模块

根据取出的数据译码

image.png

`include "defines.v"
module id(
	//from if_id   指令地址输入
	input wire[31:0] inst_i,
	input wire[31:0] inst_addr_i,
		
	// to regs   寄存器组两个读地址信号
	output reg[4:0] rs1_addr_o,
	output reg[4:0] rs2_addr_o,
	// from regs    来自寄存器组的输入
	input wire[31:0] rs1_data_i,
	input wire[31:0] rs2_data_i,
	
	//to id_ex    取出的信号,操作数1 操作数2,目的寄存器地址 和寄存器使能,以及指令和地址
	output reg[31:0] inst_o,
	output reg[31:0] inst_addr_o,
	output reg[31:0] op1_o,	
	output reg[31:0] op2_o,
	output reg[4:0]  rd_addr_o,	
	output reg 		 reg_wen
);


wire[6:0] opcode; 
wire[4:0] rd; 
wire[2:0] func3; 
wire[4:0] rs1;
wire[11:0]imm;

wire[4:0] rs2;
wire[6:0] func7;

//操作码opcode  I型指令
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 imm = inst_i[31:20];

//根据func3来判断,立即数imm要进行符号位扩展
always@(*)
    //指令和地址一直传递
    
    inst_o = inst_i;
    inst_addr_i = inst_addr_o;


    case(opcode)
        // I型指令,ADDI的译码过程
    
        `INST_TYPE_I:begin
            case(func3)
                INST_ADDI : begin
                    //地址
                    rs1_addr_o = rs1;
                    //不需要,则给定0,因为为ADDI立即数指令
                    rs2_addr_o = 5'b0
                    //操作数1来自输入,操作数2为imm立即数,需要进行符号位扩展
                    op1_o = rs1_data_i
                    //扩展为imm的11位扩展至32位总共。
                    op2_o = {{20{imm[11]}},imm}
                    
                    // 使能信号,拉高表示要写,目的寄存器赋值
                    reg_wen = 1'b1;
                    
                    rd_addr_o = rd;
                    
                    
                 end   

        default:begin
						rs1_addr_o = 5'b0;
						rs2_addr_o = 5'b0;
						op1_o 	   = 32'b0;
						op2_o      = 32'b0;
						rd_addr_o  = 5'b0;
						reg_wen    = 1'b0;						
					end
    endcase

    
`INST_TYPE_R_M:begin
				case(func3)
					`INST_ADD_SUB:begin
						rs1_addr_o = rs1;
						rs2_addr_o = rs2;
						op1_o 	   = rs1_data_i;
						op2_o      = rs2_data_i;
						rd_addr_o  = rd;
						reg_wen    = 1'b1;
					end
					default:begin
						rs1_addr_o = 5'b0;
						rs2_addr_o = 5'b0;
						op1_o 	   = 32'b0;
						op2_o      = 32'b0;
						rd_addr_o  = 5'b0;
						reg_wen    = 1'b0;						
					end
				endcase				
			end
			
		default:begin
						rs1_addr_o = 5'b0;
						rs2_addr_o = 5'b0;
						op1_o 	   = 32'b0;
						op2_o      = 32'b0;
						rd_addr_o  = 5'b0;
						reg_wen    = 1'b0;				
			end
		endcase
	end










endmodule

regs寄存器组

module regs(
	input wire clk,
	input wire rst,
	//from id
	input wire[4:0] reg1_raddr_i,
	input wire[4:0] reg2_raddr_i,
	
	//to id
	output reg[31:0] reg1_rdata_o,
	output reg[31:0] reg2_rdata_o,
		reg_wen

);

reg[31:0] regs[0:31];

always@(*)
    if(rst == 1'b0)
        reg1_rdata_0 <= 32'b0;
    else if(reg1_addi ==5'b0)
        reg1_rdata_0 <= 32'b0;
    else
        reg1_rdata_0 <= regs[reg1_addr_i];
        
always@(*)
    if(rst == 1'b0)
        reg2_rdata_0 <= 32'b0;
    else if(reg2_addi ==5'b0)
        reg2_rdata_0 <= 32'b0;
    else
        reg2_rdata_0 <= regs[reg2_addr_i];

endmodule 

id_ex模块

来自id模块的输出作为输入,打一拍。

	input wire clk,
	input wire rst,
	//from id 指令,地址, 操作数,目的寄存器地址,使能信号,
	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 		 reg_wen_i,
	//to ex
	output wire[31:0] inst_o,
	output wire[31:0] inst_addr_o,
	output wire[31:0] op1_o,	
	output wire[31:0] op2_o,
	output wire[4:0]  rd_addr_o,	
	output wire 	  reg_wen_o		
);

// 使用定义好的模块进行打拍
// 对指令进行打拍为NOP指令
	dff_set #(32) dff1(clk,rst,`INST_NOP,inst_i,inst_o);
// 地址默认给32'b0	
	dff_set #(32) dff2(clk,rst,32'b0,inst_addr_i,inst_addr_o);
// op1 op2 均为32'b0	
	dff_set #(32) dff3(clk,rst,32'b0,op1_i,op1_o);
	
	dff_set #(32) dff4(clk,rst,32'b0,op2_i,op2_o);
// 寄存器地址5位	
	dff_set #(5) dff5(clk,rst,5'b0,rd_addr_i,rd_addr_o);
	
	dff_set #(1) dff6(clk,rst,1'b0,reg_wen_i,reg_wen_o);





image.png

image.png

复盘:

取指过程:

ROM中已经在地址区存好了指令,现在从ROM中根据指令地址取出指令。需要ROM的模块,做生成指令操作。 需要PC_reg模块,生成指令地址,连续的指令地址,方便对ROM进行取指令操作。 ifetch取值模块收到PC_reg的指令地址,将指令地址给ROM,通过ROM确定指令。

pc_reg生成连续的ROM指令地址,占四个地址空间32位。

ifetch模块取出指令,通过if_id模块进行打拍,if模块借助于模块复用的dff_set模块进行模块复用,因为打拍是指令和地址需要复位,所以这里使用了提供指令参数化的defines模块,defines模块中将指令进行宏定义,方便其他模块调用。

译码过程:

id模块通过if_id模块取得指令和地址作为输入,根据指令完成了I型指令ADDI指令的译码工作,得到了rs1,rs2,操作数1,2,rd寄存器,使能信号等输出信号。这其中id模块通过rs1,rs2的地址通过regs寄存器组,得到rs1,rs2的data数据,即操作数1,2。

通过id_ex模块进行打拍。