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对应代码,增加上图所示的连接关系。
原文地址:https://www.cnblogs.com/ycc1997/p/12203195.html
时间: 2024-10-16 04:16:41