自己动手写CPU_5_5.2 OpenMIPS对数据相关问题的解决措施
作者:互联网
5.2 OpenMIPS对数据相关问题的解决措施
OpenMIPS采用数据前推的方式来解决流水线数据相关问题。如图所示(虚线),将执行阶段的结果、访存阶段的结果推到译码阶段,参与译码阶段选择运算源操作数的过程。
下图给出了实现数据前推对OpenMIPS系统结构的修改,具体为以下两方面:
1. 处于EX阶段的指令运算结果送到ID阶段。
2. 将MEM阶段的指令送到ID阶段。
为此需要修改ID模块的接口:
译码阶段的ID模块会根据送入的信息进行判断,解决数据相关,给出最后要参与运算的操作数。ID模块代码的修改部分用红字标出,需要添加的注释用绿字标识。。
`include "defines.v" module id( input wire rst, input wire[`InstAddrBus] pc_i, input wire[`InstBus] inst_i, //处于执行阶段的指令要写入的目的寄存器信息 input wire ex_wreg_i, input wire[`RegBus] ex_wdata_i, input wire[`RegAddrBus] ex_wd_i, //处于访存阶段的指令要写入的目的寄存器信息 input wire mem_wreg_i, input wire[`RegBus] mem_wdata_i, input wire[`RegAddrBus] mem_wd_i, input wire[`RegBus] reg1_data_i, input wire[`RegBus] reg2_data_i, //送到regfile的信息 output reg reg1_read_o, output reg reg2_read_o, output reg[`RegAddrBus] reg1_addr_o, output reg[`RegAddrBus] reg2_addr_o, //送到执行阶段的信息 output reg[`AluOpBus] aluop_o, output reg[`AluSelBus] alusel_o, output reg[`RegBus] reg1_o, output reg[`RegBus] reg2_o, output reg[`RegAddrBus] wd_o, output reg wreg_o );
//获取指令的指令码和功能码
//对于ori指令只需要通过判断26-31bit的值即可确定是否为ori
wire[5:0] op = inst_i[31:26];
wire[4:0] op2 = inst_i[10:6];
wire[5:0] op3 = inst_i[5:0];
wire[4:0] op4 = inst_i[20:16];
//保存指令执行需要的立即数
reg[`RegBus] imm;
//指示指令是否有效
reg instvalid;
/**
第一部分--对指令译码
**/
always @ (*) begin
if (rst == `RstEnable) begin
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
wd_o <= `NOPRegAddr;
wreg_o <= `WriteDisable;
instvalid <= `InstValid;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= `NOPRegAddr;
reg2_addr_o <= `NOPRegAddr;
imm <= 32'h0;
end else begin
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
wd_o <= inst_i[15:11];
wreg_o <= `WriteDisable;
instvalid <= `InstInvalid;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= inst_i[25:21]; //默认通过Regfile读端口1读取的寄存器地址
reg2_addr_o <= inst_i[20:16]; //默认通过Regfile读端口2读取的寄存器地址
imm <= `ZeroWord;
case (op)
`EXE_ORI: //根据op的值判断是否为ori指令
begin //ORI指令
wreg_o <= `WriteEnable; // ori需要将结果写入目的寄存器,所以wreg_o为WriteEnable
aluop_o <= `EXE_OR_OP; //算数类型
alusel_o <= `EXE_RES_LOGIC; //子运算类型
reg1_read_o <= 1'b1; // rs,需要读取
reg2_read_o <= 1'b0; // rt,不需要读取。
imm <= {16'h0, inst_i[15:0]}; //指令执行需要的立即数
wd_o <= inst_i[20:16]; // rt的寄存器地址
instvalid <= `InstValid; //指令有效
end
default: begin
end
endcase //case op
end //if
end //always
/**
第二部分--确定进行运算的源操作数1
给reg1_o赋值分两种情况:
1. 若regfile模块读端口1要读取的寄存器就是ex阶段要写的寄存器,那么直接把ex的结果ex_wdata_i作为reg1_o的值。
2. 若regfile模块读端口1要读取的寄存器就是mem阶段要写的寄存器,直接把mem_wdata_i作为reg1_o的值。
**/
always @ (*) begin
if(rst == `RstEnable) begin
reg1_o <= `ZeroWord;
end else if((reg1_read_o == 1'b1) && (ex_wreg_i == 1'b1) //情况1
&& (ex_wd_i == reg1_addr_o)) begin
reg1_o <= ex_wdata_i;
end else if((reg1_read_o == 1'b1) && (mem_wreg_i == 1'b1) //情况2
&& (mem_wd_i == reg1_addr_o)) begin
reg1_o <= mem_wdata_i;
end else if(reg1_read_o == 1'b1) begin
reg1_o <= reg1_data_i;
end else if(reg1_read_o == 1'b0) begin
reg1_o <= imm;
end else begin
reg1_o <= `ZeroWord;
end
end
/**
第三部分--确定进行运算的源操作数2
给reg2_o赋值分两种情况:
1. 若regfile模块读端口2要读取的寄存器就是ex阶段要写的寄存器,那么直接把ex的结果ex_wdata_i作为reg2_o的值。
2. 若regfile模块读端口2要读取的寄存器就是mem阶段要写的寄存器,直接把mem_wdata_i作为reg2_o的值。
**/
always @ (*) begin
if(rst == `RstEnable) begin
reg2_o <= `ZeroWord;
end else if((reg2_read_o == 1'b1) && (ex_wreg_i == 1'b1)
&& (ex_wd_i == reg2_addr_o)) begin
reg2_o <= ex_wdata_i;
end else if((reg2_read_o == 1'b1) && (mem_wreg_i == 1'b1)
&& (mem_wd_i == reg2_addr_o)) begin
reg2_o <= mem_wdata_i;
end else if(reg2_read_o == 1'b1) begin
reg2_o <= reg2_data_i;
end else if(reg2_read_o == 1'b0) begin
reg2_o <= imm;
end else begin
reg2_o <= `ZeroWord;
end
end
endmodule
除修改ID模块代码外还要修改顶层模块OpenMIPS对应代码,增加上图所示的连接关系。
标签:begin,OpenMIPS,end,read,reg2,5.2,reg1,wire,CPU 来源: https://www.cnblogs.com/ycc1997/p/12203195.html