自己动手写cpu[解决流水线数据冲突问题]

自己动手写cpu[解决流水线数据冲突问题]

  • 一. 前言
  • 二.RAW问题
    • 2.1 相邻指令存在数据相关
    • 2.2 相隔一条指令存在数据相关
    • 2.3 相隔两条指令存在数据相关
  • 三.数据问题的解决方案
    • 3.1 如何解决RAW数据冲突问题
    • 3.2 OpenMips修改图
  • 四.代码修改
  • 五.测试与验证

一. 前言

自己动手写CPU【环境配置以及第一条ori指令】
中实现的五级流水线结构很简单,如果按照简单即美的标准,流水线结构是美的,但是并不完美,现实往往是复杂的。流水线中经常有一些被称为“相关”的情况发生,它让指令序列中下一条指令无法按照设计的时钟周期执行,这些问题会降低流水线的性能,流水线中的相关问题分为以下三种类型。

(1)结构相关:指的是在指令执行的过程中,由于硬件资源满足不了指令执行的要求,发生硬件资源冲突而产生的相关。比如:指令和数据都共享一个存储器,在某个时钟周期,流水线既要完成某条指令对存储器中数据的访问操作,又要完成对后续取指令的操作,这要就会发生存储器访问冲突,产生结构相关。
(2)数据相关:指的是在流水线执行的几条指令中,一条指令依赖于前面指令的执行结果。
(3)控制相关:指的是流水线中的分支指令或者其他需要改写PC的指令造成的相关。
本篇博客重点讨论数据相关,数据相关分为三种情况:RAW,WAR,WAW。

  • RAW:Read After Write ,假设指令j是在指令i后面执行的指令,RAW表示指令将数据写入寄存器后,指令j才能从这个寄存器读取数据。如果指令j在指令i写入寄存器前尝试读出该寄存器的内容,将得到不正确的数据。
  • WAR:Read After Write ,假设指令j是在指令i后面执行的指令,WAR表示指令i读取数据后,指令j才能从这个寄存器写数据。如果指令j在指令i读取寄存器前就写该寄存器,将使指令i读取的数据不正确。
    • WAW:Read After Read ,假设指令j是在指令i后面执行的指令,WAW表示指令i读取数据后,指令j才能从这个寄存器读取数据。如果指令j在指令i读取寄存器前就读取该寄存器,将使指令i读取的数据不正确。

对于上一篇博客创建的原始OpenMips五级流水线而言,从ori指令的执行过程中来看。

  • 只有流水线回写阶段才能写会寄存器,不存在WAW相关。
  • 流水线在译码阶段读寄存器,回写阶段写寄存器,不存在WAR相关。
  • 所以OpenMips只存在RAW相关。

二.RAW问题

2.1 相邻指令存在数据相关

ori $1,$0,0x1100 #$1=$0 |0x1100 = 0x1100
ori $2,$1,0x0020 #$2=$1 |0x0020 = 0x1120

自己动手写cpu[解决流水线数据冲突问题]_第1张图片

2.2 相隔一条指令存在数据相关

ori $1,$0,0x1100 #$1=$0 |0x1100 = 0x1100
ori $3,$0,0xffff #$3=$0 |0xffff = 0xffff
ori $2,$1,0x0020 #$2=$1 |0x0020 = 0x1120

自己动手写cpu[解决流水线数据冲突问题]_第2张图片

2.3 相隔两条指令存在数据相关

ori $1,$0,0x1100 #$1=$0 |0x1100 = 0x1100
ori $3,$0,0xffff #$3=$0 |0xffff = 0xffff
ori $4,$0,0xffff #$4=$0 |0xffff = 0xffff
ori $2,$1,0x0020 #$2=$1 |0x0020 = 0x1120

自己动手写cpu[解决流水线数据冲突问题]_第3张图片

三.数据问题的解决方案

3.1 如何解决RAW数据冲突问题

  • 插入暂停周期:当检测到相关问题的时候插入一些暂停周期
  • 编译器调度:编译器检测到数据相关的时候,可以改变部分指令的执行顺序。
  • 数据前推:将计算结果从产生处直接送到其他指令需要处或所有需要的功能单元处,避免流水线暂停。如下图所示新的$1的值实际上从第一条ori指令的执行阶段就已经算出来了,可以直接将该值从第一条ori指令的执行阶段送到第二条ori的译码阶段,从而使其得到新值。也可以直接将该值从访存阶段送到第三条的译码阶段。
    自己动手写cpu[解决流水线数据冲突问题]_第4张图片

