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通用寄存器的读写冲突。
指令2需要取寄存器7的值,而指令1的值没有回写完成,此处存在问题。所以在指令2译码时,如果读端口地址和写端口地址相等,直接将写入数据给读出的数据。
复盘
执行:
根据if_ex传送的操作数等,ex执行模块。通过操作码判断为I型指令,然后根据func3判断为ADDI指令,然后进行操作数相加得到结果。得到输出data,以及寄存器组地址,以及写使能信号。下来将计算结果要写入寄存器组。完成写回操作。
写回:
此处需要写数据到寄存器组,所以对regs进行完善,通过ex得到寄存器地址和寄存器地址的数据,对regs寄存器进行写入数据。 对于流水线带来的指令相关性冲突,即第二条指令读的时候,第一条指令写没有完成,这个时候,我们需要直接读取写入的值。
并且写回的时候不能对地址为0的目的寄存器进行写入,直接在写回的时候加以设置。
这个过程叫做访存和回写。
根据操作码判断为R型指令 根据func3 判断为ADD_SUB,指令。
根据fun7判断为ADD指令。