自己动手写CPU【环境配置以及第一条ori指令】
中实现的五级流水线结构很简单,如果按照简单即美的标准,流水线结构是美的,但是并不完美,现实往往是复杂的。流水线中经常有一些被称为“相关”的情况发生,它让指令序列中下一条指令无法按照设计的时钟周期执行,这些问题会降低流水线的性能,流水线中的相关问题分为以下三种类型。
(1)结构相关:指的是在指令执行的过程中,由于硬件资源满足不了指令执行的要求,发生硬件资源冲突而产生的相关。比如:指令和数据都共享一个存储器,在某个时钟周期,流水线既要完成某条指令对存储器中数据的访问操作,又要完成对后续取指令的操作,这要就会发生存储器访问冲突,产生结构相关。
(2)数据相关:指的是在流水线执行的几条指令中,一条指令依赖于前面指令的执行结果。
(3)控制相关:指的是流水线中的分支指令或者其他需要改写PC的指令造成的相关。
本篇博客重点讨论数据相关,数据相关分为三种情况:RAW,WAR,WAW。
对于上一篇博客创建的原始OpenMips五级流水线而言,从ori指令的执行过程中来看。
ori $1,$0,0x1100 #$1=$0 |0x1100 = 0x1100
ori $2,$1,0x0020 #$2=$1 |0x0020 = 0x1120
ori $1,$0,0x1100 #$1=$0 |0x1100 = 0x1100
ori $3,$0,0xffff #$3=$0 |0xffff = 0xffff
ori $2,$1,0x0020 #$2=$1 |0x0020 = 0x1120
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
主要修改了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