流水线中的三种相关问题
相邻:即译码和回写差2T
相隔1:差1T
相隔2:同一T读不到(这种回写在regfile里已被同写同读处理掉了)
没有相隔3
//读1地址和写地址相同且均使能有效时,读值直接为写值
else if((raddr1 == waddr) && (we == `WriteEnable) && (re1 == `ReadEnable)) begin
rdata1 <= wdata;
end
三种处理数据相关的方法
采取数据前堆。加了两条回传线,把执行和访存的组合逻辑阶段回传到两个mux里
给译码的ID模块增加6个分别来自执行和访存的输入及相应的逻辑
//……………………………………………………………………………………………………
//处于执行阶段的指令要写入的目的寄存器信息
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,
//……………………………………………………………………………………………………
//……………………………………………………………………………………………………
//给reg1_o赋值的过程增加了两种情况
//1.如果regfile模块读端口1要读取的寄存器就是执行阶段要写的目的寄存器
// 那么直接把执行阶段的结果ex_wdata_i给到reg1_o
//2.如果regfile模块读端口1要读取的寄存器就是访存阶段要写的目的寄存器
// 那么直接把访存阶段的结果mem_wdata_i给到reg1_o
/***************************************
***** 第二段:确定进行运算的源操作数1 *****
***************************************/
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;
end
else if(reg1_read_o == 1'b0) begin
reg1_o <= imm;
end
else begin
reg1_o <= `ZeroWord;
end
end
//给reg2_o赋值的过程增加了两种情况
//1.如果regfile模块读端口2要读取的寄存器就是执行阶段要写的目的寄存器
// 那么直接把执行阶段的结果ex_wdata_i给到reg2_o
//2.如果regfile模块读端口2要读取的寄存器就是访存阶段要写的目的寄存器
// 那么直接把访存阶段的结果mem_wdata_i给到reg2_o
/***************************************
***** 第三段:确定进行运算的源操作数2 *****
***************************************/
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
此外还要修改顶层连接,略。
8条逻辑操作 6条移位 2条空指令
sync和pref当作nop
四种R指令的逻辑运算:op均为6’b0,靠5:0的func功能码来判断具体操作。均为rs和rt运算后存到rd
2个I指令的逻辑运算,可通过op直接判断
lui指令:把16位立即数保存到rt寄存器高16位,低位补0
直接op判断
6个移位指令都是R指令,OP均为0,通过func判断
sra是rt右移后用rt[31]来补;
后三个的移位数从sa改为rs[4:0]
nop和ssnop处理为sll,向$0保存,直接被置0
需修改译码ID和执行EX
给出现阶段的完整宏定义
`define RstEnable 1'b1
`define RstDisable 1'b0
`define ZeroWord 32'h00000000
`define WriteEnable 1'b1
`define WriteDisable 1'b0
`define ReadEnable 1'b1
`define ReadDisable 1'b0
`define AluOpBus 7:0
`define AluSelBus 2:0
`define InstValid 1'b0
`define InstInvalid 1'b1
`define Stop 1'b1
`define NoStop 1'b0
`define InDelaySlot 1'b1
`define NotInDelaySlot 1'b0
`define Branch 1'b1
`define NotBranch 1'b0
`define InterruptAssert 1'b1
`define InterruptNotAssert 1'b0
`define TrapAssert 1'b1
`define TrapNotAssert 1'b0
`define True_v 1'b1
`define False_v 1'b0
`define ChipEnable 1'b1
`define ChipDisable 1'b0
//指令
`define EXE_AND 6'b100100
`define EXE_OR 6'b100101
`define EXE_XOR 6'b100110
`define EXE_NOR 6'b100111
`define EXE_ANDI 6'b001100
`define EXE_ORI 6'b001101
`define EXE_XORI 6'b001110
`define EXE_LUI 6'b001111
`define EXE_SLL 6'b000000
`define EXE_SLLV 6'b000100
`define EXE_SRL 6'b000010
`define EXE_SRLV 6'b000110
`define EXE_SRA 6'b000011
`define EXE_SRAV 6'b000111
`define EXE_SYNC 6'b001111
`define EXE_PREF 6'b110011
`define EXE_NOP 6'b000000
`define SSNOP 32'b00000000000000000000000001000000
`define EXE_SPECIAL_INST 6'b000000
`define EXE_REGIMM_INST 6'b000001
`define EXE_SPECIAL2_INST 6'b011100
//AluOp
`define EXE_AND_OP 8'b00100100
`define EXE_OR_OP 8'b00100101
`define EXE_XOR_OP 8'b00100110
`define EXE_NOR_OP 8'b00100111
`define EXE_ANDI_OP 8'b01011001
`define EXE_ORI_OP 8'b01011010
`define EXE_XORI_OP 8'b01011011
`define EXE_LUI_OP 8'b01011100
`define EXE_SLL_OP 8'b01111100
`define EXE_SLLV_OP 8'b00000100
`define EXE_SRL_OP 8'b00000010
`define EXE_SRLV_OP 8'b00000110
`define EXE_SRA_OP 8'b00000011
`define EXE_SRAV_OP 8'b00000111
`define EXE_NOP_OP 8'b00000000
//AluSel
`define EXE_RES_LOGIC 3'b001
`define EXE_RES_SHIFT 3'b010
`define EXE_RES_NOP 3'b000
//指令存储器inst_rom
`define InstAddrBus 31:0
`define InstBus 31:0
`define InstMemNum 131071
`define InstMemNumLog2 17
//通用寄存器regfile
`define RegAddrBus 4:0
`define RegBus 31:0
`define RegWidth 32
`define DoubleRegWidth 64
`define DoubleRegBus 63:0
`define RegNum 32
`define RegNumLog2 5
`define NOPRegAddr 5'b00000
sll、srl、sra三条指令需[31:21]均为0
module id(
input wire rst,
input wire[`InstAddrBus] pc_i, //[31:0]
input wire[`InstBus] inst_i, //[31:0] 接收IF/ID模块给的指令
//从regfile里输入的
input wire[`RegBus] reg1_data_i, //[31:0]
input wire[`RegBus] reg2_data_i, //[31:0]
//处于执行阶段的指令要写入的目的寄存器信息
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,
//送到regfile的信息
output reg reg1_read_o, //使能
output reg reg2_read_o, //使能
output reg[`RegAddrBus] reg1_addr_o, //[4:0]
output reg[`RegAddrBus] reg2_addr_o, //[4:0]
//送到执行阶段的信息
output reg[`AluOpBus] aluop_o, //译码阶段的输出aluop_o的宽度 7:0 运算子类型
output reg[`AluSelBus] alusel_o, //译码阶段的输出alusel_o的宽度 2:0 运算类型
//结果写入寄存器
output reg[`RegAddrBus] wd_o, //[4:0] 要写入的目的寄存器地址
output reg wreg_o, //是否有要写入的目的寄存器
//这两个是最终输出的操作数
output reg[`RegBus] reg1_o, //[31:0] 源操作数1
output reg[`RegBus] reg2_o //[31:0] 源操作数2
);
//取得指令的指令码,功能码 即分解帧格式
//对于ori指令只需通过判断第26-31bit的值,即可判断是否是ori指令;对应I类型指令
wire[5:0] op = inst_i[31:26]; //op指令段
wire[4:0] op2 = inst_i[10:6];
wire[5:0] op3 = inst_i[5:0];
wire[4:0] op4 = inst_i[20:16];
//保存指令执行需要的立即数[31:0]
reg[`RegBus] imm;
//指示指令是否有效
reg instvalid;
//译码操作 组合逻辑实时译码
always @ (*)
if (rst == `RstEnable) begin
aluop_o <= `EXE_NOP_OP; //8'b0000_0000
alusel_o <= `EXE_RES_NOP; //3'b000
wd_o <= `NOPRegAddr; //5'b00000
wreg_o <= `WriteDisable;
instvalid <= `InstValid; //指令有效
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= `NOPRegAddr; //5'b00000
reg2_addr_o <= `NOPRegAddr; //5'b00000
imm <= `ZeroWord;
end
else begin
//以下是非复位且无法判断指令时的默认情况
aluop_o <= `EXE_NOP_OP; //8'b0000_0000
alusel_o <= `EXE_RES_NOP; //3'b000
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]; //rs寄存器
reg2_addr_o <= inst_i[20:16]; //rt寄存器
imm <= `ZeroWord;
case (op)
`EXE_SPECIAL_INST: begin //op为special 去判断op2
case (op2)
5'b00000: begin //5-2阶段非0无效 继续判断op3功能码 以此可跟sll区别
case (op3) //功能码 操作均是:类型与子类型,写使能、两个读使能、有无效(这几个操作要写入的寄存器就是默认的rd!)
`EXE_OR: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_OR_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_AND: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_AND_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_XOR: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_XOR_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_NOR: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_NOR_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SLLV: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_SLL_OP;
alusel_o <= `EXE_RES_SHIFT; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SRLV: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_SRL_OP;
alusel_o <= `EXE_RES_SHIFT; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SRAV: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_SRA_OP;
alusel_o <= `EXE_RES_SHIFT; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SYNC: begin
wreg_o <= `WriteDisable; aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP; reg1_read_o <= 1'b0; reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
default: begin
end
endcase
end
default: begin
end
endcase
end
//这几个写入的寄存器不同,所以单独赋值wd_o
`EXE_ORI: begin //ORI指令 用立即数和rs计算后写入rt
alusel_o <= `EXE_RES_LOGIC;
aluop_o <= `EXE_OR_OP;
wreg_o <= `WriteEnable;
wd_o <= inst_i[20:16]; //立即数与rs的结果写入rt
imm <= {16'h0,inst_i[15:0]};
reg1_read_o <= 1'b1; //要去读一个rs
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
end
`EXE_ANDI: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_AND_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b0;
imm <= {16'h0, inst_i[15:0]}; wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_XORI: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_XOR_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b0;
imm <= {16'h0, inst_i[15:0]}; wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_LUI: begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_OR_OP;
alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1; reg2_read_o <= 1'b0;
imm <= {inst_i[15:0], 16'h0}; wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_PREF: begin
wreg_o <= `WriteDisable; aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP; reg1_read_o <= 1'b0; reg2_read_o <= 1'b0;
instvalid <= `InstValid;
end
default:;
endcase
//sll、srl、sra 把移位数给到imm,作为1个源操作数
if(inst_i[31:21] == 11'b00000000000) begin //新的位段判断指令
if (op3 == `EXE_SLL) begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_SLL_OP;
alusel_o <= `EXE_RES_SHIFT; reg1_read_o <= 1'b0; reg2_read_o <= 1'b1;
imm[4:0] <= inst_i[10:6]; wd_o <= inst_i[15:11];
instvalid <= `InstValid;
end
else if ( op3 == `EXE_SRL ) begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_SRL_OP;
alusel_o <= `EXE_RES_SHIFT; reg1_read_o <= 1'b0; reg2_read_o <= 1'b1;
imm[4:0] <= inst_i[10:6]; wd_o <= inst_i[15:11];
instvalid <= `InstValid;
end
else if ( op3 == `EXE_SRA ) begin
wreg_o <= `WriteEnable; aluop_o <= `EXE_SRA_OP;
alusel_o <= `EXE_RES_SHIFT; reg1_read_o <= 1'b0; reg2_read_o <= 1'b1;
imm[4:0] <= inst_i[10:6]; wd_o <= inst_i[15:11];
instvalid <= `InstValid;
end
end
end
//确定2个源操作数
always@(*)
if(rst == `RstEnable)
reg1_o <= `ZeroWord;
else if((reg1_read_o == 1'b1)&&(ex_wreg_i == 1'b1)&&(reg1_addr_o == ex_wd_i))
reg1_o <= ex_wdata_i;
else if((reg1_read_o == 1'b1)&&(mem_wreg_i == 1'b1)&&(reg1_addr_o == mem_wd_i))
reg1_o <= mem_wdata_i;
else if(reg1_read_o == 1'b1)
reg1_o <= reg1_data_i;
else if(reg1_read_o == 1'b0)
reg1_o <= imm;
else
reg1_o <= `ZeroWord;
always@(*)
if(rst == `RstEnable)
reg2_o <= `ZeroWord;
else if((reg2_read_o == 1'b1)&&(ex_wreg_i == 1'b1)&&(reg2_addr_o == ex_wd_i))
reg2_o <= ex_wdata_i;
else if((reg2_read_o == 1'b1)&&(mem_wreg_i == 1'b1)&&(reg2_addr_o == mem_wd_i))
reg2_o <= mem_wdata_i;
else if(reg2_read_o == 1'b1)
reg2_o <= reg2_data_i;
else if(reg2_read_o == 1'b0)
reg2_o <= imm;
else
reg2_o <= `ZeroWord;
endmodule
对应修改执行模块EX。EX相比于ID没有那么多项,因为把相同的操作合并了
`include "defines.v"
module ex(
input wire rst,
//送到执行阶段的信息
input wire[`AluOpBus] aluop_i,
input wire[`AluSelBus] alusel_i,
input wire[`RegBus] reg1_i,
input wire[`RegBus] reg2_i,
input wire[`RegAddrBus] wd_i,
input wire wreg_i,
//执行结果
output reg[`RegAddrBus] wd_o,
output reg wreg_o,
output reg[`RegBus] wdata_o
);
//保存逻辑运算的结果
reg[`RegBus] logicout; //保存逻辑运算结果
reg[`RegBus] shiftres; //保存移位运算结果
//根据大类型判断输出
always @ (*) begin
wd_o <= wd_i; //传递写目的寄存器地址
wreg_o <= wreg_i; //传递寄存器写使能信号
case ( alusel_i )
`EXE_RES_LOGIC: begin
wdata_o <= logicout;
end
`EXE_RES_SHIFT: begin
wdata_o <= shiftres;
end
default: begin
wdata_o <= `ZeroWord;
end
endcase
end
//根据小类型作逻辑运算
always @ (*)
if(rst == `RstEnable) begin
logicout <= `ZeroWord;
end
else begin
case (aluop_i)
`EXE_OR_OP: begin
logicout <= reg1_i | reg2_i;
end
`EXE_AND_OP: begin
logicout <= reg1_i & reg2_i;
end
`EXE_NOR_OP: begin
logicout <= ~(reg1_i |reg2_i);
end
`EXE_XOR_OP: begin
logicout <= reg1_i ^ reg2_i;
end
default: begin
logicout <= `ZeroWord;
end
endcase
end
//根据小类型作移位运算
always @ (*)
if(rst == `RstEnable) begin
shiftres <= `ZeroWord;
end
else begin
case (aluop_i)
`EXE_SLL_OP: begin
shiftres <= reg2_i << reg1_i[4:0] ;
end
`EXE_SRL_OP: begin
shiftres <= reg2_i >> reg1_i[4:0];
end
`EXE_SRA_OP: begin //sra和srav都是这个指令
shiftres <= ({32{reg2_i[31]}} << (6'd32-{1'b0, reg1_i[4:0]}))
| reg2_i >> reg1_i[4:0];
end
default: begin
shiftres <= `ZeroWord;
end
endcase
end
endmodule