概述
转移指令包括跳转、分支两种。跳转指令是绝对转移,分支指令是相对转移,两者实现方法相似。转移指令涉及延迟槽,所以首先介绍延迟槽的概念。
#延迟槽
控制相关指流水线中的转移指令或者其他需要改写PC的指令造成的相关。这些指令改变了PC值,导致后面已经进入流水线的几条指令无效。在流水线执行阶段进行转移判断,并且转移发生,会有2条无效指令,浪费两个时钟周期。为了减少损失,规定转移指令后面的指令位置为延迟槽,延迟槽中的指令被称为“延迟指令”。延迟指令总会被执行,与转移发生与否没有关系。但是即使引入延迟槽,在转移发生时依然会导致已经进入取指阶段的指令无效,也就是说,仍浪费一个时钟周期,要解决这个问题,可以在译码阶段进行判断,避免浪费时钟周期。
转移指令说明
跳转指令
31-26 | 25-21 | 20-16 | 15-11 | 10-6 | 5-0 | useage | function |
---|---|---|---|---|---|---|---|
SPECIAL(000000) | rs | 00000 | 00000 | 00000 | JR(001000) | jr rs | pc<-rs将地址为rs的通用寄存器的值赋给寄存器PC,作为新的指令地址 |
SPECIAL(000000) | rs | 00000 | rd | 00000 | JALR(001001) | jalr rs | rd<-return_address,pc<-rs将地址为rs的通用寄存器的值赋给寄存器PC,作为新的指令地址,同时跳转指令后面第二条指令的地址作为返回地址保存到地址为rd的通用寄存器,如果没有在指令中指明rd,那么默认将返回地址保存到寄存器$31 |
31-26 | 25-0 | useage | function |
---|---|---|---|
J(000010) | instr_index | j target | pc<-(pc+4)[31,28] ll target ll '00’转移到新的指令地址,其中新地址地址的低28位是指令中的target左移两位的值,新指令地址的高4位是跳转指令后面延迟槽指令的地址高4位 |
JAL(000011) | instr_index | jal target | pc<-(pc+4)[31-28] ll target ll '00’转移到新的指令地址,其中新指令地址与指令j相同,不再解释。但是指令jal还要跳转指令后面第2条指令的地址作为返回地址保存到寄存器$1 |
j,jar,jr,jalr指令在转移之前都要先执行延迟槽指令
分支指令
31-26 | 25-21 | 20-16 | 15-0 | useage | function |
---|---|---|---|---|---|
BEQ(000100) | rs | rt | offset | beq rs,rt,offset | if rs=rt then branch将地址为rs的通用寄存器的值与地址为rt的通用寄存器的值相比计较,如果相等,那么发生转移 |
BEQ(000100) | 00000 | 00000 | offset | b offset | 无条件转移,b指令可以认为是beq指令的特殊情况,当beq指令的rs,rt都等于0时,即为b指令,在实现OpenMIPS实现的时候不需要特意实现b指令,只需要实现beq指令即可 |
BGTZ(000111) | rs | 00000 | offset | bgtz rs,offset | if rs>0 then branch如果地址为rs的通用寄存器的值大于0,那么发生转移 |
BLEZ(000110) | rs | 00000 | offset | blez rs,offset | if rs<=0 then branch如果地址为rs的通用寄存器的值小于等于0,那么发生转移 |
BNE(000101 | rs | rt | offset | bne rs,rt,offset | if rs!=rt then branch如果地址为rs的通用寄存器的值不等于地址为rt的通用寄存器的值,那么发生转移 |
REGIMM(000001) | rs | BLTZ(00000) | offset | bltz rs,offset | if rs<0 then branch如果地址为rs的通用寄存器的值小于0,那么发生转移 |
REGIMM(000001) | rs | BLTZAL(10000) | offset | bltzal rs,offset | if rs<0 then branch如果地址为rs的通用寄存器的值小于0,那么发生转移,并且将转移指令后面第2条指令的地址作为返回地址,保存到通用寄存器$31 |
REGIMM(000001) | rs | BGEZ(00001) | offset | bgez rs,offset | if rs>=0 then branch如果地址为rs的通用寄存器的值大于0,那么发生转移 |
REGIMM(000001) | rs | BGEZAL(10001) | offset | bgezal rs,offset | if rs>=0 then branch如果地址为rs的通用寄存器的值大于0,那么发生转移,并且将转移指令后面第2条指令的地址作为返回地址,保存到通用寄存器$31 |
REGIMM(000001) | 00000 | BGEZAL(10001) | offset | bal offset | 无条件转移,并且将转移指令后面第2条指令的地址作为返回地址,保存到通用寄存器$31。bal指令是bgezal指令的特殊情况,当bgezal指令的rs为0时,就是bal指令,所以在OpenMIPS实现时,不用特意考虑bal指令,只要实现bgezal指令即可 |
数据流图的修改
需要对数据流图进行修改
译码阶段多了转移判断的步骤
- PC=PC+4 一般情况,每个时钟周期PC+4,指向下一条指令
- PC保持不变,流水线暂停时,出现这种情况
- PC等于转移判断的结果,转移指令,满足转移条件,那么转移的目标地址赋值给PC
系统结构的修改
- 如果处于译码阶段的指令是转移指令,并且满足转移条件,那么ID模块设置转移发生标志branch_flag_o为Branch,同时通过branch_target_address_o接口给出转移目的地址,送到PC模块,PC模块据此修改取指地址。
- 如果处于译码阶段指令时转移指令,并且满足转移条件,ID模块会设置next_inst_in_delayslot_o为InDelaySlot,表示下一条指令是延迟槽指令,next_inst_in_delayslot_o信号会送入ID/EX模块,并在下一个时钟周期通过ID/EX模块的is_in_delayslot_o接口送回ID模块,ID模块可以据此判断当前处于译码阶段的指令是否是延迟槽指令。
- 如果转移指令需要保存返回地址,那么ID模块还要计算返回地址,并通过link_addr_o接口输出,该值最终会传递到EX模块,并作为要写入的目的寄存器。
代码
define.v
//全局
`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_MOVZ 6'b001010
`define EXE_MOVN 6'b001011
`define EXE_MFHI 6'b010000
`define EXE_MTHI 6'b010001
`define EXE_MFLO 6'b010010
`define EXE_MTLO 6'b010011
`define EXE_SLT 6'b101010
`define EXE_SLTU 6'b101011
`define EXE_SLTI 6'b001010
`define EXE_SLTIU 6'b001011
`define EXE_ADD 6'b100000
`define EXE_ADDU 6'b100001
`define EXE_SUB 6'b100010
`define EXE_SUBU 6'b100011
`define EXE_ADDI 6'b001000
`define EXE_ADDIU 6'b001001
`define EXE_CLZ 6'b100000
`define EXE_CLO 6'b100001
`define EXE_MULT 6'b011000
`define EXE_MULTU 6'b011001
`define EXE_MUL 6'b000010
`define EXE_MADD 6'b000000
`define EXE_MADDU 6'b00001
`define EXE_MSUB 6'b000100
`define EXE_MSUBU 6'b000101
`define EXE_DIV 6'b011010
`define EXE_DIVU 6'b011011
`define EXE_J 6'b000010
`define EXE_JAL 6'b000011
`define EXE_JALR 6'b001001
`define EXE_JR 6'b001000
`define EXE_BEQ 6'b000100
`define EXE_BGEZ 5'b00001
`define EXE_BGEZAL 5'b10001
`define EXE_BGTZ 6'b000111
`define EXE_BLEZ 6'b000110
`define EXE_BLTZ 5'b00000
`define EXE_BLTZAL 5'b10000
`define EXE_BNE 6'b000101
`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_MOVZ_OP 8'b00001010
`define EXE_MOVN_OP 8'b00001011
`define EXE_MFHI_OP 8'b00010000
`define EXE_MTHI_OP 8'b00010001
`define EXE_MFLO_OP 8'b00010010
`define EXE_MTLO_OP 8'b00010011
`define EXE_SLT_OP 8'b00101010
`define EXE_SLTU_OP 8'b00101011
`define EXE_SLTI_OP 8'b01010111
`define EXE_SLTIU_OP 8'b01011000
`define EXE_ADD_OP 8'b00100000
`define EXE_ADDU_OP 8'b00100001
`define EXE_SUB_OP 8'b00100010
`define EXE_SUBU_OP 8'b00100011
`define EXE_CLZ_OP 8'b10110000
`define EXE_CLO_OP 8'b10110001
`define EXE_ADDI_OP 8'b01010101
`define EXE_ADDIU_OP 8'b01010110
`define EXE_MULT_OP 8'b00011000
`define EXE_MULTU_OP 8'b00011001
`define EXE_MUL_OP 8'b10101001
`define EXE_MADD_OP 8'b10100110
`define EXE_MADDU_OP 8'b10101000
`define EXE_MSUB_OP 8'b10101010
`define EXE_MSUBU_OP 8'b10101011
`define EXE_NOP_OP 8'b00000000
`define EXE_DIV_OP 8'b00011010
`define EXE_DIVU_OP 8'b00011011
`define EXE_J_OP 8'b01001111
`define EXE_JAL_OP 8'b01010000
`define EXE_JALR_OP 8'b00001001
`define EXE_JR_OP 8'b00001000
`define EXE_BEQ_OP 8'b01010001
`define EXE_BGEZ_OP 8'b01000001
`define EXE_BGEZAL_OP 8'b01001011
`define EXE_BGTZ_OP 8'b01010100
`define EXE_BLEZ_OP 8'b01010011
`define EXE_BLTZ_OP 8'b01000000
`define EXE_BLTZAL_OP 8'b01001010
`define EXE_BNE_OP 8'b01010010
//AluSel
`define EXE_RES_LOGIC 3'b001
`define EXE_RES_SHIFT 3'b010
`define EXE_RES_MOVE 3'b011
`define EXE_RES_ARITHMETIC 3'b100
`define EXE_RES_MUL 3'b101
`define EXE_RES_NOP 3'b000
`define EXE_RES_JUMP_BRANCH 3'b110
//指令存储器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
`define Stop 1'b1 //流水线暂停
`define NoStop 1'b0 //流水线继续
//用二进制码表示不同的状态
`define DivFree 2'b00
`define DivByZero 2'b01
`define DivOn 2'b10
`define DivEnd 2'b11
`define DivResultReady 1'b1
`define DivResultNotReady 1'b0
`define DivStart 1'b1
`define DivStop 1'b0
`define Branch 1'b1//转移
`define NotBranch 1'b0//不转移
`define InDelaySlot 1'b1//在延迟槽中
`define NotInDelaySlot 1'b0 //不在延迟槽中
id.v
`include "define.v"
module id(
input wire rst,
input wire[`InstAddrBus] pc_i,
input wire[`InstBus] inst_i,
input wire[`RegBus] reg1_data_i,
input wire[`RegBus] reg2_data_i,
input wire is_in_delayslot_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,
//处于执行阶段的指令的运算结果
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,
output wire stallreq,
output reg next_inst_in_delayslot_o,//下条指令是否是延迟槽
output reg branch_flag_o,//是否发生转移
output reg[`RegBus] branch_target_address_o,//转移到的目标地址
output reg[`RegBus] link_addr_o,//转移指令要保存的返回地址
output reg is_in_delayslot_o//当前处于译码指令是否位于延迟槽
);
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];
wire[`RegBus] pc_plus_8;//保存当前译码阶段指令后面第二条指令的地址
wire[`RegBus] pc_plus_4;//保存当前译码阶段指令后面紧接着的指令地址
wire[`RegBus] imm_sll2_signedext;//对应分支指令中offset左移两位,再符号扩展至32位的值
reg[`RegBus] imm;
reg instvalid;
assign stallreq = `NoStop;//在实现加载、存储指令时会给该信号赋值
assign imm_sll2_signedext = {{14{inst_i[15]}},inst_i[15:0],2'b00};
//imm_sll2_signedext对应分支指令中的offset左移两位,再符号扩展至32位的值
assign pc_plus_8 = pc_i+8;
assign pc_plus_4 = pc_i+4;
assign stallreq = `NoStop;
always @ (*) begin
if (rst == `RstEnable) begin
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;//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;
link_addr_o <= `ZeroWord;//转移指令要保存的返回地址
branch_target_address_o <= `ZeroWord;//转移到的目标地址
branch_flag_o <= `NotBranch;//不发生转移
next_inst_in_delayslot_o <= `NotInDelaySlot;//下一条指令是否位于延迟槽
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];//rs
reg2_addr_o <= inst_i[20:16];//rt
imm <= `ZeroWord;
link_addr_o <= `ZeroWord;
branch_target_address_o <= `ZeroWord;
branch_flag_o <= `NotBranch;
next_inst_in_delayslot_o <= `NotInDelaySlot;
case (op)//指令码
`EXE_SPECIAL_INST: begin //指令码是SPECIAL
case(op2)//功能码
5'b00000: begin
case(op3) //依据功能码判断是哪一个指令
`EXE_OR: begin //or R型指令 rs|rt -> rd
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;//R rs&rt ->rd
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;// R rs^rt ->rd
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;// R rs~|rt ->rd
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_SRLV_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_SRAV_OP;
alusel_o <= `EXE_RES_SHIFT;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SYNC:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_MFHI:begin//将特殊寄存器hi的值赋给地址为rd的寄存器
wreg_o <= `WriteEnable;
aluop_o <= `EXE_MFHI_OP;
alusel_o <= `EXE_RES_MOVE;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
end
`EXE_MFLO:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_MFLO_OP;
alusel_o <= `EXE_RES_MOVE;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
end
`EXE_MTHI:begin//hi<-rs 写特殊寄存器
wreg_o <= `WriteEnable;
aluop_o <= `EXE_MTHI_OP;
alusel_o <= `EXE_RES_MOVE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
end
`EXE_MTLO:begin //lo<-rs 写特殊寄存器
wreg_o <= `WriteEnable;
aluop_o <= `EXE_MTLO_OP;
reg1_read_o <= 1'b1;//rs
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
end
`EXE_MOVN:begin//判断rt寄存器的值 如果不为0 将rs的值赋给rd 反之rd值不变
//wreg_o <= `WriteEnable;
aluop_o <= `EXE_MOVN_OP;
alusel_o <= `EXE_RES_MOVE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
//reg2_o的值就是地址为rt的寄存器的值
if(reg2_o != `ZeroWord)begin
wreg_o <= `WriteEnable;
end else begin
wreg_o <= `WriteDisable;
end
end
`EXE_MOVZ:begin //判断rt寄存器的值 如果是0 将rs的值赋给rd 反之rd值不变
aluop_o <= `EXE_MOVZ_OP;
alusel_o <= `EXE_RES_MOVE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
if(reg2_o == `ZeroWord)begin
wreg_o <= `WriteEnable;
end else begin
wreg_o <= `WriteDisable;
end
end
`EXE_SLT:begin//slt指令 rd<-(rs<rt)
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SLT_OP;
alusel_o <= `EXE_RES_ARITHMETIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SLTU:begin //sltu指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SLTU_OP;
alusel_o <= `EXE_RES_ARITHMETIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_ADD:begin//rd<-rs+rt
wreg_o <= `WriteEnable;
aluop_o <= `EXE_ADD_OP;
alusel_o <= `EXE_RES_ARITHMETIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_ADDU:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_ADDU_OP;
alusel_o <= `EXE_RES_ARITHMETIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SUB:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SUB_OP;
alusel_o <= `EXE_RES_ARITHMETIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SUBU:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SUBU_OP;
alusel_o <= `EXE_RES_ARITHMETIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_MULT:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_MULT_OP;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_MULTU:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_MULTU_OP;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_DIV:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_DIV_OP;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_DIVU:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_DIVU_OP;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_JR:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_JR_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= 1'b1;//rs寄存器需要被使用
reg2_read_o <= 1'b0;
wd_o <= inst_i[15:11];
link_addr_o <= pc_plus_8;//返回地址
branch_target_address_o <= reg1_o;//转移到的目标地址
branch_flag_o <= `Branch;//是否发生转移
next_inst_in_delayslot_o <= `InDelaySlot;//下一条指令不位于延迟槽
instvalid <= `InstValid;
end
`EXE_JALR:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_JALR_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
wd_o <= inst_i[15:11];
link_addr_o <= pc_plus_8;
branch_target_address_o <= reg1_o;
branch_flag_o <= `Branch;
next_inst_in_delayslot_o <= `InDelaySlot;
instvalid <= `InstValid;
end
default:begin
end
endcase
end
default:begin
//aluop_o <= `EXE_NOP_OP;
//alusel_o <= `EXE_RES_OP;
//wd_o <= `NOPRegAddr;
//wreg_o <= `WriteDisable;
//instvalid <= `InstValid;
//reg1_read_o <= `ReadDisable;
//reg2_read_o <= `ReadDisable;
//reg1_addr_o <= inst_i[25:21];
//reg2_addr_o <= inst_i[20:16];
end
endcase//op3
end // 5'b00000
`EXE_J:begin
wreg_o <= `WriteDisable;//译码阶段是否要写入目的寄存器
aluop_o <= `EXE_J_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
link_addr_o <= `ZeroWord;
branch_flag_o <= `Branch;
next_inst_in_delayslot_o <= `InDelaySlot;
instvalid <= `InstValid;
branch_target_address_o <= {pc_plus_4[31:28],inst_i[25:0],2'b00};//转移目的的地址
end
`EXE_JAL:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_JAL_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
wd_o <= 5'b11111;//要写入的目的寄存器的地址 寄存器$1
link_addr_o <= pc_plus_8;//转移指令要保存的返回地址
branch_flag_o <= `Branch;//转移发生的标志
next_inst_in_delayslot_o <= `InDelaySlot;
instvalid <= `InstValid;
branch_target_address_o <= {pc_plus_4[31:28],inst_i[25:0],2'b00};
end
`EXE_BEQ:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_BEQ_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= 1'b1;//需要比较rs与rt
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
if(reg1_o == reg2_o)begin //如果rs的值reg1_o与rd的值reg2_o相等 发生转移
branch_target_address_o <= pc_plus_4 + imm_sll2_signedext;
branch_flag_o <= `Branch;
next_inst_in_delayslot_o <= `InDelaySlot;
end
end
`EXE_BGTZ:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_BGTZ_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= 1'b1;//rs
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
if((reg1_o[31] == 1'b0)&&(reg1_o != `ZeroWord))begin
branch_target_address_o <= pc_plus_4 + imm_sll2_signedext;
branch_flag_o <= `Branch;
next_inst_in_delayslot_o <= `InDelaySlot;
end
end
`EXE_BLEZ:begin
wreg_o <= `WriteDisable;//译码阶段是否要写入目的寄存器
aluop_o <= `EXE_BLEZ_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
if((reg1_o[31] == 1'b1)&&(reg1_o != `ZeroWord))begin
branch_target_address_o <= pc_plus_4 + imm_sll2_signedext;
branch_flag_o <= `Branch;
next_inst_in_delayslot_o <= `InDelaySlot;
end
end
`EXE_BNE:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_BNE_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
if(reg1_o != reg2_o)begin
branch_target_address_o <= pc_plus_4+imm_sll2_signedext;
branch_flag_o <= `Branch;
next_inst_in_delayslot_o <= `InDelaySlot;
end
end
`EXE_REGIMM_INST:begin
case(op4)
`EXE_BLTZAL:begin//bltzal
wreg_o <= `WriteEnable;
aluop_o <= `EXE_BGEZAL_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
link_addr_o <= pc_plus_8;
wd_o <= 5'b11111;
instvalid <= `InstValid;
if(reg1_o[31] == 1'b1) begin//reg1_o<0
branch_target_address_o <= pc_plus_4+imm_sll2_signedext;
branch_flag_o <= `Branch;
next_inst_in_delayslot_o <= `InDelaySlot;
end
end
`EXE_BLTZ:begin//bltz
wreg_o <= `WriteDisable;
aluop_o <= `EXE_BGEZAL_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
if(reg1_o[31] == 1'b1)begin
branch_target_address_o <= pc_plus_4 + imm_sll2_signedext;
branch_flag_o <= `Branch;
next_inst_in_delayslot_o <= `InDelaySlot;
end
end
`EXE_BGEZ:begin//bgez
wreg_o <= `WriteDisable;
aluop_o <= `EXE_BGEZ_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
if(reg1_o[31] == 1'b0)begin//rs的值大于等于0
branch_target_address_o <= pc_plus_4 + imm_sll2_signedext;
branch_flag_o <= `Branch;
next_inst_in_delayslot_o <= `InDelaySlot;
end
end
`EXE_BGEZAL:begin//bgezal
wreg_o <= `WriteEnable;
aluop_o <= `EXE_BGEZAL_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
link_addr_o <= pc_plus_8;
wd_o <= 5'b11111;
instvalid <= `InstValid;
if(reg1_o[31] == 1'b0)begin
branch_target_address_o <= pc_plus_4 + imm_sll2_signedext;
branch_flag_o <= `Branch;
next_inst_in_delayslot_o <= `InDelaySlot;
end
end
default:begin
end
endcase // op2
end
`EXE_ORI:begin //ORI指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_OR_OP;
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
imm <= {16'h0, inst_i[15:0]}; //立即数0扩展
wd_o <= inst_i[20:16]; // 读取rt地址
instvalid <= `InstValid;
end
`EXE_ANDI:begin //andi
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];//rt
instvalid = `InstValid;
end
`EXE_XORI:begin//xori
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//lui
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//pref
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 */
`EXE_SLTI:begin //slti rt <- (rs < (sign_extended)immediate)
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SLT_OP;
alusel_o <= `EXE_RES_ARITHMETIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
imm <= {{16{inst_i[15]}},inst_i[15:0]};
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_SLTIU:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SLTU_OP;
alusel_o <= `EXE_RES_ARITHMETIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
imm <= {{16{inst_i[15]}},inst_i[15:0]};
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_ADDI:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_ADDI_OP;
alusel_o <= `EXE_RES_ARITHMETIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
imm <= {{16{inst_i[15]}},inst_i[15:0]};
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_ADDIU:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_ADDIU_OP;
alusel_o <= `EXE_RES_ARITHMETIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
imm <= {{16{inst_i[15]}},inst_i[15:0]};
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_SPECIAL2_INST:begin//(op)
case(op3)
`EXE_CLZ:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_CLZ_OP;
alusel_o <= `EXE_RES_ARITHMETIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
end
`EXE_CLO:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_CLO_OP;
alusel_o <= `EXE_RES_ARITHMETIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
end
`EXE_MUL:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_MUL_OP;
alusel_o <= `EXE_RES_MUL;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_MADD:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_MADD_OP;
alusel_o <= `EXE_RES_MUL;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_MADDU:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_MADDU_OP;
alusel_o <= `EXE_RES_MUL;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_MSUB:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_MSUB_OP;
alusel_o <= `EXE_RES_MUL;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_MSUBU:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_MSUBU_OP;
alusel_o <= `EXE_RES_MUL;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
default:begin
end
endcase //EXE_SPECIAL_INST2 case
end
default:begin
end
endcase //case op
if(inst_i[31:21] == 11'b00000000000)begin //sll,srl,sra
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
//endcase
//end
end //if
end
end //always
/* 数据前推
给reg1_o赋值过程增加了两种情况
1:如果Regfile模块读端口1要读取的寄存器就是执行阶段要写的目的寄存器,那么直接把执行阶段的结果ex_wdata_i作为reg1_o的值
2:如果Regfile模块读端口1要读取的寄存器就是访存阶段要写的目的寄存器,那么直接把访存阶段的结果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)
&& (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
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
//输出变量is_in_delayslot_o表示当前译码阶段指令是否是延迟槽指令
always @ (*)begin
if(rst == `RstEnable)begin
is_in_delayslot_o <= `NotInDelaySlot;
end else begin
//直接等于is_in_delayslot_i
is_in_delayslot_o <= is_in_delayslot_i;
end
end
endmodule
id_ex.v
`include "define.v"
//在时钟周期的上升沿,将译码阶段的结果传递到执行阶段*/
module id_ex(
input wire clk,
input wire rst,
//从译码阶段传递的信息
input wire[`AluOpBus] id_aluop,
input wire[`AluSelBus] id_alusel,
input wire[`RegBus] id_reg1,
input wire[`RegBus] id_reg2,
input wire[`RegAddrBus] id_wd,
input wire id_wreg,
//来自控制模块的信息
input wire[5:0] stall ,
input wire[`RegBus] id_link_address,
input wire id_is_in_delayslot,
input wire next_inst_in_delayslot_i,
//传递到执行阶段的信息
output reg[`AluOpBus] ex_aluop,
output reg[`AluSelBus] ex_alusel,
output reg[`RegBus] ex_reg1,
output reg[`RegBus] ex_reg2,
output reg[`RegAddrBus] ex_wd,
output reg ex_wreg,
output reg[`RegBus] ex_link_address,
output reg ex_is_in_delayslot,
output reg is_in_delayslot_o
);
always @ (posedge clk) begin
if (rst == `RstEnable) begin
ex_aluop <= `EXE_NOP_OP;
ex_alusel <= `EXE_RES_NOP;
ex_reg1 <= `ZeroWord;
ex_reg2 <= `ZeroWord;
ex_wd <= `NOPRegAddr;
ex_wreg <= `WriteDisable;
ex_link_address <= `ZeroWord;
ex_is_in_delayslot <= `NotInDelaySlot;
end else if(stall[2] == `Stop && stall[3] == `NoStop)begin//执行停止访存继续
ex_aluop <= `EXE_NOP_OP;
ex_alusel <= `EXE_RES_NOP;
ex_reg1 <= `ZeroWord;
ex_reg2 <= `ZeroWord;
ex_wd <= `NOPRegAddr;
ex_wreg <= `WriteDisable;
ex_link_address <= `ZeroWord;
ex_is_in_delayslot <= `NotInDelaySlot;
end else if(stall[2] == `NoStop)begin//执行继续
ex_aluop <= id_aluop;
ex_alusel <= id_alusel;
ex_reg1 <= id_reg1;
ex_reg2 <= id_reg2;
ex_wd <= id_wd;
ex_wreg <= id_wreg;
ex_link_address <= id_link_address;
ex_is_in_delayslot <= id_is_in_delayslot;
is_in_delayslot_o <= next_inst_in_delayslot_i;
/*end else begin
ex_aluop <= id_aluop;
ex_alusel <= id_alusel;
ex_reg1 <= id_reg1;
ex_reg2 <= id_reg2;
ex_wd <= id_wd;
ex_wreg <= id_wreg;
end*/
end
end
endmodule
ex.v
`include "define.v"
//ex.v 执行模块
module ex(
//译码阶段送到执行阶段的信息
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,
input wire rst,
//HILO模块给出HI,LO寄存器的值
input wire[`RegBus] hi_i,
input wire[`RegBus] lo_i,
//回写阶段的指令是否要写HI,LO,用于检测HI,LO寄存器带来的数据相关的问题
input wire[`RegBus] wb_hi_i,
input wire[`RegBus] wb_lo_i,
input wire wb_whilo_i,
//访存阶段的指令是否要写HI,LO,用于检测HI,LO寄存器带来的数据相关的问题
input wire[`RegBus] mem_hi_i,
input wire[`RegBus] mem_lo_i,
input wire mem_whilo_i,
//增加的输入端口
input wire[`DoubleRegBus] hilo_temp_i,//保存乘法结果
input wire[1:0] cnt_i,//处于执行阶段的第几个周期
//新增来自除法模块的输入
input wire[`DoubleRegBus] div_result_i,
input wire div_ready_i,
//处于执行阶段的转移指令要保存的返回地址
input wire[`RegBus] link_address_i,
//当前执行阶段指令是否处于延迟槽
input wire is_in_delayslot_i,
//处于执行阶段指令对LO,HI寄存器的写操作请求
output reg[`RegBus] hi_o,
output reg[`RegBus] lo_o,
output reg whilo_o,
//执行的结果
output reg[`RegAddrBus] wd_o,
output reg wreg_o,
output reg[`RegBus] wdata_o,
output reg stallreq,
output reg[`DoubleRegBus] hilo_temp_o,
output reg[1:0] cnt_o,
//新增到除法模块的输出
output reg[`RegBus] div_opdata1_o,
output reg[`RegBus] div_opdata2_o,
output reg div_start_o,
output reg signed_div_o
//output reg is_in_delayslot_o,
);
//保存逻辑运算的结果
reg[`RegBus] logicout;
//保存移位运算的结果
reg[`RegBus] shiftres;
//保存移动操作的结果
reg[`RegBus] moveres;
//保存HI,LO寄存器的最新值
reg[`RegBus] HI;
reg[`RegBus] LO;
//是否由于除法运算导致流水线暂停
reg stallreq_for_div;
/***********************第七章新定义一些变量***********************/
wire ov_sum;//保存溢出情况
wire reg1_eq_reg2;//第一个操作数是否等于第二个操作数
wire reg1_lt_reg2;//第一个操作数是否小于第二个操作数
reg[`RegBus] arithmeticres;//保存算术运算的结果
reg[`DoubleRegBus] mulres;//保存乘法运算的结果
wire[`RegBus] reg2_i_mux;//保存输入的第二个操作数reg2_i的补码
wire[`RegBus] reg1_i_not;//保存输入的第一个操作数reg1_i取反后的值
wire[`RegBus] result_sum;//保存加法结果
wire[`RegBus] opdata1_mult;//乘法操作中的被乘数
wire[`RegBus] opdata2_mult;//乘法操作中的乘数
wire[`DoubleRegBus] hilo_temp;//临时保存乘法结果,宽度为64位
reg [`DoubleRegBus] hilo_temp1;
reg stallreq_for_madd_msub;
/*******************************************************************
** 第一段:依据aluop_i指示的运算子类型进行运算 **
*******************************************************************/
always @ (*)
begin//1
if(rst == `RstEnable)
begin//2
logicout <= `ZeroWord;
end//2
else
begin//3
case(aluop_i)//4
`EXE_OR_OP:begin//5 逻辑或
logicout <= reg1_i|reg2_i;
end
`EXE_AND_OP: begin //逻辑与
logicout <= reg1_i®2_i;
end
`EXE_NOR_OP:begin //逻辑或非
logicout <= ~(reg1_i|reg2_i);
end
`EXE_XOR_OP:begin //逻辑异或
logicout <= reg1_i^reg2_i;
end
default:begin//6
logicout <= `ZeroWord;
end//6
endcase//4
end//3
end//1
always @ (*) begin
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//算术右移1
shiftres <= ({32{reg2_i[31]}}<<(6'd32-{1'b0,reg1_i[4:0]})) |/*rt向右移sa位*/ reg2_i>>reg1_i[4:0];
end
default:begin
shiftres <= `ZeroWord;
end
endcase
end
end
/******************************************************************
****第三段:得到最新的HI,LO寄存器的值,此处要解决数据相关的问题****
******************************************************************/
always @ (*)begin
if(rst == `RstEnable) begin
{HI,LO} <= {`ZeroWord,`ZeroWord};
end else if(mem_whilo_i == `WriteEnable)begin
{HI,LO} <= {mem_hi_i,mem_lo_i};//访存阶段的指令要写HI,LO寄存器
end else if(wb_whilo_i == `WriteEnable)begin
{HI,LO} <= {wb_hi_i,wb_lo_i};//回写阶段的指令要写HI,LO寄存器
end
end
/*******************************************************************
******************第四段:MFHI,MFLO,MOVN,MOVZ指令********************
*******************************************************************/
always @ (*) begin
if(rst == `RstEnable) begin
moveres <= `ZeroWord;
end else begin
moveres <= `ZeroWord;
case(aluop_i)
`EXE_MFHI_OP:begin
//rd<-hi
moveres <= HI;//HI的值是移动操作的结果
end
`EXE_MFLO_OP:begin
//rd<-lo
moveres <= LO;
end
`EXE_MOVN_OP:begin
//rd<-rs
moveres <= reg1_i;
end
`EXE_MOVZ_OP:begin
//rd<-rs
moveres <= reg1_i;
end
default:begin
end
endcase
end
end
/***************************************************************
*******如果是MTHI,MTLO指令,需要给出whilo_o,hi_o,lo_o的值*******
***************************************************************/
always @ (*)begin
if(rst == `RstEnable) begin
whilo_o <= `WriteDisable;
hi_o <= `ZeroWord;
lo_o <= `ZeroWord;
end else if(aluop_i == `EXE_MTHI_OP)begin
whilo_o <= `WriteEnable;
hi_o <= reg1_i;
lo_o <= LO;//写HI寄存器所以LO保持不变
end else if(aluop_i == `EXE_MTLO_OP)begin
whilo_o <= `WriteEnable;
hi_o <= HI;
lo_o <= reg1_i;
end else begin
whilo_o <= `WriteDisable;
hi_o <= `ZeroWord;
lo_o <= `ZeroWord;
end
end
//end
//endmodule
/*************************************************************
******************第五段:计算以下5个变量的值******************
*************************************************************/
/*(1)如果是减法或者是有符号比较运算,那么reg2_i_mux等于第二个操作数reg2_i的补码,
否则reg2_i_mux就等于第二个操作数reg2_i*/
assign reg2_i_mux = ((aluop_i == `EXE_SUB_OP)||(aluop_i == `EXE_SUBU_OP)||(aluop_i == `EXE_SLT_OP))?(~reg2_i)+1:reg2_i;
/*(2)分三种情况:
A:如果是加法运算,此时reg2_i_mux就是第二个操作数reg2_i,所以result_sum就是加法运算结果
B:如果是减法运算,此时reg2_i_mux就是第二个操作数reg2_i的补码,所以result_sum就是减法运算的结果
C:如果是有符号的比较运算,此时reg2_i_mux也就是第二个操作数reg2_i的补码,所以result_sum也就是减法
运算的结果,可以通过判断减法的结果是否小于0,进而判断第一个操作数reg1_i是否小于第二个操作数reg2_i*/
assign result_sum = reg1_i + reg2_i_mux;
/*(3)计算是否溢出,加法指令add和addi,减法指令sub执行的时候,需要判断是否溢出,满足以下两种情况之一的时候
A:reg1_i为正数,reg2_i_mux为正数,但两者之和为负数
B:reg1_i为负数,reg2_i_mux为负数,但是两者之和为正数*/
//这个我不理解 艹 2022.3.10理解了
assign ov_sum = ((!reg1_i[31] && !reg2_i_mux[31]) && result_sum[31])||((reg1_i[31] && reg2_i_mux[31])&&(!result_sum[31]));
/*(4)计算操作数1是否小于操作数2,分两种情况:
A:aluop_i为EXE_SLT_OP表示有符号比较运算
1.reg1_i为负数、reg2_i为正数,显然reg1_i小于reg2_i
2.reg1_i为正数、reg2_i为正数,并且reg1_i减去reg2_i的值小于0(result_sum为负)此时reg1_i小于reg2_i
3.reg1_i为负数、reg2_i为负数,并且reg1_i减去reg2_i的值小于0(result_sum为负)此时reg1_i小于reg2_i
B:无符号数比较运算的时候,直接使用比较运算符比较reg1_i和reg2_i*/
assign reg1_lt_reg2 = ((aluop_i == `EXE_SLT_OP))?((reg1_i[31]&&!reg2_i[31])||(!reg1_i[31]&&!reg2_i[31]&&result_sum[31])||(reg1_i[31]&®2_i[31]&&result_sum[31])):(reg1_i<reg2_i);
//(5)对操作数1逐位取反 赋值给reg1_i_not
assign reg1_i_not = ~reg1_i;
/*****************************************************************
*****第六段:依据不同的算术运算类型,给arithmeticres变量赋值*******
*****************************************************************/
always @ (*) begin
if(rst == `RstEnable)begin
arithmeticres <= `ZeroWord;
end else begin
case(aluop_i) //选择运算类型
`EXE_SLT_OP,`EXE_SLTU_OP:begin
arithmeticres <= reg1_lt_reg2;//比较运算
end
`EXE_ADD_OP,`EXE_ADDU_OP,`EXE_ADDI_OP,`EXE_ADDIU_OP:begin
arithmeticres <= result_sum;//加法运算
end
`EXE_SUB_OP,`EXE_SUBU_OP:begin
arithmeticres <= result_sum;//减法运算
end
`EXE_CLZ_OP:begin //计数运算clz
arithmeticres <= reg1_i[31]?0:reg1_i[30]?1:
reg1_i[29]?2:reg1_i[28]?3:
reg1_i[27]?4:reg1_i[26]?5:
reg1_i[25]?6:reg1_i[24]?7:
reg1_i[23]?8:reg1_i[22]?9:
reg1_i[21]?10:reg1_i[20]?11:
reg1_i[19]?12:reg1_i[18]?13:
reg1_i[17]?14:reg1_i[16]?15:
reg1_i[15]?16:reg1_i[14]?17:
reg1_i[13]?18:reg1_i[12]?19:
reg1_i[11]?20:reg1_i[10]?21:
reg1_i[9]?22:reg1_i[8]?23:
reg1_i[7]?24:reg1_i[6]?25:
reg1_i[5]?26:reg1_i[4]?27:
reg1_i[3]?28:reg1_i[2]?29:
reg1_i[1]?20:reg1_i[0]?31:32;
end
`EXE_CLO_OP:begin //计数运算clo
arithmeticres <= (reg1_i_not[31]?0:
reg1_i_not[30]?1:
reg1_i_not[29]?2:
reg1_i_not[28]?3:
reg1_i_not[27]?4:
reg1_i_not[26]?5:
reg1_i_not[25]?6:
reg1_i_not[24]?7:
reg1_i_not[23]?8:
reg1_i_not[22]?9:
reg1_i_not[21]?10:
reg1_i_not[20]?11:
reg1_i_not[19]?12:
reg1_i_not[18]?13:
reg1_i_not[17]?14:
reg1_i_not[16]?15:
reg1_i_not[15]?16:
reg1_i_not[14]?17:
reg1_i_not[13]?18:
reg1_i_not[12]?19:
reg1_i_not[11]?20:
reg1_i_not[10]?21:
reg1_i_not[9]?22:
reg1_i_not[8]?23:
reg1_i_not[7]?24:
reg1_i_not[6]?25:
reg1_i_not[5]?26:
reg1_i_not[4]?27:
reg1_i_not[3]?28:
reg1_i_not[2]?29:
reg1_i_not[1]?30:
reg1_i_not[0]?31:32);
end
default:begin
arithmeticres <= `ZeroWord;
end
endcase
end
end
/*****************************************************************
************************第七段:进行乘法运算***********************
*****************************************************************/
/*(1)取得乘法运算的被乘数 指令 madd,msub都是有符号乘法,如果第一个操作数reg1_i是负数
那么取reg1_i的补码为被乘数,反之直接使用reg1_i作为被乘数 如果是有符号乘法且被乘数是负数 那么取补码*/
assign opdata1_mult = (((aluop_i == `EXE_MUL_OP)||(aluop_i == `EXE_MULT_OP)||(aluop_i == `EXE_MADD_OP)||(aluop_i == `EXE_MSUB_OP))&&(reg1_i[31] == 1'b1)) ? (~reg1_i+1):reg1_i;
//(2)取得乘法运算的乘数 如果是有符号乘法且乘数是负数 那么取补码
assign opdata2_mult = (((aluop_i == `EXE_MUL_OP)||(aluop_i == `EXE_MULT_OP)||(aluop_i == `EXE_MADD_OP)||(aluop_i == `EXE_MSUB_OP))&&(reg2_i[31] == 1'b1)) ? (~reg2_i+1):reg2_i;
//(3)得到临时乘法结果 保存在变量hilo_temp中
assign hilo_temp = opdata1_mult * opdata2_mult;
/*(4)对临时乘法结果进行修正 最终的乘法结果保存在变量mulres中 主要有以下两点
A:如果是有符号乘法指令mult、mul,那么需要修正临时乘法结果,如下:
A1:如果被乘数与乘数两者为一正一负,那么需要对临时乘法结果hilo_temp求补码,作为最终乘法结果,赋给mulres
A2:如果被乘数与乘数同号,那么hilo_temp的值就作为最终的乘法结果,赋给变量mulres
B:如果是无符号乘法指令multu,那么hilo_temp的值就作为最终的乘法结果,赋给变量mulres
*/
always @ (*) begin//1
if(rst == `RstEnable) begin//2
mulres <= {`ZeroWord,`ZeroWord};
end/*2*/ else if((aluop_i == `EXE_MULT_OP)||(aluop_i == `EXE_MUL_OP)||(aluop_i == `EXE_MADD_OP)||(aluop_i == `EXE_MSUB_OP))begin//3
if(reg1_i[31]^reg2_i[31] == 1'b1)begin//4 被乘数和乘数一正一负
mulres <= ~hilo_temp+1;
end/*4*/ else begin//5 被乘数和乘数同号
mulres <= hilo_temp;
end//5
end/*3*/ else begin//6 无符号乘法
mulres <= hilo_temp;
end//6
end//1
/*****************************************************************
****************第八段:确定要写入目的寄存器的数据*****************
*****************************************************************/
always @ (*)begin
wd_o <= wd_i;
//如果是add,addi,sub,subi指令,且发生溢出,那么设置wreg_o为WriteEnable 表示不写目的寄存器
if(((aluop_i == `EXE_ADD_OP)||(aluop_i == `EXE_ADDI_OP)||(aluop_i == `EXE_SUB_OP))&&(ov_sum == 1'b1))begin
wreg_o <= `WriteDisable;
end else begin
wreg_o <= wreg_i;
end
case(alusel_i)
`EXE_RES_LOGIC:begin
wdata_o <= logicout;
end
`EXE_RES_SHIFT:begin
wdata_o <= shiftres;
end
`EXE_RES_MOVE:begin
wdata_o <= moveres;
end
`EXE_RES_ARITHMETIC:begin//除乘法外的简单算术操作指令
wdata_o <= arithmeticres;
end
`EXE_RES_MUL:begin//乘法指令mul
wdata_o <= mulres[31:0];
end
`EXE_RES_JUMP_BRANCH:begin
wdata_o <= link_address_i;
end
default:begin
wdata_o <= `ZeroWord;
end
endcase
end//always
/****************************************************************
********************第十段:乘累加、乘累减************************
****************************************************************/
//MADD MADDU MSUB MSUBU指令
always @ (*) begin
if(rst == `RstEnable) begin
hilo_temp_o <= {`ZeroWord,`ZeroWord};
cnt_o <= 2'b00;
stallreq_for_madd_msub <= `NoStop;
end else begin
case(aluop_i)
`EXE_MADD_OP,`EXE_MADDU_OP:begin
if(cnt_i == 2'b00) begin //执行第一个时钟周期
hilo_temp_o <= mulres;//此时将乘法结果mulres通过接口hilo_temp_o输出到EX/MEM模块 以便在下一个时钟周期使用
cnt_o <= 2'b01;
hilo_temp1 <= {`ZeroWord,`ZeroWord};
stallreq_for_madd_msub <= `Stop;//乘累加指令请求流水线暂停
end else if(cnt_i == 2'b01) begin//执行第二个时钟周期
hilo_temp_o <= {`ZeroWord,`ZeroWord};
cnt_o <= 2'b10;
hilo_temp1 <= hilo_temp_i+{HI,LO}; //hilo_temp_i是上一个时钟周期得到的乘法结果
stallreq_for_madd_msub <= `NoStop;//乘累加指令执行结束 不再请求流水线暂停
end
end
`EXE_MSUB_OP,`EXE_MSUBU_OP:begin
if(cnt_i == 2'b00) begin
hilo_temp_o <= ~mulres+1;
cnt_o <= 2'b01;
stallreq_for_madd_msub <= `Stop;
end else if(cnt_i == 2'b01) begin
hilo_temp_o <= {`ZeroWord,`ZeroWord};
cnt_o <= 2'b10;
hilo_temp1 <= hilo_temp_i +{HI,LO};
stallreq_for_madd_msub <= `NoStop;
end
end
default:begin
hilo_temp_o <= {`ZeroWord,`ZeroWord};
cnt_o <= 2'b00;
stallreq_for_madd_msub <= `NoStop;
end
endcase
end
end
/****************************************************************
******第十二段:输出DIV模块控制信息,获取DIV模块给出的结果*********
****************************************************************/
always @ (*) begin
if(rst == `RstEnable)begin
stallreq_for_div <= `NoStop;
div_opdata1_o <= `ZeroWord;
div_opdata2_o <= `ZeroWord;
div_start_o <= `DivStop;
signed_div_o <= 1'b0;
end else begin
stallreq_for_div <= `NoStop;
div_opdata1_o <= `ZeroWord;
div_opdata2_o <= `ZeroWord;
div_start_o <= `DivStop;
signed_div_o <= 1'b0;
case(aluop_i)
`EXE_DIV_OP:begin
if(div_ready_i == `DivResultNotReady) begin
div_opdata1_o <= reg1_i;//被除数
div_opdata2_o <= reg2_i;//除数
div_start_o <= `DivStart;//开始除法运算
signed_div_o <= 1'b1;//有符号除法
stallreq_for_div <= `Stop;//请求流水线暂停
end else if(div_ready_i == `DivResultReady) begin
div_opdata1_o <= reg1_i;
div_opdata2_o <= reg2_i;
div_start_o <= `DivStop;//结束除法运算
signed_div_o <= 1'b1;
stallreq_for_div <= `NoStop;//不再请求流水线暂停
end else begin
div_opdata1_o <= `ZeroWord;
div_opdata2_o <= `ZeroWord;
div_start_o <= `DivStop;
signed_div_o <= 1'b0;
stallreq_for_div <= `NoStop;
end
end
`EXE_DIVU_OP:begin
if(div_ready_i == `DivResultNotReady) begin
div_opdata1_o <= reg1_i;
div_opdata2_o <= reg2_i;
div_start_o <= `DivStart;
signed_div_o <= 1'b0;//无符号除法
stallreq_for_div <= `Stop;
end else if(div_ready_i == `DivResultReady) begin
div_opdata1_o <= reg1_i;
div_opdata2_o <= reg2_i;
div_start_o <= `DivStop;
signed_div_o <= 1'b0;
stallreq_for_div <= `NoStop;
end else begin
div_opdata1_o <= `ZeroWord;
div_opdata2_o <= `ZeroWord;
div_start_o <= `DivStop;
signed_div_o <= 1'b0;
stallreq_for_div <= `NoStop;
end
end
default:begin
end
endcase
end
end
/****************************************************************
*********************第十一段:暂停流水线*************************
****************************************************************/
//目前只有乘累加和乘累减指令会导致流水线暂停,所以stallreq=stallreq_for_madd_msub
always @ (*) begin
stallreq = stallreq_for_madd_msub || stallreq_for_div;
end
/****************************************************************
****************第九段:确定(修改)对HI,LO寄存器的操作信息*********
****************************************************************/
always @ (*)begin
if(rst == `RstEnable) begin
whilo_o <= `WriteDisable;
hi_o <= `ZeroWord;
lo_o <= `ZeroWord;
end else if((aluop_i == `EXE_MULT_OP)||(aluop_i == `EXE_MULTU_OP))begin //mult,multu指令
whilo_o <= `WriteEnable;
hi_o <= mulres[63:32];
lo_o <= mulres[31:0];
end else if(aluop_i == `EXE_MTHI_OP)begin
whilo_o <= `WriteEnable;
hi_o <= reg1_i;
lo_o <= LO;//写HI寄存器所以LO保持不变
end else if(aluop_i == `EXE_MTLO_OP)begin
whilo_o <= `WriteEnable;
hi_o <= HI;
lo_o <= reg1_i;
end else if((aluop_i == `EXE_MSUB_OP)||(aluop_i == `EXE_MSUBU_OP))begin
whilo_o <= `WriteEnable;
hi_o <= hilo_temp1[63:32];
lo_o <= hilo_temp1[31:0];
end else if((aluop_i == `EXE_MADD_OP)||(aluop_i == `EXE_MADDU_OP))begin
whilo_o <= `WriteEnable;
hi_o <= hilo_temp1[63:32];
lo_o <= hilo_temp1[31:0];
end else if((aluop_i == `EXE_DIV_OP)||(aluop_i == `EXE_DIVU_OP))begin
whilo_o <= `WriteEnable;
hi_o <= div_result_i[63:32];
lo_o <= div_result_i[31:0];
end else begin
whilo_o <= `WriteDisable;
hi_o <= `ZeroWord;
lo_o <= `ZeroWord;
end
end
endmodule
openmips.v
`include "define.v"
/*module openmips(
input wire clk,
input wire rst,
input wire[`RegBus] rom_data_i,
output wire[`RegBus] rom_addr_o,
output wire rom_ce_o
);
wire[`InstAddrBus] pc;
wire[`InstAddrBus] id_pc_i;
wire[`InstBus] id_inst_i;
//连接译码阶段ID模块的输出与ID/EX模块的输入
wire[`AluOpBus] id_aluop_o;
wire[`AluSelBus] id_alusel_o;
wire[`RegBus] id_reg1_o;
wire[`RegBus] id_reg2_o;
wire id_wreg_o;
wire[`RegAddrBus] id_wd_o;
wire id_is_in_delayslot_o;
wire[`RegBus] id_link_address_o;
//连接ID/EX模块的输出与执行阶段EX模块的输入
wire[`AluOpBus] ex_aluop_i;
wire[`AluSelBus] ex_alusel_i;
wire[`RegBus] ex_reg1_i;
wire[`RegBus] ex_reg2_i;
wire ex_wreg_i;
wire[`RegAddrBus] ex_wd_i;
wire ex_is_in_delayslot_i;
wire[`RegBus] ex_link_address_i;
//连接执行阶段EX模块的输出与EX/MEM模块的输入
wire ex_wreg_o;
wire[`RegAddrBus] ex_wd_o;
wire[`RegBus] ex_wdata_o;
wire[`RegBus] ex_hi_o;
wire[`RegBus] ex_lo_o;
wire ex_whilo_o;
//连接EX/MEM模块的输出与访存阶段MEM模块的输入
wire mem_wreg_i;
wire[`RegAddrBus] mem_wd_i;
wire[`RegBus] mem_wdata_i;
wire[`RegBus] mem_hi_i;
wire[`RegBus] mem_lo_i;
wire mem_whilo_i;
//连接访存阶段MEM模块的输出与MEM/WB模块的输入
wire mem_wreg_o;
wire[`RegAddrBus] mem_wd_o;
wire[`RegBus] mem_wdata_o;
wire[`RegBus] mem_hi_o;
wire[`RegBus] mem_lo_o;
wire mem_whilo_o;
//连接MEM/WB模块的输出与回写阶段的输入
wire wb_wreg_i;
wire[`RegAddrBus] wb_wd_i;
wire[`RegBus] wb_wdata_i;
wire[`RegBus] wb_hi_i;
wire[`RegBus] wb_lo_i;
wire wb_whilo_i;
//连接译码阶段ID模块与通用寄存器Regfile模块
wire reg1_read;
wire reg2_read;
wire[`RegBus] reg1_data;
wire[`RegBus] reg2_data;
wire[`RegAddrBus] reg1_addr;
wire[`RegAddrBus] reg2_addr;
//连接执行阶段与hilo模块的输出 读取HI,LO寄存器
wire[`RegBus] hi;
wire[`RegBus] lo;
//连接执行阶段与ex_reg模块 用于多周期的MADD MADDU MSUB MSUBU指令
wire[`DoubleRegBus] hilo_temp_o;
wire[1:0] cnt_o;
wire[`DoubleRegBus] hilo_temp_i;
wire[1:0] cnt_i;
wire[`DoubleRegBus] div_result;
wire div_ready;
wire[`RegBus] div_opdata1;
wire[`RegBus] div_opdata2;
wire div_start;
wire div_annul;
wire signed_div;
wire is_in_delayslot_i;
wire is_in_delayslot_o;
wire next_inst_in_delayslot_o;
wire id_branch_flag_o;
wire[`RegBus] branch_target_address;
wire[5:0] stall;
wire stallreq_from_id;
wire stallreq_from_ex;
//pc_reg例化
pc_reg pc_reg0(
.clk(clk),
.rst(rst),
.stall(stall),
.pc(pc),
.ce(rom_ce_o) ,
.branch_flag_i(id_branch_flag_o),
.branch_target_address_i(branch_target_address)
);
assign rom_addr_o = pc;
//IF/ID模块例化
if_id if_id0(
.clk(clk),
.rst(rst),
.if_pc(pc),
.stall(stall),
.if_inst(rom_data_i),
.id_pc(id_pc_i),
.id_inst(id_inst_i)
);
//译码阶段ID模块
id id0(
.rst(rst),
.pc_i(id_pc_i),
.inst_i(id_inst_i),
.reg1_data_i(reg1_data),
.reg2_data_i(reg2_data),
//处于执行阶段的指令要写入的目的寄存器信息
.ex_wreg_i(ex_wreg_o),
.ex_wdata_i(ex_wdata_o),
.ex_wd_i(ex_wd_o),
//处于访存阶段的指令要写入的目的寄存器信息
.mem_wreg_i(mem_wreg_o),
.mem_wdata_i(mem_wdata_o),
.mem_wd_i(mem_wd_o),
.is_in_delayslot_i(is_in_delayslot_i),
//送到regfile的信息
.reg1_read_o(reg1_read),
.reg2_read_o(reg2_read),
.reg1_addr_o(reg1_addr),
.reg2_addr_o(reg2_addr),
//送到ID/EX模块的信息
.aluop_o(id_aluop_o),
.alusel_o(id_alusel_o),
.reg1_o(id_reg1_o),
.reg2_o(id_reg2_o),
.wd_o(id_wd_o),
.wreg_o(id_wreg_o),
.stallreq(stallreq_from_id),
.next_inst_in_delayslot_o(next_inst_in_delayslot_o),
.branch_flag_o(id_branch_flag_o),
.branch_target_address_o(branch_target_address),
.link_addr_o(id_link_address_o),
.is_in_delayslot_o(id_is_in_delayslot_o)
);
//通用寄存器Regfile例化
regfile regfile1(
.clk (clk),
.rst (rst),
.we (wb_wreg_i),
.waddr (wb_wd_i),
.wdata (wb_wdata_i),
.re1 (reg1_read),
.raddr1 (reg1_addr),
.rdata1 (reg1_data),
.re2 (reg2_read),
.raddr2 (reg2_addr),
.rdata2 (reg2_data)
);
//ID/EX模块
id_ex id_ex0(
.clk(clk),
.rst(rst),
.stall(stall),
//从译码阶段ID模块传递的信息
.id_aluop(id_aluop_o),
.id_alusel(id_alusel_o),
.id_reg1(id_reg1_o),
.id_reg2(id_reg2_o),
.id_wd(id_wd_o),
.id_wreg(id_wreg_o),
.id_link_address(id_link_address_o),
.id_is_in_delayslot(id_is_in_delayslot_o),
.next_inst_in_delayslot_i(next_inst_in_delayslot_o),
//传递到执行阶段EX模块的信息
.ex_aluop(ex_aluop_i),
.ex_alusel(ex_alusel_i),
.ex_reg1(ex_reg1_i),
.ex_reg2(ex_reg2_i),
.ex_wd(ex_wd_i),
.ex_wreg(ex_wreg_i),
.ex_link_address(ex_link_address_i),
.ex_is_in_delayslot(ex_is_in_delayslot_i),
.is_in_delayslot_o(is_in_delayslot_i)
);
//EX模块
ex ex0(
.rst(rst),
//送到执行阶段EX模块的信息
.aluop_i(ex_aluop_i),
.alusel_i(ex_alusel_i),
.reg1_i(ex_reg1_i),
.reg2_i(ex_reg2_i),
.wd_i(ex_wd_i),
.wreg_i(ex_wreg_i),
.hi_i(hi),
.lo_i(lo),
.wb_hi_i(wb_hi_i),
.wb_lo_i(wb_lo_i),
.wb_whilo_i(wb_whilo_i),
.mem_hi_i(mem_hi_o),
.mem_lo_i(mem_lo_o),
.mem_whilo_i(mem_whilo_o),
.hilo_temp_i(hilo_temp_i),
.cnt_i(cnt_i),
.div_result_i(div_result),
.div_ready_i(div_ready),
.link_address_i(ex_link_address_i),
.is_in_delayslot_i(ex_is_in_delayslot_i),
//EX模块的输出到EX/MEM模块信息
.wd_o(ex_wd_o),
.wreg_o(ex_wreg_o),
.wdata_o(ex_wdata_o),
.hi_o(ex_hi_o),
.lo_o(ex_lo_o),
.whilo_o(ex_whilo_o),
.hilo_temp_o(hilo_temp_o),
.cnt_o(cnt_o),
.div_opdata1_o(div_opdata1),
.div_opdata2_o(div_opdata2),
.div_start_o(div_start),
.signed_div_o(signed_div),
.stallreq(stallreq_from_ex)
);
//EX/MEM模块
ex_mem ex_mem0(
.clk(clk),
.rst(rst),
.stall(stall),
//来自执行阶段EX模块的信息
.ex_wd(ex_wd_o),
.ex_wreg(ex_wreg_o),
.ex_wdata(ex_wdata_o),
.ex_hi(ex_hi_o),
.ex_lo(ex_lo_o),
.ex_whilo(ex_whilo_o),
.hilo_i(hilo_temp_o),
.cnt_i(cnt_o),
//送到访存阶段MEM模块的信息
.mem_wd(mem_wd_i),
.mem_wreg(mem_wreg_i),
.mem_wdata(mem_wdata_i),
.mem_hi(mem_hi_i),
.mem_lo(mem_lo_i),
.mem_whilo(mem_whilo_i),
.hilo_o(hilo_temp_i),
.cnt_o(cnt_i)
);
//MEM模块例化
mem mem0(
.rst(rst),
//来自EX/MEM模块的信息
.wd_i(mem_wd_i),
.wreg_i(mem_wreg_i),
.wdata_i(mem_wdata_i),
.hi_i(mem_hi_i),
.lo_i(mem_lo_i),
.whilo_i(mem_whilo_i),
//送到MEM/WB模块的信息
.wd_o(mem_wd_o),
.wreg_o(mem_wreg_o),
.wdata_o(mem_wdata_o),
.hi_o(mem_hi_o),
.lo_o(mem_lo_o),
.whilo_o(mem_whilo_o)
);
//MEM/WB模块
mem_wb mem_wb0(
.clk(clk),
.rst(rst),
.stall(stall),
//来自访存阶段MEM模块的信息
.mem_wd(mem_wd_o),
.mem_wreg(mem_wreg_o),
.mem_wdata(mem_wdata_o),
.mem_hi(mem_hi_o),
.mem_lo(mem_lo_o),
.mem_whilo(mem_whilo_o),
//送到回写阶段的信息
.wb_wd(wb_wd_i),
.wb_wreg(wb_wreg_i),
.wb_wdata(wb_wdata_i),
.wb_hi(wb_hi_i),
.wb_lo(wb_lo_i),
.wb_whilo(wb_whilo_i)
);
hilo_reg hilo_reg0(
.clk(clk),
.rst(rst),
//写端口
.we(wb_whilo_i),
.hi_i(wb_hi_i),
.lo_i(wb_lo_i),
//读端口1
.hi_o(hi),
.lo_o(lo)
);
ctrl ctrl0(
.rst(rst),
.stallreq_from_id(stallreq_from_id),
.stallreq_from_ex(stallreq_from_ex),
.stall(stall)
);
div div0(
.clk(clk),
.rst(rst),
.signed_div_i(signed_div),
.opdata1_i(div_opdata1),
.opdata2_i(div_opdata2),
.start_i(div_start),
.annul_i(1'b0),
.result_o(div_result),
.ready_o(div_ready)
);
endmodule */
module openmips(
input wire clk,
input wire rst,
input wire[`RegBus] rom_data_i,
output wire[`RegBus] rom_addr_o,
output wire rom_ce_o
);
wire[`InstAddrBus] pc;
wire[`InstAddrBus] id_pc_i;
wire[`InstBus] id_inst_i;
//连接译码阶段ID模块的输出与ID/EX模块的输入
wire[`AluOpBus] id_aluop_o;
wire[`AluSelBus] id_alusel_o;
wire[`RegBus] id_reg1_o;
wire[`RegBus] id_reg2_o;
wire id_wreg_o;
wire[`RegAddrBus] id_wd_o;
wire id_is_in_delayslot_o;
wire[`RegBus] id_link_address_o;
//连接ID/EX模块的输出与执行阶段EX模块的输入
wire[`AluOpBus] ex_aluop_i;
wire[`AluSelBus] ex_alusel_i;
wire[`RegBus] ex_reg1_i;
wire[`RegBus] ex_reg2_i;
wire ex_wreg_i;
wire[`RegAddrBus] ex_wd_i;
wire ex_is_in_delayslot_i;
wire[`RegBus] ex_link_address_i;
//连接执行阶段EX模块的输出与EX/MEM模块的输入
wire ex_wreg_o;
wire[`RegAddrBus] ex_wd_o;
wire[`RegBus] ex_wdata_o;
wire[`RegBus] ex_hi_o;
wire[`RegBus] ex_lo_o;
wire ex_whilo_o;
//连接EX/MEM模块的输出与访存阶段MEM模块的输入
wire mem_wreg_i;
wire[`RegAddrBus] mem_wd_i;
wire[`RegBus] mem_wdata_i;
wire[`RegBus] mem_hi_i;
wire[`RegBus] mem_lo_i;
wire mem_whilo_i;
//连接访存阶段MEM模块的输出与MEM/WB模块的输入
wire mem_wreg_o;
wire[`RegAddrBus] mem_wd_o;
wire[`RegBus] mem_wdata_o;
wire[`RegBus] mem_hi_o;
wire[`RegBus] mem_lo_o;
wire mem_whilo_o;
//连接MEM/WB模块的输出与回写阶段的输入
wire wb_wreg_i;
wire[`RegAddrBus] wb_wd_i;
wire[`RegBus] wb_wdata_i;
wire[`RegBus] wb_hi_i;
wire[`RegBus] wb_lo_i;
wire wb_whilo_i;
//连接译码阶段ID模块与通用寄存器Regfile模块
wire reg1_read;
wire reg2_read;
wire[`RegBus] reg1_data;
wire[`RegBus] reg2_data;
wire[`RegAddrBus] reg1_addr;
wire[`RegAddrBus] reg2_addr;
//连接执行阶段与hilo模块的输出,读取HI、LO寄存器
wire[`RegBus] hi;
wire[`RegBus] lo;
//连接执行阶段与ex_reg模块,用于多周期的MADD、MADDU、MSUB、MSUBU指令
wire[`DoubleRegBus] hilo_temp_o;
wire[1:0] cnt_o;
wire[`DoubleRegBus] hilo_temp_i;
wire[1:0] cnt_i;
wire[`DoubleRegBus] div_result;
wire div_ready;
wire[`RegBus] div_opdata1;
wire[`RegBus] div_opdata2;
wire div_start;
wire div_annul;
wire signed_div;
wire is_in_delayslot_i;
wire is_in_delayslot_o;
wire next_inst_in_delayslot_o;
wire id_branch_flag_o;
wire[`RegBus] branch_target_address;
wire[5:0] stall;
wire stallreq_from_id;
wire stallreq_from_ex;
//pc_reg例化
pc_reg pc_reg0(
.clk(clk),
.rst(rst),
.stall(stall),
.branch_flag_i(id_branch_flag_o),
.branch_target_address_i(branch_target_address),
.pc(pc),
.ce(rom_ce_o)
);
assign rom_addr_o = pc;
//IF/ID模块例化
if_id if_id0(
.clk(clk),
.rst(rst),
.stall(stall),
.if_pc(pc),
.if_inst(rom_data_i),
.id_pc(id_pc_i),
.id_inst(id_inst_i)
);
//译码阶段ID模块
id id0(
.rst(rst),
.pc_i(id_pc_i),
.inst_i(id_inst_i),
.reg1_data_i(reg1_data),
.reg2_data_i(reg2_data),
//处于执行阶段的指令要写入的目的寄存器信息
.ex_wreg_i(ex_wreg_o),
.ex_wdata_i(ex_wdata_o),
.ex_wd_i(ex_wd_o),
//处于访存阶段的指令要写入的目的寄存器信息
.mem_wreg_i(mem_wreg_o),
.mem_wdata_i(mem_wdata_o),
.mem_wd_i(mem_wd_o),
.is_in_delayslot_i(is_in_delayslot_i),
//送到regfile的信息
.reg1_read_o(reg1_read),
.reg2_read_o(reg2_read),
.reg1_addr_o(reg1_addr),
.reg2_addr_o(reg2_addr),
//送到ID/EX模块的信息
.aluop_o(id_aluop_o),
.alusel_o(id_alusel_o),
.reg1_o(id_reg1_o),
.reg2_o(id_reg2_o),
.wd_o(id_wd_o),
.wreg_o(id_wreg_o),
.next_inst_in_delayslot_o(next_inst_in_delayslot_o),
.branch_flag_o(id_branch_flag_o),
.branch_target_address_o(branch_target_address),
.link_addr_o(id_link_address_o),
.is_in_delayslot_o(id_is_in_delayslot_o),
.stallreq(stallreq_from_id)
);
//通用寄存器Regfile例化
regfile regfile1(
.clk (clk),
.rst (rst),
.we (wb_wreg_i),
.waddr (wb_wd_i),
.wdata (wb_wdata_i),
.re1 (reg1_read),
.raddr1 (reg1_addr),
.rdata1 (reg1_data),
.re2 (reg2_read),
.raddr2 (reg2_addr),
.rdata2 (reg2_data)
);
//ID/EX模块
id_ex id_ex0(
.clk(clk),
.rst(rst),
.stall(stall),
//从译码阶段ID模块传递的信息
.id_aluop(id_aluop_o),
.id_alusel(id_alusel_o),
.id_reg1(id_reg1_o),
.id_reg2(id_reg2_o),
.id_wd(id_wd_o),
.id_wreg(id_wreg_o),
.id_link_address(id_link_address_o),
.id_is_in_delayslot(id_is_in_delayslot_o),
.next_inst_in_delayslot_i(next_inst_in_delayslot_o),
//传递到执行阶段EX模块的信息
.ex_aluop(ex_aluop_i),
.ex_alusel(ex_alusel_i),
.ex_reg1(ex_reg1_i),
.ex_reg2(ex_reg2_i),
.ex_wd(ex_wd_i),
.ex_wreg(ex_wreg_i),
.ex_link_address(ex_link_address_i),
.ex_is_in_delayslot(ex_is_in_delayslot_i),
.is_in_delayslot_o(is_in_delayslot_i)
);
//EX模块
ex ex0(
.rst(rst),
//送到执行阶段EX模块的信息
.aluop_i(ex_aluop_i),
.alusel_i(ex_alusel_i),
.reg1_i(ex_reg1_i),
.reg2_i(ex_reg2_i),
.wd_i(ex_wd_i),
.wreg_i(ex_wreg_i),
.hi_i(hi),
.lo_i(lo),
.wb_hi_i(wb_hi_i),
.wb_lo_i(wb_lo_i),
.wb_whilo_i(wb_whilo_i),
.mem_hi_i(mem_hi_o),
.mem_lo_i(mem_lo_o),
.mem_whilo_i(mem_whilo_o),
.hilo_temp_i(hilo_temp_i),
.cnt_i(cnt_i),
.div_result_i(div_result),
.div_ready_i(div_ready),
.link_address_i(ex_link_address_i),
.is_in_delayslot_i(ex_is_in_delayslot_i),
//EX模块的输出到EX/MEM模块信息
.wd_o(ex_wd_o),
.wreg_o(ex_wreg_o),
.wdata_o(ex_wdata_o),
.hi_o(ex_hi_o),
.lo_o(ex_lo_o),
.whilo_o(ex_whilo_o),
.hilo_temp_o(hilo_temp_o),
.cnt_o(cnt_o),
.div_opdata1_o(div_opdata1),
.div_opdata2_o(div_opdata2),
.div_start_o(div_start),
.signed_div_o(signed_div),
.stallreq(stallreq_from_ex)
);
//EX/MEM模块
ex_mem ex_mem0(
.clk(clk),
.rst(rst),
.stall(stall),
//来自执行阶段EX模块的信息
.ex_wd(ex_wd_o),
.ex_wreg(ex_wreg_o),
.ex_wdata(ex_wdata_o),
.ex_hi(ex_hi_o),
.ex_lo(ex_lo_o),
.ex_whilo(ex_whilo_o),
.hilo_i(hilo_temp_o),
.cnt_i(cnt_o),
//送到访存阶段MEM模块的信息
.mem_wd(mem_wd_i),
.mem_wreg(mem_wreg_i),
.mem_wdata(mem_wdata_i),
.mem_hi(mem_hi_i),
.mem_lo(mem_lo_i),
.mem_whilo(mem_whilo_i),
.hilo_o(hilo_temp_i),
.cnt_o(cnt_i)
);
//MEM模块例化
mem mem0(
.rst(rst),
//来自EX/MEM模块的信息
.wd_i(mem_wd_i),
.wreg_i(mem_wreg_i),
.wdata_i(mem_wdata_i),
.hi_i(mem_hi_i),
.lo_i(mem_lo_i),
.whilo_i(mem_whilo_i),
//送到MEM/WB模块的信息
.wd_o(mem_wd_o),
.wreg_o(mem_wreg_o),
.wdata_o(mem_wdata_o),
.hi_o(mem_hi_o),
.lo_o(mem_lo_o),
.whilo_o(mem_whilo_o)
);
//MEM/WB模块
mem_wb mem_wb0(
.clk(clk),
.rst(rst),
.stall(stall),
//来自访存阶段MEM模块的信息
.mem_wd(mem_wd_o),
.mem_wreg(mem_wreg_o),
.mem_wdata(mem_wdata_o),
.mem_hi(mem_hi_o),
.mem_lo(mem_lo_o),
.mem_whilo(mem_whilo_o),
//送到回写阶段的信息
.wb_wd(wb_wd_i),
.wb_wreg(wb_wreg_i),
.wb_wdata(wb_wdata_i),
.wb_hi(wb_hi_i),
.wb_lo(wb_lo_i),
.wb_whilo(wb_whilo_i)
);
hilo_reg hilo_reg0(
.clk(clk),
.rst(rst),
//写端口
.we(wb_whilo_i),
.hi_i(wb_hi_i),
.lo_i(wb_lo_i),
//读端口1
.hi_o(hi),
.lo_o(lo)
);
ctrl ctrl0(
.rst(rst),
.stallreq_from_id(stallreq_from_id),
//来自执行阶段的暂停请求
.stallreq_from_ex(stallreq_from_ex),
.stall(stall)
);
div div0(
.clk(clk),
.rst(rst),
.signed_div_i(signed_div),
.opdata1_i(div_opdata1),
.opdata2_i(div_opdata2),
.start_i(div_start),
.annul_i(1'b0),
.result_o(div_result),
.ready_o(div_ready)
);
endmodule
inst_rom1.data 测试跳转指令
34010001
08000008
34010002
34011111
34011100
00000000
00000000
00000000
34010003
0c000010
03e1001a
34010005
34010006
08000018
00000000
00000000
03e01009
00400825
34010009
3401000a
08000020
00000000
00000000
00000000
34010007
00400008
34010008
34011111
34011100
00000000
00000000
00000000
00000000
08000021
00000000
inst_rom1.S
.org 0x0
.set noat
.set noreorder
.set nomacro
.global _start
_start:
ori $1,$0,0x0001 # $1 = 0x1
j 0x20
ori $1,$0,0x0002 # $1 = 0x2
ori $1,$0,0x1111
ori $1,$0,0x1100
.org 0x20
ori $1,$0,0x0003 # $1 = 0x3
jal 0x40
div $zero,$31,$1 # $31 = 0x2c, $1 = 0x3
# HI = 0x2, LO = 0xe
ori $1,$0,0x0005 # r1 = 0x5
ori $1,$0,0x0006 # r1 = 0x6
j 0x60
nop
.org 0x40
jalr $2,$31
or $1,$2,$0 # $1 = 0x48
ori $1,$0,0x0009 # $1 = 0x9
ori $1,$0,0x000a # $1 = 0xa
j 0x80
nop
.org 0x60
ori $1,$0,0x0007 # $1 = 0x7
jr $2
ori $1,$0,0x0008 # $1 = 0x8
ori $1,$0,0x1111
ori $1,$0,0x1100
.org 0x80
nop
_loop:
j _loop
nop
跳转结果
inst_rom2.data 条件转移
34038000
00031c00
34010001
10000004
34010002
34011111
34011100
00000000
34010003
0411000a
03e1001a
34011100
34011111
14200012
00000000
34011100
34011111
00000000
00000000
00000000
34010004
1063000a
03e00825
34011111
34011100
34010007
34010008
1c200024
34010009
34011111
34011100
00000000
34010005
0421fff7
34010006
34011111
34011100
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
3401000a
0471ffde
001f0825
3401000b
3401000c
3401000d
3401000e
04600004
3401000f
34011100
00000000
00000000
34010010
1820ffcb
34010011
34010012
34010013
04700006
001f0825
34011100
00000000
00000000
00000000
00000000
34010014
00000000
0800005a
00000000
inst_rom2.S
.org 0x0
.set noat
.set noreorder
.set nomacro
.global _start
_start:
ori $3,$0,0x8000
sll $3,16 # $3 = 0x80000000
ori $1,$0,0x0001 # $1 = 0x1
b s1
ori $1,$0,0x0002 # $1 = 0x2
1:
ori $1,$0,0x1111
ori $1,$0,0x1100
.org 0x20
s1:
ori $1,$0,0x0003 # $1 = 0x3
bal s2
div $zero,$31,$1 # $31 = 0x2c, $1 =0x3
# HI = 0x2, LO = 0xe
ori $1,$0,0x1100
ori $1,$0,0x1111
bne $1,$0,s3
nop
ori $1,$0,0x1100
ori $1,$0,0x1111
.org 0x50
s2:
ori $1,$0,0x0004 # $1 = 0x4
beq $3,$3,s3
or $1,$31,$0 # $1 = 0x2c
ori $1,$0,0x1111
ori $1,$0,0x1100
2:
ori $1,$0,0x0007 # $1 = 0x7
ori $1,$0,0x0008 # $1 = 0x8
bgtz $1,s4
ori $1,$0,0x0009 # $1 = 0x9
ori $1,$0,0x1111
ori $1,$0,0x1100
.org 0x80
s3:
ori $1,$0,0x0005 # $1 = 0x5
BGEZ $1,2b
ori $1,$0,0x0006 # $1 = 0x6
ori $1,$0,0x1111
ori $1,$0,0x1100
.org 0x100
s4:
ori $1,$0,0x000a # $1 = 0xa
BGEZAL $3,s3
or $1,$0,$31 # $1 = 0x10c
ori $1,$0,0x000b # $1 = 0xb
ori $1,$0,0x000c # $1 = 0xc
ori $1,$0,0x000d # $1 = 0xd
ori $1,$0,0x000e # $1 = 0xe
bltz $3,s5
ori $1,$0,0x000f # $1 = 0xf
ori $1,$0,0x1100
.org 0x130
s5:
ori $1,$0,0x0010 # $1 = 0x10
blez $1,2b
ori $1,$0,0x0011 # $1 = 0x11
ori $1,$0,0x0012 # $1 = 0x12
ori $1,$0,0x0013 # $1 = 0x13
bltzal $3,s6
or $1,$0,$31 # $1 = 0x14c
ori $1,$0,0x1100
.org 0x160
s6:
ori $1,$0,0x0014 # $1 = 0x14
nop
_loop:
j _loop
nop
条件转移结果