自己动手写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对应代码,增加上图所示的连接关系。

原文地址:https://www.cnblogs.com/ycc1997/p/12203195.html

时间: 2024-10-16 04:16:41

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

自己动手写CPU之第五阶段(2)——OpenMIPS对数据相关问题的解决措施

将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第16篇,我尽量每周四篇 5.2 OpenMIPS对数据相关问题的解决措施 OpenMIPS处理器采用数据前推的方法来解决流水线数据相关问题.通过补充完善图4-4原始的数据流图,添加部分信号使得可以完成数据前推的工作,如图5-7所示.主要是将执行阶段的结果.访存阶段的结果前推到译码阶段,参与译码阶段选择运算源操作数的过程. 图5-8给出了为实现数据前推而对OpenMIPS系统结构所做的修改.有两个方面. (1)将处于流水线执行阶段的

自己动手写CPU_5_5.5 修改OpenMIPS以实现逻辑、位移操作和空指令

5.5 修改OpenMIPS以实现逻辑.位移操作和空指令 为了实现逻辑.位移操作与空指令,需要修改ID和EX模块. 5.5.1 修改译码阶段的ID模块 修改宏定义defines.v defines.v += /** EXE_* 功能码 或 指令码 **/ `define EXE_AND 6'b100100 `define EXE_OR 6'b100101 `define EXE_XOR 6'b100110 `define EXE_NOR 6'b100111 `define EXE_ANDI 6'

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

5.4 逻辑.位移操作与空指令说明 5.4.1 and.or.xor.nor 指令格式 指令用法 5.4.2 andi.xori指令 指令格式 指令用法 5.4.3 lui 指令格式 指令用法 5.4.4 sll.slv.sra.srav.srl.srlv 指令格式 指令用法 指令命名方式 5.4.5 nop.ssnop.sync.pref 原文地址:https://www.cnblogs.com/ycc1997/p/12207452.html

自己动手写CPU之第五阶段(1)——流水线数据相关问题

将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第15篇,我尽量每周四篇 上一章建立了原始的OpenMIPS五级流水线结构,但是只实现了一条ori指令,从本章开始,将逐步完善.本章首先讨论了流水线数据相关问题,然后修改OpenMIPS以解决该问题,并在5.3节验证了解决效果.接着对逻辑.移位操作与空指令的指令格式.用法.作用进行了一一说明,在5.5节通过扩展OpenMIPS实现了这些指令,最后编写测试程序,对实现效果进行了检验. 5.1 流水线数据相关问题 我们在第4章实现的五级

自己动手写处理器开篇介绍

将陆续上传本人写的新书<自己动手写处理器>(尚未出版),今天是开篇,我尽量每周四篇 内容简介 本书使用Verilog HDL设计实现了一款兼容MIPS32指令集架构的处理器--OpenMIPS.OpenMIPS处理器具有两个版本,分别是教学版和实践版.教学版的主要设想是尽量简单,处理器的运行情况比较理想化,与教科书相似,便于使用其进行教学.学术研究和讨论,也有助于学生理解课堂上讲授的知识.实践版的设计目标是能完成特定功能,发挥实际作用. 全书分为三部分.第一部分是理论篇,介绍了指令集架构.Ve

自己动手写CPU之第六阶段(2)——移动操作指令实现思路

将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第21篇,我尽量每周四篇 6.2 移动操作指令实现思路 6.2.1 实现思路 这6条移动操作指令可以分为两类:一类是不涉及特殊寄存器HI.LO的指令,包括movn.movz:另一类是涉及特殊寄存器HI.LO的指令,包括mfhi.mflo.mthi.mtlo.前一类很好实现,基本思路与第5章实现逻辑.移位操作指令时类似,只需要修改ID.EX模块即可.后一类涉及到特殊寄存器HI.LO,需要为OpenMIPS添加HI.LO寄存器,以及相应

自己动手写CPU之第六阶段(1)——移动操作指令说明

将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第20篇,我尽量每周四篇 本章将实现移动操作指令,首先在6.1节介绍了MIPS32指令集架构中定义的移动操作指令的格式.作用,接着在6.2节给出移动操作指令实现思路,介绍了修改后的数据流图.新出现的数据相关问题及其解决措施,并给出了修改后的OpenMIPS系统结构图.在6.3节列出了详细的修改过程.本章最后通过一个测试程序验证移动操作指令是否实现正确. 6.1 移动操作指令说明 MIPS32指令集架构中定义的移动操作指令共有6条:m

自己动手写CPU之第九阶段(9)——修改OpenMIPS以实现ll、sc指令

将陆续上传新书<自己动手写CPU>,今天是第48篇. 9.8 修改OpenMIPS以实现ll.sc指令 9.8.1 LLbit寄存器的实现 LLbit寄存器在LLbit模块中实现,模块接口如图9-30所示,各接口描述如表9-8所示. LLbit寄存器的代码如下,源文件是本书光盘Code\Chapter9_2目录下的LLbit_reg.v文件. module LLbit_reg( input wire clk, input wire rst, // 异常是否发生,为1表示异常发生,为0表示没有异

自己动手写处理器之第三阶段——教学版OpenMIPS处理器蓝图

将陆续上传本人写的新书<自己动手写处理器>(尚未出版),今天是第十篇,我尽量每周四篇 从本章开始将一步一步地实现教学版OpenMIPS处理器.本章给出了教学版OpenMIPS的系统蓝图,首先介绍了系统的设计目标,其中详细说明了OpenMIPS处理器计划实现的5级流水线.3.2节给出了OpenMIPS处理器的接口示意图,及各个接口的作用.3.3节简单解释了各个源代码文件的作用.最后描述了OpenMIPS处理器的实现方法,读者将发现本书给出的实现方法与现有书籍的方法完全不同,更加易于理解.便于实践