ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2020-01-16 20:54:30  阅读:351  来源: 互联网

标签:begin OpenMIPS end read reg2 5.2 reg1 wire CPU


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有