协处理器介绍
MIPS32架构定义的协处理器及其作用
协处理器 | 作用 |
---|---|
CP0 | 系统控制 |
CP1 | FPU(浮点数处理单元) |
CP2 | 特定实现 |
CP3 | FPU(浮点数处理单元) |
CP0负责的主要工作
- 配置CPU工作状态:通过读写一些个内部寄存器来改变一些很根本的CPU特性
- 高速缓存控制:集成缓存控制器,来控制、读、写缓存
- 异常控制:异常发生时的检测和处理都由CP0中的一些控制寄存器来定义和控制
- 存储管理单元控制:对系统的存储区域进行合理的控制、管理和分配,主要是对MMU,TLB的一些配置、管理、访问
- 其他:当要把额外功能集成在CPU中,但又不方便当作外设访问时,常常在CP0中增加一些模块来实现这些功能。(例如:时钟、时间计数器、奇偶校验错误检测等)
CP0中寄存器描述
重要寄存器
Count寄存器
不停计数的32位寄存器,计数频率一般与CPU时钟频率相同,当计数达到32位无符号数上限时,从0开始计数。Count寄存器可读可写
Count寄存器的字段
Bit | 31-0 |
---|---|
标志名 | Count |
Compare寄存器
32位寄存器,与Count寄存器一起完成定时中断的功能。当Count寄存器中的值和Compare寄存器中的值一样时,会产生定时中断,这个中断会一直保持,直到有数据被写入Compare寄存器
,Compare寄存器可读可写
Compare寄存器的字段
Bit | 31-0 |
---|---|
标志名 | Compare |
Status寄存器
32位可读可写寄存器,用来控制处理器的操作模式、中断使能以及诊断状态
Status寄存器的各个字段
Bit | 31-28 | 27 | 26 | 25 | 24-23 | 22 | 21 | 20 | 19 |
---|---|---|---|---|---|---|---|---|---|
标志名 | CU3-CU0 | RP | R | RE | 0 | BEV | TS | SR | NMI |
Bit | 18-16 | 15-8 | 7-5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
标志名 | 0 | IM7-IM0 | R | UM | R | ERL | EXL | IE |
标识为R是保留字段
- CU3-CU0表示协处理器是否可用,分别控制协处理器CP3,CP2,CP1,CP0。为0时,表示相应的协处理器不可用,为1时,表示相应的协处理器可用。此处只有协处理器CP0,所以设置本字段为
4'b0001
- SR表示是否软重启,为1表示重启异常是有软重启引起的
- NMI表示是否不可屏蔽中断,为1表示重启异常是由不可屏蔽中断引起的
- IM7-IM0表示是否屏蔽相应中断,0表示屏蔽,1表示不屏蔽,MIPS处理器有8个中断源,对应IM字段的8位,其中6个中断源是处理器外部硬件中断,另外2个是软件中断,中断是都能被处理器响应是由Status寄存器和Cause寄存器共同决定的,如果Status寄存器的IM字段与Cause寄存器的IP字段的相应位都为1,而且Status寄存器的IE字段也为1时,处理器才相应中断
- EXL表示手处于异常级,当异常发生时,会设置本字段为1,表示处理器处于异常级,此时,处理器会进入内核模式下工作,并且禁止中断
- IE表示是否使能中断,这是全局中断使能标志位,为1表示中断使能,为0表示中断禁止
Cause寄存器
EPC寄存器和PRId寄存器
Config寄存器
CP0的实现
`include "define.v"
module cp0_reg(
input wire clk,
input rst,
input wire we_i,//是否要写CP0中的寄存器
input wire[4:0] waddr_i,//要写的CP0中的寄存器地址
input wire[4:0] raddr_i,//要读取的CP0中寄存器的地址
input wire[`RegBus] wdata_i,//要写入CP0中寄存器的数据
input wire[5:0] int_i,//6个外部硬件中断输入
output reg[`RegBus] data_o,//读出CP0中某个寄存器的值
output reg[`RegBus] count_o,//Count寄存器的值
output reg[`RegBus] compare_o,//Compare寄存器的值
output reg[`RegBus] status_o,//Status寄存器的值
output reg[`RegBus] cause_o,//Cause寄存器的值
output reg[`RegBus] epc_o,//EPC寄存器的值
output reg[`RegBus] config_o,//Config寄存器的值
output reg[`RegBus] prid_o,//PRId寄存器的值
output reg timer_int_o//是否有定时中断产生
);
//第一段:对CP0中寄存器的写操作
always @(posedge clk)begin
if(rst == `RstEnable)begin
count_o <= `ZeroWord;//Count寄存器的初始值为0
compare_o <= `ZeroWord;//Compare寄存器的初始值为0
status_o <= 32'b0001_0000_0000_0000_0000_0000_0000_0000;//CU字段为0001表示协处理器CP0存在
cause_o <= `ZeroWord;//Cause寄存器的初始值为0
epc_o <= `ZeroWord;
config_o <= 32'b0000_0000_0000_0000_1000_0000_0000_0000;//BE字段为1 表示工作在大端模式
prid_o <= 32'b0000_0000_0100_1100_0000_0001_0000_0010;//制作者L 对应0x48 类型0x1 表示是基本类型 版本号1.0
timer_int_o <= `InterrputNotAssert;
end else begin
count_o <= count_o+1;//Count寄存器的值在每个时钟周期都加1
cause_o[15:0] <= int_i;//Cause的第10到15bit保存外部中断声明
//当Compare不为0且Count寄存器的值等于Compare寄存器的值时,将输出信号timer_int_o置为1 表示时钟中断发生
if(compare_o != `ZeroWord && count_o == compare_o) begin
timer_int_o <= `InterrputNotAssert;
end
if(we_i == `WriteEnable)begin
case(waddr_i)
`CP0_REG_COUNT:begin
count_o <= data_i;//写Count寄存器
end
`CP0_REG_COMPARE:begin
compare_o <= data_i;//写Compare寄存器
end
`CP0_REG_STATUS:begin
status_o <= data_i;
end
`CP0_REG_EPC:begin
epc_o <= data_i;
end
`CP0_REG_CAUSE:begin
//Cause寄存器只有IP[1:0] IV WP字段是可写的
cause_o[9:8] <= data_i[9:8];
case_o[23] <= data_i[23];
case_o[22] <= data_i[22];
end
endcase
end
end
end
//第二段:对CP0中寄存器的读操作
always @ (*) begin
if(rst == `RstEnable)begin
data_o <= `ZeroWord;
end else begin
case(raddr_i)
`CP0_REG_COUNT:begin
data_o <= count_o;
end
`CP0_REG_COMPARE:begin
data_o <= compare_o;
end
`CP0_REG_STATUS:begin
data_o <= status_o;
end
`CP0_REG_CAUSE:begin
data_o <= cause_o;
end
`CP0_REG_EPC:begin
data_o <= epc_o;
end
`CP0_REG_PRId:begin
data_o <= prid_o;
end
`CP0_REG_CONFIG:begin
data_o <= config_o;
end
default:begin
end
endcase
end
end
endmodule
CP0访问指令说明
31-26 | 25-21 | 20-16 | 15-11 | 10-3 | 2-0 | useage | function |
---|---|---|---|---|---|---|---|
COP0(010000) | MT(00100) | rt | rd | 00000000 | sel | mtc0 rt,rd | CPR[0,rd]<-GPR[rt]将地址为rt的通用寄存器的值 赋给协处理器CP0中地址为rd的寄存器 |
COP0(010000) | MF(00000) | rt | rd | 00000000 | sel | mfc0 rt,rd | GPR[rt]<-CPR[0,rd]读出协处理器CP0中地址为rd的通用寄存器 ,并赋值给地址为rt的通用寄存器 |
实现思路
mtc0
- 在
译码阶段
依据指令,读出地址为rt的通用寄存器
- 在
执行阶段
确定要写入CP0寄存器中的值
,即译码阶段读出的地址为rt的通用寄存器的值
,将这些信息传递到访存阶段
访存阶段
再将这些信息传递到回写阶段
回写阶段
依据这些信息修改CP0中地址为rd的寄存器
mfc0
- 在
执行阶段
获取CP0中指定寄存器的值
,作为要写入的通用寄存器的数据
,并将这些信息传递到访存阶段
访存阶段
再将这些信息传递到回写阶段
回写阶段
依据这些信息修改地址为rt的通用寄存器
数据流图的修改
在回写阶段增加了CP0模块,并在CP0模块的输出数据传递到执行阶段,用于确定最后参与运算的操作数,如:mfc0指令在执行阶段就会选择从CP0传递来的数据,作为运算结果,写入目的寄存器
代码的修改
我不想写代码了,我只想把这个流程记住并理解。。。。
id.v
之前id模块diamante写错了,之后有空具体写一期如何改bug。
`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[`AluOpBus] ex_aluop_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,//当前处于译码指令是否位于延迟槽
output wire[`RegBus] inst_o //新增加的输出接口
//input wire[`AluOpBus] ex_aluop_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];
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;
assign inst_o = inst_i;//译码阶段的指令
//新定义一个变量 表示要读取的寄存器1是否与上一条指令存在load相关
reg stallreq_for_reg1_loadrelate;
//新定义一个变量 表示要读取的寄存器2是否与上一条指令存在load相关
reg stallreq_for_reg2_loadrelate;
//新定义一个变量 表示上一条指令是否是加载指令
wire pre_inst_is_load;
//依据输入信号ex_aluop_i值 判断上一条指令是否是加载指令
//如果是加载指令 那么置pre_inst_is_load为1 反之置0
assign pre_inst_is_load = ((ex_aluop_i == `EXE_LB_OP)||
(ex_aluop_i == `EXE_LBU_OP)||
(ex_aluop_i == `EXE_LH_OP)||
(ex_aluop_i==`EXE_LHU)||
(ex_aluop_i==`EXE_LW_OP)||
(ex_aluop_i==`EXE_LWR_OP)||
(ex_aluop_i==`EXE_LWL_OP)||
(ex_aluop_i==`EXE_LL_OP)||
(ex_aluop_i==`EXE_SC_OP)) ? 1'b1 : 1'b0;
/*如果上一条指令是加载指令 且该加载指令要加载到目的寄存器就是当前指令
要通过Regfile模块读取端口1读取的通用寄存器,那么表示存在load相关*/
//设置stallreq_for_reg1_loadrelate为Stop
always @(*)begin
stallreq_for_reg1_loadrelate <= `NoStop;
if(rst == `RstEnable)begin
reg1_o <= `ZeroWord;
end else if(pre_inst_is_load == 1'b1 && ex_wd_i == reg1_addr_o && reg1_read_o == 1'b1)begin
stallreq_for_reg1_loadrelate <= `Stop;//存在load相关 Stop
end
end
//reg2与reg1同理
always @(*)begin
stallreq_for_reg2_loadrelate <= `NoStop;
if(rst == `RstEnable)begin
reg2_o <= `ZeroWord;
end else if(pre_inst_is_load == 1'b1 && ex_wd_i == reg2_addr_o && reg2_read_o == 1'b1)begin
stallreq_for_reg2_loadrelate <= `Stop;//存在load相关 Stop
end
end
assign stallreq = stallreq_for_reg1_loadrelate | stallreq_for_reg2_loadrelate;
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_LB:begin//将加载结果写入目的寄存器
wreg_o <= `WriteEnable;
aluop_o <= `EXE_LB_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= 1'b1;//计算加载目标地址需要使用地址为base的寄存器值
reg2_read_o <= 1'b0;
wd_o <= inst_i[20:16];//目的寄存器地址
instvalid <= `InstValid;
end
`EXE_LBU:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_LBU_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_LH:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_LH_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_LHU:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_LHU_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_LW:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_LW_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_LWL:begin//向左加载 加载结果需要写入目的寄存器 [20:16]
wreg_o <= `WriteEnable;
aluop_o <= `EXE_LWL_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_LWR:begin//向右加载
wreg_o <= `WriteEnable;
aluop_o <= `EXE_LWR_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_SB:begin //不需要写通用寄存器 计算存储目标地址需要使用的地址为base的寄存器的值
wreg_o <= `WriteDisable;
aluop_o <= `EXE_SB_OP;
reg1_read_o <= 1'b1; //[25:21] reg1_addr_o ======> base
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
alusel_o <= `EXE_RES_LOAD_STORE;
end
`EXE_SH:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_SH_OP;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
alusel_o <= `EXE_RES_LOAD_STORE;
end
`EXE_SW:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_SW_OP;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
alusel_o <= `EXE_RES_LOAD_STORE;
end
`EXE_SWL:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_SWL_OP;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
alusel_o <= `EXE_RES_LOAD_STORE;
end
`EXE_SWR:begin
wreg_o <= `WriteDisable;
aluop_o <= `EXE_SWR_OP;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
alusel_o <= `EXE_RES_LOAD_STORE;
end
`EXE_LL:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_LL_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_SC:begin
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SC_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
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
end
//endcase
//end
// end else begin //if
if(inst_i[31:21]==11'b010_0000_0000&&inst_i[10:0]==11'b000_0000_0000)//mfc0指令
begin
aluop_o <= `EXE_MFC0_OP;
alusel_o <= `EXE_RES_MOVE;//一种移动运算
wd_o <= inst_i[20:16];//rt 要写的目的寄存器是指令中rt的值
wreg_o <= `WriteEnable;//需要读取CP0中的寄存器的值写入目的寄存器,要写通用寄存器
instvalid <= `InstValid;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
end else if(inst_i[31:21]==11'b010_0000_0100&&inst_i[10:0]==11'b00000000000)//mtc0指令
begin
aluop_o <= `EXE_MTC0_OP;
alusel_o <= `EXE_RES_MOVE;
wreg_o <= `WriteDisable;//不需要写通用寄存器
instvalid <= `InstValid;
reg1_read_o <= 1'b1;//需要读取通用寄存器 通过Regfile1端口读取数据
reg1_addr_o <= inst_i[20:16];//读取地址是指令中16-20位 是rt的值
reg2_read_o <= 1'b0;
end
end //if
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
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,
//新增输入端口inst_i,其值就是当前处于执行阶段的指令
input wire[`RegBus] inst_i,//当前处于执行阶段的指令
//访存阶段的指令是否要写CP0中的寄存器 用来检测数据相关
input wire mem_cp0_reg_we,
input wire[4:0] mem_cp0_reg_write_addr,
input wire[`RegBus] mem_cp0_reg_data,
//回写阶段的指令是否要写CP0中的寄存器 也是用来检测数据相关
input wire wb_cp0_reg_we,
input wire[4:0] wb_cp0_reg_write_addr,
input wire[`RegBus] wb_cp0_reg_data,
//与CP0直接相连 用于读取其中指定寄存器的值
input wire[`RegBus] cp0_reg_data_i,
output reg[4:0] cp0_reg_read_addr_o,
//向流水线下一级传递 用于写CP0的指定寄存器的值
output reg cp0_reg_we_o,
output reg[4:0] cp0_reg_write_addr_o,
output reg[`RegBus] cp0_reg_data_o,
//处于执行阶段指令对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,
//下面新增的几个输出接口是为 加载、 存储指令准备的
output wire[`AluOpBus] aluop_o, //执行阶段要进行运算的子类型
output wire[`RegBus] mem_addr_o,//加载存储指令对应的存储器地址
output wire[`RegBus] reg2_o//存储指令要存储的数据,或者lwl,lwr指令要加载到的目的寄存器的地址
);
//保存逻辑运算的结果
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_o会传递到访存阶段,届时将利用其确定加载、存储类型
assign aluop_o = aluop_i;
//mem_addr_o会传递到访存阶段,是加载、存储指令对应的存储器地址,此处reg1_i就是加载、存储指令中地址为base的通用寄存器的值
//通过计算mem_addr_o,了解为何要在译码阶段ID模块新增输出接口inst_o
assign mem_addr_o = reg1_i + {{16{inst_i[15]}},inst_i[15:0]};
//reg2_i是存储指令要存储的数据,或者是lwl,lwr指令要加载到的目的寄存器的原始值,该值通过reg2_o接口传递到访存阶段
assign reg2_o = reg2_i;
/*******************************************************************
** 第一段:依据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
`EXE_MFC0_OP:begin
//要从CP0中读取寄存器的地址
cp0_reg_read_addr_o <= inst_i[15:11];//通过cp0_reg_read_addr_o向CP0模块送出要读取的CP0中寄存器地址
//读取到的CP0中指定寄存器的值
moveres <= cp0_reg_data_i;//通过cp0_reg_data_i接口送入ex 赋值给变量moveres
//判断是否存在数据相关 此时moveres并不一定是CP0中寄存器的最新值
if(mem_cp0_reg_we == `WriteEnable && mem_cp0_reg_write_addr == inst_i[15:11])
begin
moveres <= mem_cp0_reg_data;//与访存阶段存在数据相关
end else if(wb_cp0_reg_we == `WriteEnable && wb_cp0_reg_write_addr == inst_i[15:11])
begin
moveres <= wb_cp0_reg_data;//与回写阶段存在数据相关
end
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
/*********************************************************************
******************第十三段:给出mtc0指令执行的结果*********************
*********************************************************************/
always @ (*) begin
if(rst == `RstEnable)begin
cp0_reg_write_addr_o <= 5'b00000;
cp0_reg_we_o <= `WriteDisable;
cp0_reg_data_o <= `ZeroWord;
end else if(aluop_i == `EXE_MTC0_OP)begin//如果是mtc0指令
cp0_reg_write_addr_o <= inst_i[15:11];//写入地址为指令中第11-15为的值
cp0_reg_we_o <= `WriteEnable;//设置写操作信号cp0_reg_we_o为可写
cp0_reg_data_o <= reg1_i;//写入的值就是译码阶段传递来的reg1_i的值 正是rt通用寄存器的值
end else begin
cp0_reg_write_addr_o <= 5'b00000;
cp0_reg_we_o <= `WriteDisable;
cp0_reg_data_o <= `ZeroWord;
end
end
endmodule
ex_mem.v
`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[`AluOpBus] ex_aluop,
input wire[`RegBus] ex_mem_addr,
input wire[`RegBus] ex_reg2,
input wire[`DoubleRegBus] hilo_i,
input wire[1:0] cnt_i,
input wire ex_cp0_reg_we,
input wire[4:0] ex_cp0_reg_write_addr,
input wire[`RegBus] ex_cp0_reg_data,
//送到访存阶段的信息
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[`AluOpBus] mem_aluop,
output reg[`RegBus] mem_mem_addr,
output reg[`RegBus] mem_reg2,
output reg mem_cp0_reg_we,
output reg[4:0] mem_cp0_reg_write_addr,
output reg[`RegBus] mem_cp0_reg_data,
output reg[`DoubleRegBus] hilo_o,
output reg[1:0] cnt_o
);
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;
mem_aluop <= `EXE_NOP_OP;
mem_mem_addr <= `ZeroWord;
mem_reg2 <= `ZeroWord;
mem_cp0_reg_we <= `WriteDisable;
mem_cp0_reg_write_addr <= 5'b00000;
mem_cp0_reg_data <= `ZeroWord;
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;
mem_aluop <= `EXE_NOP_OP;
mem_mem_addr <= `ZeroWord;
mem_reg2 <= `ZeroWord;
mem_cp0_reg_we <= `WriteDisable;
mem_cp0_reg_write_addr <= 5'b00000;
mem_cp0_reg_data <= `ZeroWord;
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;
mem_aluop <= ex_aluop;
mem_mem_addr <= ex_mem_addr;
mem_reg2 <= ex_reg2;
mem_cp0_reg_we <= ex_cp0_reg_we;
mem_cp0_reg_write_addr <= ex_cp0_reg_write_addr;
mem_cp0_reg_data <= ex_cp0_reg_data;
end else begin
hilo_o <= hilo_i;
cnt_o <= cnt_i;
end //if
end //always
endmodule
mem.v
`include "define.v"
module mem(
input wire rst,
//来自执行阶段的信息
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,
input wire[`AluOpBus] aluop_i,
input wire[`RegBus] mem_addr_i,
input wire[`RegBus] reg2_i,
//来自memory的信息
input wire[`RegBus] mem_data_i,
//LLbit_i是LLbit寄存器的值
input wire LLbit_i,
//但不一定是最新值,回写阶段可能要写LLbit,所以还要进一步判断
input wire wb_LLbit_we_i,
input wire wb_LLbit_value_i,
//协处理器CP0的写信号
input wire cp0_reg_we_i,
input wire[4:0] cp0_reg_write_addr_i,
input wire[`RegBus] cp0_reg_data_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,
output reg LLbit_we_o,
output reg LLbit_value_o,
output reg cp0_reg_we_o,
output reg[4:0] cp0_reg_write_addr_o,
output reg[`RegBus] cp0_reg_data_o,
//送到memory的信息
output reg[`RegBus] mem_addr_o,
output wire mem_we_o,
output reg[3:0] mem_sel_o,
output reg[`RegBus] mem_data_o,
output reg mem_ce_o
);
reg LLbit;
wire[`RegBus] zero32;
reg mem_we;
assign mem_we_o = mem_we ;
assign zero32 = `ZeroWord;
//获取最新的LLbit的值
always @ (*) begin
if(rst == `RstEnable) begin
LLbit <= 1'b0;
end else begin
if(wb_LLbit_we_i == 1'b1) begin
LLbit <= wb_LLbit_value_i;
end else begin
LLbit <= LLbit_i;
end
end
end
always @ (*) begin
if(rst == `RstEnable) begin
wd_o <= `NOPRegAddr;
wreg_o <= `WriteDisable;
wdata_o <= `ZeroWord;
hi_o <= `ZeroWord;
lo_o <= `ZeroWord;
whilo_o <= `WriteDisable;
mem_addr_o <= `ZeroWord;
mem_we <= `WriteDisable;
mem_sel_o <= 4'b0000;
mem_data_o <= `ZeroWord;
mem_ce_o <= `ChipDisable;
LLbit_we_o <= 1'b0;
LLbit_value_o <= 1'b0;
cp0_reg_we_o <= `WriteDisable;
cp0_reg_write_addr_o <= 5'b00000;
cp0_reg_data_o <= `ZeroWord;
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;
mem_we <= `WriteDisable;
mem_addr_o <= `ZeroWord;
mem_sel_o <= 4'b1111;
mem_ce_o <= `ChipDisable;
LLbit_we_o <= 1'b0;
LLbit_value_o <= 1'b0;
cp0_reg_we_o <= cp0_reg_we_i;
cp0_reg_write_addr_o <= cp0_reg_write_addr_i;
cp0_reg_data_o <= cp0_reg_data_i;
case (aluop_i)
`EXE_LB_OP: begin
mem_addr_o <= mem_addr_i;
mem_we <= `WriteDisable;
mem_ce_o <= `ChipEnable;
case (mem_addr_i[1:0])
2'b00: begin
wdata_o <= {{24{mem_data_i[31]}},mem_data_i[31:24]};
mem_sel_o <= 4'b1000;
end
2'b01: begin
wdata_o <= {{24{mem_data_i[23]}},mem_data_i[23:16]};
mem_sel_o <= 4'b0100;
end
2'b10: begin
wdata_o <= {{24{mem_data_i[15]}},mem_data_i[15:8]};
mem_sel_o <= 4'b0010;
end
2'b11: begin
wdata_o <= {{24{mem_data_i[7]}},mem_data_i[7:0]};
mem_sel_o <= 4'b0001;
end
default: begin
wdata_o <= `ZeroWord;
end
endcase
end
`EXE_LBU_OP: begin
mem_addr_o <= mem_addr_i;
mem_we <= `WriteDisable;
mem_ce_o <= `ChipEnable;
case (mem_addr_i[1:0])
2'b00: begin
wdata_o <= {{24{1'b0}},mem_data_i[31:24]};
mem_sel_o <= 4'b1000;
end
2'b01: begin
wdata_o <= {{24{1'b0}},mem_data_i[23:16]};
mem_sel_o <= 4'b0100;
end
2'b10: begin
wdata_o <= {{24{1'b0}},mem_data_i[15:8]};
mem_sel_o <= 4'b0010;
end
2'b11: begin
wdata_o <= {{24{1'b0}},mem_data_i[7:0]};
mem_sel_o <= 4'b0001;
end
default: begin
wdata_o <= `ZeroWord;
end
endcase
end
`EXE_LH_OP: begin
mem_addr_o <= mem_addr_i;
mem_we <= `WriteDisable;
mem_ce_o <= `ChipEnable;
case (mem_addr_i[1:0])
2'b00: begin
wdata_o <= {{16{mem_data_i[31]}},mem_data_i[31:16]};
mem_sel_o <= 4'b1100;
end
2'b10: begin
wdata_o <= {{16{mem_data_i[15]}},mem_data_i[15:0]};
mem_sel_o <= 4'b0011;
end
default: begin
wdata_o <= `ZeroWord;
end
endcase
end
`EXE_LHU_OP: begin
mem_addr_o <= mem_addr_i;
mem_we <= `WriteDisable;
mem_ce_o <= `ChipEnable;
case (mem_addr_i[1:0])
2'b00: begin
wdata_o <= {{16{1'b0}},mem_data_i[31:16]};
mem_sel_o <= 4'b1100;
end
2'b10: begin
wdata_o <= {{16{1'b0}},mem_data_i[15:0]};
mem_sel_o <= 4'b0011;
end
default: begin
wdata_o <= `ZeroWord;
end
endcase
end
`EXE_LW_OP: begin
mem_addr_o <= mem_addr_i;
mem_we <= `WriteDisable;
wdata_o <= mem_data_i;
mem_sel_o <= 4'b1111;
mem_ce_o <= `ChipEnable;
end
`EXE_LWL_OP: begin
mem_addr_o <= {mem_addr_i[31:2], 2'b00};
mem_we <= `WriteDisable;
mem_sel_o <= 4'b1111;
mem_ce_o <= `ChipEnable;
case (mem_addr_i[1:0])
2'b00: begin
wdata_o <= mem_data_i[31:0];
end
2'b01: begin
wdata_o <= {mem_data_i[23:0],reg2_i[7:0]};
end
2'b10: begin
wdata_o <= {mem_data_i[15:0],reg2_i[15:0]};
end
2'b11: begin
wdata_o <= {mem_data_i[7:0],reg2_i[23:0]};
end
default: begin
wdata_o <= `ZeroWord;
end
endcase
end
`EXE_LWR_OP: begin
mem_addr_o <= {mem_addr_i[31:2], 2'b00};
mem_we <= `WriteDisable;
mem_sel_o <= 4'b1111;
mem_ce_o <= `ChipEnable;
case (mem_addr_i[1:0])
2'b00: begin
wdata_o <= {reg2_i[31:8],mem_data_i[31:24]};
end
2'b01: begin
wdata_o <= {reg2_i[31:16],mem_data_i[31:16]};
end
2'b10: begin
wdata_o <= {reg2_i[31:24],mem_data_i[31:8]};
end
2'b11: begin
wdata_o <= mem_data_i;
end
default: begin
wdata_o <= `ZeroWord;
end
endcase
end
`EXE_LL_OP: begin
mem_addr_o <= mem_addr_i;
mem_we <= `WriteDisable;
wdata_o <= mem_data_i;
LLbit_we_o <= 1'b1;
LLbit_value_o <= 1'b1;
mem_sel_o <= 4'b1111;
mem_ce_o <= `ChipEnable;
end
`EXE_SB_OP: begin
mem_addr_o <= mem_addr_i;
mem_we <= `WriteEnable;
mem_data_o <= {reg2_i[7:0],reg2_i[7:0],reg2_i[7:0],reg2_i[7:0]};
mem_ce_o <= `ChipEnable;
case (mem_addr_i[1:0])
2'b00: begin
mem_sel_o <= 4'b1000;
end
2'b01: begin
mem_sel_o <= 4'b0100;
end
2'b10: begin
mem_sel_o <= 4'b0010;
end
2'b11: begin
mem_sel_o <= 4'b0001;
end
default: begin
mem_sel_o <= 4'b0000;
end
endcase
end
`EXE_SH_OP: begin
mem_addr_o <= mem_addr_i;
mem_we <= `WriteEnable;
mem_data_o <= {reg2_i[15:0],reg2_i[15:0]};
mem_ce_o <= `ChipEnable;
case (mem_addr_i[1:0])
2'b00: begin
mem_sel_o <= 4'b1100;
end
2'b10: begin
mem_sel_o <= 4'b0011;
end
default: begin
mem_sel_o <= 4'b0000;
end
endcase
end
`EXE_SW_OP: begin
mem_addr_o <= mem_addr_i;
mem_we <= `WriteEnable;
mem_data_o <= reg2_i;
mem_sel_o <= 4'b1111;
mem_ce_o <= `ChipEnable;
end
`EXE_SWL_OP: begin
mem_addr_o <= {mem_addr_i[31:2], 2'b00};
mem_we <= `WriteEnable;
mem_ce_o <= `ChipEnable;
case (mem_addr_i[1:0])
2'b00: begin
mem_sel_o <= 4'b1111;
mem_data_o <= reg2_i;
end
2'b01: begin
mem_sel_o <= 4'b0111;
mem_data_o <= {zero32[7:0],reg2_i[31:8]};
end
2'b10: begin
mem_sel_o <= 4'b0011;
mem_data_o <= {zero32[15:0],reg2_i[31:16]};
end
2'b11: begin
mem_sel_o <= 4'b0001;
mem_data_o <= {zero32[23:0],reg2_i[31:24]};
end
default: begin
mem_sel_o <= 4'b0000;
end
endcase
end
`EXE_SWR_OP: begin
mem_addr_o <= {mem_addr_i[31:2], 2'b00};
mem_we <= `WriteEnable;
mem_ce_o <= `ChipEnable;
case (mem_addr_i[1:0])
2'b00: begin
mem_sel_o <= 4'b1000;
mem_data_o <= {reg2_i[7:0],zero32[23:0]};
end
2'b01: begin
mem_sel_o <= 4'b1100;
mem_data_o <= {reg2_i[15:0],zero32[15:0]};
end
2'b10: begin
mem_sel_o <= 4'b1110;
mem_data_o <= {reg2_i[23:0],zero32[7:0]};
end
2'b11: begin
mem_sel_o <= 4'b1111;
mem_data_o <= reg2_i[31:0];
end
default: begin
mem_sel_o <= 4'b0000;
end
endcase
end
`EXE_SC_OP: begin
if(LLbit == 1'b1) begin
LLbit_we_o <= 1'b1;
LLbit_value_o <= 1'b0;
mem_addr_o <= mem_addr_i;
mem_we <= `WriteEnable;
mem_data_o <= reg2_i;
wdata_o <= 32'b1;
mem_sel_o <= 4'b1111;
mem_ce_o <= `ChipEnable;
end else begin
wdata_o <= 32'b0;
end
end
default: begin
//什么也不做
end
endcase
end //if
end //always
endmodule
mem_wb.v
`include "define.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,
input wire mem_LLbit_we,
input wire mem_LLbit_value,
input wire mem_cp0_reg_we,
input wire[4:0] mem_cp0_reg_write_addr,
input wire[`RegBus] mem_cp0_reg_data,
//送到回写阶段的信息
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,
output reg wb_LLbit_we,
output reg wb_LLbit_value,
output reg wb_cp0_reg_we,
output reg[4:0] wb_cp0_reg_write_addr,
output reg[`RegBus] wb_cp0_reg_data
);
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;
wb_LLbit_we <= 1'b0;
wb_LLbit_value <= 1'b0;
wb_cp0_reg_we <= `WriteDisable;
wb_cp0_reg_write_addr <= 5'b00000;
wb_cp0_reg_data <= `ZeroWord;
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;
wb_LLbit_we <= 1'b0;
wb_LLbit_value <= 1'b0;
wb_cp0_reg_we <= `WriteDisable;
wb_cp0_reg_write_addr <= 5'b00000;
wb_cp0_reg_data <= `ZeroWord;
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;
wb_LLbit_we <= mem_LLbit_we;
wb_LLbit_value <= mem_LLbit_value;
wb_cp0_reg_we <= mem_cp0_reg_we;
wb_cp0_reg_write_addr <= mem_cp0_reg_write_addr;
wb_cp0_reg_data <= mem_cp0_reg_data;
end //if
end //always
endmodule
cp0_reg.v
`include "define.v"
module cp0_reg(
input wire clk,
input wire rst,
input wire we_i,
input wire[4:0] waddr_i,
input wire[4:0] raddr_i,
input wire[`RegBus] data_i,
// input wire[31:0] excepttype_i,
input wire[5:0] int_i,
// input wire[`RegBus] current_inst_addr_i,
// input wire is_in_delayslot_i,
output reg[`RegBus] data_o,
output reg[`RegBus] count_o,
output reg[`RegBus] compare_o,
output reg[`RegBus] status_o,
output reg[`RegBus] cause_o,
output reg[`RegBus] epc_o,
output reg[`RegBus] config_o,
output reg[`RegBus] prid_o,
output reg timer_int_o
);
always @ (posedge clk) begin
if(rst == `RstEnable) begin
count_o <= `ZeroWord;
compare_o <= `ZeroWord;
//status寄存器的CU为0001,表示协处理器CP0存在
status_o <= 32'b00010000000000000000000000000000;
cause_o <= `ZeroWord;
epc_o <= `ZeroWord;
//config寄存器的BE为1,表示Big-Endian;MT为00,表示没有MMU
config_o <= 32'b00000000000000001000000000000000;
//制作者是L,对应的是0x48,类型是0x1,基本类型,版本号是1.0
prid_o <= 32'b00000000010011000000000100000010;
timer_int_o <= `InterruputNotAssert;
end else begin
count_o <= count_o + 1 ;
cause_o[15:10] <= int_i;
if(compare_o != `ZeroWord && count_o == compare_o) begin
timer_int_o <= `InterruputAssert;
end
if(we_i == `WriteEnable) begin
case (waddr_i)
`CP0_REG_COUNT: begin
count_o <= data_i;
end
`CP0_REG_COMPARE: begin
compare_o <= data_i;
//count_o <= `ZeroWord;
timer_int_o <= `InterruputNotAssert;
end
`CP0_REG_STATUS: begin
status_o <= data_i;
end
`CP0_REG_EPC: begin
epc_o <= data_i;
end
`CP0_REG_CAUSE: begin
//cause寄存器只有IP[1:0]、IV、WP字段是可写的
cause_o[9:8] <= data_i[9:8];
cause_o[23] <= data_i[23];
cause_o[22] <= data_i[22];
end
endcase //case addr_i
end
end //if
end //always
always @ (*) begin
if(rst == `RstEnable) begin
data_o <= `ZeroWord;
end else begin
case (raddr_i)
`CP0_REG_COUNT: begin
data_o <= count_o ;
end
`CP0_REG_COMPARE: begin
data_o <= compare_o ;
end
`CP0_REG_STATUS: begin
data_o <= status_o ;
end
`CP0_REG_CAUSE: begin
data_o <= cause_o ;
end
`CP0_REG_EPC: begin
data_o <= epc_o ;
end
`CP0_REG_PrId: begin
data_o <= prid_o ;
end
`CP0_REG_CONFIG: begin
data_o <= config_o ;
end
default: begin
end
endcase //case addr_i
end //if
end //always
endmodule
openmips.v也要修改 就是改一下例化 增加连线和一个cp0_reg模块
inst_rom.data
3401000f
40815800
3c011000
34210401
40816000
40026000
08000006
00000000
inst_rom.S
.org 0x0
.set noat
.set noreorder
.set nomacro
.global _start
_start:
ori $1,$0,0xf
mtc0 $1,$11,0x0 #写compare寄存器,开始计时
lui $1,0x1000
ori $1,$1,0x401
mtc0 $1,$12,0x0 #将0x401写如status寄存器
mfc0 $2,$12,0x0 #读status寄存器,$2=0x401
_loop:
j _loop
nop
仿真结果