自己动手写CPU_5_5.2 OpenMIPS对数据相关问题的解决措施

Posted ycc1997

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自己动手写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对应代码,增加上图所示的连接关系。

以上是关于自己动手写CPU_5_5.2 OpenMIPS对数据相关问题的解决措施的主要内容,如果未能解决你的问题,请参考以下文章

自己动手写CPU——第一篇

自己动手写CPU_5_5.4 逻辑位移操作与空指令的说明

自己动手写CPU之第四阶段——MIPS编译环境的建立

自己动手写一个操作系统——总目录

自己动手写一个操作系统——总目录

自己动手写一个操作系统——MBR_调试_elf_bin