R型的源操作数为两个寄存器
上述区别:加减是否判断溢出、比较分有无符号数
下述比较:找0/1
就是加点判断逻辑,这儿略过
修改EX
`include "defines.v"
module ex(
input wire rst,
//送到执行阶段的信息
input wire[`AluOpBus] aluop_i,
input wire[`AluSelBus] alusel_i,
input wire[`RegBus] reg1_i,
input wire[`RegBus] reg2_i,
input wire[`RegAddrBus] wd_i,
input wire wreg_i,
//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,
//执行结果
output reg[`RegAddrBus] wd_o,
output reg wreg_o,
output reg[`RegBus] wdata_o,
//执行阶段的指令对HI、LO寄存器的写操作请求 通用寄存器赋值给HILO
output reg[`RegBus] hi_o,
output reg[`RegBus] lo_o,
output reg whilo_o
);
//保存逻辑运算的结果
reg[`RegBus] logicout; //保存逻辑运算结果
reg[`RegBus] shiftres; //保存移位运算结果
reg[`RegBus] moveres; //保存移动操作结果
reg[`RegBus] arithmeticres; //保存算术运算的结果
reg[`RegBus] HI; //保存HI最新值
reg[`RegBus] LO; //保存LO最新值
//reg [`DoubleRegBus] hilo_temp1;
//reg stallreq_for_madd_msub;
wire[`RegBus] reg2_i_mux; //保存输入的第二个操作数reg2_i的补码
wire[`RegBus] reg1_i_not; //保存输入的第一个操作数reg1_i取反后的值
wire[`RegBus] result_sum; //保存加法结果
wire ov_sum; //保存溢出情况
wire reg1_eq_reg2; //第一个操作数是否等于第二个操作数
wire reg1_lt_reg2; //第一个操作数是否小于第二个操作数
wire[`RegBus] opdata1_mult; //乘法操作中的被乘数
wire[`RegBus] opdata2_mult; //乘法操作中的乘数
wire[`DoubleRegBus] hilo_temp; //临时保存乘法的结果,宽度为64位
reg[`DoubleRegBus] mulres; //保存乘法结果,宽度为64位
//根据大类型判断输出
always @ (*) begin
wd_o <= wd_i; //传递写目的寄存器地址
//下述指令且溢出 则不写
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
wdata_o <= mulres[31:0];
end
default: begin
wdata_o <= `ZeroWord;
end
endcase
end
//根据小类型作逻辑运算
always @ (*)
if(rst == `RstEnable) begin
logicout <= `ZeroWord;
end
else begin
case (aluop_i)
`EXE_OR_OP: begin
logicout <= reg1_i | reg2_i;
end
`EXE_AND_OP: begin
logicout <= reg1_i & reg2_i;
end
`EXE_NOR_OP: begin
logicout <= ~(reg1_i |reg2_i);
end
`EXE_XOR_OP: begin
logicout <= reg1_i ^ reg2_i;
end
default: begin
logicout <= `ZeroWord;
end
endcase
end
//根据小类型作移位运算
always @ (*)
if(rst == `RstEnable) begin
shiftres <= `ZeroWord;
end
else begin
case (aluop_i)
`EXE_SLL_OP: begin
shiftres <= reg2_i << reg1_i[4:0] ;
end
`EXE_SRL_OP: begin
shiftres <= reg2_i >> reg1_i[4:0];
end
`EXE_SRA_OP: begin //sra和srav都是这个指令
shiftres <= ({32{reg2_i[31]}} << (6'd32-{1'b0, reg1_i[4:0]}))
| reg2_i >> reg1_i[4:0];
end
default: begin
shiftres <= `ZeroWord;
end
endcase
end
//MFHI、MFLO、MOVN、MOVZ指令 移动操作
always @ (*) begin
if(rst == `RstEnable) begin
moveres <= `ZeroWord;
end
else begin
moveres <= `ZeroWord;
case (aluop_i)
`EXE_MFHI_OP: begin //将HI的值作为移动操作的结果
moveres <= HI;
end
`EXE_MFLO_OP: begin //将LO的值作为移动操作的结果
moveres <= LO;
end
`EXE_MOVZ_OP: begin //将reg1_i的值作为移动操作的结果
moveres <= reg1_i;
end
`EXE_MOVN_OP: begin //将reg1_i的值作为移动操作的结果
moveres <= reg1_i;
end
default : begin
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};
end
else if(wb_whilo_i == `WriteEnable) begin
{HI,LO} <= {wb_hi_i,wb_lo_i};
end
else begin
{HI,LO} <= {hi_i,lo_i};
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_MULT_OP) || (aluop_i == `EXE_MULTU_OP)) begin
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;
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
//SUB和SUBU为溢出和不溢出检查的减法
//SLT是符号位的两寄存器比较和立即数寄存器比较
//是上述运算则变为补码,不是则本身
assign reg2_i_mux = ((aluop_i == `EXE_SUB_OP) || (aluop_i == `EXE_SUBU_OP) ||
(aluop_i == `EXE_SLT_OP) ) ? (~reg2_i)+1 : reg2_i;
//加法如下;减法为加补码;有符号数比较加补码就是相减
assign result_sum = reg1_i + reg2_i_mux;
//加减法时的溢出判断
//A.均为正但和为负
//B.均为负但和为正
assign ov_sum = ((!reg1_i[31] && !reg2_i_mux[31]) && result_sum[31]) ||
((reg1_i[31] && reg2_i_mux[31]) && (!result_sum[31]));
//计算操作数1是否小于2,分两种情况
// A.为EXE_SLT_OP有符号比较运算时,分三种
// A1. reg1负 reg2正 ,则1<2
// A2. 1正2正,result_sum为负,则1<2
// A3. 1负2负,result_sum为负,则1<2
// B.无符号时,直接比较
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] && reg2_i[31] && result_sum[31])) : (reg1_i < reg2_i);
//对操作数1逐位取反
assign reg1_i_not = ~reg1_i;
//根据不同的算术运算类型,给变量赋值
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 //找1
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] ? 30 : reg1_i[0] ? 31 : 32 ;
end
`EXE_CLO_OP: begin //找0
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
//乘法运算
//取得乘法操作的被乘数,如果是有符号乘法且被乘数是负数,那么取补码
assign opdata1_mult = (((aluop_i == `EXE_MUL_OP) || (aluop_i == `EXE_MULT_OP))
&& (reg1_i[31] == 1'b1)) ? (~reg1_i + 1) : reg1_i;
//取得乘法操作的乘数,如果是有符号乘法且乘数是负数,那么取补码
assign opdata2_mult = (((aluop_i == `EXE_MUL_OP) || (aluop_i == `EXE_MULT_OP))
&& (reg2_i[31] == 1'b1)) ? (~reg2_i + 1) : reg2_i;
//得到临时乘法结果
assign hilo_temp = opdata1_mult * opdata2_mult;
//修正乘法结果 最终保存到mulres中
// A.有符号乘法指令mult、mul
// A1.如果一正一负,那需要求补码
// A2.同号 则保存过去
// B.如果是无符号乘法multu,保存过去
always @ (*) begin
if(rst == `RstEnable) begin
mulres <= {`ZeroWord,`ZeroWord};
end
else if ((aluop_i == `EXE_MULT_OP) || (aluop_i == `EXE_MUL_OP))begin
if(reg1_i[31] ^ reg2_i[31] == 1'b1) begin
mulres <= ~hilo_temp + 1;
end
else begin
mulres <= hilo_temp;
end
end
else begin
mulres <= hilo_temp;
end
end
endmodule
要进行多周期运算,所以要实现流水线暂停
`include "defines.v"
module ctrl(
input wire rst,
input wire stallreq_from_id,
input wire stallreq_from_ex,
output reg[5:0] stall
);
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
对应修改PC_REG模块
`include "defines.v"
module pc_reg(
input wire clk,
input wire rst,
//来自控制模块的信息
input wire[5:0] stall,
output reg [`InstAddrBus] pc, //InstAddrBus为宏定义 31:0 代表ROM的地址总线宽度 输出给ROM的addr
output reg ce //给到ROM的使能
);
//如果复位,则使能信号ce拉0,否则置1
always @ (posedge clk) begin
if (rst == `RstEnable) begin //复位信号有效 1'b1
ce <= `ChipDisable;
end
else begin
ce <= `ChipEnable;
end
end
//如果使能信号为低,则指令地址拉0;使能有效,则指令地址自加4'h4
always @ (posedge clk) begin
if (ce == `ChipDisable) begin //芯片禁止宏定义 1'b0
pc <= 32'h00000000;
end
else if(stall[0] == `NoStop)begin
pc <= pc + 4'h4; //自加4 字节寻址
end
end
endmodule
修改IF/ID模块
`include "defines.v"
module if_id(
input wire clk,
input wire rst,
input wire [`InstBus] if_inst, //InstBus为ROM数据宽度,同样为31:0
input wire[`InstAddrBus] if_pc , //ROM地址总线宽度 31:0
//来自控制模块的信息
input wire[5:0] stall,
output reg [`InstBus] id_inst,
output reg[`InstAddrBus] id_pc
);
//复位有效 输出为32位的0;否则则寄存传递来的取指指令地址和指令
//1.当stall[1]为stop,stall[2]为nostop,表示取指阶段暂停,译码阶段继续
// 使用空指令作为下一个周期进入译码阶段的指令
//2.当stall[1]为nostop,取指阶段继续,取得的指令进入译码阶段
//3.其余情况,保持不变
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
endmodule
修改ID/EX
`include "defines.v"
module id_ex(
input wire clk,
input wire rst,
//从译码阶段传递的信息
input wire[`AluOpBus] id_aluop, //[7:0]
input wire[`AluSelBus] id_alusel, //[2:0]
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
);
//打一拍传过去
//1.当stall[2]为stop,stall[3]为nostop,表示译码阶段暂停,执行阶段继续
// 使用空指令作为下一个周期进入执行阶段的指令
//2.当stall[2]为nostop,译码阶段继续,译码指令进入执行阶段
//3.其余情况,保持不变
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
end
endmodule
EX后续修改;修改EX/MEM
`include "defines.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, //NEW
input wire[`RegBus] ex_lo, //NEW
input wire ex_whilo, //NEW
//送到访存阶段的信息
output reg[`RegAddrBus] mem_wd,
output reg mem_wreg,
output reg[`RegBus] mem_wdata,
output reg[`RegBus] mem_hi, //NEW
output reg[`RegBus] mem_lo, //NEW
output reg mem_whilo //NEW
);
//1.当stall[3]为stop,stall[4]为nostop,表示执行阶段暂停,访存阶段继续
// 使用空指令作为下一个周期进入访存阶段的指令
//2.当stall[3]为nostop,执行阶段继续,执行后的指令进入访存阶段
//3.其余情况,保持不变
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
end
endmodule
修改MEM/WB
`include "defines.v"
module mem_wb(
input wire clk,
input wire rst,
//来自控制模块的信息
input wire[5:0] stall,
//来自访存阶段的信息
input wire[`RegAddrBus] mem_wd,
input wire mem_wreg,
input wire[`RegBus] mem_wdata,
input wire[`RegBus] mem_hi,
input wire[`RegBus] mem_lo,
input wire mem_whilo,
//送到回写阶段的信息
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
);
//1.当stall[4]为stop,stall[5]为nostop,表示访存阶段暂停,回写阶段继续
// 使用空指令作为下一个周期进入回写阶段的指令
//2.当stall[4]为nostop,访存阶段继续,访存后的指令进入回写阶段
//3.其余情况,保持不变
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
实现思路是重点
修改ID译码模块
`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
修改EX
`include "defines.v"
module ex(
input wire rst,
//送到执行阶段的信息
input wire[`AluOpBus] aluop_i,
input wire[`AluSelBus] alusel_i,
input wire[`RegBus] reg1_i,
input wire[`RegBus] reg2_i,
input wire[`RegAddrBus] wd_i,
input wire wreg_i,
//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, //乘累加累减的周期数
//执行结果
output reg[`RegAddrBus] wd_o,
output reg wreg_o,
output reg[`RegBus] wdata_o,
//执行阶段的指令对HI、LO寄存器的写操作请求 通用寄存器赋值给HILO
output reg[`RegBus] hi_o,
output reg[`RegBus] lo_o,
output reg whilo_o,
output reg[`DoubleRegBus] hilo_temp_o, //乘累加累减的乘结果
output reg[1:0] cnt_o, //乘累加累减的周期数
output reg stallreq //给到CTRL的暂停信号
);
//保存逻辑运算的结果
reg[`RegBus] logicout; //保存逻辑运算结果
reg[`RegBus] shiftres; //保存移位运算结果
reg[`RegBus] moveres; //保存移动操作结果
reg[`RegBus] arithmeticres; //保存算术运算的结果
reg[`RegBus] HI; //保存HI最新值
reg[`RegBus] LO; //保存LO最新值
reg [`DoubleRegBus] hilo_temp1; //乘累加累减第2阶段
reg stallreq_for_madd_msub;
wire[`RegBus] reg2_i_mux; //保存输入的第二个操作数reg2_i的补码
wire[`RegBus] reg1_i_not; //保存输入的第一个操作数reg1_i取反后的值
wire[`RegBus] result_sum; //保存加法结果
wire ov_sum; //保存溢出情况
wire reg1_eq_reg2; //第一个操作数是否等于第二个操作数
wire reg1_lt_reg2; //第一个操作数是否小于第二个操作数
wire[`RegBus] opdata1_mult; //乘法操作中的被乘数
wire[`RegBus] opdata2_mult; //乘法操作中的乘数
wire[`DoubleRegBus] hilo_temp; //临时保存乘法的结果,宽度为64位
reg[`DoubleRegBus] mulres; //保存乘法结果,宽度为64位
//根据大类型判断输出
always @ (*) begin
wd_o <= wd_i; //传递写目的寄存器地址
//下述指令且溢出 则不写
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
wdata_o <= mulres[31:0];
end
default: begin
wdata_o <= `ZeroWord;
end
endcase
end
//根据小类型作逻辑运算
always @ (*)
if(rst == `RstEnable) begin
logicout <= `ZeroWord;
end
else begin
case (aluop_i)
`EXE_OR_OP: begin
logicout <= reg1_i | reg2_i;
end
`EXE_AND_OP: begin
logicout <= reg1_i & reg2_i;
end
`EXE_NOR_OP: begin
logicout <= ~(reg1_i |reg2_i);
end
`EXE_XOR_OP: begin
logicout <= reg1_i ^ reg2_i;
end
default: begin
logicout <= `ZeroWord;
end
endcase
end
//根据小类型作移位运算
always @ (*)
if(rst == `RstEnable) begin
shiftres <= `ZeroWord;
end
else begin
case (aluop_i)
`EXE_SLL_OP: begin
shiftres <= reg2_i << reg1_i[4:0] ;
end
`EXE_SRL_OP: begin
shiftres <= reg2_i >> reg1_i[4:0];
end
`EXE_SRA_OP: begin //sra和srav都是这个指令
shiftres <= ({32{reg2_i[31]}} << (6'd32-{1'b0, reg1_i[4:0]}))
| reg2_i >> reg1_i[4:0];
end
default: begin
shiftres <= `ZeroWord;
end
endcase
end
//MFHI、MFLO、MOVN、MOVZ指令 移动操作
always @ (*) begin
if(rst == `RstEnable) begin
moveres <= `ZeroWord;
end
else begin
moveres <= `ZeroWord;
case (aluop_i)
`EXE_MFHI_OP: begin //将HI的值作为移动操作的结果
moveres <= HI;
end
`EXE_MFLO_OP: begin //将LO的值作为移动操作的结果
moveres <= LO;
end
`EXE_MOVZ_OP: begin //将reg1_i的值作为移动操作的结果
moveres <= reg1_i;
end
`EXE_MOVN_OP: begin //将reg1_i的值作为移动操作的结果
moveres <= reg1_i;
end
default : begin
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};
end
else if(wb_whilo_i == `WriteEnable) begin
{HI,LO} <= {wb_hi_i,wb_lo_i};
end
else begin
{HI,LO} <= {hi_i,lo_i};
end
end
//HILO
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
whilo_o <= `WriteEnable;
hi_o <= mulres[63:32];
lo_o <= mulres[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_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_MTHI_OP) begin
whilo_o <= `WriteEnable;
hi_o <= reg1_i;
lo_o <= 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
//SUB和SUBU为溢出和不溢出检查的减法
//SLT是符号位的两寄存器比较和立即数寄存器比较
//是上述运算则变为补码,不是则本身
assign reg2_i_mux = ((aluop_i == `EXE_SUB_OP) || (aluop_i == `EXE_SUBU_OP) ||
(aluop_i == `EXE_SLT_OP) ) ? (~reg2_i)+1 : reg2_i;
//加法如下;减法为加补码;有符号数比较加补码就是相减
assign result_sum = reg1_i + reg2_i_mux;
//加减法时的溢出判断
//A.均为正但和为负
//B.均为负但和为正
assign ov_sum = ((!reg1_i[31] && !reg2_i_mux[31]) && result_sum[31]) ||
((reg1_i[31] && reg2_i_mux[31]) && (!result_sum[31]));
//计算操作数1是否小于2,分两种情况
// A.为EXE_SLT_OP有符号比较运算时,分三种
// A1. reg1负 reg2正 ,则1<2
// A2. 1正2正,result_sum为负,则1<2
// A3. 1负2负,result_sum为负,则1<2
// B.无符号时,直接比较
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] && reg2_i[31] && result_sum[31])) : (reg1_i < reg2_i);
//对操作数1逐位取反
assign reg1_i_not = ~reg1_i;
//根据不同的算术运算类型,给变量赋值
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 //找1
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] ? 30 : reg1_i[0] ? 31 : 32 ;
end
`EXE_CLO_OP: begin //找0
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
//乘法运算
//取得乘法操作的被乘数,如果是有符号乘法且被乘数是负数,那么取补码
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;
//取得乘法操作的乘数,如果是有符号乘法且乘数是负数,那么取补码
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;
//得到临时乘法结果
assign hilo_temp = opdata1_mult * opdata2_mult;
//修正乘法结果 最终保存到mulres中
// A.有符号乘法指令mult、mul
// A1.如果一正一负,那需要求补码
// A2.同号 则保存过去
// B.如果是无符号乘法multu,保存过去
always @ (*) begin
if(rst == `RstEnable) begin
mulres <= {`ZeroWord,`ZeroWord};
end
else if ((aluop_i == `EXE_MULT_OP) || (aluop_i == `EXE_MUL_OP) ||
(aluop_i == `EXE_MADD_OP) || (aluop_i == `EXE_MSUB_OP))begin
if(reg1_i[31] ^ reg2_i[31] == 1'b1) begin
mulres <= ~hilo_temp + 1;
end
else begin
mulres <= hilo_temp;
end
end
else begin
mulres <= hilo_temp;
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 //执行阶段第1个时钟周期
hilo_temp_o <= mulres;
cnt_o <= 2'b01;
stallreq_for_madd_msub <= `Stop;
hilo_temp1 <= {`ZeroWord,`ZeroWord};
end
else if(cnt_i == 2'b01) begin //执行阶段第2个时钟周期 在下一拍才会回来
hilo_temp_o <= {`ZeroWord,`ZeroWord};
cnt_o <= 2'b10; //设为10而不是00,防止重复进行
hilo_temp1 <= hilo_temp_i + {HI,LO};
stallreq_for_madd_msub <= `NoStop;
end
end
`EXE_MSUB_OP, `EXE_MSUBU_OP: begin
if(cnt_i == 2'b00) begin //执行阶段第1个时钟周期
hilo_temp_o <= ~mulres + 1 ;
cnt_o <= 2'b01;
stallreq_for_madd_msub <= `Stop;
end
else if(cnt_i == 2'b01)begin //执行阶段第2个时钟周期
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
//暂停流水线
//目前如此
always @ (*) begin
stallreq = stallreq_for_madd_msub;
end
endmodule
EX/MEM见上一部分,已修改完毕
除法器已流水线实现过
7-16的图是重点
`include "defines.v"
module div(
input wire clk,
input wire rst,
input wire signed_div_i, //是否是有符号除法
input wire[31:0] opdata1_i, //被除数
input wire[31:0] opdata2_i, //除法
input wire start_i, //是否开始除法运算
input wire annul_i, //是否取消除法运算
output reg[63:0] result_o, //除法运算结果
output reg ready_o //除法运算是否结束
);
wire[32:0] div_temp;
reg [5:0] cnt; //记录试商法记录了几轮,32时表示结束
reg [64:0] dividend;
reg [1:0] state;
reg [31:0] divisor;
reg [31:0] temp_op1;
reg [31:0] temp_op2;
//dividend的低32位保存被除数、中间结果,第k次迭代结束的时候dividend[k:0]
//保存的就是当前中间结果,dividend[31:k+1]保存的是被除数中还没有参与运算
//的数据,dividend高32位是每次迭代的被减数,minuend-n运算结果保存在div_temp
assign div_temp = {1'b0,dividend[63:32]} - {1'b0,divisor};
always @ (posedge clk) begin
if (rst == `RstEnable) begin
state <= `DivFree;
ready_o <= `DivResultNotReady;
result_o <= {`ZeroWord,`ZeroWord};
end
else begin
case (state)
//分三种情况
//1.开始除法运算,但除数为0,进入DivByZero
//2.开始除法运算,且除数不为0,进入DivOn,初始化cnt为0,如果是有符号除法且被除数或除数为负
// 则先取补码。除数保存到divisor中,被除数的最高位保存到dividend第32位,进行第一次迭代
//3.没有开始除法运算,保持ready_o为DivResultNotReady和result_o为0
`DivFree: begin
if(start_i == `DivStart && annul_i == 1'b0) begin
if(opdata2_i == `ZeroWord) begin //除数为0
state <= `DivByZero;
end
else begin
state <= `DivOn;
cnt <= 6'b000000;
if(signed_div_i == 1'b1 && opdata1_i[31] == 1'b1 ) begin //有符号数 先变为正
temp_op1 = ~opdata1_i + 1;
end
else begin
temp_op1 = opdata1_i;
end
if(signed_div_i == 1'b1 && opdata2_i[31] == 1'b1 ) begin
temp_op2 = ~opdata2_i + 1;
end
else begin
temp_op2 = opdata2_i;
end
dividend <= {`ZeroWord,`ZeroWord};
dividend[32:1] <= temp_op1; //首先把被除数给到低位
divisor <= temp_op2; //除数
end
end
else begin
ready_o <= `DivResultNotReady;
result_o <= {`ZeroWord,`ZeroWord};
end
end
//
`DivByZero: begin //DivByZero状态
dividend <= {`ZeroWord,`ZeroWord};
state <= `DivEnd;
end
//DivOn
//分三种情况
//1.如果输入信号annul_i为1,表示处理器取消除法运算,DIV直接回到DivFree
//2.如果annul_i为0且cnt不为32,表示试商法还没结束,此时如果减法结果div_temp
// 为负,则此次迭代结果为0,如果为正,则迭代结果为1,dividend最低位保存每次
// 的迭代结果,同时保持DivOn,且cnt+1
//3.cnt32表示结束,如果为有符号除法,且一正一负,则结果取补码,商保存在dividend
// 低32位,余数在高32位
`DivOn: begin //DivOn状态
if(annul_i == 1'b0) begin
if(cnt != 6'b100000) begin
//如果div_temp[32]为1,表示结果为负
//将dividend左移一位,将被除数中还没有参与运算的最高位
//加入到下一次迭代的被减数中,同时将0追加到中间结果
if(div_temp[32] == 1'b1) begin
dividend <= {dividend[63:0] , 1'b0};
end
else begin
//如果为0,将减法的结果与被除数还没有参与运算的最高位
//加入到下一次迭代的被减数中,同时将1追加到中间结果
dividend <= {div_temp[31:0] , dividend[31:0] , 1'b1};
end
cnt <= cnt + 1;
end
else begin //试商法结束
if((signed_div_i == 1'b1) && ((opdata1_i[31] ^ opdata2_i[31]) == 1'b1)) begin
dividend[31:0] <= (~dividend[31:0] + 1); //商求补码
end
if((signed_div_i == 1'b1) && ((opdata1_i[31] ^ dividend[64]) == 1'b1)) begin
dividend[64:33] <= (~dividend[64:33] + 1);//余数求补码
end
state <= `DivEnd;
cnt <= 6'b000000;
end
end
else begin
state <= `DivFree;
end
end
//设置输出信号ready_o,等待EX送来divStop信号,送来后回到divfree
`DivEnd: begin //DivEnd状态
result_o <= {dividend[64:33], dividend[31:0]};
ready_o <= `DivResultReady;
if(start_i == `DivStop) begin
state <= `DivFree;
ready_o <= `DivResultNotReady;
result_o <= {`ZeroWord,`ZeroWord};
end
end
endcase
end
end
endmodule
修改ID
修改EX