概览:CP0(Coprocessor 0)是 MIPS 架构中的系统控制协处理器,负责计时、中断管理、异常处理
及特权寄存器访问。本模块实现了 CP0 的主要寄存器:Count、Compare、Status、Cause、EPC、Config、
PRId,以及定时器中断输出。
cp0_reg 模型代码
cp0_reg.v
`timescale 1ns / 1ps
`include "define.v"
// CP0 (Coprocessor 0) Register File
// 实现 MIPS 协处理器 CP0 的主要寄存器:
// Count、Compare、Status、Cause、EPC、Config、PRId,以及定时器中断输出
module cp0_reg(
input
clk,
// 时钟信号
input
rst,
// 复位,高电平有效
input
we_i,
// 写使能,由 MTC0 指令驱动
input
[4:0]
waddr_i,
// 写地址,选择要写的 CP0 寄存器
input
[4:0]
raddr_i,
// 读地址,选择要读的 CP0 寄存器
input
[`RegBus] data_i,
// 写入数据总线
input
[5:0]
// 外部中断请求向量
int_i,
output reg [`RegBus] data_o,
// 读出数据总线,用于 MFC0 指令
output reg [`RegBus] count_o,
// CP0.Count 寄存器:系统计时器
output reg [`RegBus] compare_o,
// CP0.Compare 寄存器:定时器比较值
output reg [`RegBus] status_o,
// CP0.Status 寄存器:中断/异常使能与模式控制
output reg [`RegBus] cause_o,
// CP0.Cause 寄存器:中断挂起、异常类型
output reg [`RegBus] epc_o,
// CP0.EPC 寄存器:异常返回地址
output reg [`RegBus] config_o,
// CP0.Config 寄存器:架构与实现信息
output reg [`RegBus] prid_o,
// CP0.PRId 寄存器:处理器 ID/版本号
output reg
// 定时器中断输出,当 Count==Compare 时置 1
timer_int_o
);
//************ 写操作(时序) ************
always @(posedge clk) begin
if (rst == `RstEna) begin
// 复位期间,初始化所有 CP0 寄存器
count_o
<= `ZeroWord;
compare_o
<= `ZeroWord;
status_o
<= `ZeroWord;
cause_o
<= `ZeroWord;
epc_o
<= `ZeroWord;
config_o
<= 32'h00008000;
// BE=1: 大端存储模式
prid_o
<= `ZeroWord;
// Processor ID 默认 0
timer_int_o <= `InterruptNotAssert; // 定时器中断关闭
end else begin
// 仅在写使能且地址匹配时更新对应寄存器
case (waddr_i)
`CP0_REG_COUNT: begin
// Count 寄存器可由软件写入,用于同步或调试
count_o <= data_i;
end
`CP0_REG_COMPARE: begin
// Compare 寄存器写入时,清除定时器中断
compare_o
<= data_i;
timer_int_o <= `InterruptNotAssert;
end
`CP0_REG_STATUS: begin
// Status 寄存器:控制中断使能、虚拟页
status_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];
// IV: 中断向量选择
cause_o[22]
<= data_i[22];
// WP: 写保护
end
`CP0_REG_EPC: begin
// EPC 寄存器:异常返回地址
epc_o <= data_i;
end
default: begin
// 其他寄存器只读或暂未实现,不处理
end
endcase
end
end
//************ 读操作(组合) ************
always @(*) begin
if (rst == `RstEna) begin
// 复位期间,输出 0
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
// PRId 为只读寄存器,提供处理器型号/版本信息
data_o <= prid_o;
end
`CP0_REG_CONFIG: begin
data_o <= config_o;
end
default: begin
data_o <= `ZeroWord;
end
endcase
end
end
endmodule
endmodule
cpu 模型代码
cpu
`timescale 1ns / 1ps
`include "define.v"
//==================================================
// 模块功能:五级流水线 MIPS CPU 顶层,中文注释版
// 设计思路:采用经典五级流水线结构,分为 IF、ID、EX、MEM、WB 五个阶段。
// 1.IF(取指令):由 pc_reg 生成 PC 并读取 inst_rom
// 2.ID(译码):解析指令、读取寄存器、检测冒险并实现前推/插泡
// 3.EX(执行):ALU 运算、乘除法多周期单元、HI/LO 和 CP0 旁路
// 4.MEM(访存):data_ram 加载存储指令、支持 LL/SC 原子操作
// 5.WB(写回):将结果写入寄存器堆和 HI/LO、CP0
// 通过 ctrl 模块统一产生 stall,消除数据和控制冒险。
//==================================================
module cpu(
input
clk,
// 时钟信号
input
rst,
// 复位信号(高电平有效)
input [`InstBus] rom_data_i,
output
rom_ce_o,
// 来自指令存储器的数据(指令)
// 指令存储器使能
output [`InstAddrBus] rom_addr_o, // 指令存储器地址
// 数据存储器接口
input [`RegBus] ram_data_o,
// 从 data_ram 读出的数据
output [`RegBus] ram_addr_i,
// data_ram 地址信号
output [`RegBus] ram_data_i,
// 写入 data_ram 的数据
output
output [3:0]
output
ram_we_i,
// data_ram 写使能
ram_sel_i,
// data_ram 字节选择
ram_ce_i,
// data_ram 片选
// 中断信号
input [5:0]
int_i,
// 外部中断输入
output
timer_int_o
// 来自 CP0 定时器中断
);
//================= IF/ID 边界信号 =================
wire [`InstAddrBus] pc_if;
// IF 阶段 PC 输出 → IF/ID 输入
// IF/ID → ID
wire [`InstAddrBus] pc_id;
// 转发到 ID 用于分支判断
wire [`InstBus]
// 取到的指令
inst;
//============== ID 阶段信号 =================
// 寄存器读取控制
wire [`RegAddrBus]
reg1_addr;
// 源操作数 1 地址
wire
reg1_read;
// 源操作数 1 读使能
wire [`RegAddrBus]
reg2_addr;
// 源操作数 2 地址
wire
reg2_read;
// 源操作数 2 读使能
branch_flag;
// 分支是否成立
// 转移/分支信号
wire
wire [`InstAddrBus] branch_address; // 分支目标地址
// 寄存器堆输出
wire [`RegBus]
reg1_data;
// 读出的寄存器 1 数据
wire [`RegBus]
reg2_data;
// 读出的寄存器 2 数据
// ID → EX 边界
wire [`AluOpBus]
aluop_id;
// ALU 操作码
wire [`AluSelBus]
alusel_id;
// ALU 功能选择
wire [`RegBus]
reg1_id;
// EX 阶段操作数 1
wire [`RegBus]
reg2_id;
// EX 阶段操作数 2
wire [`RegAddrBus] reg_addr_id;
// EX 阶段写回寄存器地址
wire
wreg_id;
// EX 阶段写回使能
wire
next_is_delay;
// 下一条为延迟槽指令
wire
is_delay;
// 当前为延迟槽指令
wire [`InstAddrBus] link_addr;
// 链接地址(JAL/JALR)
wire [`InstBus]
// 原始指令传至 EX
inst_id;
//============== EX 边界信号 =================
wire [`AluOpBus]
aluop_ex;
wire [`AluSelBus]
alusel_ex;
wire [`RegBus]
reg1_ex;
wire [`RegBus]
reg2_ex;
wire [`RegAddrBus] reg_addr_ex;
wire
wreg_ex;
wire
is_delay_ex;
wire [`InstAddrBus] link_addr_ex;
wire [`InstBus]
wire
inst_ex;
is_delay_inst; // 反馈到 ID 的延迟槽标志
//============== EX/MEM 边界信号 ==============
wire [`RegBus]
wdata_ex;
// ALU 结果/链接地址
wire [`RegAddrBus] waddr_ex;
wire
wreg_ex_mem;
wire [`RegBus]
hi_ex, lo_ex;
wire
whilo_ex;
wire [`AluOpBus]
aluop_ex_mem;
wire [`RegBus]
reg2_ex_mem;
wire [`RegBus]
mem_addr_ex_mem;
wire [`RegBus]
cp0_reg_data_ex;
// HI/LO 数据
// HI/LO 写使能
wire [`RegAddrBus] cp0_reg_write_addr_ex;
wire
cp0_reg_we_ex;
//============== MEM/WB 边界信号 ==============
wire [`RegBus]
wdata_mem_mem;
wire [`RegAddrBus] waddr_mem_mem;
wire
wreg_mem_mem;
wire [`RegBus]
hi_mem_mem, lo_mem_mem;
wire
whilo_mem_mem;
wire
LLbit_we_mem_mem;
wire
LLbit_value_mem_mem;
wire [`RegBus]
cp0_reg_data_mem_mem;
wire [`RegAddrBus] cp0_reg_write_addr_mem_mem;
wire
cp0_reg_we_mem_mem;
//============== WB 阶段输入 =================
wire [`RegBus]
wdata_reg;
wire [`RegAddrBus] waddr_reg;
wire
wreg_reg;
wire [`RegBus]
hi_hilo, lo_hilo;
wire
whilo_hilo;
wire
LLbit_we;
wire
LLbit_value;
wire
cp0_reg_we;
wire [`RegAddrBus] cp0_reg_waddr;
wire [`RegAddrBus] cp0_reg_raddr;
wire [`RegBus]
cp0_reg_data;
//============== 旁路 HI/LO & CP0 =============
wire [`RegBus]
hi, lo;
wire [`RegBus]
cp0_reg_data_to_ex;
//============== 流水线控制信号 =============
wire
stallreq_from_ex;
wire
stallreq_from_id;
wire [5:0]
stall;
// 多周期除法单元信号
wire [1:0]
cnt_ex_i, cnt_ex_o;
wire [`DoubleRegBus] hilo_temp_ex_o;
wire [`RegBus]
div_opdata1, div_opdata2;
wire
div_start, div_annul;
wire [`DoubleRegBus] div_result;
wire
div_ready;
//================ 各模块实例化 ================
// 1. 通用寄存器堆:支持两读一路写,读写冲突前推
regfile regfile0(
.clk(clk), .rst(rst), .we(wreg_reg), .waddr(waddr_reg), .wdata(wdata_reg),
.re1(reg1_read), .raddr1(reg1_addr), .rdata1(reg1_data),
.re2(reg2_read), .raddr2(reg2_addr), .rdata2(reg2_data)
);
// 2. PC 寄存器及 IF 级:产生 PC,自增或分支跳转
pc_reg pc_reg0(
.clk(clk), .rst(rst), .stall(stall), .branch_flag_i(branch_flag), .branch_address_i(branch_address),
.pc(pc_if), .ce(rom_ce_o)
);
assign rom_addr_o = pc_if;
// 3. IF/ID 流水线寄存器:隔离 IF 与 ID 阶段
if_id if_id0(
.clk(clk), .rst(rst), .stall(stall), .if_pc(pc_if), .if_inst(rom_data_i),
.id_pc(pc_id), .id_inst(inst)
);
// 4. ID 级:译码、读寄存器、冒险检测与前推、分支决策
id id0(
.rst(rst), .pc_i(pc_id), .inst_i(inst),
.reg1_data_i(reg1_data), .reg2_data_i(reg2_data),
.ex_wdata_i(wdata_ex), .ex_waddr_i(waddr_ex), .ex_wreg_i(wreg_ex_mem),
.mem_wdata_i(wdata_mem_mem), .mem_waddr_i(waddr_mem_mem), .mem_wreg_i(wreg_mem_mem
),
.is_delay_inst_i(is_delay_inst),
.reg1_read_o(reg1_read), .reg2_read_o(reg2_read), .reg1_addr_o(reg1_addr), .reg2_addr_o(reg2_addr
),
.branch_flag_o(branch_flag), .branch_addr_inst_o(branch_address),
.wreg_o(wreg_id), .waddr_o(reg_addr_id), .reg1_o(reg1_id), .reg2_o(reg2_id),
.aluop_o(aluop_id), .alusel_o(alusel_id), .stallreq(stallreq_from_id),
.next_inst_is_delay_o(next_is_delay), .is_delay_inst_o(is_delay),
.link_addr_o(link_addr), .inst_o(inst_id)
);
// 5. ID/EX 流水线寄存器:隔离 ID 与 EX 阶段,处理气泡插入
id_ex id_ex0(
.clk(clk), .rst(rst), .stall(stall),
.id_alusel(alusel_id), .id_aluop(aluop_id), .id_wreg(wreg_id), .id_waddr(reg_addr_id),
.id_reg1(reg1_id), .id_reg2(reg2_id), .next_is_delay(next_is_delay), .id_is_delay(is_delay),
.id_link_addr(link_addr), .id_inst(inst_id),
.ex_alusel(alusel_ex), .ex_aluop(aluop_ex), .ex_wreg(wreg_ex), .ex_waddr(reg_addr_ex),
.ex_reg1(reg1_ex), .ex_reg2(reg2_ex), .ex_is_delay(is_delay_ex), .is_delay(is_delay_inst),
.ex_link_addr(link_addr_ex), .ex_inst(inst_ex)
);
// 6. EX 级:ALU 运算、多周期乘除法、HI/LO 和 CP0 旁路
ex ex0(
.rst(rst), .alusel_i(alusel_ex), .aluop_i(aluop_ex), .reg1_i(reg1_ex), .reg2_i(reg2_ex),
.wreg_i(wreg_ex), .waddr_i(reg_addr_ex), .hi_i(hi), .lo_i(lo),
.mem_whilo_i(whilo_mem_mem), .mem_hi_i(hi_mem_mem), .mem_lo_i(lo_mem_mem),
.wb_whilo_i(whilo_hilo), .wb_hi_i(hi_hilo), .wb_lo_i(lo_hilo),
.cnt_i(cnt_ex_i), .div_result(div_result), .div_ready(div_ready),
.cp0_reg_data_i(cp0_reg_data_to_ex), .wb_cp0_reg_data(cp0_reg_data), .wb_cp0_reg_we(cp0_reg_we
), .wb_cp0_reg_write_addr(cp0_reg_waddr),
.mem_cp0_reg_data(cp0_reg_data_mem_mem), .mem_cp0_reg_we(cp0_reg_we_mem_mem), .mem_c
p0_reg_write_addr(cp0_reg_write_addr_mem_mem),
.wdata_o(wdata_ex), .waddr_o(waddr_ex), .wreg_o(wreg_ex_mem),
.whilo_o(whilo_ex), .hi_o(hi_ex), .lo_o(lo_ex), .stallreq(stallreq_from_ex),
.cnt_o(cnt_ex_o), .hilo_temp_o(hilo_temp_ex_o),
.div_start(div_start), .div_annul(div_annul), .div_opdata1(div_opdata1), .div_opdata2(div_opdata2),
.reg2_o(reg2_ex_mem), .aluop_o(aluop_ex_mem), .mem_addr_o(mem_addr_ex_mem),
.cp0_reg_read_addr_o(cp0_reg_raddr), .cp0_reg_data_o(cp0_reg_data_ex), .cp0_reg_write_addr_o(cp
0_reg_write_addr_ex), .cp0_reg_we_o(cp0_reg_we_ex)
);
// 7. EX/MEM 流水线寄存器
ex_mem ex_mem0(
.clk(clk), .rst(rst), .stall(stall),
.ex_waddr(waddr_ex), .ex_wdata(wdata_ex), .ex_wreg(wreg_ex_mem),
.ex_whilo(whilo_ex), .ex_hi(hi_ex), .ex_lo(lo_ex), .cnt_i(cnt_ex_o),
.ex_aluop(aluop_ex_mem), .ex_mem_addr(mem_addr_ex_mem), .ex_reg2(reg2_ex_mem),
.ex_cp0_reg_data(cp0_reg_data_ex), .ex_cp0_reg_write_addr(cp0_reg_write_addr_ex), .ex_cp0_reg_w
e(cp0_reg_we_ex),
.mem_waddr(waddr_mem), .mem_wdata(wdata_mem), .mem_wreg(wreg_mem),
.mem_whilo(whilo_mem), .mem_hi(hi_mem), .mem_lo(lo_mem),
.cnt_o(cnt_ex_i), .mem_aluop(aluop_mem), .mem_mem_addr(mem_addr_mem), .mem_reg2(reg2_me
m),
.mem_cp0_reg_data(cp0_reg_data_mem), .mem_cp0_reg_write_addr(cp0_reg_write_addr_mem), .me
m_cp0_reg_we(cp0_reg_we_mem)
);
// 8. MEM 级:加载/存储、LL/SC 原子操作
mem mem0(
.rst(rst), .wreg_i(wreg_mem), .waddr_i(waddr_mem), .wdata_i(wdata_mem),
.whilo_i(whilo_mem), .hi_i(hi_mem), .lo_i(lo_mem), .aluop_i(aluop_mem),
.mem_addr_i(mem_addr_mem), .reg2_i(reg2_mem), .mem_data_i(ram_data_o),
.LLbit_i(LLbit), .wb_LLbit_we_i(LLbit_we), .wb_LLbit_value_i(LLbit_value),
.cp0_reg_data_i(cp0_reg_data_mem), .cp0_reg_write_addr_i(cp0_reg_write_addr_mem), .cp0_reg_we_
i(cp0_reg_we_mem),
.wreg_o(wreg_mem_mem), .waddr_o(waddr_mem_mem), .wdata_o(wdata_mem_mem),
.whilo_o(whilo_mem_mem), .hi_o(hi_mem_mem), .lo_o(lo_mem_mem),
.mem_data_o(ram_data_i), .mem_addr_o(ram_addr_i), .mem_we_o(ram_we_i), .mem_sel_o(ram_sel_i)
, .mem_ce_o(ram_ce_i),
.LLbit_we_o(LLbit_we_mem_mem), .LLbit_value_o(LLbit_value_mem_mem),
.cp0_reg_data_o(cp0_reg_data_mem_mem), .cp0_reg_write_addr_o(cp0_reg_write_addr_mem_mem),
.cp0_reg_we_o(cp0_reg_we_mem_mem)
);
// 9. MEM/WB 流水线寄存器及写回
mem_wb mem_wb0(
.clk(clk), .rst(rst), .stall(stall),
.mem_reg(wreg_mem_mem), .mem_waddr(waddr_mem_mem), .mem_wdata(wdata_mem_mem),
.mem_whilo(whilo_mem_mem), .mem_hi(hi_mem_mem), .mem_lo(lo_mem_mem),
.mem_LLbit_we(LLbit_we_mem_mem), .mem_LLbit_value(LLbit_value_mem_mem),
.mem_cp0_reg_data(cp0_reg_data_mem_mem), .mem_cp0_reg_write_addr(cp0_reg_write_addr_mem
_mem), .mem_cp0_reg_we(cp0_reg_we_mem_mem),
.wb_reg(wreg_reg), .wb_waddr(waddr_reg), .wb_wdata(wdata_reg),
.wb_whilo(whilo_hilo), .wb_hi(hi_hilo), .wb_lo(lo_hilo),
.wb_LLbit_we(LLbit_we), .wb_LLbit_value(LLbit_value),
.wb_cp0_reg_data(cp0_reg_data), .wb_cp0_reg_write_addr(cp0_reg_waddr), .wb_cp0_reg_we(cp0_reg
_we)
);
// 10. HI/LO 寄存器
hilo_reg hilo_reg0(
.clk(clk), .rst(rst), .we(whilo_hilo), .hi_i(hi_hilo), .lo_i(lo_hilo), .hi_o(hi), .lo_o(lo)
);
// 11. 流水线控制单元:根据数据、结构、控制冒险请求生成 stall
ctrl ctrl0(
.rst(rst), .stallreq_from_ex(stallreq_from_ex), .stallreq_from_id(stallreq_from_id), .stall(stall)
);
// 12. 多周期除法单元(div)实例化
//
- start_i: 当 EX 级检测到除法指令时拉高,启动除法运算
//
- annul_i: 可用于取消正在进行的除法(此处暂未使用,写死为 0)
//
- result_o: 输出 64 位结果,高 32 位为商,低 32 位为余数
//
- ready_o: 当运算完成时置 1,通知 EX 级取消流水线停顿
div div0(
.clk(clk),
.rst(rst),
.opdata1_i(div_opdata1),
.opdata2_i(div_opdata2),
.start_i(div_start),
.annul_i(1'b0),
.result_o(div_result),
.ready_o(div_ready)
);
// 13. LLbit 寄存器(LLbit_reg)实例化
//
- 维护 Load-Link/Store-Conditional 原子操作标志
//
- we: 写使能,SC 成功或 LL 操作时更新标志
//
- LLbit_i: 输入新标志值
//
- LLbit_o: 输出当前标志,供 MEM 级判断 SC 是否成功
LLbit_reg LLbit_reg0(
.clk(clk),
.rst(rst),
.flush(1'b0),
// 异常时可通过 flush 清零,此处暂未集成异常
.we(LLbit_we),
.LLbit_i(LLbit_value),
.LLbit_o(LLbit)
);
// 14. 协处理器 CP0 寄存器(cp0_reg)实例化
//
- 管理 Count/Compare 定时器和中断、Status/Cause/EPC 异常寄存器等
//
- waddr_i/raddr_i: MTC0/MFC0 的目标寄存器编号
//
- data_i: 写入数据,data_o: 读出数据
//
- we_i: 写使能,int_i: 外部中断请求
//
- timer_int_o: 定时器匹配中断信号
cp0_reg cp0_reg0(
.clk(clk),
.rst(rst),
.waddr_i(cp0_reg_waddr),
.raddr_i(cp0_reg_raddr),
.data_i(cp0_reg_data),
.we_i(cp0_reg_we),
.int_i(int_i),
.data_o(cp0_reg_data_to_ex),
.timer_int_o(timer_int_o)
);
endmodule
DATAMERORY 模型代码
`timescale 1ns / 1ps
`include "define.v"
//==================================================
// 模块功能:流水线控制单元(Pipeline Control Unit)
// 设计思路:根据 ID 和 EX 阶段冒险检测信号,生成 6 位 stall 总线,
// 对 PC、IF/ID、ID/EX、EX/MEM、MEM/WB 寄存器以及寄存器堆进行统一暂停控制,
// 以实现冒险处理(气泡插入)并保证指令执行正确性。
//--------------------------------------------------
// Stall 总线位定义:
// stall[0] -> PC 寄存器暂停控制
// stall[1] -> IF/ID 流水线寄存器暂停控制
// stall[2] -> ID/EX 流水线寄存器暂停控制
// stall[3] -> EX/MEM 流水线寄存器暂停控制
// stall[4] -> MEM/WB 流水线寄存器暂停控制
// stall[5] -> 写回寄存器堆暂停控制
//==================================================
module ctrl(
input
rst,
input
stallreq_from_id, // 来自译码阶段的冒险请求(Load-Use 冒险)
input
stallreq_from_ex, // 来自执行阶段的冒险请求(多周期运算冒险)
output reg [5:0] stall
// 复位信号(高电平有效)
// 全局暂停信号,总线宽度 6
);
// 在组合逻辑中根据请求信号生成 stall,
// 当 rst 为有效时,清除所有暂停。
always @(*) begin
if (rst == `RstEna) begin
// 复位期间,不暂停任何阶段
stall <= 6'b000000;
end else begin
// 优先检测 ID 阶段提出的冒险请求
if (stallreq_from_id == `Stop) begin
// Load-Use 冒险:暂停 PC, IF/ID, ID/EX 三个阶段,插入一个气泡到 EX
stall <= 6'b000111;
end
// 然后检测 EX 阶段提出的冒险请求
else if (stallreq_from_ex == `Stop) begin
// 多周期运算(如 DIV/MULT)需暂停 PC, IF/ID, ID/EX, EX/MEM 四个阶段
stall <= 6'b001111;
end
else begin
// 无冒险请求,流水线继续运行
stall <= 6'b000000;
end
end
end
endmodule
`timescale 1ns / 1ps
// 设置仿真时间单位为 1 纳秒,精度为 1 皮秒
`include "define.v"
// 包含外部宏定义文件,通常在其中定义了 `DataBus`、`DataAddrBus`、`ByteWidth`、
// `ZeroWord`、`ChipDisa`、`IsWrite`、`IsRead` 等宏
module data_ram(
input clk,
// 时钟信号,所有写操作在 clk 上升沿触发
input ce,
// 芯片使能,高电平有效;低电平时读写都被禁止
input [`DataBus] data_i,
// 写入数据总线(32 位宽)
input [`DataAddrBus] addr, // 访问地址总线;这里假定地址已按字对齐
input we,
input [3:0] sel,
// 读写控制:高电平表示写操作,低电平表示读操作
// 字节选择信号,每一位对应 8 位(Byte)使能
output reg [`DataBus] data_o // 读出数据总线(32 位宽),在组合逻辑或时序逻辑中驱动
);
// 四个独立的字节存储阵列,实现字节寻址:
// data_mem0 存最低 8 位(Byte0),data_mem3 存最高 8 位(Byte3)
// 每个阵列深度 201,即支持地址 0…200
reg [`ByteWidth] data_mem0[0:200];
reg [`ByteWidth] data_mem1[0:200];
reg [`ByteWidth] data_mem2[0:200];
reg [`ByteWidth] data_mem3[0:200];
//********** 写操作:在时钟上升沿触发 **********
always @(posedge clk) begin
if (ce == `ChipDisa) begin
// 芯片使能信号失能时,不进行写操作,同时将输出清零
data_o <= `ZeroWord;
end
else if (we == `IsWrite) begin
// 在写模式下,根据 sel 位决定写入哪些字节
// sel[3] 对应最高字节 data_i[31:24] → data_mem3
if (sel[3]) begin
data_mem3[addr[15:0]] <= data_i[31:24];
end
// sel[2] 对应次高字节 data_i[23:16] → data_mem2
if (sel[2]) begin
data_mem2[addr[15:0]] <= data_i[23:16];
end
// sel[1] 对应次低字节 data_i[15:8] → data_mem1
if (sel[1]) begin
data_mem1[addr[15:0]] <= data_i[15:8];
end
// sel[0] 对应最低字节 data_i[7:0] → data_mem0
if (sel[0]) begin
data_mem0[addr[15:0]] <= data_i[7:0];
end
end
end
//********** 读操作:组合逻辑 **********
always @(*) begin
if (ce == `ChipDisa) begin
// 芯片失能时,输出清零
data_o <= `ZeroWord;
end
else if (we == `IsRead) begin
// 在读模式下,将四个字节按大端(高字节在前)拼接成 32 位输出
data_o <= {
data_mem3[addr[15:0]], // 输出 bits [31:24]
data_mem2[addr[15:0]], // 输出 bits [23:16]
data_mem1[addr[15:0]], // 输出 bits [15:8]
data_mem0[addr[15:0]] // 输出 bits [7:0]
};
end
else begin
// 其他情况下(既非写也非读),保持输出清零
data_o <= `ZeroWord;
end
end
endmodule
//*********** 全局的宏定义 **********************
`define RstEna
1'b1
//复位信号有效
`define RstDisa
1'b0
//复位信号无效
`define ZeroWord
32'h00000000
`define WriteEna
1'b1
//使能写
`define WriteDisa
1'b0
//禁止写
//32 位的数值 0
`define ReadEna
1'b1
//使能读
`define ReadDisa
1'b0
//禁止读
`define AluOpBus
7:0
//译码阶段的输出 aluop_o 的宽度
`define AluSelBus
2:0
//译码阶段的输出 alusel_o 的宽度
`define InstValid
1'b1
//指令有效
`define InstInvalid 1'b0
//指令无效
`define True_v
1'b1
//逻辑“真”
`define False_v
1'b0
//逻辑“假”
`define ChipEna
1'b1
//芯片使能
`define ChipDisa
1'b0
//芯片禁止
`define Stop
1'b1
//流水暂停
`define NoStop
1'b0
//流水继续
//*********** 与具体指令有关的宏定义 **********************
//为什么指令码和功能码用同样的表示方法呢?
`define EXE_AND
6'b100100
//and 的功能码
`define EXE_OR
6'b100101
//or 的功能码
`define EXE_XOR
6'b100110
//xor 的功能码
`define EXE_NOR
6'b100111
//nor 的功能码
`define EXE_ANDI
6'b001100
//andi 的指令码
`define EXE_ORI
6'b001101
//ori 的指令码
`define EXE_XORI
6'b001110
//xori 的指令码
`define EXE_LUI
6'b001111
//lui 的指令码
`define EXE_SLL
6'b000000
//sll 的功能码
`define EXE_SLLV
6'b000100
//sllv 的功能码
`define EXE_SRL
6'b000010
//srl 的功能码
`define EXE_SRLV
6'b000110
//srlv 的功能码
`define EXE_SRA
6'b000011
//sra 的功能码
`define EXE_SRAV
6'b000111
//srav 的功能码
`define EXE_MOVZ
6'b001010
//movz 的功能码
`define EXE_MOVN
6'b001011
//movn 的功能码
`define EXE_MFHI
6'b010000
//mfhi 的功能码
`define EXE_MTHI
6'b010001
//mthi 的功能码
`define EXE_MFLO
6'b010010
//mflo 的功能码
`define EXE_MTLO
6'b010011
//mflo 的功能码
`define EXE_ADD
6'b100000
//add 的功能码
`define EXE_ADDU
6'b100001
//addu 的功能码
`define EXE_SUB
6'b100010
//sub 的功能码
`define EXE_SUBU
6'b100011
//subu 的功能码
`define EXE_SLT
6'b101010
//slt 的功能码
`define EXE_SLTU
6'b101011
//sltu 的功能码
`define EXE_MULT
6'b011000
//mult 的功能码
`define EXE_MULTU
6'b011001
//multu 的功能码
`define EXE_DIV
6'b011010
//div 的功能码
`define EXE_DIVU
6'b011011
//divu 的功能码
`define EXE_ADDI
6'b001000
//addi 的指令码
`define EXE_ADDIU
6'b001001
//addiu 的指令码
`define EXE_SLTI
6'b001010
//slti 的指令码
`define EXE_SLTIU
6'b001011
//sltiu 的指令码
`define EXE_JR
6'b001000
//jr 的功能码
`define EXE_JALR
6'b001001
//jarl 的功能码
`define EXE_J
6'b000010
//j 的指令码
`define EXE_JAL
6'b000011
//jal 的指令码
//跳转指令
//分支指令,都是通过指令码控制的
`define EXE_BEQ
6'b000100
//指令码
`define EXE_BGTZ
6'b000111
//指令码
`define EXE_BLEZ
6'B000110
//指令码
`define EXE_BNE
6'b000101
//指令码
//加载存储指令
`define EXE_LB
6'b100000
//LB 的指令码
`define EXE_LBU
6'b100100
//LBU 的指令码
`define EXE_LH
6'b100001
//LH 的指令码
`define EXE_LHU
6'b100101
//LHU 的指令码
`define EXE_LW
6'b100011
//LW 的指令码
`define EXE_LWL
6'b100010
//LWL 的指令码
`define EXE_LWR
6'b100110
//LWR 的指令码
`define EXE_SB
6'b101000
//SB 的指令码
`define EXE_SH
6'b101001
//SH 的指令码
`define EXE_SW
6'b101011
//SW 的指令码
`define EXE_SWL
6'b101010
//SWL 的指令码
`define EXE_SWR
6'b101110
//SWR 的指令码
`define EXE_LL
6'b110000
//LL 的指令码
`define EXE_SC
6'b111000
//SC 的指令码
//跟在 REGIMM 后面的分支指令
`define EXE_BLTZ
6'b00000
//op4 的功能码
`define EXE_BLTZAL 6'b10000
//op4 的功能码
`define EXE_BGEZ
6'b00001
//op4 的功能码
`define EXE_BGEZAL 6'b10001
//op4 的功能码
//接在 special2 类的后面
`define EXE_CLZ
6'b100000
//clk 的功能码
`define EXE_CLO
6'b100001
//clo 的功能码
`define EXE_MUL
6'b000010
//mul 的功能码
`define EXE_MADD
6'b000000
//madd 的功能码
`define EXE_MADDU
6'b000001
//s2 后面的功能码
`define EXE_MSUB
6'b000100
//s2 后面的功能码
`define EXE_MSUBU
6'b000101
//s2 后面的功能码
//接在 CP0 指令码后面的,位于原本的 rs 寄存器所在的部分
`define EXE_MTC0
5'b00100
//mtc0
`define EXE_MFC0
5'b00000
//mfc0
`define EXE_SYNC
6'b001111
//sync 的功能码
`define EXE_PREF
6'b110011
//pref 的指令码
`define EXE_NOP
6'b000000
//nop 的指令码
`define EXE_SPECIAL_INST
6'b000000
//SPECIAL 类的指令码,用于在 op 为 0 的时候
`define EXE_REGIMM_INST
6'b000001
//REGIMM 类的指令码
`define EXE_SPECIAL2_INST
6'b011100
//SPECIAL2 类的指令码
`define EXE_CP0_INST
6'b010000
//用于判断 cp 相关的两条指令
//AluOp
`define EXE_AND_OP
8'b00000001
//AND 控制信号
`define EXE_OR_OP
8'b00000010
//这个是在 ALU 单元运用的,每一个指令有不同的 ALUop,
单独进行设置的,书上的控制信号是两位的,也就是只有两种情况
`define EXE_XOR_OP
8'b00000011
//XOR
`define EXE_NOR_OP
8'b00000100
//NOR
`define EXE_LUI_OP
8'b00000101
//lui rt,imm
`define EXE_SLL_OP
8'b00000110
//SLL 逻辑左移
`define EXE_SRA_OP
8'b00000111
//SRA 算术右移
`define EXE_SRL_OP
8'b00001000
//SRL 逻辑右移
`define EXE_MOVZ_OP
8'b00001001
//movz rd,rs,rt; if(rt==0) rd <- rs;
`define EXE_MOVN_OP
8'b00001010
//movn rd,rs,rt; if(rt!=0) rd <- rs;
`define EXE_MFHI_OP
8'b00001011
//mfhi rd; rd <- hi;
`define EXE_MFLO_OP
8'b00001100
//mflo rd; rd <- lo;
`define EXE_MTHI_OP
8'b00001101
//mthi rs; hi <- rs;
`define EXE_MTLO_OP
8'b00001110
//mtlo rs; lo <- rs;
`define EXE_ADD_OP
8'b00001111
//这类运算指令都是对 rs 以及 rt 进行计算,结果存入 rd 中
`define EXE_ADDU_OP
8'b00010000
将立即数保存到 rt 的高 16 位
`define EXE_SUB_OP
8'b00010001
`define EXE_SUBU_OP
8'b00010010
`define EXE_SLT_OP
8'b00010011
`define EXE_SLTU_OP
8'b00010100
`define EXE_MADD_OP
8'b00010101
`define EXE_MADDU_OP
8'b00010110
`define EXE_MSUB_OP
8'b00010111
`define EXE_MSUBU_OP
8'b00011000
`define EXE_CLZ_OP
8'b00011001
//clz rd,rs; rd <- (rs 中前面的 0 的个数)
`define EXE_CLO_OP
8'b00011010
`define EXE_MUL_OP
8'b00011011
//只保留低 32 位在 rd 中
`define EXE_MULT_OP
8'b00011100
//高 32 位在 HI 中,低 32 位在 LO 中
`define EXE_MULTU_OP
8'b00011101
`define EXE_DIV_OP
8'b00011110
//div rs,rt; {HI,LO} <- rs/rt
`define EXE_DIVU_OP
8'b00011111
//和乘法一样,先换成正数,最后通过异或判断正负,无符号
`define EXE_JR_OP
8'b00100000
//jr rs;
`define EXE_JALR_OP
8'b00100001
//jalr rs;|| jalr rs,rd;
`define EXE_J_OP
8'b00100010
//j target;
`define EXE_JAL_OP
8'b00100011
//jal target;
`define EXE_BEQ_OP
8'b00100100
//beq rs,rt,offset; 相等则转移
数则不用管
//转移分支指令
`define EXE_BGTZ_OP
8'b00100101
//bgtz rs,offset;
大于 0 则转移
`define EXE_BLEZ_OP
8'b00100110
//blez rs,offset;
小于等于 0 则转移
`define EXE_BNE_OP
8'b00100111
//bne rs,rt,offset; 不相等则转移
`define EXE_BLTZ_OP
8'b00101000
//bltz rs,offset;
`define EXE_BLTZAL_OP
8'b00101001
//bltzal rs,offset; 同上,保存返回地址到$31 中
`define EXE_BGEZ_OP
8'b00101010
//bgez rs,offset;
`define EXE_BGEZAL_OP
8'b00101011
//bgezal rs,offset; 同上
rs 的值小于 0 则转移
rs 的值大于等于 0 则转移
//加载存储指令
`define EXE_LB_OP
8'b00101100
//lb rt,offset(base)读取一个字节放到 rt 中,符号扩展
`define EXE_LBU_OP
8'b00101101
//同上,无符号扩展
`define EXE_LH_OP
8'b00101110
//读取半个字
`define EXE_LHU_OP
8'b00101111
//同上
`define EXE_LW_OP
8'b00110000
//读取一个字
`define EXE_LWL_OP
8'b00110001
//从左边开始读取这个字剩下的数据,比如读取地址为 5,则
8'b00110010
//读取包含这个字的数据,比如读取地址为 5,则读取包含的
`define EXE_SB_OP
8'b00110011
//sb rt,offset(base)将 rt 中的最低字节存放到对应地址中
`define EXE_SH_OP
8'b00110100
//最低半个字
`define EXE_SW_OP
8'b00110101
//直接将 rt 放进去就行了
`define EXE_SWL_OP
8'b00110110
//将 rt 中的左边部分放进去,具体多少位由地址决定,这部分
`define EXE_SWR_OP
8'b00110111
//右边部分,假设存放的地址是 5,则将右边半个字存放到 4 5
`define EXE_LL_OP
8'b00111000
//ll rt,offset(base)只是多了对 LLbit 寄存器的处理
读取剩下的 6 7
`define EXE_LWR_OP
45
和 lwl 是类似的
中
`define EXE_SC_OP
8'b00111001
//sc rt,offset(base)
8'b00111010
//mtc0 rt,rd;
将 rt 的值赋值到 CP0 中地址为 rd 中的值的寄
8'b00111011
//mfc0 rt,rd;
读出 CP0 中的值存放到 rt 寄存器中
//协处理器访问指令
`define EXE_MTC0_OP
存器
`define EXE_MFC0_OP
`define EXE_PREF_OP 8'b11111111
//PREF
`define EXE_NOP_OP 8'b00000000
//这个就是流水线中的气泡
//AluSel
`define EXE_RES_LOGIC
3'b001
//用来确定运算类型的,由于现在只有 ori 操作,所以只有逻
辑运算
`define EXE_RES_MOVE
3'b010
`define EXE_RES_SHIFT
3'b011
`define EXE_RES_ARITH
3'b100
`define EXE_RES_MUL
3'b101
//shift 有什么作用
`define EXE_RES_JUMP_BRANCH 3'b110
`define EXE_RES_LOAD_STORE 3'b111
`define EXE_RES_NOP
3'b000
//*********** 与指令存储器 ROM 有关的宏定义 **********************
`define InstAddrBus
31:0
//ROM 的地址总线宽度
`define InstBus
31:0
//ROM 的数据总线宽度
`define InstMemNum
131071
`define InstMemNumLog2 17
//ROM 的实际大小 128KB
//ROM 实际使用的地址线宽度
//*********** 与通用寄存器 Regfile 有关的宏定义 **********************
`define RegAddrBus
4:0
//Regfile 模块的地址线宽度
`define RegBus
31:0
//Regfile 模块的数据线宽度
`define RegWidth
32
//通用寄存器的宽度
`define DoubleRegBus
63:0
//两倍的通用寄存器的数据线宽度
`define DoubleRegWidth 64
//两倍的通用寄存器的宽度
`define RegNum
32
//通用寄存器的数量
`define RegNumLog2
5
//寻址通用寄存器使用的地址位数
`define NOPRegAddr
5'b00000
//************ 与除法相关的宏定义 *********************************
`define DivFree
2'b00
`define DivZero
2'b01
`define DivOn
2'b10
`define DivEnd
2'b11
//************ 加载存储的宏定义 **********************************
`define IsWrite
1'b1
`define IsRead
1'b0
`define DataAddrBus
31:0
//地址总线宽度
`define DataBus
31:0
//数据总线宽度
`define DataMemNum
131017
`define DataMemNumlog2 15
`define ByteWidth
7:0
//RAM 的大小,单位是字,这里是 128K word
//实际使用的地址宽度
//一个字的宽度,这里是 8bit
//************ 协处理器 cp0 的宏定义 ******************************
`define CP0_REG_COUNT
5'b01001
//对应寄存器的标号,这里只实现这七种寄存器
`define CP0_REG_COMPARE 5'b01011
`define CP0_REG_STATUS 5'b01100
`define CP0_REG_CAUSE
5'b01101
`define CP0_REG_EPC
5'b01110
`define CP0_REG_PRID
5'b01111
`define CP0_REG_CONFIG 5'b10000
`define InterruptAssert 1'b1
`define InterruptNotAssert 1'b0
`timescale 1ns / 1ps
`include"define.v"
module div(
input clk,rst,
input [`RegBus] opdata1_i,
//这两个可以用前面乘法的处理过的数据
input [`RegBus] opdata2_i,
input start_i,
input annul_i,
//除法的周期太长了,所以添加一个取消命令,取消则直接结束就好
output reg [`DoubleRegBus] result_o,
//为什么除法的结果会有 64 位,我改成了 32 位,应该是 64 位,
因为高 32 位为商,低 32 位为余数
output reg ready_o
//ready_o 是给 ex 用的,表示结果可以用,开始和结束是由 ex 控制的,
与这个无关
);
reg [`RegAddrBus] cnt;
//用来控制次数以及操作
reg [`RegBus] opdata1_temp; //用来保存临时的数据,书上将结果和这个 temp 放在了一起,组成了 64
位
wire [`RegBus] opdata1_next;
reg [1:0] state;
//改成“无符号数”与除数相减,判断大小,以及用于之后添加新位
assign opdata1_next = opdata1_temp - opdata2_i;
always @(posedge clk)begin
if(rst == `RstEna)begin
state <= `DivFree;
ready_o <= 1'b0;
result_o <= {`ZeroWord,`ZeroWord};
cnt <= 0;
end
case(state)
`DivFree:
begin //将数据准备好,默认的运行状态
if(start_i)begin
state <= `DivOn;
end
opdata1_temp <= opdata1_i[31]; //进行赋初值的操作
ready_o <= 1'b0;
result_o <= {`ZeroWord,`ZeroWord};
cnt <= 0;
end
`DivOn: begin
cnt <= cnt + 1;
if(cnt == 31)begin
//最后一位单独进行处理,并结束
ready_o <= 1'b1;
//等于 31 表示除法运算完成
if(opdata1_next[31])begin
//为真表示小于
result_o[0] <= 0;
result_o[63:32] <= opdata1_temp; //表示余数,小于则直接是余数
end
else begin
result_o[0] <= 1;
result_o[63:32] <= opdata1_next; //表示余数,相减之后的结果表示余数
end
state <= `DivFree;
//回到原来的状态
end
else if(opdata2_i == 0)begin
ready_o <= 1'b1;
result_o <= 0;
//当除数为 0 时,其结果直接输出为 0
state <= `DivFree;
//回到原来的状态
end
//真正进行运算操作的部分
else begin
if(opdata1_next[31])begin
//不管怎样都会添加下一位,但有的是直接添加,有的则
需要减了之后添加,也就是有的用 temp,有的用 next
result_o[31-cnt] <= 0; //小于则用原来的 temp 进行添加,高 32 位为商
opdata1_temp <= {opdata1_temp[30:0],opdata1_i[30-cnt]};
end
else begin
result_o[31-cnt] <= 1; //大于等于则用 next 进行添加
opdata1_temp <= {opdata1_next[30:0],opdata1_i[30-cnt]};
end
end
end //DivOn
default:begin
end
endcase
end
endmodule
`timescale 1ns / 1ps
`include"define.v"
module ex(
input rst,
input [`AluSelBus] alusel_i,
input [`AluOpBus] aluop_i,
input [`RegBus] reg1_i,
input [`RegBus] reg2_i,
input wreg_i,
input [`RegAddrBus] waddr_i,
input [`RegBus] hi_i,
input [`RegBus] lo_i,
//wb 和 mem 的旁路数据
input [`RegBus] wb_hi_i,
input [`RegBus] wb_lo_i,
input wb_whilo_i,
input [`RegBus] mem_hi_i,
input [`RegBus] mem_lo_i,
input mem_whilo_i,
//来自 ex_mem 保存的数据
// input [`DoubleRegBus] hilo_temp_i, 发现没必要这样去绕一圈,难道有什么地方会用到这个数据?
input [1:0] cnt_i,
input [`DoubleRegBus] div_result,
//未判断正负
input div_ready,
//转移指令相关
input is_delay_i,
//至少暂时没用上,也不知道之后会不会用上
input [`InstAddrBus] link_addr,
input [`InstBus] inst_i,
//协处理器访问指令
input [`RegBus] cp0_reg_data_i,
input [`RegBus] wb_cp0_reg_data,
//wb 阶段的旁路
input [`RegAddrBus] wb_cp0_reg_write_addr,
input wb_cp0_reg_we,
input [`RegBus] mem_cp0_reg_data,
//mem 阶段的旁路
input [`RegAddrBus] mem_cp0_reg_write_addr,
input mem_cp0_reg_we,
output reg wreg_o,
output reg [`RegAddrBus] waddr_o,
output reg [`RegBus] wdata_o,
output reg [`RegBus] hi_o,
output reg [`RegBus] lo_o,
output reg whilo_o,
output reg stallreq,
output reg [`DoubleRegBus] hilo_temp_o,
output reg [1:0] cnt_o,
output reg div_start,
output reg div_annul,
output reg [`RegBus] div_opdata1,
output reg [`RegBus] div_opdata2,
//传输到 mem 阶段进行处理
output [`RegBus] reg2_o,
output [`AluOpBus] aluop_o,
output [`RegBus] mem_addr_o,
//这个可以通过 inst 进行获取,需要在 ex 阶段计算出最终的值,
base 以及 offset
//协处理器
output [`RegAddrBus] cp0_reg_read_addr_o,
//直接读取协处理器
output reg [`RegBus] cp0_reg_data_o,
output reg [`RegAddrBus] cp0_reg_write_addr_o,
output reg cp0_reg_we_o
);
//保存逻辑运算的结果(因为现在只有一个 ori 指令,所以只考虑这个)
reg[`RegBus] logicout;
reg[`RegBus] shifters; //移位运算的结果
reg[`RegBus] moveres;
reg[`RegBus] arithout;
reg[`DoubleRegBus] hilo_div;
//保存除法的结果
reg[`RegBus] HI;
reg[`RegBus] LO;
reg[`RegBus] CP0_reg_data;
wire ov_sum;
// wire reg1_eq_reg2;
//相等,相等有什么用呢?答:没用到
wire reg1_lt_reg2;
//1 小于 2
wire [`RegBus] reg2_i_mux;
//reg2 的补码
// wire [`RegBus] reg1_i_not;
//reg1 的反码,这个有什么用呢?答:书上是在找 1 的时候用到
了,而我不需要这个
wire [`RegBus] result_sum;
//加法的结果
wire [`RegBus] opdata1_mult;
//被乘数,这两个有什么用呢?
wire [`RegBus] opdata2_mult;
//乘数,答:这两个是用来转换成正数的,只有都为正数时,其乘
法的结果才是正确的
wire [`DoubleRegBus] hilo_temp;
//临时保存乘法结果,这个结果中可能是为负的,用于接下来转成
负数形式
reg [`DoubleRegBus] hilo_temp1;
//用于临时保存乘累的操作
reg [`DoubleRegBus] mulres;
//保存乘法的结果
reg stallreq_mas;
reg stallreq_div;
wire [`RegBus] hilo_div_temp1;
wire [`RegBus] hilo_div_temp2;
//对比 id.v 以及 ex.v,可以发现:对于输出的数据,一般是在另一个块里面进行操作的
//我觉得 alusel 存在的意义在于使不同之类的指令并行化,不然每次只有一个结果,那么输出一个结果就
可以了,何必多此一举进行选择
//逻辑运算
always @(*)begin
if(rst == `RstEna)begin
logicout <= `ZeroWord;
end
else begin
case(aluop_i)
`EXE_AND_OP:begin
logicout <= reg1_i & reg2_i;
end
`EXE_OR_OP:begin
logicout <= reg1_i | reg2_i;
end
`EXE_XOR_OP:begin
logicout <= reg1_i ^ reg2_i;
end
`EXE_NOR_OP:begin
logicout <= ~(reg1_i | reg2_i);
end
`EXE_LUI_OP:
begin
logicout <= reg2_i;
end
default:
logicout <= `ZeroWord;
endcase
end
end
//移位运算
always @(*)begin
if(rst == `RstEna) begin
shifters <= `ZeroWord;
end
case(aluop_i)
`EXE_SLL_OP:
begin
shifters <= reg2_i << reg1_i[4:0];//低 5 位
end
`EXE_SRL_OP:
begin
shifters <= reg2_i >> reg1_i[4:0];
end
`EXE_SRA_OP:
begin
// shifters <= reg1_i >>> reg2_i;//>>>表示算术右移,不能这样用,因为>>>会根据数据类型
进行相应的操作,而我不知道怎么将无符号数转化为有符号数
shifters <= {{32{reg2_i[31]}}}<<(6'd32-{1'b0,reg1_i[4:0]})
| reg2_i >> reg1_i[4:0];//太巧妙了,不过其实就是对高位进行补符号位,
end
default:begin
shifters <= `ZeroWord;
end
endcase
end
//移动运算
always @(*) begin
if(rst == `RstEna) begin
moveres <= `ZeroWord;
end
else begin
moveres <= `ZeroWord;
case(aluop_i)
`EXE_MFHI_OP:
begin
moveres <= HI;
end
`EXE_MFLO_OP:
begin
moveres <= LO;
end
`EXE_MOVZ_OP:
begin
moveres <= reg1_i;
end
`EXE_MOVN_OP:
begin
moveres <= reg1_i;
end
`EXE_MFC0_OP:
begin
moveres <= CP0_reg_data;
end
default:begin
end
endcase
end
end
//简单算术运算
//发现了一个特点,在 always 块中只进行最后的赋值操作,其余的操作都是放到外面进行的
//算术运算的预处理,组合逻辑用 assign 赋值语句就好了
//当发生减法以及有符号数比较时,需要转换为补码,其中有符号数比较其实就是拿两个数相减的结果进
行判断的
//计算机中存储的数据已经是补码的形式了,再次取补码是因为 -A = A 的补码,也就是为了将减法转换为
加法运算
//这个其实并不是补码运算,因为补码运算是将非符号位取反,而这里是对所有进行取反
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 reg1_i_not = ~reg1_i;
//还是不知道这个有什么用,没用
assign result_sum = reg1_i + reg2_i_mux;
//溢出判断先不考虑指令类型,只要符号不同则是溢出,不一定会用到
assign
ov_sum
(reg1_i[`RegWidth-1]==reg2_i[`RegWidth-1]&&result_sum[`RegWidth-1]!=reg1_i[`RegWidth-1])?1:0;
// assign reg1_eq_reg2 = (reg1_i == reg2_i)? 1:0;
//当为无符号比较时,或者都为正数或都为负数时则需要比较
//否则当前负后正时返回 1,否则返回 0TODO:这里与书上不一样,有可能出错
assign reg1_lt_reg2 = ((aluop_i == `EXE_SLTU_OP)||(reg1_i[`RegWidth-1]&&reg2_i[`RegWidth-1]) ||
(!reg1_i[`RegWidth-1]&&!reg2_i[`RegWidth-1])) ?
(reg1_i<reg2_i):((reg1_i[`RegWidth-1]&&!reg2_i[`RegWidth-1]) ? //前负后正:说明前小后大
1:0);
always @(*)begin
if(rst == `RstEna)begin
arithout <= `ZeroWord;
end
else begin
=
case(aluop_i)
//运算的结果都是存放到 regfile 里面的,所以用一个相同的输出就行了
//加法运算,所有的运算都是一样的,其实加 I 的在前面就不应该有
`EXE_ADD_OP,`EXE_ADDU_OP:
begin
arithout <= result_sum;
end
//减法运算
`EXE_SUB_OP,`EXE_SUBU_OP: begin
arithout <= result_sum;
end
//比较运算
`EXE_SLT_OP,`EXE_SLTU_OP: begin
arithout <= reg1_lt_reg2;
end
//找 rs 的 0,不是 0 时退出,是 0 则继续查找,从高位开始
`EXE_CLZ_OP:
begin
//因为 for 循环不能和非阻塞赋值语句一起使用,所以只能一位
一位的查找
arithout <= reg1_i[`RegWidth-1]?0:
reg1_i[`RegWidth-2]?1:
reg1_i[`RegWidth-3]?2:
reg1_i[`RegWidth-4]?3:
reg1_i[`RegWidth-5]?4:
reg1_i[`RegWidth-6]?5:
reg1_i[`RegWidth-7]?6:
reg1_i[`RegWidth-8]?7:
reg1_i[`RegWidth-9]?8:
reg1_i[`RegWidth-10]?9:
reg1_i[`RegWidth-11]?10:
reg1_i[`RegWidth-12]?11:
reg1_i[`RegWidth-13]?12:
reg1_i[`RegWidth-14]?13:
reg1_i[`RegWidth-15]?14:
reg1_i[`RegWidth-16]?15:
reg1_i[`RegWidth-17]?16:
reg1_i[`RegWidth-18]?17:
reg1_i[`RegWidth-19]?18:
reg1_i[`RegWidth-20]?19:
reg1_i[`RegWidth-21]?20:
reg1_i[`RegWidth-22]?21:
reg1_i[`RegWidth-23]?22:
reg1_i[`RegWidth-24]?23:
reg1_i[`RegWidth-25]?24:
reg1_i[`RegWidth-26]?25:
reg1_i[`RegWidth-27]?26:
reg1_i[`RegWidth-28]?27:
reg1_i[`RegWidth-29]?28:
reg1_i[`RegWidth-30]?29:
reg1_i[`RegWidth-31]?30:
reg1_i[`RegWidth-32]?31:32;
end
//找 1,当为 1 时,则继续向下,为 0 则直接出结果
`EXE_CLO_OP:
begin
arithout <= !reg1_i[`RegWidth-1]?0:
!reg1_i[`RegWidth-2]?1:
!reg1_i[`RegWidth-3]?2:
!reg1_i[`RegWidth-4]?3:
!reg1_i[`RegWidth-5]?4:
!reg1_i[`RegWidth-6]?5:
!reg1_i[`RegWidth-7]?6:
!reg1_i[`RegWidth-8]?7:
!reg1_i[`RegWidth-9]?8:
!reg1_i[`RegWidth-10]?9:
!reg1_i[`RegWidth-11]?10:
!reg1_i[`RegWidth-12]?11:
!reg1_i[`RegWidth-13]?12:
!reg1_i[`RegWidth-14]?13:
!reg1_i[`RegWidth-15]?14:
!reg1_i[`RegWidth-16]?15:
!reg1_i[`RegWidth-17]?16:
!reg1_i[`RegWidth-18]?17:
!reg1_i[`RegWidth-19]?18:
!reg1_i[`RegWidth-20]?19:
!reg1_i[`RegWidth-21]?20:
!reg1_i[`RegWidth-22]?21:
!reg1_i[`RegWidth-23]?22:
!reg1_i[`RegWidth-24]?23:
!reg1_i[`RegWidth-25]?24:
!reg1_i[`RegWidth-26]?25:
!reg1_i[`RegWidth-27]?26:
!reg1_i[`RegWidth-28]?27:
!reg1_i[`RegWidth-29]?28:
!reg1_i[`RegWidth-30]?29:
!reg1_i[`RegWidth-31]?30:
!reg1_i[`RegWidth-32]?31:32;
end
default:begin
arithout <= `ZeroWord;
end
endcase
end
end
//乘法运算
//如果按照现实中的乘法,乘出来的结果是源码,而在计算机中应该是补码,所以需要取其补码,这个补
码与上面的补码不同
//如果是负数,那么需要对其取补码计算,只有在是有符号乘法时,才需要进行补码处理
assign opdata1_mult = ((aluop_i == `EXE_MULT_OP || aluop_i == `EXE_MUL_OP||
aluop_i == `EXE_MADD_OP || aluop_i == `EXE_MSUB_OP||
aluop_i == `EXE_DIV_OP)
&&reg1_i[`RegWidth-1])?(~reg1_i+1):reg1_i;
assign opdata2_mult = ((aluop_i == `EXE_MULT_OP || aluop_i == `EXE_MUL_OP||
aluop_i == `EXE_MADD_OP || aluop_i == `EXE_MSUB_OP||
aluop_i == `EXE_DIV_OP)
&&reg2_i[`RegWidth-1])?(~reg2_i+1):reg2_i;
assign hilo_temp = opdata1_mult * opdata2_mult;
always @(*) begin
if(rst == `RstEna)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[`RegWidth-1] ^ reg2_i[`RegWidth-1]) begin
mulres <= (~hilo_temp + 1);
end
else begin
mulres <= hilo_temp;
end
end
else begin
mulres <= hilo_temp;
end
end
//乘累加以及乘累减运算
always @(*)begin
if(rst == `RstEna)begin
hilo_temp_o <= {`ZeroWord,`ZeroWord};
cnt_o <= 2'b00;
hilo_temp1 <= {`ZeroWord,`ZeroWord};
stallreq_mas <= `NoStop;
end
else begin
if(aluop_i == `EXE_MADD_OP || aluop_i == `EXE_MADDU_OP)begin
if(cnt_i == 2'b00)begin
hilo_temp_o <= mulres;
//这个是在 mulres 发生变化的时候执行,所以不用担心赋值
失败
cnt_o <= 2'b01;
stallreq_mas <= `Stop;
end
else if(cnt_i == 2'b01)begin
// hilo_temp_o <= {`ZeroWord,`ZeroWord};
cnt_o <= 2'b10;
hilo_temp1 <= hilo_temp_o + {HI,LO};
stallreq_mas <= `NoStop;
end
else begin
end
end
else if(aluop_i == `EXE_MSUB_OP || aluop_i == `EXE_MSUBU_OP)begin
if(cnt_i == 2'b00)begin
hilo_temp_o <= ~mulres + 1;
//是 HILO 减去乘的结果,所以需要对其进行取反
cnt_o <= 2'b01;
stallreq_mas <= `Stop;
end
else if(cnt_i == 2'b01)begin
// hilo_temp_o <= {`ZeroWord,`ZeroWord};
cnt_o <= 2'b10;
hilo_temp1 <= hilo_temp_o + {HI,LO};
stallreq_mas <= `NoStop;
end
else begin
end
end
else begin
hilo_temp_o <= {`ZeroWord,`ZeroWord};
cnt_o <= 2'b00;
stallreq_mas <= `NoStop;
end
end //!rst
end
//除法运算
assign hilo_div_temp1 = (reg1_i[31] ^ reg2_i[31])?(~div_result[31:0] + 1):div_result[31:0];
assign hilo_div_temp2 = (reg1_i[31] ^ div_result[63])?(~div_result[63:32] + 1):div_result[63:32];
always @(*)begin
if(rst == `RstEna)begin
hilo_div <= {`ZeroWord,`ZeroWord};
div_annul <= 1'b0;
div_start <= 1'b0;
div_opdata1 <= `ZeroWord;
div_opdata2 <= `ZeroWord;
end
else begin
div_opdata1 <= opdata1_mult;
//借用乘法的补码
div_opdata2 <= opdata2_mult;
if(aluop_i == `EXE_DIV_OP)begin
div_start <= 1'b1;
stallreq_div <= `Stop;
//只能在里面进行阻塞
hilo_div[31:0] <= hilo_div_temp1;
hilo_div[63:32] <= hilo_div_temp2;
if(div_ready)begin
//当准备好了之后才进行写入操作
div_start <= 1'b0;
stallreq_div <= `NoStop;
//完成之后,取消阻塞操作
end
else begin
end
end
else if(aluop_i == `EXE_DIVU_OP)begin
div_start <= 1'b1;
stallreq_div <= `Stop;
hilo_div <= div_result;
if(div_ready)begin
//当准备好了之后才进行写入操作
div_start <= 1'b0;
stallreq_div <= `NoStop;
//完成之后,取消阻塞操作
end
else begin
end
end
end
end
//暂停流水线
always @(*)begin
stallreq = stallreq_mas || stallreq_div;
end
//加载存储指令,在这里没有对写入 regfile 相关的进行处理,留到 mem 阶段进行处理了
assign reg2_o = reg2_i;
assign aluop_o = aluop_i;
assign mem_addr_o = reg1_i + {{16{inst_i[15]}},inst_i[15:0]};
//base + 符号扩展后的 offset
//根据 alusel 选择输出结果
always @(*)begin
waddr_o <= waddr_i;
//不论指令是什么,都将地址进行输出
if(((aluop_i == `EXE_ADD_OP) || (aluop_i == `EXE_SUB_OP))&&(ov_sum == `True_v))begin
wreg_o <= `WriteDisa;
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 <= shifters;
end
`EXE_RES_MOVE:begin
wdata_o <= moveres;
end
`EXE_RES_ARITH:begin
wdata_o <= arithout;
end
`EXE_RES_MUL:begin
wdata_o <= mulres[31:0];
end
`EXE_RES_JUMP_BRANCH:begin
wdata_o <= link_addr;
end
default: begin
wdata_o <= `ZeroWord;
end
endcase
end
//流向 hilo_reg 的数据
always @(*)begin
if(rst == `RstEna)begin
whilo_o <= `WriteDisa;
hi_o <= `ZeroWord;
lo_o <= `ZeroWord;
end
else if(aluop_i == `EXE_MTHI_OP)begin
whilo_o <= `WriteEna;
hi_o <= reg1_i;
//将 hi 赋为新值,lo 保持不变
lo_o <= LO;
end
else if(aluop_i == `EXE_MTLO_OP)begin
whilo_o <= `WriteEna;
hi_o <= HI;
lo_o <= reg1_i;
end
//为什么是相同的,不应该一个是有符号数,一个是无符号数吗?之前计算的是有符号数乘法,那么
无符号数乘法有是什么样的呢?
//答:在之前的处理中,已经根据运算的不同的处理 mulres,也就是说:其结果既可能是有符号的,
也可能是无符号的
else if(aluop_i == `EXE_MULT_OP || aluop_i == `EXE_MULTU_OP)begin
whilo_o <= `WriteEna;
hi_o <= mulres[63:32];
lo_o <= mulres[31:0];
end
else if(aluop_i == `EXE_MADD_OP || aluop_i == `EXE_MADDU_OP ||
aluop_i == `EXE_MSUB_OP || aluop_i == `EXE_MSUBU_OP)begin
whilo_o <= `WriteEna;
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 <= `WriteEna;
hi_o <= hilo_div[63:32];
lo_o <= hilo_div[31:0];
end
else begin
whilo_o <= `WriteDisa;
hi_o <= `ZeroWord;
lo_o <= `ZeroWord;
end
end
//流向 CP0 的数据
always @(*)begin
if(rst == `RstEna)begin
cp0_reg_data_o <= `ZeroWord;
cp0_reg_write_addr_o <= 5'b0;
cp0_reg_we_o <= 1'b0;
end
else begin
if(aluop_i == `EXE_MTC0_OP)begin
cp0_reg_data_o <= reg2_i;
//这里和书上不一样,因为 reg2 就是 rt 的值,而书上是
改了 reg1 的地址进行获取 rt 的值
cp0_reg_write_addr_o <= inst_i[15:11];
cp0_reg_we_o <= 1'b1;
end
else begin
cp0_reg_data_o <= `ZeroWord;
cp0_reg_write_addr_o <= 5'b0;
cp0_reg_we_o <= 1'b0;
end
end
end
//准备工作:将 HI 和 LO 的现值准备好
always @(*) begin
if(rst == `RstEna)begin
{HI,LO} <= {`ZeroWord,`ZeroWord};
end
//旁路的选择
else if(mem_whilo_i == `WriteEna) begin
//优先判断 mem,因为 mem 比 wb 后写回,也就是比
wb 更新
{HI,LO} <= {mem_hi_i,mem_lo_i};
end
else if(wb_whilo_i == `WriteEna) begin
{HI,LO} <= {wb_hi_i,wb_lo_i};
end
else begin
{HI,LO} <= {hi_i,lo_i};
end
end
//准备工作:获取协处理中的最新值
assign cp0_reg_read_addr_o = inst_i[15:11];
always @(*)begin
if(rst == `RstEna)begin
CP0_reg_data <= `ZeroWord;
end //当能写且地址与接下来读取的地址相同时,则直接获取该数据
else if(mem_cp0_reg_we == 1'b1 && mem_cp0_reg_write_addr == inst_i[15:11])begin
CP0_reg_data <= mem_cp0_reg_data;
end
else if(wb_cp0_reg_we == 1'b1 && wb_cp0_reg_write_addr == inst_i[15:11])begin
CP0_reg_data <= wb_cp0_reg_data;
end
else begin
CP0_reg_data <= cp0_reg_data_i;
end
end
endmodule
`timescale 1ns / 1ps
`include"define.v"
module ex_mem(
input clk,rst,
input [`RegAddrBus] ex_waddr,
input [`RegBus] ex_wdata,
input ex_wreg,
input ex_whilo,
input [`RegBus] ex_hi,
input [`RegBus] ex_lo,
input [5:0] stall,
//ex 阶段存入的多周期数据
input [1:0] cnt_i,
// input [`DoubleRegBus] hilo_temp_i,
input [`AluOpBus] ex_aluop,
input [`RegBus] ex_mem_addr,
input [`RegBus] ex_reg2,
//协处理器
input [`RegBus] ex_cp0_reg_data,
input [`RegAddrBus] ex_cp0_reg_write_addr,
input ex_cp0_reg_we,
output reg [`RegAddrBus] mem_waddr,
output reg [`RegBus] mem_wdata,
output reg mem_wreg,
output reg mem_whilo,
output reg [`RegBus] mem_hi,
output reg [`RegBus] mem_lo,
//输出到 ex 的多周期数据
output reg [1:0] cnt_o,
// output reg [`DoubleRegBus] hilo_temp_o
output reg [`AluOpBus] mem_aluop,
output reg [`RegBus] mem_mem_addr,
output reg [`RegBus] mem_reg2,
//协处理器
output reg [`RegBus] mem_cp0_reg_data,
output reg [`RegAddrBus] mem_cp0_reg_write_addr,
output reg mem_cp0_reg_we
);
always @(posedge clk)begin
if(rst == `RstEna)begin
mem_waddr <= `NOPRegAddr;
mem_wdata <= `ZeroWord;
mem_wreg <= `WriteDisa;
mem_whilo <= `WriteDisa;
mem_hi <= `ZeroWord;
mem_lo <= `ZeroWord;
cnt_o <= 2'b00;
// hilo_temp_o <= {`ZeroWord,`ZeroWord};
mem_aluop <= `EXE_NOP_OP;
mem_mem_addr <= `ZeroWord;
mem_reg2 <= `ZeroWord;
mem_cp0_reg_data <= `ZeroWord;
mem_cp0_reg_write_addr <= 5'b0;
mem_cp0_reg_we <= 1'b0;
end
else if(stall[3] == `Stop && stall[4] == `NoStop)begin
mem_waddr <= `NOPRegAddr;
mem_wdata <= `ZeroWord;
mem_wreg <= `WriteDisa;
mem_whilo <= `WriteDisa;
mem_hi <= `ZeroWord;
mem_lo <= `ZeroWord;
cnt_o <= cnt_i;
//只有在流水线阻塞的时候才进行赋值
// hilo_temp_o <= hilo_temp_i;
mem_aluop <= `EXE_NOP_OP;
mem_mem_addr <= `ZeroWord;
//其他时候也可以赋值,但是没有意义
mem_reg2 <= `ZeroWord;
mem_cp0_reg_data <= `ZeroWord;
mem_cp0_reg_write_addr <= 5'b0;
mem_cp0_reg_we <= 1'b0;
end
else if(stall[3] == `NoStop)begin
//在乘累加的第二阶段就已经解除阻塞了,也就是在最后一个阶段
都需要解除阻塞
mem_waddr <= ex_waddr;
mem_wdata <= ex_wdata;
mem_wreg <= ex_wreg;
mem_whilo <= ex_whilo;
mem_hi <= ex_hi;
mem_lo <= ex_lo;
cnt_o <= `ZeroWord;
//在这里进行清零操作,每次多周期操作之后都处理一下
// hilo_temp_o <= `ZeroWord;
mem_aluop <= ex_aluop;
mem_mem_addr <= ex_mem_addr;
mem_reg2 <= ex_reg2;
mem_cp0_reg_data <= ex_cp0_reg_data;
mem_cp0_reg_write_addr <= ex_cp0_reg_write_addr;
mem_cp0_reg_we <= ex_cp0_reg_we;
end
else begin
// hilo_temp_o <= hilo_temp_i;
cnt_o <= cnt_i;
end
end
endmodule
`timescale 1ns / 1ps
`include "define.v"
module hilo_reg(
input clk,
input rst,
input we,
input [`RegBus] hi_i,
input [`RegBus] lo_i,
output reg [`RegBus] hi_o,
output reg [`RegBus] lo_o
);
always@(*)begin
if(rst == `RstEna)begin
hi_o <= `ZeroWord;
lo_o <= `ZeroWord;
end
else if(we == `WriteEna)begin
hi_o <= hi_i;
lo_o <= lo_i;
end
end
endmodule
`timescale 1ns / 1ps
//与 pc 和 regfile 组合,完成译码功能,取数也是在这时候开始的
//所谓的译码就是将指令中需要的东西准备好
//相关的操作就是根据指令来对 regfile 进行读写操作,只是一个中间件
//MIPS 的指令类型:(具体的操作在 alu 单元中)
//1.R 型指令
//2.I 型指令
//3.J 型指令
`include"define.v"
module id(
input rst,
input [`InstAddrBus] pc_i,
//从 pc 传输来的指令地址
input [`InstBus] inst_i,
//用地址从 regfile 中得到的指令,果然留着是有用的,传递到 ex
//读取的 Regfile 的值
input [`RegBus] reg1_data_i,
//从 regfile 输入的第一个读输入
input [`RegBus] reg2_data_i,
//第二个读输入
//来自 ex 阶段的旁路数据,当相邻指令发生数据冲突时
input [`RegBus] ex_wdata_i,
input [`RegAddrBus] ex_waddr_i,
input ex_wreg_i,
input [`AluOpBus] ex_aluop_i,
//来自 mem 阶段的旁路数据,当间隔一条指令发生数据发生数据冲突时
input [`RegBus] mem_wdata_i,
input [`RegAddrBus] mem_waddr_i,
input mem_wreg_i,
//来自 id_ex 的输入,判断指令是否为延迟指令
input is_delay_inst_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 wreg_o,
//写使能信号
output reg [`RegAddrBus] waddr_o,
//写入寄存器地址(目的寄存器 rd )
output reg [`RegBus] reg1_o,
//输出的源操作数 1
output reg [`RegBus] reg2_o,
//源操作数 2
output reg [`AluOpBus] aluop_o,
//alu 控制信号
output reg [`AluSelBus] alusel_o,
//运算类型
output reg is_delay_inst_o,
//当前指令是否为延迟槽中的指令,实际上没有什么用
output reg [`InstAddrBus] link_addr_o, //返回地址
output [`InstBus] inst_o,
//指令
output reg stallreq,
//用于解决 beq 与 lw 这类指令之间的数据冲突
//送回 pc 的数据
output reg branch_flag_o,
output reg [`InstAddrBus] branch_addr_inst_o,
//取 id_ex 绕了一个周期后返回,用来判断是否为延迟指令
output reg next_inst_is_delay_o
);
//对 inst_o 进行赋值
assign inst_o = inst_i;
//不同指令对应的指令段不同,op > op2 > op3 > op4,对指令的判断顺序
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];
//立即数,等待后面扩展为 32 位之后再赋值
reg [`RegBus] imm;
wire [`RegBus] pc_plus_4;
//用来暂时存储下一条指令的地址,pc_i + 4;
wire [`RegBus] pc_plus_8;
//用来存储返回地址
//指示指令是否有效,没考虑到这个,实际上暂时没用到,后面异常处理可能会用上
reg instvalid;
//判断上一条指令是否为 load 指令以及判断是否需要阻塞
reg stallreq_for_reg1;
reg stallreq_for_reg2;
wire inst_is_load;
assign pc_plus_4 = (pc_i + 4);
assign pc_plus_8 = (pc_i + 8);
//没有将 ll 以及 sc 加入进去
assign 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_OP || ex_aluop_i == `EXE_LW_OP || ex_aluop_i
== `EXE_LWL_OP ||
ex_aluop_i == `EXE_LWR_OP || ex_aluop_i == `EXE_LL_OP || ex_aluop_i
== `EXE_SC_OP)
?1'b1:1'b0;
/**********************一、对指令进行译码*****************************/
always @(*)begin
if(rst == `RstEna)begin
reg1_read_o <= `ReadDisa;
reg2_read_o <= `ReadDisa;
reg1_addr_o <= `RegNumLog2'b0;
reg2_addr_o <= `RegNumLog2'b0;
wreg_o <= `WriteDisa;
waddr_o <= `NOPRegAddr;
//宏定义:默认地址为空时
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
imm <= 32'h0;
instvalid <= `InstValid;
branch_flag_o <= 1'b0;
link_addr_o <= `ZeroWord;
next_inst_is_delay_o <= 1'b0;
branch_addr_inst_o <= `ZeroWord;
end
//协处理器访问指令
else if(inst_i[31:21] == 11'b01000000100 &&
//mtc0 指令
inst_i[10:0] == 11'b0)begin
aluop_o <= `EXE_MTC0_OP;
alusel_o <= `EXE_RES_MOVE;
wreg_o <= `WriteDisa;
//这个写是对寄存器组来说的
reg1_read_o <= `ReadDisa;
reg2_read_o <= `ReadEna;
//获取 rt 寄存器的值,用来写入到地址为 rd 的协处理器寄
存器中
instvalid <= `InstValid;
end
else if(inst_i[31:21] == 11'b01000000000 &&
//mfc0 指令
inst_i[10:0] == 11'b0)begin
aluop_o <= `EXE_MFC0_OP;
alusel_o <= `EXE_RES_MOVE;
waddr_o <= inst_i[20:16];
wreg_o <= `WriteEna;
reg1_read_o <= `ReadDisa;
reg2_read_o <= `ReadDisa;
instvalid <= `InstValid;
end
else begin
//先对共用的部分进行初始化,主要是对输出到执行阶段的部分进行赋值,只是进行初始化,设置一
些默认值
aluop_o <= `EXE_NOP_OP;
//先初始化为气泡
alusel_o <= `EXE_RES_NOP;
waddr_o <= inst_i[15:11];
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
reg1_read_o <= `ReadDisa;
reg2_read_o <= `ReadDisa;
//默认为 rd 寄存器
reg1_addr_o <= inst_i[25:21];
//rs 寄存器,每次进行赋值是为了避免后面覆盖之后获取的就变
reg2_addr_o <= inst_i[20:16];
//rt 寄存器
了
imm <= `ZeroWord;
branch_flag_o <= 1'b0;
link_addr_o <= `ZeroWord;
next_inst_is_delay_o <= 1'b0;
branch_addr_inst_o <= `ZeroWord;
//指令码控制
case(op)
//这里面主要是对控制信号以及地址进行操作
`EXE_ANDI: begin
aluop_o <= `EXE_AND_OP;
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
imm <= {16'b0,inst_i[15:0]};
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_ORI:
begin
//读取 rs 的数据,目的寄存器为 rt
aluop_o <= `EXE_OR_OP;
alusel_o <= `EXE_RES_LOGIC;
//读取数据
reg1_read_o <= `ReadEna;
//ori 操作只需要 rs
reg2_read_o <= `ReadDisa;
imm <= {16'b0,inst_i[15:0]};
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_XORI: begin
aluop_o <= `EXE_XOR_OP;
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
imm <= {16'b0,inst_i[15:0]};
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_LUI:
begin
//高 16 位存放立即数数据,低 16 位存 0
aluop_o <= `EXE_LUI_OP;
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= `ReadEna;
//源操作数 1 赋值为 0
reg2_read_o <= `ReadDisa;
//源操作数 2 赋值为立即数
imm <= {inst_i[15:0],16'h0};
//直接利用立即数,所以不用扩展,但是 imm 还是 32
位的,所以还是要扩展,后面也是按 32 位进行处理
wreg_o <= `WriteEna;
//因为在 ex 没有相应的操作,所以只有这里赋值为
立即数
waddr_o <= inst_i[20:16];
//写入的地址是 rt
instvalid <= `InstValid;
end
`EXE_PREF: begin
//TODO:不知道 pref 指令有什么用
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
reg1_read_o <= `ReadDisa;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
`EXE_ADDI: begin
aluop_o <= `EXE_ADD_OP;
alusel_o <= `EXE_RES_ARITH;
reg1_read_o <= `ReadEna;
//立即数运算,rt <- rs + imm
reg2_read_o <= `ReadDisa;
imm <= {{16{inst_i[15]}},inst_i[15:0]};
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_ADDIU: begin
aluop_o <= `EXE_ADDU_OP;
alusel_o <= `EXE_RES_ARITH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
imm <= {{16{inst_i[15]}},inst_i[15:0]};
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_SLTI: begin
aluop_o <= `EXE_SLT_OP;
alusel_o <= `EXE_RES_ARITH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
imm <= {{16{inst_i[15]}},inst_i[15:0]};
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_SLTIU: begin
aluop_o <= `EXE_SLTU_OP;
alusel_o <= `EXE_RES_ARITH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
imm <= {{16{inst_i[15]}},inst_i[15:0]};//符号扩展
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
//转移指令
`EXE_J: begin
aluop_o <= `EXE_J_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= `ReadDisa;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
branch_flag_o <= 1'b1;
next_inst_is_delay_o <= 1'b1;
branch_addr_inst_o <= {pc_plus_4[31:28],inst_i[25:0],2'b00};
end
`EXE_JAL:
begin
aluop_o <= `EXE_JAL_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= `ReadDisa;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteEna;
waddr_o <= 5'b11111;
//指定将返回地址存储到$31 中
instvalid <= `InstValid;
branch_flag_o <= 1'b1;
next_inst_is_delay_o <= 1'b1;
branch_addr_inst_o <= {pc_plus_4[31:28],inst_i[25:0],2'b00};
link_addr_o <= pc_plus_8;
end
`EXE_BEQ:
begin
aluop_o <= `EXE_BEQ_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
if(reg1_o == reg2_o)begin
//相等才转移
branch_flag_o <= 1'b1;
next_inst_is_delay_o <= 1'b1;
branch_addr_inst_o <= pc_plus_4 + {{14{inst_i[15]}},inst_i[15:0],2'b00};
两位后符号扩展为 32 位,再与延迟槽指令地址相加
end
else begin
end
end
`EXE_BGTZ:
begin
aluop_o <= `EXE_BGTZ_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
//左移
instvalid <= `InstValid;
if(reg1_o[31] == 1'b0 && reg1_o != `ZeroWord)begin
//书上不是直接用的大小与
符号进行比较的,因为这样比较,不论正负都是大于 0 的,因为补码
branch_flag_o <= 1'b1;
next_inst_is_delay_o <= 1'b1;
branch_addr_inst_o <= pc_plus_4 + {{14{inst_i[15]}},inst_i[15:0],2'b00};
end
else begin
end
end
`EXE_BLEZ:
begin
aluop_o <= `EXE_BLEZ_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
if(reg1_o[31] == 1'b1 || reg1_o == `ZeroWord)begin
branch_flag_o <= 1'b1;
next_inst_is_delay_o <= 1'b1;
branch_addr_inst_o <= pc_plus_4 + {{14{inst_i[15]}},inst_i[15:0],2'b00};
end
else begin
end
end
`EXE_BNE:
begin
aluop_o <= `EXE_BNE_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
if(reg1_o != reg2_o)begin
branch_flag_o <= 1'b1;
next_inst_is_delay_o <= 1'b1;
branch_addr_inst_o <= pc_plus_4 + {{14{inst_i[15]}},inst_i[15:0],2'b00};
end
else begin
end
end
//加载存储指令
`EXE_LB:
begin
aluop_o <= `EXE_LB_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
//rs 中存放的是 base,所以需要进行读取
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
//将读取结果放到 rt 寄存器中
end
`EXE_LBU:
begin
aluop_o <= `EXE_LBU_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_LH:
begin
aluop_o <= `EXE_LH_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_LHU:
begin
aluop_o <= `EXE_LHU_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_LW:
begin
aluop_o <= `EXE_LW_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_LWL:
begin
aluop_o <= `EXE_LWL_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
//因为只是修改其中的一部分,所以需要进行读取,也
就是后面会用连接的方式进行放入 rt
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_LWR:
begin
aluop_o <= `EXE_LWR_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_SB:
begin
aluop_o <= `EXE_SB_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
//rs 中的值作为基地址
reg2_read_o <= `ReadEna;
//将 rt 中的值存放到内存中
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
`EXE_SH:
begin
aluop_o <= `EXE_SH_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
`EXE_SW:
begin
aluop_o <= `EXE_SW_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
`EXE_SWL:
begin
aluop_o <= `EXE_SWL_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
`EXE_SWR:
begin
aluop_o <= `EXE_SWR_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
`EXE_LL:
begin
aluop_o <= `EXE_LL_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteEna;
waddr_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_SC:
begin
aluop_o <= `EXE_SC_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
//R 型指令
`EXE_SPECIAL_INST: begin
//指令码为 0 的情况,R 型指令
case(op2)
5'b00000: begin
case(op3)
`EXE_AND:
begin
aluop_o <= `EXE_AND_OP;
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
// waddr_o <= inst_i[20:16];这个是 ori 中将数据存到 rt 中,默认是存
到 rd 中,所以不用单独赋值
instvalid <= `InstValid;
end
`EXE_OR:
begin
aluop_o <= `EXE_OR_OP;
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
`EXE_XOR:
begin
aluop_o <= `EXE_XOR_OP;
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
`EXE_NOR:
begin
aluop_o <= `EXE_NOR_OP;
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
//移位操作
`EXE_SLLV: begin
aluop_o <= `EXE_SLL_OP;
alusel_o <= `EXE_RES_SHIFT;
reg1_read_o <= `ReadEna;
//用 rs 作为偏移量
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
`EXE_SRLV: begin
aluop_o <= `EXE_SRL_OP;
alusel_o <= `EXE_RES_SHIFT;
reg1_read_o <= `ReadEna;
//用 rs 作为偏移量
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
`EXE_SRAV: begin
aluop_o <= `EXE_SRA_OP;
alusel_o <= `EXE_RES_SHIFT;
reg1_read_o <= `ReadEna;
//用 rs 作为偏移量
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
//移动操作
`EXE_MFHI: begin
wreg_o <= `WriteEna;
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 <= `WriteEna;
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
wreg_o <= `WriteDisa;
aluop_o <= `EXE_MTHI_OP;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
end
`EXE_MTLO: begin
wreg_o <= `WriteDisa;
aluop_o <= `EXE_MTLO_OP;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
end
`EXE_MOVN: begin
aluop_o <= `EXE_MOVN_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 <= `WriteEna;
end
else begin
wreg_o <= `WriteDisa;
end
end
`EXE_MOVZ: begin
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 <= `WriteEna;
end
else begin
wreg_o <= `WriteDisa;
end
end
//算术操作
`EXE_ADD:
begin
aluop_o <= `EXE_ADD_OP;
alusel_o <= `EXE_RES_ARITH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
`EXE_ADDU:
begin
aluop_o <= `EXE_ADDU_OP;
alusel_o <= `EXE_RES_ARITH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
`EXE_SUB:
begin
aluop_o <= `EXE_SUB_OP;
alusel_o <= `EXE_RES_ARITH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
`EXE_SUBU:
begin
aluop_o <= `EXE_SUBU_OP;
alusel_o <= `EXE_RES_ARITH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
`EXE_SLT:
begin
aluop_o <= `EXE_SLT_OP;
alusel_o <= `EXE_RES_ARITH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
`EXE_SLTU:
begin
aluop_o <= `EXE_SLTU_OP;
alusel_o <= `EXE_RES_ARITH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
`EXE_MULT: begin
aluop_o <= `EXE_MULT_OP;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
//结果写入 hi(高位)与 lo(低位)中
instvalid <= `InstValid;
end
`EXE_MULTU: begin //无符号数乘法运算
aluop_o <= `EXE_MULTU_OP;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
`EXE_DIV:
begin
aluop_o <= `EXE_DIV_OP;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
`EXE_DIVU: begin
aluop_o <= `EXE_DIVU_OP;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
//跳转指令
`EXE_JR:
begin
aluop_o <= `EXE_JR_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
//不需要保存数据到 regfile 中
branch_flag_o <= 1'b1;
next_inst_is_delay_o <= 1'b1;
branch_addr_inst_o <= reg1_o;
end
`EXE_JALR: begin
aluop_o <= `EXE_JALR_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteEna;
//最后要将地址写入到寄存器中
instvalid <= `InstValid;
branch_flag_o <= 1'b1;
link_addr_o <= pc_plus_8;
//需要将返回地址保存下来
next_inst_is_delay_o <= 1'b1;
branch_addr_inst_o <= reg1_o;
//可以直接在这里面使用,也就是
说改变之后会马上作用到这里
end
//空指令,nop 以及 snop 不用单独处理,一种特殊的移位操作
`EXE_SYNC: begin
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
reg1_read_o <= `ReadDisa;
reg2_read_o <= `ReadDisa;//为什么空指令都是将 reg2 设置为可读,
答:在 ex 阶段都没有对其进行处理,是什么都无所谓
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
default:begin
end
endcase //case(op3)
end // op2=5'b00000
default:begin
end
endcase //case(op2)
end //SPECIAL 指令
`EXE_SPECIAL2_INST: begin
case(op2)
5'b00000:
begin
case(op3)
`EXE_CLZ:
begin
aluop_o <= `EXE_CLZ_OP;
alusel_o <= `EXE_RES_ARITH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
`EXE_CLO:
begin
//clz rd,rs;找 rs 中 0 的个数,z:zero
aluop_o <= `EXE_CLO_OP;
alusel_o <= `EXE_RES_ARITH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
`EXE_MUL:
begin
aluop_o <= `EXE_MUL_OP;
alusel_o <= `EXE_RES_MUL;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteEna;
instvalid <= `InstValid;
end
`EXE_MADD: begin
aluop_o <= `EXE_MADD_OP;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
`EXE_MADDU: begin
aluop_o <= `EXE_MADDU_OP;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
`EXE_MSUB: begin
aluop_o <= `EXE_MSUB_OP;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
`EXE_MSUBU: begin
aluop_o <= `EXE_MSUBU_OP;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadEna;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
end
endcase //case(op3)special2 中
end
default:begin
end
endcase //case(op2)special2 中
end //SPECIAL2 指令
//REGIMM 类型指令
`EXE_REGIMM_INST:
begin
case(op4)
`EXE_BLTZ:
begin
aluop_o <= `EXE_BLTZ_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
if(reg1_o[31] == 1'b1)begin
branch_flag_o <= 1'b1;
next_inst_is_delay_o <= 1'b1;
branch_addr_inst_o <= pc_plus_4 + {{14{inst_i[15]}},inst_i[15:0],2'b00};
end
else begin
end
end
`EXE_BLTZAL:
begin
aluop_o <= `EXE_BLTZAL_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
instvalid <= `InstValid;
if(reg1_o[31] == 1'b1)begin
//小于时转移,并保存返回地址
wreg_o <= `WriteEna;
waddr_o <= 5'b11111;
branch_flag_o <= 1'b1;
next_inst_is_delay_o <= 1'b1;
branch_addr_inst_o <= pc_plus_4 + {{14{inst_i[15]}},inst_i[15:0],2'b00};
link_addr_o <= pc_plus_8;
end
else begin
end
end
`EXE_BGEZ:
begin
aluop_o <= `EXE_BGEZ_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
wreg_o <= `WriteDisa;
instvalid <= `InstValid;
if(reg1_o[31] == 1'b0)begin
branch_flag_o <= 1'b1;
next_inst_is_delay_o <= 1'b1;
branch_addr_inst_o <= pc_plus_4 + {{14{inst_i[15]}},inst_i[15:0],2'b00};
end
else begin
end
end
`EXE_BGEZAL:
begin
aluop_o <= `EXE_BGEZAL_OP;
alusel_o <= `EXE_RES_JUMP_BRANCH;
reg1_read_o <= `ReadEna;
reg2_read_o <= `ReadDisa;
instvalid <= `InstValid;
if(reg1_o[31] == 1'b0)begin
wreg_o <= `WriteEna;//书上将 wwl 放到了外面进行赋值,但是如果不满
足条件的话,为什么还是将返回地址写入呢?答:没有影响
waddr_o <= 5'b11111;
branch_flag_o <= 1'b1;
next_inst_is_delay_o <= 1'b1;
branch_addr_inst_o <= pc_plus_4 + {{14{inst_i[15]}},inst_i[15:0],2'b00};
link_addr_o <= pc_plus_8;
end
else begin
end
end
endcase
end //EXE_REGIMM_INST 指令类型
default:begin
//必须要加一个 default,避免成为所以锁存器,即使 default 为空
end
endcase //case(op)
if(inst_i[31:21] == 11'd0)begin
case(op3)
`EXE_SLL:
begin
//逻辑左移,用到了 shamt,也就是 op2
aluop_o <= `EXE_SLL_OP;
alusel_o <= `EXE_RES_SHIFT;
reg1_read_o <= `ReadDisa;
reg2_read_o <= `ReadEna;
//读取 rt 寄存器的值
imm[4:0] <= inst_i[10:6];//用 shamt 作为输出,本来只有 4 位,我还补 16 个 0?
wreg_o <= `WriteEna;
waddr_o <= inst_i[15:11];
instvalid <= `InstValid;
end
`EXE_SRL:
begin
//逻辑右移
aluop_o <= `EXE_SRL_OP;
alusel_o <= `EXE_RES_SHIFT;
reg1_read_o <= `ReadDisa;
reg2_read_o <= `ReadEna;
imm[4:0] <= inst_i[10:6];
wreg_o <= `WriteEna;
waddr_o <= inst_i[15:11];
instvalid <= `InstValid;
end
`EXE_SRA:
begin
//算术右移
aluop_o <= `EXE_SRA_OP;
alusel_o <= `EXE_RES_SHIFT;
reg1_read_o <= `ReadDisa;
reg2_read_o <= `ReadEna;
imm[4:0] <= inst_i[10:6];
wreg_o <= `WriteEna;
waddr_o <= inst_i[15:11];
instvalid <= `InstValid;
end
default:begin
end
endcase //case(op3)
end
end //if
end
//always
通过这样的方法,使块更加可读
//分开写的原因:
//1.不同的敏感列表,随时需要读取操作
//2.所有指令公用这些,只需要设置读使能信号就行了
//3.不是两个操作数都会读取
/**********************二、读取源操作数 1*****************************/
always @(*)begin
if(rst == `RstEna)begin
reg1_o <= `ZeroWord;
end
else if(reg1_read_o == `ReadEna)begin
//当读地址与写地址相同,并且写使能为真时,说明发生了数据相关,这是需要旁路
if((reg1_addr_o==ex_waddr_i)&&(ex_wreg_i==`WriteEna))begin
reg1_o <= ex_wdata_i;
end
else if((reg1_addr_o==mem_waddr_i)&&(mem_wreg_i==`WriteEna))begin
reg1_o <= mem_wdata_i;
end
//当没有发生旁路时,则从 regfile 中读取数据
else begin
reg1_o <= reg1_data_i; //regfile 读端口 1 的值
end
end
else if(reg1_read_o == `ReadDisa)begin
reg1_o <= imm;
//为什么赋值为立即数呢?因为有可能是会其他的部分作为输出
end
else begin
reg1_o <= `ZeroWord;
end
end
/**********************三、读取源操作数 2*****************************/
always @(*)begin
if(rst == `RstEna)begin
reg2_o <= `ZeroWord;
end
else if(reg2_read_o == `ReadEna)begin
//当读地址与写地址相同,并且写使能为真时,说明发生了数据相关,这时需要旁路
if((reg2_addr_o==ex_waddr_i)&&(ex_wreg_i==`WriteEna))begin
reg2_o <= ex_wdata_i;
end
else if((reg2_addr_o==mem_waddr_i)&&(mem_wreg_i==`WriteEna))begin
reg2_o <= mem_wdata_i;
end
//当没有发生旁路时,则从 regfile 中读取数据
else begin
reg2_o <= reg2_data_i; //regfile 读端口 2 的值
end
end
else if(reg2_read_o == `ReadDisa)begin
reg2_o <= imm;
end
else begin
reg2_o <= `ZeroWord;
end
end
//为 is_delay 进行赋值操作,放在外面是因为没必要因为这一个而执行某个 always 块中的所有部分
always @(*) begin
if(rst == `RstEna)begin
is_delay_inst_o <= 1'b0;
end
else begin
is_delay_inst_o <= is_delay_inst_i;
end
end
//处理加载存储指令与转移指令之间的数据冲突,主要是两者相邻时进行阻塞操作,对 reg1 进行判断
always @(*)begin
stallreq_for_reg1 <= `NoStop;
if(rst == `RstEna)begin
stallreq_for_reg1 <= `NoStop;
end
else begin
if(inst_is_load == 1'b1 && reg1_read_o == `ReadEna && reg1_addr_o == ex_waddr_i)begin
stallreq_for_reg1 <= `Stop;
end
end
end
//对 reg2 进行判断,不放在一起是因为这两个是并行的操作
always @(*)begin
stallreq_for_reg2 <= `NoStop;
if(rst == `RstEna)begin
stallreq_for_reg2 <= `NoStop;
end
else begin
if(inst_is_load == 1'b1 && reg2_read_o == `ReadEna && reg2_addr_o == ex_waddr_i)begin
stallreq_for_reg2 <= `Stop;
end
end
end
//对阻塞进行赋值
always @(*)begin
if(rst == `RstEna)begin
stallreq <= `NoStop;
end
else begin
stallreq <= stallreq_for_reg1 | stallreq_for_reg2;
end
end
endmodule
`timescale 1ns / 1ps
`include"define.v"
module id_ex(
input clk,rst,
input [`AluSelBus] id_alusel,
input [`AluOpBus] id_aluop,
input id_wreg,
input [`RegAddrBus] id_waddr,
input [`RegBus] id_reg1,
input [`RegBus] id_reg2,
input [5:0] stall,
input id_is_delay,
input next_is_delay,
//流向 id 的,告诉 id 这条指令是延迟指令,传回去已经是一个周期之后了,所
以是 next
input [`InstAddrBus] id_link_addr,
input [`InstBus] id_inst,
output reg [`AluSelBus] ex_alusel,
output reg [`AluOpBus] ex_aluop,
output reg ex_wreg,
output reg [`RegAddrBus] ex_waddr,
output reg [`RegBus] ex_reg1,
output reg [`RegBus] ex_reg2,
output reg ex_is_delay,
output reg is_delay,
output reg [`InstAddrBus] ex_link_addr,
output reg [`InstBus] ex_inst
);
always @(posedge clk)begin
if(rst == `RstEna)begin
ex_alusel <= `EXE_RES_NOP;
ex_aluop <= `EXE_NOP_OP;
ex_wreg <= `WriteDisa;
ex_waddr <= `NOPRegAddr;
ex_reg1 <= `ZeroWord;
ex_reg2 <= `ZeroWord;
ex_is_delay <= 1'b0;
is_delay <= 1'b0;
ex_link_addr <= `ZeroWord;
ex_inst <= `ZeroWord;
end
else if(stall[2] == `Stop && stall[3] == `NoStop)begin
ex_alusel <= `EXE_RES_NOP;
ex_aluop <= `EXE_NOP_OP;
ex_wreg <= `WriteDisa;
ex_waddr <= `NOPRegAddr;
ex_reg1 <= `ZeroWord;
ex_reg2 <= `ZeroWord;
ex_is_delay <= 1'b0;
is_delay <= 1'b0;
ex_link_addr <= `ZeroWord;
ex_inst <= `ZeroWord;
end
else if(stall[2] == `NoStop)begin
ex_alusel <= id_alusel;
ex_aluop <= id_aluop;
ex_wreg <= id_wreg;
ex_waddr <= id_waddr;
ex_reg1 <= id_reg1;
ex_reg2 <= id_reg2;
ex_is_delay <= id_is_delay;
is_delay <= next_is_delay;
ex_link_addr <= id_link_addr;
ex_inst <= id_inst;
end
else begin
end
end
endmodule
`timescale 1ns / 1ps
`include "define.v"
module if_id(
input clk,
input rst,
//来自取指阶段的信号
input [`InstAddrBus] if_pc,//取指阶段的地址
input [`InstBus] if_inst,//取指阶段的指令
input [5:0] stall,
//对应译码阶段的信号
output reg[`InstAddrBus] id_pc,//译码阶段的地址
output reg[`InstBus] id_inst//译码阶段的指令
);
always @(posedge clk)begin
if(rst==`RstEna)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
`timescale 1ns / 1ps
`include"define.v"
module inst_rom(
input ce,
input [`InstAddrBus]addr,
output reg [`InstBus] inst
);
reg [`InstBus] inst_mem[0:`InstMemNum-1];
initial
$readmemh("F:/vivado/lesson_design/MIPS_CPU_five_pipeline/MIPS_CPU_five_pipeline/rtl/inst_rom.data",inst_m
em);
always @(*)begin
if(ce == `ChipDisa)begin
inst <= `ZeroWord;
end
else begin
inst <= inst_mem[addr[`InstMemNumLog2+1:2]];
end
end
endmodule
`timescale 1ns / 1ps
`include"define.v"
module LLbit_reg(
input clk,
input rst,
input flush,
input we,
input LLbit_i,
output reg LLbit_o
);
always @(posedge clk)begin
if(rst == `RstEna)begin
LLbit_o <= 1'b0;
end
else if(flush == 1'b1)begin
LLbit_o <= 1'b0;
end
else if(we == `WriteEna)begin
LLbit_o <= LLbit_i;
end
end
endmodule
`timescale 1ns / 1ps
//发生异常时,设置为 0
`include"define.v"
module mem(
input rst,
input wreg_i,
input [`RegAddrBus] waddr_i,
input [`RegBus] wdata_i,
input [`RegBus] hi_i,
input [`RegBus] lo_i,
input whilo_i,
input [`AluOpBus]aluop_i,
//根据指令类型进行相应的处理
input [`RegBus] mem_addr_i,
//只是最原始的地址,还需要进行处理
input [`RegBus] reg2_i,
//写入 ram 的数据
//来自 ram 的数据
input [`RegBus] mem_data_i,
//放进 rt 的数据
//来自 LLbit_reg 的输入
input LLbit_i,
//来自 mem_wb 的旁路(用于 sc 指令)
input wb_LLbit_we_i,
input wb_LLbit_value_i,
//协处理器
input [`RegBus] cp0_reg_data_i,
input [`RegAddrBus] cp0_reg_write_addr_i,
input cp0_reg_we_i,
output reg wreg_o,
output reg [`RegAddrBus] waddr_o,
output reg [`RegBus] wdata_o,
output reg [`RegBus] hi_o,
output reg [`RegBus] lo_o,
output reg whilo_o,
//通过 mem_wb 输出到 LLbit_reg 的数据
output reg LLbit_we_o,
output reg LLbit_value_o,
//输出到 ram 的数据
output reg [`RegBus] mem_data_o,
//存储到 ram 的数据
output reg [`RegBus] mem_addr_o,
//存放的地址
output reg mem_we_o,
//指定是加载还是存储操作
output reg mem_ce_o,
//相当于是读使能
//字节选择信号,ram 是从传入的地址开始,选择 sel 个字节的数据的,比如传入的地址是 5,sel 是 1,则
选择地址为 5,6 的数据
output reg [3:0] mem_sel_o, //这个应该是哪一位为 1 则获取哪些位置的数据,比如 4'b0011 则获取后两
位的数据
//协处理器
output reg [`RegBus] cp0_reg_data_o,
output reg [`RegAddrBus] cp0_reg_write_addr_o,
output reg cp0_reg_we_o
);
wire [1:0] n;
wire [`RegBus] mem_addr;
//l r 类型指令,数据的位数
//用来存放对齐后的地址
reg LLbit;
//用于保存 LLbit 的最新值,这样就可以不用在后面进行判断,从而减少重复的操作
assign n = 4 - mem_addr_i[1:0];
assign mem_addr = mem_addr_i - mem_addr_i[1:0];
//更新 LLbit 的值
always @(*)begin
if(rst == `RstEna)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 == `RstEna)begin
wreg_o <= `WriteDisa;
waddr_o <= `NOPRegAddr;
wdata_o <= `ZeroWord;
whilo_o <= `WriteDisa;
//减去最低两位后,保持最低两位为 0
hi_o <= `ZeroWord;
lo_o <= `ZeroWord;
cp0_reg_data_o <= `ZeroWord;
cp0_reg_write_addr_o <= 5'b0;
cp0_reg_we_o <= 1'b0;
mem_data_o <= `ZeroWord;
mem_addr_o <= `ZeroWord;
mem_we_o <= `IsRead;
//默认为读操作
mem_ce_o <= `ChipDisa;
//默认不可操作
mem_sel_o <= 4'b0000;
//不会出现 4'b0000 的情况
end
else begin
case(aluop_i)
//与之前不同,这里是按照字节来进行处理的,需要转换一下思维,一个字
节对应 8 位数据
`EXE_LB_OP: begin
mem_data_o <= `ZeroWord;
mem_addr_o <= mem_addr;
//我都是用的对齐之后的地址,因为都是处理的
mem_we_o <= `IsRead;
//比如地址为 5,对齐后的地址为 4,后面的处理
对齐后的一个字
都是在 4567 这一个字上进行的
mem_ce_o <= `ChipEna;
wreg_o <= `WriteEna;
//进行读取操作
//写到 rt 里面
case(mem_addr_i[1:0])
2'b00: begin
中是低地址
//记住,
这是大端存储的方式,
所以 mem_data_i 的高位对应在 ram
wdata_o <= {{24{mem_data_i[31]}},mem_data_i[31:24]}; //进行符号扩展,高
位是低地址
end
2'b01: begin
wdata_o <= {{24{mem_data_i[23]}},mem_data_i[23:16]};
end
2'b10: begin
wdata_o <= {{24{mem_data_i[15]}},mem_data_i[15:8]};
end
2'b11: begin
wdata_o <= {{24{mem_data_i[7]}},mem_data_i[7:0]};
// mem_sel_o <= 4'b0001;
//书上有这种,但是我觉得这个在加载中没有用,
所以去掉了
end
endcase
waddr_o <= waddr_i;
end
`EXE_LBU_OP: begin
mem_data_o <= `ZeroWord;
mem_addr_o <= mem_addr;
mem_we_o <= `IsRead;
mem_ce_o <= `ChipEna;
wreg_o <= `WriteEna;
case(mem_addr_i[1:0])
2'b00: begin
//在前面对 waddr_i 已经进行了处理,就是 rt 的地址
wdata_o <= {24'b0,mem_data_i[31:24]}; //进行无符号扩展
mem_sel_o <= 4'b1000;
end
2'b01: begin
wdata_o <= {24'b0,mem_data_i[23:16]};
mem_sel_o <= 4'b0100;
end
2'b10: begin
wdata_o <= {24'b0,mem_data_i[15:8]};
mem_sel_o <= 4'b0010;
end
2'b11: begin
wdata_o <= {24'b0,mem_data_i[7:0]};
mem_sel_o <= 4'b0001;
end
endcase
waddr_o <= waddr_i;
end
`EXE_LH_OP: begin
if(mem_addr_i[0] == 1'b0)
begin
//地址对齐才能读取
mem_data_o <= `ZeroWord;
mem_addr_o <= mem_addr;
取数据的,比如地址为 6,则获取 6,7 上的数据
mem_we_o <= `IsRead;
mem_ce_o <= `ChipEna;
//因为是直接在这个地址的基础上进行获
wreg_o <= `WriteEna;
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
end
endcase
waddr_o <= waddr_i;
end
else begin
end
end
`EXE_LHU_OP: begin
if(mem_addr_i[0] == 1'b0)
begin
mem_data_o <= `ZeroWord;
mem_addr_o <= mem_addr;
mem_we_o <= `IsRead;
mem_ce_o <= `ChipEna;
//进行符号扩
wreg_o <= `WriteEna;
case(mem_addr_i[1:0])
2'b00: begin
wdata_o <= {16'b0,mem_data_i[31:16]};
mem_sel_o <= 4'b1100;
end
2'b10: begin
wdata_o <= {16'b0,mem_data_i[15:0]};
mem_sel_o <= 4'b0011;
end
default:begin
end
endcase
waddr_o <= waddr_i;
end
else begin
end
end
`EXE_LW_OP: begin
if(mem_addr_i[1:0] == 2'b00) begin
//对齐才能读取
mem_data_o <= `ZeroWord;
mem_addr_o <= mem_addr;
//书上都是用的原始的地址,我用的是处理之后
的地址,可能是后面不太一样,暂时先不改,后面看情况再说
mem_sel_o <= 4'b1111;
mem_we_o <= `IsRead;
//读取四个字节(一个字)
mem_ce_o <= `ChipEna;
wreg_o <= `WriteEna;
wdata_o <= mem_data_i;
waddr_o <= waddr_i;
end
else begin
end
end
`EXE_LWL_OP: begin //放在高位,从左边开始放 (L)
mem_data_o <= `ZeroWord;
mem_addr_o <= mem_addr;
//还是用对齐后的地址获取数据,因为都是在这一个
字内进行操作的
mem_sel_o <= 4'b1111;
//把一个字都读取出来,因为具体存放什么是在后面控
制的
mem_we_o <= `IsRead;
mem_ce_o <= `ChipEna;
wreg_o <= `WriteEna;
case(mem_addr_i[1:0])
2'd3: begin
//地址为 7,获取 7
wdata_o <= {mem_data_i[7:0],reg2_i[23:0]}; //将写入的数据放在 reg 的高位
end
2'd2: begin
//地址为 6,获取 67
wdata_o <= {mem_data_i[15:0],reg2_i[15:0]};//mem_data_i 中存放的是一个
字的数据,其低位为高地址
end
2'd1: begin
//当地址为 5 时,mem_data_i 中是 4567 的数据,而要获取的是
567
wdata_o <= {mem_data_i[23:0],reg2_i[7:0]};
end
2'd0:
begin
//地址为 4,获取 4567
wdata_o <= mem_data_i;
end
endcase
waddr_o <= waddr_i;
end
`EXE_LWR_OP: begin //放到低位,从右边开始
mem_data_o <= `ZeroWord;
mem_addr_o <= mem_addr;
//用对齐后的地址获取数据,因为地址为 5 时,获取
的地址是 45
mem_sel_o <= 4'b1111;
mem_we_o <= `IsRead;
mem_ce_o <= `ChipEna;
wreg_o <= `WriteEna;
case(mem_addr_i[1:0])
2'd3: begin
//地址为 7,获取 4567
wdata_o <= mem_data_i;
end
2'd2: begin
//当地址为 6 时,要获取 456
wdata_o <= {reg2_i[31:24],mem_data_i[31:8]}; //将写入的数据放在 reg 的低
位
end
2'd1: begin
//当地址为 5 时 mem_data_i 中是 4567,而要获取的是 45,低地
址在高位
wdata_o <= {reg2_i[31:16],mem_data_i[31:16]};
end
2'd0:
begin
//地址为 4,获取 4
wdata_o <= {reg2_i[31:8],mem_data_i[31:24]};
end
endcase
waddr_o <= waddr_i;
end
`EXE_SB_OP: begin
wdata_o <= `ZeroWord;
wreg_o <= `WriteDisa;
mem_addr_o <= mem_addr;
mem_we_o <= `IsWrite;
mem_ce_o <= `ChipEna;
//在我这里,存储操作都是用的低位的数据,考虑到后面的
统一性,所以将每一位都
mem_data_o <= {reg2_i[7:0],reg2_i[7:0],reg2_i[7:0],reg2_i[7:0]};
case(mem_addr_i[1:0])
//因为在 ram 中的处理是根据 addr 以及 sel 进行的
2'b00:begin
mem_sel_o <= 4'b1000;
//地址为 4,存放到第一个
end
2'b01:begin
mem_sel_o <= 4'b0100;
//地址为 5,存放到第二个
end
2'b10:begin
mem_sel_o <= 4'b0010;
end
2'b11:begin
mem_sel_o <= 4'b0001;
end
endcase
end
`EXE_SH_OP: begin
wdata_o <= `ZeroWord;
wreg_o <= `WriteDisa;
mem_addr_o <= mem_addr;
mem_we_o <= `IsWrite;
mem_ce_o <= `ChipEna;
mem_data_o <= {reg2_i[15:0],reg2_i[15:0]}; //低位是我要加载的数据(向内存,应该
是加载才对)
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;
endcase
end
`EXE_SW_OP: begin
wdata_o <= `ZeroWord;
wreg_o <= `WriteDisa;
mem_addr_o <= mem_addr;
mem_we_o <= `IsWrite;
mem_ce_o <= `ChipEna;
mem_sel_o <= 4'b1111;
mem_data_o <= reg2_i;
end
`EXE_SWL_OP: begin
//从右往左
wdata_o <= `ZeroWord;
wreg_o <= `WriteDisa;
mem_addr_o <= mem_addr;
mem_we_o <= `IsWrite;
mem_ce_o <= `ChipEna;
case(mem_addr_i[1:0])
2'b00:
begin //地址为 4,存放 4567
mem_data_o <= reg2_i;
mem_sel_o <= 4'b1111;
end
2'b01:
begin //地址为 5,存放 567
mem_data_o <= {8'b0,reg2_i[23:0]};
mem_sel_o <= 4'b0111;
end
2'b10:
begin //地址为 6,存放 67
mem_data_o <= {16'b0,reg2_i[15:0]};
mem_sel_o <= 4'b0011;
end
2'b11:
begin //地址为 7,存放 7
mem_data_o <= {24'b0,reg2_i[7:0]};
mem_sel_o <= 4'b0001;
end
endcase
end
`EXE_SWR_OP: begin
//从左往右
wdata_o <= `ZeroWord;
wreg_o <= `WriteDisa;
mem_addr_o <= mem_addr;
mem_we_o <= `IsWrite;
mem_ce_o <= `ChipEna;
case(mem_addr_i[1:0]) //这里只需要放进去就好了,处理是在 ram 中的,所以在这里
我把所有的都放到了 mem_data_o 的低位,在 ram 中根据 sel 选择获取的字节数(n+1)
2'b00:
begin //地址为 4,加载到 4
mem_data_o <= {reg2_i[7:0],24'b0};
mem_sel_o <= 4'b1000;
end
2'b01:
begin //地址为 5,加载到 45
mem_data_o <= {reg2_i[15:0],16'b0};
mem_sel_o <= 4'b1100;
end
2'b10:
begin
mem_data_o <= {reg2_i[23:0],8'b0};
mem_sel_o <= 4'b1110;
end
2'b11:
begin
mem_data_o <= reg2_i;
mem_sel_o <= 4'b1111;
end
endcase
end
`EXE_LL_OP: begin
//对比 lw 指令,只是多了对 LLbit_reg 的操作
mem_data_o <= `ZeroWord;
mem_addr_o <= mem_addr;
//书上都是用的原始的地址,我用的是处理之后的地
址,可能是后面不太一样,暂时先不改,后面看情况再说
mem_sel_o <= 4'b1111;
//读取四个字节(一个字)
mem_we_o <= `IsRead;
mem_ce_o <= `ChipEna;
wreg_o <= `WriteEna;
wdata_o <= mem_data_i;
waddr_o <= waddr_i;
//正式操作,主要是控制 LLbit_reg 的信号,这是对 mem_wb 新增的端口
LLbit_we_o <= 1'b1;
LLbit_value_o <= 1'b1;
end
`EXE_SC_OP: begin
//对比 sb 指令,只是多了个判断以及对 LLbit_reg 的操作
if(LLbit == 1'b1)begin
wdata_o <= `ZeroWord;
wreg_o <= `WriteDisa;
mem_addr_o <= mem_addr;
mem_we_o <= `IsWrite;
mem_ce_o <= `ChipEna;
mem_sel_o <= 4'b1111;
mem_data_o <= reg2_i;
LLbit_we_o <= 1'b1;
LLbit_value_o <= 1'b0;
end
else begin
wdata_o <= 32'b0;
end
end
default:
begin
//当不是加载存储指令时
wreg_o <= wreg_i;
waddr_o <= waddr_i;
wdata_o <= wdata_i;
whilo_o <= whilo_i;
hi_o <= hi_i;
lo_o <= lo_i;
cp0_reg_data_o <= cp0_reg_data_i;
cp0_reg_write_addr_o <= cp0_reg_write_addr_i;
cp0_reg_we_o <= cp0_reg_we_i;
mem_we_o <= `WriteDisa;
mem_addr_o <= `ZeroWord;
mem_sel_o <= 4'b1111;
//默认读取一个字
mem_ce_o <= 1'b0;
end
endcase
end
end
endmodule
`timescale 1ns / 1ps
`include"define.v"
module mem_wb(
//这种中间寄存器都是在一个时钟周期之后将数据传过去
input clk,rst,
input mem_reg,
input [`RegAddrBus] mem_waddr,
input [`RegBus] mem_wdata,
input mem_whilo,
input [`RegBus] mem_hi,
input [`RegBus] mem_lo,
input [5:0] stall,
input mem_LLbit_we,
input mem_LLbit_value,
input [`RegBus] mem_cp0_reg_data,
input [`RegAddrBus] mem_cp0_reg_write_addr,
input mem_cp0_reg_we,
output reg wb_reg,
output reg [`RegAddrBus] wb_waddr,
output reg [`RegBus] wb_wdata,
output reg wb_whilo,
output reg [`RegBus] wb_hi,
output reg [`RegBus] wb_lo,
output reg wb_LLbit_we,
output reg wb_LLbit_value,
output reg [`RegBus] wb_cp0_reg_data,
output reg [`RegAddrBus] wb_cp0_reg_write_addr,
output reg wb_cp0_reg_we
);
always @(posedge clk)begin
if(rst == `RstEna)begin
wb_reg <= `WriteDisa;
wb_waddr <= `NOPRegAddr;
wb_wdata <= `ZeroWord;
wb_whilo <= `WriteDisa;
wb_hi <= `ZeroWord;
wb_lo <= `ZeroWord;
wb_LLbit_we <= 1'b0;
wb_LLbit_value <= 1'b0;
wb_cp0_reg_data <= `ZeroWord;
wb_cp0_reg_write_addr <= 5'b0;
wb_cp0_reg_we <= 1'b0;
end
//很典型的一种情况,下一个周期是单周期的操作,所以需要在其执行完之后输入空指令,否则会出
现重复运行的情况
else if(stall[4] == `Stop && stall[5] == `NoStop)begin
wb_reg <= `WriteDisa;
wb_waddr <= `NOPRegAddr;
wb_wdata <= `ZeroWord;
wb_whilo <= `WriteDisa;
wb_hi <= `ZeroWord;
wb_lo <= `ZeroWord;
wb_LLbit_we <= 1'b0;
wb_LLbit_value <= 1'b0;
wb_cp0_reg_data <= `ZeroWord;
wb_cp0_reg_write_addr <= 5'b0;
wb_cp0_reg_we <= 1'b0;
end
else if(stall[4] == `NoStop)begin
wb_reg <= mem_reg;
wb_waddr <= mem_waddr;
wb_wdata <= mem_wdata;
wb_whilo <= mem_whilo;
wb_hi <= mem_hi;
wb_lo <= mem_lo;
wb_LLbit_we <= mem_LLbit_we;
wb_LLbit_value <= mem_LLbit_value;
wb_cp0_reg_data <= mem_cp0_reg_data;
wb_cp0_reg_write_addr <= mem_cp0_reg_write_addr;
wb_cp0_reg_we <= mem_cp0_reg_we;
end
end
endmodule
`timescale 1ns / 1ps
`include"define.v"
module min_sopc(
input clk,
input rst
);
//指令存储器的输出,cpu 的输入
wire [`InstBus] inst;
wire [`InstAddrBus] rom_addr;
wire rom_ce;
//用于 ram
wire [`DataAddrBus] ram_addr;
wire ram_ce;
wire [3:0] sel;
wire [`DataBus] ram_data_i;
wire [`DataBus] ram_data_o;
wire we;
//inst_rom 的实例化
inst_rom inst_rom0(
.ce(rom_ce),
.addr(rom_addr),
.inst(inst)
);
//cpu 的实例化
cpu cpu0(
.clk(clk),
.rst(rst),
//与 inst_rom 之间
.rom_data_i(inst),
.rom_ce_o(rom_ce),
.rom_addr_o(rom_addr),
//与 data_ram 之间
.ram_data_o(ram_data_o),
.ram_data_i(ram_data_i),
.ram_addr_i(ram_addr),
.ram_we_i(we),
.ram_sel_i(sel),
.ram_ce_i(ram_ce)
);
//data_ram 的实例化
data_ram data_ram0(
.clk(clk),
.ce(ram_ce),
.data_i(ram_data_i),
.addr(ram_addr),
.we(we),
.sel(sel),
.data_o(ram_data_o)
);
endmodule
`timescale 1ns / 1ps
`include "define.v"
module pc_reg(//实际上只完成了简单的指令加 4 的功能以及清零的功能,也不需要其他功能了,更多的操作
是在 id 中进行的
input clk,
input rst,
input [5:0] stall, //暂停信号
input branch_flag_i,
input [`InstAddrBus] branch_address_i,
output reg[`InstAddrBus] pc, //要读取的指令的地址
output reg ce
);
always @(posedge clk)begin
if(rst==`RstEna)begin
ce<=`ChipDisa; //复位的时候指令存储器禁用
end//这样做是因为在两个不同的 always 里面,所以需要一个额外的通信,TODO:为什么不放到一个
always 里面呢?
else begin
ce<=`ChipEna;//复位结束后,指令存储器使能,一个过程中只能有一个操作
end
end
always @(posedge clk)begin
if(ce==`ChipDisa)begin
pc<=32'h00000000;//指令存储器禁用的时候,PC 为 0
end
else if(stall[0] == `NoStop) begin //不暂停才赋值,暂停则保持不变
if(branch_flag_i == 1'b1)begin
pc <= branch_address_i;
end
else begin
pc<=pc+4'h4; //直接就在这里自动完成了加 4 的功能
end
end
else begin
end
end
endmodule
`timescale 1ns / 1ps
`include"define.v"
module regfile(
input clk,rst,
input we,
input [`RegAddrBus] waddr,
input [`RegBus] wdata,
input re1,
input [`RegAddrBus] raddr1,
output reg [`RegBus] rdata1,
input re2,
input [`RegAddrBus] raddr2,
output reg [`RegBus] rdata2
);
//******定义寄存 ?************
reg [`RegBus] regs[0:`RegNum-1];
integer i;
initial begin
for(i=0;i<`RegNum;i=i+1)begin
regs[i] = 0;
end
end
always @(posedge clk)begin
if(rst==`RstDisa)begin
if((we==`WriteEna)&&(waddr!=`RegNumLog2'h0))begin
regs[waddr] <= wdata;
end
end
end
always @(*) begin
//这样做的目的:保证在译码阶段取得要读取的值(任何时 ? 都有可能读
取)
if(rst==`RstEna)begin
//并且 ?要在 ?个时钟周期内进行多次读操 ?
rdata1<=`ZeroWord;
end
else if(raddr1== `RegNumLog2'h0)begin
rdata1<=`ZeroWord;
end
else if((raddr1==waddr)&&(we==`WriteEna)&&(re1==`ReadEna))begin
rdata1<=wdata;//当同时发生读写时,直接将写的值传送给 ?,因为是异步的,所以可能发生冲 ?
end
else if(re1==`ReadEna)begin
rdata1 <= regs[raddr1];
end
else begin
rdata1 <= `ZeroWord;
end
end
//******************读操 ?2****************
always @(*) begin
if(rst==`RstEna)begin
rdata2<=`ZeroWord;
end
else if(raddr2== `RegNumLog2'h0)begin
rdata2<=`ZeroWord;
end
else if((raddr2==waddr)&&(we==`WriteEna)&&(re2==`ReadEna))begin
rdata2<=wdata;
end
else if(re2==`ReadEna)begin
rdata2 <= regs[raddr2];
end
else begin
rdata2 <= `ZeroWord;
end
end
endmodule
0
You can add this document to your study collection(s)
Sign in Available only to authorized usersYou can add this document to your saved list
Sign in Available only to authorized users(For complaints, use another form )