乘累加,乘累减,除法指令在流水线执行阶段占用多个周期,因此需要暂停流水线,以等待这些多周期指令执行完毕。
暂停流水线有两种方法实现。
一种直观的实现方法:保持取指令地址PC的值保持不变,同时保持流水线各个阶段的寄存器不变。
OpenMIPS采用的是一种改进过的方法:假如位于流水线第n阶段的指令需要多少个时钟周期,进行请求流水线暂停。 需要保持指令地址PC值不变,同时保持流水线第n个阶段,第n个阶段之前的各个阶段的寄存器不变,而第n阶段后面的指令继续进行。例如:流水线执行阶段的指令请求流水线暂停,保持PC不变,同时保持取指、译码、执行阶段的寄存器不变,但是可以允许访存、回写阶段的指令继续运行。
为此,添加CTRL模块,接收各阶段传递过来的流水线暂停请求信号,从而控制流水线各阶段的运行。
CTRL模块输入来自ID,EX模块的请求暂停信号stallreq。该模块对暂停请求信号进行判断,然后输出流水线暂停信号stall。
对于教学版OpenMIPS而言,只有译码、执行阶段可能会有暂停请求,取指、访存阶段没有暂停请求。
为了实现流水线暂停机制而对系统结构所做的修改
接口名 | 宽度(bit) | 输入/输出 | 作用 |
---|---|---|---|
rst | 1 | 输入 | 复位信号 |
stallreq_from_id | 1 | 输入 | 处于译码阶段的指令是否请求流水线暂停 |
stallreq_from_ex | 1 | 输入 | 处于执行阶段的指令是否请求流水线暂停 |
stall | 6 | 输出 | 暂停流水线控制信号 |
//ctrl.v
module ctrl(
input wire rst,
input wire stallreq_from_id,
input wire stallreq_from_ex,
output reg[5:0] stall
);
/**************************说明******************************
*****stall[0]表示取指地址PC是否保持不变,为1表示保持不变*****
*******stall[1]表示流水线取指阶段是否暂停,为1表示暂停*******
*******stall[2]表示流水线译码阶段是否暂停,为1表示暂停*******
*******stall[3]表示流水线执行阶段是否暂停,为1表示暂停*******
*******stall[4]表示流水线访存阶段是否暂停,为1表示暂停*******
*******stall[5]表示流水线回写阶段是否暂停,为1表示暂停*******
************************************************************/
always @ (*) begin
if(rst == `RstEnable) begin
stall <= 6'b000000;
end else if(stallreq_from_ex == `Stop) begin
stall <= 6'b001111;
end else if(stallreq_from_id == `Stop) begin
stall <= 6'b000111;
end else begin
stall <= 6'b000000;
end
end
endmodule
SPECIAL=011100
31-26 | 25-21 | 20-16 | 15-11 | 10-6 | 5-0 | useage | function |
---|---|---|---|---|---|---|---|
SPECIAL2 | rs | rt | 00000 | 00000 | MADD(000000) | madd rs,rt | {HI,LO}<-{HI,LO}+rs*rt |
SPECIAL2 | rs | rt | 00000 | 00000 | MADDU(000001) | maddu rs,rt | {HI,LO}<-{HI,LO}+rs*rt |
SPECIAL2 | rs | rt | 00000 | 00000 | MSUB(000100) | msub rs,rt | {HI,LO}<-{HI,LO}-rs*rt |
SPECIAL2 | rs | rt | 00000 | 00000 | MUSBU(000101) | msubu rs,rt | {HI,LO}<-{HI,LO}-rs*rt |
在流水线执行阶段采用两个时钟周期完成运算,第一个时钟周期进行乘法运算,第二个时钟周期将乘法结果与HI,LO寄存器进行加/减法。
需要保存的两个信息:
//全局
`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_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
//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
//指令存储器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 //流水线继续
`include "define.v"
//PC模块:给出指令地址
/*module pc_reg(
input wire clk,//时钟信号
input wire rst,//复位信号
output reg[`InstAddrBus]pc,//[31:0]要读取的指令地址,指令地址线宽度为32
output reg ce//指令存储器使能信号
);
always @(posedge clk)
begin
if(rst==`RstEnable)//rst=1,复位信号有效,当输入rst为高电平时,复位信号有效
begin
ce<= `ChipDisable;//指令存储器禁用
end
else //rst=0
begin
ce<=`ChipEnable;//复位结束后,指令存储器使能
end
end
always @ (posedge clk)
begin
if(ce==`ChipDisable)//指令存储器禁用
begin
pc<=32'h00000000;//指令存储器禁用时,PC为0
end
else
begin
pc<=pc+4'h4;//指令存储器使能时,PC的值每时钟周期加4
end
end
endmodule*/
//`include "defines.v"
module pc_reg(
input wire clk,
input wire rst,
input wire[5:0] stall,//来自控制模块CTRL
output reg[`InstAddrBus] pc,
output reg ce
);
always @ (posedge clk) begin
if (rst == `RstEnable) begin
ce <= `ChipDisable;
end else begin
ce <= `ChipEnable;
end
end
always @ (posedge clk) begin
if (ce == `ChipDisable) begin
pc <= 32'h00000000;
end else if(stall[0] == `NoStop)begin
//end else begin
pc <= pc + 4'h4;
end
end
/*always @ (posedge clk) begin
if (rst == `RstEnable) begin
ce <= `ChipDisable;
end else begin
ce <= `ChipEnable;
end
end*/
endmodule
`include "define.v"
//inst_rom.v
module inst_rom(
input wire ce,
input wire[`InstAddrBus] addr,
output reg[`InstBus] inst
);
//定义一个数组,大小是InstMemNum,元素宽度是InstBus
reg[`InstBus] inst_mem[0:`InstMemNum-1];
//使用文件inst_rom.data 初始化指令存储器
initial $readmemh ("F:/test_2/inst_rom_7_2.data",inst_mem);
always @ (*)
begin
if(ce == `ChipDisable)//当复位信号无效时,依据输入的地址,给出指令存储器ROM中对应的元素
begin
inst <= `ZeroWord;
end
else begin
inst <= inst_mem[addr[`InstMemNumLog2+1:2]];
end
end
endmodule
`include "define.v"
module if_id(
input wire clk,
input wire rst,
input wire[`InstAddrBus] if_pc,
input wire[`InstBus] if_inst,
input wire[5:0] stall, //来自控制模块的信息
output reg[`InstAddrBus] id_pc,
output reg[`InstBus] id_inst
);
/**************************说明******************************
*****stall[0]表示取指地址PC是否保持不变,为1表示保持不变*****
*******stall[1]表示流水线取指阶段是否暂停,为1表示暂停*******
*******stall[2]表示流水线译码阶段是否暂停,为1表示暂停*******
*******stall[3]表示流水线执行阶段是否暂停,为1表示暂停*******
*******stall[4]表示流水线访存阶段是否暂停,为1表示暂停*******
*******stall[5]表示流水线回写阶段是否暂停,为1表示暂停*******
(1)stall[1]为Stop,stall[5]为NoStop,表示取指阶段暂停,译码阶段
继续,所以使用空指令作为下一个周期进入译码阶段的指令
(2)stall[1]为NoStop,取指阶段继续,取得的指令进入译码阶段
(3)其余情况下,保持译码阶段的寄存器id_pc,id_inst不变
************************************************************/
always @ (posedge clk) begin
if (rst == `RstEnable) begin
id_pc <= `ZeroWord;
id_inst <= `ZeroWord;
end else if(stall[1] == `Stop && stall[2] == `NoStop)begin
id_pc <= `ZeroWord;
id_inst <= `ZeroWord;
end else if(stall[1] == `NoStop)begin
id_pc <= if_pc;
id_inst <= if_inst;
end
/*end else if(stall[4] == `Stop && stall[5] == `NoStop)begin
wb_wd <= `NOPRegAddr;
wb_wreg <= `WriteDisable;
wb_wdata <= `ZeroWord;
wb_hilo <= `ZeroWord;
wb_hi <= `ZeroWord;
wb_lo <= `ZeroWord;
wb_whilo <= `WriteDisable;
end else if(stall[4] == `NoStop) begin
wb_wd <= mem_wd;
wb_wreg <= mem_wreg;
wb_wdata <= mem_wdata;
wb_hi <= mem_hi;
wb_lo <= mem_lo;
wb_whilo <= mem_whilo;
end
/* end else begin
id_pc <= if_pc;
id_inst <= if_inst;
end*/
end
endmodule
`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,
//送到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
);
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;
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;
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;
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
`include "define.v"
//回写阶段的实现实际上是在Regfile模块中实现的
//Regfile模块 寄存器堆
module regfile(
input wire clk,//时钟信号
input wire rst,//复位信号,高电平有效
//写端口
input wire we,//写使能信号
input wire[`RegAddrBus] waddr,//要写入的寄存器地址
input wire[`RegBus] wdata,//要写入的数据
//读端口1
input wire re1,//第一个读寄存器端口读使能信号
input wire[`RegAddrBus] raddr1,//第一个读寄存器端口要读取的寄存器的地址
output reg[`RegBus] rdata1,//第一个读寄存器端口输出寄存器的值
//读端口2
input wire re2,//第二个读寄存器端口读使能信号
input wire[`RegAddrBus] raddr2,//第二个读寄存器端口要读取的寄存器地址
output reg[`RegBus] rdata2//第二个读寄存器端口要输出的寄存器的值
);
/*******第一段: 定义32个32位寄存器*******/
/*******第二段:写操作******************/
/*******第三段:读端口1的读操作********/
/*******第四段:读端口2的读操作*******/
//第一段:定义32个32位寄存器
reg[`RegBus] regs[0:`RegNum-1];
//第二段:实现写寄存器操作
always @ (posedge clk)
begin
if(rst==`RstDisable)//rst==1,复位信号无效
begin
if((we==`WriteEnable)&&(waddr!=`RegNumLog2'h0))//写使能信号we有效 且 写操作目的寄存器不等于0的情况下
begin//MIPS32架构规定 $0的值只能为0 所以不需要写入
regs[waddr]<=wdata;//将写输入数据保存到目的寄存器
end
end
end
//第三段:读端口1的读操作 实现第一个读寄存器端口
always @(*)
begin
if(rst==`RstEnable)//当复位信号有效时
begin
rdata1 <= `ZeroWord;//第一个读寄存器端口的输出始终为0
end
else if(raddr1==`RegNumLog2'h0)//当复位信号无效时 如果读取的是$0
begin
rdata1<=`ZeroWord;//直接给出0
end
else if((raddr1==waddr)&&(we==`WriteEnable)&&(re1==`ReadEnable))//如果第一个读寄存器端口要读取的目标寄存器与要写入的目的寄存器是同一个
begin
rdata1<=wdata;//直接将要写入的值作为第一个寄存器端口的输出
end
else if(re1==`ReadEnable)//上述情况都不满足时
begin
rdata1<=regs[raddr1];//给出第一个读寄存器端口要读取的目标寄存器地址对应寄存器的值
end
else//当第一个寄存器端口不能使用时
begin
rdata1<=`ZeroWord;//直接输出0
end
end
//第四段 读端口2的操作 实现第二个寄存器端口 具体过程和第三段相似 注意地址的变化
always @ (*)
begin
if(rst==`RstEnable)
begin
rdata2<=`ZeroWord;
end
else if(raddr2==`RegNumLog2'h0)
begin
rdata2<=`ZeroWord;
end
else if((raddr2==waddr)&&(we==`WriteEnable)&&(re2==`ReadEnable))
begin
rdata2<=wdata;
end
else if(re2==`ReadEnable)
begin
rdata2<=regs[raddr2];
end
else
begin
rdata2<=`ZeroWord;
end
end
endmodule
`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,//译码阶段指令要进行的运算的源操作数1
input wire[`RegBus] id_reg2,//译码阶段指令要执行的运算的源操作数2
input wire[`RegAddrBus] id_wd,//译码阶段的指令要写入的目的寄存器的地址
input wire id_wreg,//译码阶段的指令是否有要写入的目的寄存器
//传递到执行阶段的信息
output reg[`AluOpBus] ex_aluop,//执行阶段的指令要进行的运算的子类型
output reg[`AluSelBus] ex_alusel,//执行阶段要进行的类型
output reg[`RegBus] ex_reg1,//执行阶段的指令要进行运算的源操作数1
output reg[`RegBus] ex_reg2,//执行阶段的指令要进行运算的源操作数2
output reg[`RegAddrBus] ex_wd,//执行阶段的指令要写入的目的寄存器地址
output reg ex_wreg //执行阶段的指令是否要写入的目的寄存器
);
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;
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
endmodule
//在时钟周期的上升沿,将译码阶段的结果传递到执行阶段*/
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 ,
//传递到执行阶段的信息
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
);
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;
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;
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;
/*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
`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,//处于执行阶段的第几个周期
//处于执行阶段指令对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
);
//保存逻辑运算的结果
reg[`RegBus] logicout;
//保存移位运算的结果
reg[`RegBus] shiftres;
//保存移动操作的结果
reg[`RegBus] moveres;
//保存HI,LO寄存器的最新值
reg[`RegBus] HI;
reg[`RegBus] LO;
/***********************第七章新定义一些变量***********************/
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
default:begin
wdata_o <= `ZeroWord;
end
endcase
end//always
/****************************************************************
****************第九段:确定(修改)对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 begin
whilo_o <= `WriteDisable;
hi_o <= `ZeroWord;
lo_o <= `ZeroWord;
end
end
/****************************************************************
********************第十段:乘累加、乘累减************************
****************************************************************/
//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
/****************************************************************
*********************第十一段:暂停流水线*************************
****************************************************************/
//目前只有乘累加和乘累减指令会导致流水线暂停,所以stallreq=stallreq_for_madd_msub
always @ (*) begin
stallreq = stallreq_for_madd_msub;
end
endmodule
`include "define.v"
module ex_mem(
input wire clk,
input wire rst,
//来自控制模块的信息
input wire[5:0] stall,
//来自执行阶段的信息
input wire[`RegAddrBus] ex_wd,
input wire ex_wreg,
input wire[`RegBus] ex_wdata,
input wire[`RegBus] ex_hi,
input wire[`RegBus] ex_lo,
input wire ex_whilo,
//增加的输入接口
input wire[`DoubleRegBus] hilo_i,
input wire[1:0] cnt_i,
//input wire[5:0] stall,
//送到访存阶段的信息
output reg[`RegAddrBus] mem_wd,
output reg mem_wreg,
output reg[`RegBus] mem_wdata,
output reg[`RegBus] mem_hi,
output reg[`RegBus] mem_lo,
output reg mem_whilo ,
//增加的输出端口
output reg[`DoubleRegBus] hilo_o,
output reg[1:0] cnt_o
);
/*在流水线执行阶段暂停的时候,将输入信号hilo_i通过输出接口hilo_o送出,
输入信号cnt_i通过输出接口cnt_o送出。其余时刻,hilo_o为0,cnt_o也为0。*/
always @ (posedge clk) begin
if(rst == `RstEnable) begin
mem_wd <= `NOPRegAddr;
mem_wreg <= `WriteDisable;
mem_wdata <= `ZeroWord;
mem_hi <= `ZeroWord;
mem_lo <= `ZeroWord;
mem_whilo <= `WriteDisable;
hilo_o <= {`ZeroWord,`ZeroWord};
cnt_o <= 2'b00;
end else if(stall[3] == `Stop && stall[4] == `NoStop)begin
mem_wd <= `NOPRegAddr;
mem_wreg <= `WriteDisable;
mem_wdata <= `ZeroWord;
mem_hi <= `ZeroWord;
mem_lo <= `ZeroWord;
mem_whilo <= `WriteDisable;
hilo_o <= hilo_i;
cnt_o <= cnt_i;
end else if(stall[3] == `NoStop)begin
mem_wd <= ex_wd;
mem_wreg <= ex_wreg;
mem_wdata <= ex_wdata;
mem_hi <= ex_hi;
mem_lo <= ex_lo;
mem_whilo <= ex_whilo;
hilo_o <= {`ZeroWord,`ZeroWord};
cnt_o <= 2'b00;
end else begin
hilo_o <= hilo_i;
cnt_o <= cnt_i;
end //if
end //always
endmodule
`include "define.v"
//mem.v 访存
//将输入的执行阶段的结果直接作为输出
module mem(
input wire rst,
input wire clk,
//来自执行阶段的信息
input wire[`RegAddrBus] wd_i,
input wire wreg_i,
input wire[`RegBus] wdata_i,
input wire[`RegBus] hi_i,
input wire[`RegBus] lo_i,
input wire whilo_i,
//访存阶段的结果
output reg[`RegAddrBus] wd_o,
output reg wreg_o,
output reg[`RegBus] wdata_o,
output reg[`RegBus] hi_o,
output reg[`RegBus] lo_o,
output reg whilo_o
);
always @ (*)
begin
if(rst == `RstEnable)
begin
wd_o <= `NOPRegAddr;
wreg_o <= `WriteDisable;
wdata_o <= `ZeroWord;
hi_o <= `ZeroWord;
lo_o <= `ZeroWord;
whilo_o <= `WriteDisable;
end
else
begin
wd_o <= wd_i;
wreg_o <= wreg_i;
wdata_o <= wdata_i;
hi_o <= hi_i;
lo_o <= lo_i;
whilo_o <= whilo_i;
end
end
endmodule
`include "define.v"
//mem_wb.v
//将访存阶段的运算结果在下一个时钟传递到回写阶段
module mem_wb(
input wire clk,
input wire rst,
//访存阶段的结果
input wire[`RegAddrBus] mem_wd,
input wire[`RegBus] mem_wdata,
input wire mem_wreg,
input wire[`RegBus] mem_hi,
input wire[`RegBus] mem_lo,
input wire mem_whilo,
input wire[5:0] stall,
//送到回写阶段信息
output reg[`RegAddrBus] wb_wd,
output reg wb_wreg,
output reg[`RegBus] wb_wdata,
output reg[`RegBus] wb_hi,
output reg[`RegBus] wb_lo,
output reg wb_whilo
);
always @ (posedge clk)
begin
if(rst==`RstEnable)
begin
wb_wd <= `NOPRegAddr;
wb_wreg <= `WriteDisable;
wb_wdata <= `ZeroWord;
wb_hi <= `ZeroWord;
wb_lo <= `ZeroWord;
wb_whilo <= `WriteDisable;
end
else if(stall[4] == `Stop && stall[5] == `NoStop)
begin
wb_wd <= `NOPRegAddr;
wb_wreg <= `WriteDisable;
wb_wdata <= `ZeroWord;
wb_hi <= `ZeroWord;
wb_lo <= `ZeroWord;
wb_whilo <= `WriteDisable;
end else if(stall[4] == `NoStop)begin
wb_wd <= mem_wd;
wb_wreg <= mem_wreg;
wb_wdata <= mem_wdata;
wb_hi <= mem_hi;
wb_lo <= mem_lo;
wb_whilo <= mem_whilo;
end
end
endmodule
//ctrl.v
`include "define.v"
module ctrl(
input wire rst,
input wire stallreq_from_id,
input wire stallreq_from_ex,
output reg[5:0] stall
);
/**************************说明******************************
*****stall[0]表示取指地址PC是否保持不变,为1表示保持不变*****
*******stall[1]表示流水线取指阶段是否暂停,为1表示暂停*******
*******stall[2]表示流水线译码阶段是否暂停,为1表示暂停*******
*******stall[3]表示流水线执行阶段是否暂停,为1表示暂停*******
*******stall[4]表示流水线访存阶段是否暂停,为1表示暂停*******
*******stall[5]表示流水线回写阶段是否暂停,为1表示暂停*******
************************************************************/
always @ (*) begin
if(rst == `RstEnable) begin
stall <= 6'b000000;
end else if(stallreq_from_ex == `Stop) begin
stall <= 6'b001111;
end else if(stallreq_from_id == `Stop) begin
stall <= 6'b000111;
end else begin
stall <= 6'b000000;
end
end
endmodule
`include "define.v"
//hilo_reg.v
module hilo_reg(
input clk,
input rst,
//写端口
input wire we,//写使能端口
input wire[`RegBus] hi_i,
input wire[`RegBus] lo_i,
//读端口
output reg[`RegBus] hi_o,
output reg[`RegBus] lo_o
);
always @ (posedge clk) begin
if(rst == `RstEnable)begin
hi_o <= `ZeroWord;
lo_o <= `ZeroWord;
end else if(we == `WriteEnable)begin
hi_o <= hi_i;
lo_o <= lo_i;
end
end
endmodule
顶层模块 例化部分在每一章增加指令时,先将例化写清楚,再去修改id,ex等
`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;
//连接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;
//连接执行阶段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[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)
);
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),
//送到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)
);
//通用寄存器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),
//传递到执行阶段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模块
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),
//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),
.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)
);
endmodule
`include "define.v"
//openmips_min_sopc.v
module openmips_min_sopc(
input wire clk,
input wire rst
);
//连接指令存储器
wire[`InstAddrBus] inst_addr;
wire[`InstBus] inst;
wire rom_ce;
//例化处理器
openmips openmips0(
.clk(clk), .rst(rst),
.rom_addr_o(inst_addr), .rom_data_i(inst),
.rom_ce_o(rom_ce)
);
//例化指令存储器
inst_rom inst_rom0(
.ce(rom_ce), .inst(inst), .addr(inst_addr)
);
endmodule
/*module openmips_min_sopc(
input wire clk,
input wire rst
);
//连接指令存储器
wire[`InstAddrBus] inst_addr;
wire[`InstBus] inst;
wire rom_ce;
openmips openmips0(
.clk(clk),
.rst(rst),
.rom_addr_o(inst_addr),
.rom_data_i(inst),
.rom_ce_o(rom_ce)
);
inst_rom inst_rom0(
.ce(rom_ce),
.addr(inst_addr),
.inst(inst)
);
endmodule*/
`include "define.v"
//·*//建立TestBench文件
//时间单位是1ns,精度是1ps
`timescale 1ns/1ps
module openmips_min_sopc_tb();
reg CLOCK_50;
reg rst;
//每隔10ns,CLOCK_50信号翻转一次,所以下一个时钟周期是20ns,对应50MHz
initial begin
CLOCK_50 = 1'b0;
forever #10 CLOCK_50 = ~CLOCK_50;
end
//最初时刻,复位信号有效,在第195ns复位信号无效 最小SOPC开始运行
//运行1000ns后,暂停仿真
initial begin
rst = `RstEnable;
#195 rst = `RstDisable;
#1000 $stop;
end
//例化最小SOPC
openmips_min_sopc openmips_min_sopc0(
.clk(CLOCK_50),
.rst(rst)
);
endmodule
/*`timescale 1ns/1ps
module openmips_min_sopc_tb();
reg CLOCK_50;
reg rst;
initial begin
CLOCK_50 = 1'b0;
forever #10 CLOCK_50 = ~CLOCK_50;
end
initial begin
rst = `RstEnable;
#195 rst= `RstDisable;
#1000 $stop;
end
openmips_min_sopc openmips_min_sopc0(
.clk(CLOCK_50),
.rst(rst)
);
endmodule*/
3401ffff
00010c00
3421fffb
34020006
00220018
70220000
70220001
70220004
70220005
.org 0x0
.set noat
.global _start
_start:
ori $1,$0,0xffff
sll $1,$1,16
ori $1,$1,0xfffb # $1 = -5 为寄存器$1赋初值
ori $2,$0,6 # $2 = 6 为寄存器$2赋初值
mult $1,$2 # hi = 0xffffffff
# lo = 0xffffffe2
madd $1,$2 # hi = 0xffffffff
# lo = 0xffffffc4
maddu $1,$2 # hi = 0x5
# lo = 0xffffffa6
msub $1,$2 # hi = 0x5
# lo = 0xffffffc4
msubu $1,$2 # hi = 0xffffffff
# lo = 0xffffffe2