3.2 OpenMips修改图

自己动手写cpu[解决流水线数据冲突问题]_第5张图片

四.代码修改

主要修改了id.v部分

`timescale 1ns/1ps
`include "./src/defines.v"
module id (
    //处于执行阶段的指令运算结果
    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     rst,
    input wire[`InstAddrBus] pc_i,
    input wire[`InstBus] inst_i,
    //read the value of Regfile
    input wire[`RegBus] reg1_data_i,
    input wire[`RegBus] reg2_data_i,
    //output to Regfile
    output reg          reg1_read_o,
    output reg          reg2_read_o,
    output reg[`RegAddrBus] reg1_addr_o,
    output reg[`RegAddrBus] reg2_addr_o,
    //output to execute stage
    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
);
//get order num and get function
//[26-31] is for ori ,you can judge if ori from it
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];
// save need imediate num
reg[`RegBus] imm;
//judge vaildable
reg instvaild;
//stage one : decode
always @(*) begin
    if(rst ==`RstEnable) begin
        aluop_o <= `EXE_NOP_OP;
        alusel_o <= `EXE_RES_NOP;
        wd_o   <= `NOPRegAddr;
        wreg_o <= `WriteDisable;
        instvaild <= `InstVaild;
        reg1_read_o <= 1'b0;
        reg2_addr_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;
        instvaild <= `InstInvaild;
        reg1_read_o <= 1'b0;
        reg2_read_o <= 1'b0;
        reg1_addr_o <= inst_i[25:21];//default read from regfile pole one
        reg2_addr_o <= inst_i[20:16];//default read from regfile pole two
        imm <= `ZeroWord;       
    case (op)
        `EXE_ORI:   begin
        //ori want to write in wanted write,so put wreg_o to writeable
        wreg_o <= `WriteEnable;
        //the subkind of compute is or
        aluop_o <= `EXE_OR_OP;
        //the kind of compute is  logic
        alusel_o <= `EXE_RES_LOGIC;
        //need Regfile one to read pole one
        reg1_read_o <= 1'b1;
        // not need Regfile two to read pole two
        reg2_read_o <= 1'b0;
        //execute immediate num
        imm <= {16'h0, inst_i[15:0]};
        //execute where you want to write in
        wd_o <= inst_i[20:16];
        // ori is Vaild;
        instvaild <= `InstVaild;
        end 
        default: begin
            
        end
    endcase// case op
    end //if
end // always
//stage two : confirm source data one
//第五章增加了两种情况,如果Regfile模块读端口1或2读取的寄存器就是执行阶段要写的目的寄存器,那么直接把执行阶段的结果作为reg1_o的值
//二是读取的寄存器就是访问存储器阶段要写的寄存器,那么直接将访问存储器的结果作为reg的值
always @(*) begin
    if(rst == `RstEnable) begin
        reg1_o <= `ZeroWord;
    end else if((reg1_read_o == 1'b1) && (ex_wreg_i == 1'b1) && (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) && (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; // Regfile Readone as input
    end else if (reg1_read_o == 1'b0) begin
        reg1_o <= imm; //imediate num
    end else begin
        reg1_read_o <=`ZeroWord;
    end
end
//stage three : confirm source data two
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 <= reg1_data_i; // Regfile Readone as input
    end else if (reg2_read_o == 1'b0) begin
        reg2_o <= imm; //imediate num
    end else begin
        reg2_read_o <=`ZeroWord;
    end
end
endmodule

五.测试与验证

测试程序如下:

	.org 0x0
.global _start
   .set noat
_start:
   ori $1,$0,0x1100        # $1 = $0 | 0x1100 = 0x1100
   ori $1,$1,0x0020        # $1 = $1 | 0x0020 = 0x1120
   ori $1,$1,0x4400        # $1 = $1 | 0x4400 = 0x5520
   ori $1,$1,0x0044        # $1 = $1 | 0x0044 = 0x5564

测试功能
在这里插入图片描述
在这里插入图片描述

你可能感兴趣的:(openmips,java,开发语言)