id译码模块
根据取出的数据译码
`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);
复盘:
取指过程:
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模块进行打拍。