分为两部分实现,一部分处理器内核,一部分存储器。
整体参考这个图:
先是参数宏定义,包括全局参数、指令字参数和通用寄存器参数:
`timescale 1ns / 1ps
/*------------------- 全局参数 -------------------*/
`define RST_ENABLE 1'b0 // 复位信号有效 RST_ENABLE
`define RST_DISABLE 1'b1 // 复位信号无效
`define ZERO_WORD 32'h00000000 // 32位的数值0
`define ZERO_DWORD 64'b0 // 64位的数值0
`define WRITE_ENABLE 1'b1 // 使能写
`define WRITE_DISABLE 1'b0 // 禁止写
`define READ_ENABLE 1'b1 // 使能读
`define READ_DISABLE 1'b0 // 禁止读
`define ALUOP_BUS 7 : 0 // 译码阶段的输出aluop_o的宽度
`define SHIFT_ENABLE 1'b1 // 移位指令使能
`define ALUTYPE_BUS 2 : 0 // 译码阶段的输出alutype_o的宽度
`define TRUE_V 1'b1 // 逻辑“真”
`define FALSE_V 1'b0 // 逻辑“假”
`define CHIP_ENABLE 1'b1 // 芯片使能
`define CHIP_DISABLE 1'b0 // 芯片禁止
`define WORD_BUS 31: 0 // 32位宽
`define DOUBLE_REG_BUS 63: 0 // 两倍的通用寄存器的数据线宽度
`define RT_ENABLE 1'b1 // rt选择使能
`define SIGNED_EXT 1'b1 // 符号扩展使能
`define IMM_ENABLE 1'b1 // 立即数选择使能
`define UPPER_ENABLE 1'b1 // 立即数移位使能
`define MREG_ENABLE 1'b1 // 写回阶段存储器结果选择信号
`define BSEL_BUS 3 : 0 // 数据存储器字节选择信号宽度
`define PC_INIT 32'h00000000 // PC初始值
/*------------------- 指令字参数 -------------------*/
`define INST_ADDR_BUS 31: 0 // 指令的地址宽度
`define INST_BUS 31: 0 // 指令的数据宽度
// 操作类型alutype
`define NOP 3'b000
`define ARITH 3'b001
`define LOGIC 3'b010
`define MOVE 3'b011
`define SHIFT 3'b100
// 内部操作码aluop
`define MINIMIPS32_LUI 8'h05
`define MINIMIPS32_MFHI 8'h0C
`define MINIMIPS32_MFLO 8'h0D
`define MINIMIPS32_SLL 8'h11
`define MINIMIPS32_MULT 8'h14
`define MINIMIPS32_ADD 8'h18
`define MINIMIPS32_ADDIU 8'h19
`define MINIMIPS32_SUBU 8'h1B
`define MINIMIPS32_AND 8'h1C
`define MINIMIPS32_ORI 8'h1D
`define MINIMIPS32_SLT 8'h26
`define MINIMIPS32_SLTIU 8'h27
`define MINIMIPS32_LB 8'h90
`define MINIMIPS32_LW 8'h92
`define MINIMIPS32_SB 8'h98
`define MINIMIPS32_SW 8'h9A
/*------------------- 通用寄存器堆参数 -------------------*/
`define REG_BUS 31: 0 // 寄存器数据宽度
`define REG_ADDR_BUS 4 : 0 // 寄存器的地址宽度
`define REG_NUM 32 // 寄存器数量32个
`define REG_NOP 5'b00000 // 零号寄存器
然后取指模块:
`include "defines.v"
module if_stage (
input wire cpu_clk_50M,
input wire cpu_rst_n,
output reg ice,
output reg [`INST_ADDR_BUS] pc,
output wire [`INST_ADDR_BUS] iaddr
);
wire [`INST_ADDR_BUS] pc_next;
assign pc_next = pc + 4; // 计算下一条指令的地址
always @(posedge cpu_clk_50M) begin
if (cpu_rst_n == `RST_ENABLE) begin
ice <= `CHIP_DISABLE; // 复位的时候指令存储器禁用
end else begin
ice <= `CHIP_ENABLE; // 复位结束后,指令存储器使能
end
end
always @(posedge cpu_clk_50M) begin
if (ice == `CHIP_DISABLE)
pc <= `PC_INIT; // 指令存储器禁用的时候,PC保持初始值(MiniMIPS32中设置为0x00000000)
else begin
pc <= pc_next; // 指令存储器使能后,PC值每时钟周期加4
end
end
assign iaddr = (ice == `CHIP_DISABLE) ? `PC_INIT : pc; // 获得访问指令存储器的地址
endmodule
用于获取从取指阶段获得的信号,在时钟上升沿阶段传递给译码阶段。所以用reg来保存信息。
`include "defines.v"
module ifid_reg (
input wire cpu_clk_50M,
input wire cpu_rst_n,
// 来自取指阶段的信息
input wire [`INST_ADDR_BUS] if_pc,
// 送至译码阶段的信息
output reg [`INST_ADDR_BUS] id_pc
);
always @(posedge cpu_clk_50M) begin
// 复位的时候将送至译码阶段的信息清0
if (cpu_rst_n == `RST_ENABLE) begin
id_pc <= `PC_INIT;
end
// 将来自取指阶段的信息寄存并送至译码阶段
else begin
id_pc <= if_pc;
end
end
endmodule
I/O端口
可以看到,这个取指/译码寄存器只对PC值进行了缓存,而之前我们说要对PC值(转移指令可能用到,这里目前还用不到)和指令同时进行缓存。那为什么指令不缓存呢?因为FPGA内部的BRAM只能设计同步存储器,如果这里对指令进行缓存的话,传到译码阶段就要两个时钟周期,所以把指令存储器IM的输出口直接跳过寄存器传到译码阶段。
目前就相当于这个样子,跳过了寄存器:
译码模块是最重要的、最复杂的模块之一。
`include "defines.v"
module id_stage(
input wire cpu_rst_n,
// 从取指阶段获得的PC值
input wire [`INST_ADDR_BUS] id_pc_i,
// 从指令存储器读出的指令字
input wire [`INST_BUS ] id_inst_i,
// 从通用寄存器堆读出的数据
input wire [`REG_BUS ] rd1,
input wire [`REG_BUS ] rd2,
// 送至执行阶段的译码信息
output wire [`ALUTYPE_BUS ] id_alutype_o,
output wire [`ALUOP_BUS ] id_aluop_o,
output wire id_whilo_o,
output wire id_mreg_o,
output wire [`REG_ADDR_BUS ] id_wa_o,
output wire id_wreg_o,
output wire [`REG_BUS ] id_din_o,
// 送至执行阶段的源操作数1、源操作数2
output wire [`REG_BUS ] id_src1_o,
output wire [`REG_BUS ] id_src2_o,
// 送至读通用寄存器堆端口的使能和地址
output wire rreg1,
output wire [`REG_ADDR_BUS ] ra1,
output wire rreg2,
output wire [`REG_ADDR_BUS ] ra2
);
// 根据小端模式组织指令字
wire [`INST_BUS] id_inst = {id_inst_i[7:0], id_inst_i[15:8], id_inst_i[23:16], id_inst_i[31:24]};
// 提取指令字中各个字段的信息
wire [5 :0] op = id_inst[31:26];
wire [5 :0] func = id_inst[5 : 0];
wire [4 :0] rd = id_inst[15:11];
wire [4 :0] rs = id_inst[25:21];
wire [4 :0] rt = id_inst[20:16];
wire [4 :0] sa = id_inst[10: 6];
wire [15:0] imm = id_inst[15: 0];
/*-------------------- 第一级译码逻辑:确定当前需要译码的指令 --------------------*/
wire inst_reg = ~|op;
wire inst_add = inst_reg& func[5]&~func[4]&~func[3]&~func[2]&~func[1]&~func[0];
wire inst_subu = inst_reg& func[5]&~func[4]&~func[3]&~func[2]& func[1]& func[0];
wire inst_slt = inst_reg& func[5]&~func[4]& func[3]&~func[2]& func[1]&~func[0];
wire inst_and = inst_reg& func[5]&~func[4]&~func[3]& func[2]&~func[1]&~func[0];
wire inst_mult = inst_reg&~func[5]& func[4]& func[3]&~func[2]&~func[1]&~func[0];
wire inst_mfhi = inst_reg&~func[5]& func[4]&~func[3]&~func[2]&~func[1]&~func[0];
wire inst_mflo = inst_reg&~func[5]& func[4]&~func[3]&~func[2]& func[1]&~func[0];
wire inst_sll = inst_reg&~func[5]&~func[4]&~func[3]&~func[2]&~func[1]&~func[0];
wire inst_ori =~op[5]&~op[4]& op[3]& op[2]&~op[1]& op[0];
wire inst_lui =~op[5]&~op[4]& op[3]& op[2]& op[1]& op[0];
wire inst_addiu=~op[5]&~op[4]& op[3]&~op[2]&~op[1]& op[0];
wire inst_sltiu=~op[5]&~op[4]& op[3]&~op[2]& op[1]& op[0];
wire inst_lb = op[5]&~op[4]&~op[3]&~op[2]&~op[1]&~op[0];
wire inst_lw = op[5]&~op[4]&~op[3]&~op[2]& op[1]& op[0];
wire inst_sb = op[5]&~op[4]& op[3]&~op[2]&~op[1]&~op[0];
wire inst_sw = op[5]&~op[4]& op[3]&~op[2]& op[1]& op[0];
/*------------------------------------------------------------------------------*/
/*-------------------- 第二级译码逻辑:生成具体控制信号 --------------------*/
// 操作类型alutype
assign id_alutype_o[2] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 : inst_sll;
assign id_alutype_o[1] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
(inst_and | inst_mfhi | inst_mflo | inst_ori | inst_lui);
assign id_alutype_o[0] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
(inst_add | inst_subu | inst_slt | inst_mfhi | inst_mflo |
inst_addiu | inst_sltiu | inst_lb |inst_lw | inst_sb | inst_sw);
// 内部操作码aluop
assign id_aluop_o[7] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
(inst_lb | inst_lw | inst_sb | inst_sw);
assign id_aluop_o[6] = 1'b0;
assign id_aluop_o[5] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
(inst_slt | inst_sltiu);
assign id_aluop_o[4] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
(inst_add | inst_subu | inst_and | inst_mult | inst_sll |
inst_ori | inst_addiu | inst_lb | inst_lw | inst_sb | inst_sw);
assign id_aluop_o[3] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
(inst_add | inst_subu | inst_and | inst_mfhi | inst_mflo |
inst_ori | inst_addiu | inst_sb | inst_sw);
assign id_aluop_o[2] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
(inst_slt | inst_and | inst_mult | inst_mfhi | inst_mflo |
inst_ori | inst_lui | inst_sltiu);
assign id_aluop_o[1] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
(inst_subu | inst_slt | inst_sltiu | inst_lw | inst_sw);
assign id_aluop_o[0] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
(inst_subu | inst_mflo | inst_sll |
inst_ori | inst_lui | inst_addiu | inst_sltiu);
// 写通用寄存器使能信号
assign id_wreg_o = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
(inst_add | inst_subu | inst_slt | inst_and | inst_mfhi | inst_mflo | inst_sll |
inst_ori | inst_lui | inst_addiu | inst_sltiu | inst_lb | inst_lw);
// 写HILO寄存器使能信号
assign id_whilo_o = (cpu_rst_n == `RST_ENABLE) ? 1'b0 : inst_mult;
// 移位使能指令
wire shift = inst_sll;
// 立即数使能信号
wire immsel = inst_ori | inst_lui | inst_addiu | inst_sltiu | inst_lb | inst_lw | inst_sb | inst_sw;
// 目的寄存器选择信号
wire rtsel = inst_ori | inst_lui | inst_addiu | inst_sltiu | inst_lb | inst_lw;
// 符号扩展使能信号
wire sext = inst_addiu | inst_sltiu | inst_lb | inst_lw | inst_sb | inst_sw;
// 加载高半字使能信号
wire upper = inst_lui;
// 存储器到寄存器使能信号
assign id_mreg_o = (cpu_rst_n == `RST_ENABLE) ? 1'b0 : (inst_lb | inst_lw);
// 读通用寄存器堆端口1使能信号
assign rreg1 = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
(inst_add | inst_subu | inst_slt | inst_and | inst_mult |
inst_ori | inst_addiu | inst_sltiu | inst_lb | inst_lw | inst_sb | inst_sw);
// 读通用寄存器堆读端口2使能信号
assign rreg2 = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
(inst_add | inst_subu | inst_slt | inst_and | inst_mult | inst_sll |
inst_sb | inst_sw);
/*------------------------------------------------------------------------------*/
// 读通用寄存器堆端口1的地址为rs字段,读端口2的地址为rt字段
assign ra1 = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD : rs;
assign ra2 = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD : rt;
// 获得指令操作所需的立即数
wire [31:0] imm_ext = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD :
(upper == `UPPER_ENABLE ) ? (imm << 16) :
(sext == `SIGNED_EXT ) ? {{16{imm[15]}}, imm} : {{16{1'b0}}, imm};
// 获得待写入目的寄存器的地址(rt或rd)
assign id_wa_o = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD :
(rtsel == `RT_ENABLE ) ? rt : rd;
// 获得访存阶段要存入数据存储器的数据(来自通用寄存器堆读数据端口2)
assign id_din_o = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD : rd2;
// 获得源操作数1。如果shift信号有效,则源操作数1为移位位数;否则为从读通用寄存器堆端口1获得的数据
assign id_src1_o = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD :
(shift == `SHIFT_ENABLE ) ? {27'b0, sa} :
(rreg1 == `READ_ENABLE ) ? rd1 : `ZERO_WORD;
// 获得源操作数2。如果immsel信号有效,则源操作数1为立即数;否则为从读通用寄存器堆端口2获得的数据
assign id_src2_o = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD :
(immsel == `IMM_ENABLE ) ? imm_ext :
(rreg2 == `READ_ENABLE ) ? rd2 : `ZERO_WORD;
endmodule
代码与图对应:
这两部分相当于DCU
译码阶段目前完成除了这部分的内容:
译码模块I/O端口:
这个模块就是译码阶段缺的那个模块
`include "defines.v"
module regfile(
input wire cpu_clk_50M,
input wire cpu_rst_n,
// 写端口
input wire [`REG_ADDR_BUS] wa,
input wire [`REG_BUS ] wd,
input wire we,
// 读端口1
input wire [`REG_ADDR_BUS] ra1,
output reg [`REG_BUS ] rd1,
input wire re1,
// 读端口2
input wire [`REG_ADDR_BUS] ra2,
output reg [`REG_BUS ] rd2,
input wire re2
);
//定义32个32位寄存器
reg [`REG_BUS] regs[0:`REG_NUM-1];
always @(posedge cpu_clk_50M) begin
if (cpu_rst_n == `RST_ENABLE) begin
regs[ 0] <= `ZERO_WORD;
regs[ 1] <= `ZERO_WORD;
regs[ 2] <= `ZERO_WORD;
regs[ 3] <= `ZERO_WORD;
regs[ 4] <= `ZERO_WORD;
regs[ 5] <= `ZERO_WORD;
regs[ 6] <= `ZERO_WORD;
regs[ 7] <= `ZERO_WORD;
regs[ 8] <= `ZERO_WORD;
regs[ 9] <= `ZERO_WORD;
regs[10] <= `ZERO_WORD;
regs[11] <= `ZERO_WORD;
regs[12] <= `ZERO_WORD;
regs[13] <= `ZERO_WORD;
regs[14] <= `ZERO_WORD;
regs[15] <= `ZERO_WORD;
regs[16] <= `ZERO_WORD;
regs[17] <= `ZERO_WORD;
regs[18] <= `ZERO_WORD;
regs[19] <= `ZERO_WORD;
regs[20] <= `ZERO_WORD;
regs[21] <= `ZERO_WORD;
regs[22] <= `ZERO_WORD;
regs[23] <= `ZERO_WORD;
regs[24] <= `ZERO_WORD;
regs[25] <= `ZERO_WORD;
regs[26] <= `ZERO_WORD;
regs[27] <= `ZERO_WORD;
regs[28] <= `ZERO_WORD;
regs[29] <= `ZERO_WORD;
regs[30] <= `ZERO_WORD;
regs[31] <= `ZERO_WORD;
end
else begin
if ((we == `WRITE_ENABLE) && (wa != 5'h0))
regs[wa] <= wd;
end
end
//读端口1的读操作
// ra1是读地址、wa是写地址、we是写使能、wd是要写入的数据
always @(*) begin
if (cpu_rst_n == `RST_ENABLE)
rd1 <= `ZERO_WORD;
else if (ra1 == `REG_NOP)
rd1 <= `ZERO_WORD;
else if (re1 == `READ_ENABLE)
rd1 <= regs[ra1];
else
rd1 <= `ZERO_WORD;
end
//读端口2的读操作
// ra2是读地址、wa是写地址、we是写使能、wd是要写入的数据
always @(*) begin
if (cpu_rst_n == `RST_ENABLE)
rd2 <= `ZERO_WORD;
else if (ra2 == `REG_NOP)
rd2 <= `ZERO_WORD;
else if (re2 == `READ_ENABLE)
rd2 <= regs[ra2];
else
rd2 <= `ZERO_WORD;
end
endmodule
程序解读
如果复位则32个32位寄存器全清零,除此之外,如果写使能且写地址不为0,则把wd的值传给regs[wa]。
REG_NOP是0号寄存器。
如果不是复位,是0号寄存器就把0给对应端口,除此之外,把对应寄存器数值给对应端口。
作用就是用寄存器保存译码阶段的所有信号,并送给执行阶段
`include "defines.v"
module idexe_reg (
input wire cpu_clk_50M,
input wire cpu_rst_n,
// 来自译码阶段的信息
input wire [`ALUTYPE_BUS ] id_alutype,
input wire [`ALUOP_BUS ] id_aluop,
input wire [`REG_BUS ] id_src1,
input wire [`REG_BUS ] id_src2,
input wire [`REG_ADDR_BUS ] id_wa,
input wire id_wreg,
input wire id_mreg,
input wire [`REG_BUS ] id_din,
input wire id_whilo,
// 送至执行阶段的信息
output reg [`ALUTYPE_BUS ] exe_alutype,
output reg [`ALUOP_BUS ] exe_aluop,
output reg [`REG_BUS ] exe_src1,
output reg [`REG_BUS ] exe_src2,
output reg [`REG_ADDR_BUS ] exe_wa,
output reg exe_wreg,
output reg exe_mreg,
output reg [`REG_BUS ] exe_din,
output reg exe_whilo
);
always @(posedge cpu_clk_50M) begin
// 复位的时候将送至执行阶段的信息清0
if (cpu_rst_n == `RST_ENABLE) begin
exe_alutype <= `NOP;
exe_aluop <= `MINIMIPS32_SLL;
exe_src1 <= `ZERO_WORD;
exe_src2 <= `ZERO_WORD;
exe_wa <= `REG_NOP;
exe_wreg <= `WRITE_DISABLE;
exe_mreg <= `FALSE_V;
exe_din <= `ZERO_WORD;
exe_whilo <= `WRITE_DISABLE;
end
// 将来自译码阶段的信息寄存并送至执行阶段
else begin
exe_alutype <= id_alutype;
exe_aluop <= id_aluop;
exe_src1 <= id_src1;
exe_src2 <= id_src2;
exe_wa <= id_wa;
exe_wreg <= id_wreg;
exe_mreg <= id_mreg;
exe_din <= id_din;
exe_whilo <= id_whilo;
end
end
endmodule
不用多说…就是很简单的复位和传递…
这是第二个复杂的、重要的模块。因为涉及到ALU的设计,是很多基础模块的集合。
`include "defines.v"
module exe_stage (
input wire cpu_rst_n,
// 从译码阶段获得的信息
input wire [`ALUTYPE_BUS ] exe_alutype_i,
input wire [`ALUOP_BUS ] exe_aluop_i,
input wire [`REG_BUS ] exe_src1_i,
input wire [`REG_BUS ] exe_src2_i,
input wire [`REG_ADDR_BUS ] exe_wa_i,
input wire exe_wreg_i,
input wire exe_mreg_i,
input wire [`REG_BUS ] exe_din_i,
input wire exe_whilo_i,
// 从HILO寄存器获得的数据
input wire [`REG_BUS ] hi_i,
input wire [`REG_BUS ] lo_i,
// 送至执行阶段的信息
output wire [`ALUOP_BUS ] exe_aluop_o,
output wire [`REG_ADDR_BUS ] exe_wa_o,
output wire exe_wreg_o,
output wire [`REG_BUS ] exe_wd_o,
output wire exe_mreg_o,
output wire [`REG_BUS ] exe_din_o,
output wire exe_whilo_o,
output wire [`DOUBLE_REG_BUS] exe_hilo_o
);
// 直接传到下一阶段
assign exe_aluop_o = (cpu_rst_n == `RST_ENABLE) ? 8'b0 : exe_aluop_i;
assign exe_mreg_o = (cpu_rst_n == `RST_ENABLE) ? 1'b0 : exe_mreg_i;
assign exe_din_o = (cpu_rst_n == `RST_ENABLE) ? 32'b0 : exe_din_i;
assign exe_whilo_o = (cpu_rst_n == `RST_ENABLE) ? 1'b0 : exe_whilo_i;
wire [`REG_BUS ] logicres; // 保存逻辑运算的结果
wire [`REG_BUS ] shiftres; // 保存移位运算结果
wire [`REG_BUS ] moveres; // 保存移动操作的结果
wire [`REG_BUS ] hi_t; // 保存HI寄存器的最新值
wire [`REG_BUS ] lo_t; // 保存LO寄存器的最新值
wire [`REG_BUS ] arithres; // 保存算术操作的结果
wire [`DOUBLE_REG_BUS] mulres; // 保存乘法操作的结果
// 根据内部操作码aluop进行逻辑运算
assign logicres = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD :
(exe_aluop_i == `MINIMIPS32_AND ) ? (exe_src1_i & exe_src2_i) :
(exe_aluop_i == `MINIMIPS32_ORI ) ? (exe_src1_i | exe_src2_i) :
(exe_aluop_i == `MINIMIPS32_LUI ) ? exe_src2_i : `ZERO_WORD;
// 根据内部操作码aluop进行移位运算
assign shiftres = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD :
(exe_aluop_i == `MINIMIPS32_SLL ) ? (exe_src2_i << exe_src1_i) : `ZERO_WORD;
// 根据内部操作码aluop进行数据移动,得到最新的HI、LO寄存器的值
assign hi_t = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD : hi_i;
assign lo_t = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD : lo_i;
assign moveres = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD :
(exe_aluop_i == `MINIMIPS32_MFHI) ? hi_t :
(exe_aluop_i == `MINIMIPS32_MFLO) ? lo_t : `ZERO_WORD;
// 根据内部操作码aluop进行算术运算
assign arithres = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD :
(exe_aluop_i == `MINIMIPS32_ADD ) ? (exe_src1_i + exe_src2_i) :
(exe_aluop_i == `MINIMIPS32_LB ) ? (exe_src1_i + exe_src2_i) :
(exe_aluop_i == `MINIMIPS32_LW ) ? (exe_src1_i + exe_src2_i) :
(exe_aluop_i == `MINIMIPS32_SB ) ? (exe_src1_i + exe_src2_i) :
(exe_aluop_i == `MINIMIPS32_SW ) ? (exe_src1_i + exe_src2_i) :
(exe_aluop_i == `MINIMIPS32_ADDIU) ? (exe_src1_i + exe_src2_i) :
(exe_aluop_i == `MINIMIPS32_SUBU ) ? (exe_src1_i + (~exe_src2_i) + 1) :
(exe_aluop_i == `MINIMIPS32_SLT ) ? (($signed(exe_src1_i) < $signed(exe_src2_i)) ? 32'b1 : 32'b0) :
(exe_aluop_i == `MINIMIPS32_SLTIU) ? ((exe_src1_i < exe_src2_i) ? 32'b1 : 32'b0) : `ZERO_WORD;
// 根据内部操作码aluop进行乘法运算,并保存送至下一阶段
assign mulres = ($signed(exe_src1_i) * $signed(exe_src2_i));
assign exe_hilo_o = (cpu_rst_n == `RST_ENABLE) ? `ZERO_DWORD :
(exe_aluop_i == `MINIMIPS32_MULT) ? mulres : `ZERO_DWORD;
assign exe_wa_o = (cpu_rst_n == `RST_ENABLE ) ? 5'b0 : exe_wa_i;
assign exe_wreg_o = (cpu_rst_n == `RST_ENABLE ) ? 1'b0 : exe_wreg_i;
// 根据操作类型alutype确定执行阶段最终的运算结果(既可能是待写入目的寄存器的数据,也可能是访问数据存储器的地址)
assign exe_wd_o = (cpu_rst_n == `RST_ENABLE ) ? `ZERO_WORD :
(exe_alutype_i == `LOGIC ) ? logicres :
(exe_alutype_i == `SHIFT ) ? shiftres :
(exe_alutype_i == `MOVE ) ? moveres :
(exe_alutype_i == `ARITH ) ? arithres : `ZERO_WORD;
endmodule
`include "defines.v"
module hilo (
input wire cpu_clk_50M,
input wire cpu_rst_n,
// 写端口
input wire we,
input wire [`REG_BUS] hi_i,
input wire [`REG_BUS] lo_i,
// 读端口
output reg [`REG_BUS] hi_o,
output reg [`REG_BUS] lo_o
);
always @(posedge cpu_clk_50M) begin
if (cpu_rst_n == `RST_ENABLE) begin
hi_o <= `ZERO_WORD;
lo_o <= `ZERO_WORD;
end
else if (we == `WRITE_ENABLE)begin
hi_o <= hi_i; // 将乘法结果mulres的前32位给HI寄存器,
lo_o <= lo_i; // 后32位给lo寄存器
end
end
endmodule
和之前的各级寄存器模块一样,寄存器传递信号。
`include "defines.v"
module exemem_reg (
input wire cpu_clk_50M,
input wire cpu_rst_n,
// 来自执行阶段的信息
input wire [`ALUOP_BUS ] exe_aluop,
input wire [`REG_ADDR_BUS] exe_wa,
input wire exe_wreg,
input wire [`REG_BUS ] exe_wd,
input wire exe_mreg,
input wire [`REG_BUS ] exe_din,
input wire exe_whilo,
input wire [`DOUBLE_REG_BUS] exe_hilo,
// 送到访存阶段的信息
output reg [`ALUOP_BUS ] mem_aluop,
output reg [`REG_ADDR_BUS] mem_wa,
output reg mem_wreg,
output reg [`REG_BUS ] mem_wd,
output reg mem_mreg,
output reg [`REG_BUS ] mem_din,
output reg mem_whilo,
output reg [`DOUBLE_REG_BUS] mem_hilo
);
always @(posedge cpu_clk_50M) begin
if (cpu_rst_n == `RST_ENABLE) begin
mem_aluop <= `MINIMIPS32_SLL;
mem_wa <= `REG_NOP;
mem_wreg <= `WRITE_DISABLE;
mem_wd <= `ZERO_WORD;
mem_mreg <= `WRITE_DISABLE;
mem_din <= `ZERO_WORD;
mem_whilo <= `WRITE_DISABLE;
mem_hilo <= `ZERO_DWORD;
end
else begin
mem_aluop <= exe_aluop;
mem_wa <= exe_wa;
mem_wreg <= exe_wreg;
mem_wd <= exe_wd;
mem_mreg <= exe_mreg;
mem_din <= exe_din;
mem_whilo <= exe_whilo;
mem_hilo <= exe_hilo;
end
end
endmodule
对于访存指令要完成加载和存储操作,对于非访存指令,直接把从执行阶段的信息直接传递给下一级就行:
`include "defines.v"
module mem_stage (
input wire cpu_rst_n,
// 从执行阶段获得的信息
input wire [`ALUOP_BUS ] mem_aluop_i,
input wire [`REG_ADDR_BUS ] mem_wa_i,
input wire mem_wreg_i,
input wire [`REG_BUS ] mem_wd_i,
input wire mem_mreg_i,
input wire [`REG_BUS ] mem_din_i,
input wire mem_whilo_i,
input wire [`DOUBLE_REG_BUS] mem_hilo_i,
// 送至写回阶段的信息
output wire [`REG_ADDR_BUS ] mem_wa_o,
output wire mem_wreg_o,
output wire [`REG_BUS ] mem_dreg_o,
output wire mem_mreg_o,
output wire [`BSEL_BUS ] dre,
output wire mem_whilo_o,
output wire [`DOUBLE_REG_BUS] mem_hilo_o,
// 送至数据存储器的信号
output wire dce,
output wire [`INST_ADDR_BUS ] daddr,
output wire [`BSEL_BUS ] we,
output wire [`REG_BUS ] din
);
// 如果当前不是访存指令,则只需要把从执行阶段获得的信息直接输出
assign mem_wa_o = (cpu_rst_n == `RST_ENABLE) ? 5'b0 : mem_wa_i;
assign mem_wreg_o = (cpu_rst_n == `RST_ENABLE) ? 1'b0 : mem_wreg_i;
assign mem_dreg_o = (cpu_rst_n == `RST_ENABLE) ? 1'b0 : mem_wd_i;
assign mem_whilo_o = (cpu_rst_n == `RST_ENABLE) ? 1'b0 : mem_whilo_i;
assign mem_hilo_o = (cpu_rst_n == `RST_ENABLE) ? 64'b0 : mem_hilo_i;
assign mem_mreg_o = (cpu_rst_n == `RST_ENABLE) ? 1'b0 : mem_mreg_i;
// 确定当前的访存指令
wire inst_lb = (mem_aluop_i == 8'h90);
wire inst_lw = (mem_aluop_i == 8'h92);
wire inst_sb = (mem_aluop_i == 8'h98);
wire inst_sw = (mem_aluop_i == 8'h9A);
// 获得数据存储器的访问地址
assign daddr = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD : mem_wd_i;
// 获得数据存储器读字节使能信号
assign dre[3] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
((inst_lb & (daddr[1 : 0] == 2'b00)) | inst_lw);
assign dre[2] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
((inst_lb & (daddr[1 : 0] == 2'b01)) | inst_lw);
assign dre[1] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
((inst_lb & (daddr[1 : 0] == 2'b10)) | inst_lw);
assign dre[0] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
((inst_lb & (daddr[1 : 0] == 2'b11)) | inst_lw);
// 获得数据存储器使能信号
assign dce = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
(inst_lb | inst_lw | inst_sb | inst_sw);
// 获得数据存储器写字节使能信号
assign we[3] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
((inst_sb & (daddr[1 : 0] == 2'b00)) | inst_sw);
assign we[2] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
((inst_sb & (daddr[1 : 0] == 2'b01)) | inst_sw);
assign we[1] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
((inst_sb & (daddr[1 : 0] == 2'b10)) | inst_sw);
assign we[0] = (cpu_rst_n == `RST_ENABLE) ? 1'b0 :
((inst_sb & (daddr[1 : 0] == 2'b11)) | inst_sw);
// 确定待写入数据存储器的数据
wire [`WORD_BUS] din_reverse = {mem_din_i[7:0], mem_din_i[15:8], mem_din_i[23:16], mem_din_i[31:24]};
wire [`WORD_BUS] din_byte = {mem_din_i[7:0], mem_din_i[7:0], mem_din_i[7:0], mem_din_i[7:0]};
assign din = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD :
(we == 4'b1111 ) ? din_reverse :
(we == 4'b1000 ) ? din_byte :
(we == 4'b0100 ) ? din_byte :
(we == 4'b0010 ) ? din_byte :
(we == 4'b0001 ) ? din_byte : `ZERO_WORD;
endmodule
`include "defines.v"
module memwb_reg (
input wire cpu_clk_50M,
input wire cpu_rst_n,
// 来自访存阶段的信息
input wire [`REG_ADDR_BUS ] mem_wa,
input wire mem_wreg,
input wire [`REG_BUS ] mem_dreg,
input wire mem_mreg,
input wire [`BSEL_BUS ] mem_dre,
input wire mem_whilo,
input wire [`DOUBLE_REG_BUS] mem_hilo,
// 送至写回阶段的信息
output reg [`REG_ADDR_BUS ] wb_wa,
output reg wb_wreg,
output reg [`REG_BUS ] wb_dreg,
output reg wb_mreg,
output reg [`BSEL_BUS ] wb_dre,
output reg wb_whilo,
output reg [`DOUBLE_REG_BUS] wb_hilo
);
always @(posedge cpu_clk_50M) begin
// 复位的时候将送至写回阶段的信息清0
if (cpu_rst_n == `RST_ENABLE) begin
wb_wa <= `REG_NOP;
wb_wreg <= `WRITE_DISABLE;
wb_dreg <= `ZERO_WORD;
wb_dre <= 4'b0;
wb_mreg <= `WRITE_DISABLE;
wb_whilo <= `WRITE_DISABLE;
wb_hilo <= `ZERO_DWORD;
end
// 将来自访存阶段的信息寄存并送至写回阶段
else begin
wb_wa <= mem_wa;
wb_wreg <= mem_wreg;
wb_dreg <= mem_dreg;
wb_dre <= mem_dre;
wb_mreg <= mem_mreg;
wb_whilo <= mem_whilo;
wb_hilo <= mem_hilo;
end
end
endmodule
完成向通用寄存器堆和HILO寄存器回传相关信号。此外对于加载指令,该阶段需要从数据存储器DM中读出数据并选择相对应的字节。
`include "defines.v"
module wb_stage(
input wire cpu_rst_n,
// 从访存阶段获得的信息
input wire wb_mreg_i,
input wire [`BSEL_BUS ] wb_dre_i,
input wire [`REG_ADDR_BUS ] wb_wa_i,
input wire wb_wreg_i,
input wire [`REG_BUS ] wb_dreg_i,
input wire wb_whilo_i,
input wire [`DOUBLE_REG_BUS] wb_hilo_i,
// 从数据存储器读出的数据
input wire [`WORD_BUS ] dm,
// 写回目的寄存器的数据
output wire [`REG_ADDR_BUS ] wb_wa_o,
output wire wb_wreg_o,
output wire [`WORD_BUS ] wb_wd_o,
output wire wb_whilo_o,
output wire [`DOUBLE_REG_BUS] wb_hilo_o
);
assign wb_wa_o = (cpu_rst_n == `RST_ENABLE) ? 5'b0 : wb_wa_i;
assign wb_wreg_o = (cpu_rst_n == `RST_ENABLE) ? 1'b0 : wb_wreg_i;
assign wb_whilo_o = (cpu_rst_n == `RST_ENABLE) ? 1'b0 : wb_whilo_i;
assign wb_hilo_o = (cpu_rst_n == `RST_ENABLE) ? 64'b0 : wb_hilo_i;
wire [`WORD_BUS] data = (cpu_rst_n == `RST_ENABLE) ? `ZERO_WORD :
(wb_dre_i == 4'b1111 ) ? {dm[7:0], dm[15:8], dm[23:16], dm[31:24]} :
(wb_dre_i == 4'b1000 ) ? {{24{dm[31]}}, dm[31:24]} :
(wb_dre_i == 4'b0100 ) ? {{24{dm[23]}}, dm[23:16]} :
(wb_dre_i == 4'b0010 ) ? {{24{dm[15]}}, dm[15:8 ]} :
(wb_dre_i == 4'b0001 ) ? {{24{dm[7 ]}}, dm[7 :0 ]} : `ZERO_WORD;
assign wb_wd_o = (cpu_rst_n == `RST_ENABLE ) ? `ZERO_WORD :
(wb_mreg_i == `MREG_ENABLE) ? data : wb_dreg_i;
endmodule
对上述各个模块进行实例化和连接,但是留出对指令存储器和数据存储器的连线。结构图如下图
`include "defines.v"
module MiniMIPS32(
input wire cpu_clk_50M,
input wire cpu_rst_n,
// inst_rom
output wire [`INST_ADDR_BUS] iaddr,
output wire ice,
input wire [`INST_BUS] inst,
// data_ram
output wire dce,
output wire [`INST_ADDR_BUS] daddr,
output wire [`BSEL_BUS ] we,
output wire [`INST_BUS ] din,
input wire [`INST_BUS ] dm
);
wire [`WORD_BUS ] pc;
// 连接IF/ID模块与译码阶段ID模块的变量
wire [`WORD_BUS ] id_pc_i;
// 连接译码阶段ID模块与通用寄存器Regfile模块的变量
wire re1;
wire [`REG_ADDR_BUS ] ra1;
wire [`REG_BUS ] rd1;
wire re2;
wire [`REG_ADDR_BUS ] ra2;
wire [`REG_BUS ] rd2;
wire [`ALUOP_BUS ] id_aluop_o;
wire [`ALUTYPE_BUS ] id_alutype_o;
wire [`REG_BUS ] id_src1_o;
wire [`REG_BUS ] id_src2_o;
wire id_wreg_o;
wire [`REG_ADDR_BUS ] id_wa_o;
wire id_whilo_o;
wire id_mreg_o;
wire [`REG_BUS ] id_din_o;
wire [`ALUOP_BUS ] exe_aluop_i;
wire [`ALUTYPE_BUS ] exe_alutype_i;
wire [`REG_BUS ] exe_src1_i;
wire [`REG_BUS ] exe_src2_i;
wire exe_wreg_i;
wire [`REG_ADDR_BUS ] exe_wa_i;
wire exe_whilo_i;
wire exe_mreg_i;
wire [`REG_BUS ] exe_din_i;
wire [`REG_BUS ] exe_hi_i;
wire [`REG_BUS ] exe_lo_i;
wire [`ALUOP_BUS ] exe_aluop_o;
wire exe_wreg_o;
wire [`REG_ADDR_BUS ] exe_wa_o;
wire [`REG_BUS ] exe_wd_o;
wire exe_mreg_o;
wire [`REG_BUS ] exe_din_o;
wire exe_whilo_o;
wire [`DOUBLE_REG_BUS] exe_hilo_o;
wire [`ALUOP_BUS ] mem_aluop_i;
wire mem_wreg_i;
wire [`REG_ADDR_BUS ] mem_wa_i;
wire [`REG_BUS ] mem_wd_i;
wire mem_mreg_i;
wire [`REG_BUS ] mem_din_i;
wire mem_whilo_i;
wire [`DOUBLE_REG_BUS] mem_hilo_i;
wire mem_wreg_o;
wire [`REG_ADDR_BUS ] mem_wa_o;
wire [`REG_BUS ] mem_dreg_o;
wire mem_mreg_o;
wire [`BSEL_BUS ] mem_dre_o;
wire mem_whilo_o;
wire [`DOUBLE_REG_BUS] mem_hilo_o;
wire wb_wreg_i;
wire [`REG_ADDR_BUS ] wb_wa_i;
wire [`REG_BUS ] wb_dreg_i;
wire [`BSEL_BUS ] wb_dre_i;
wire wb_mreg_i;
wire wb_whilo_i;
wire [`DOUBLE_REG_BUS] wb_hilo_i;
wire wb_wreg_o;
wire [`REG_ADDR_BUS ] wb_wa_o;
wire [`REG_BUS ] wb_wd_o;
wire wb_whilo_o;
wire [`DOUBLE_REG_BUS] wb_hilo_o;
if_stage if_stage0(.cpu_clk_50M(cpu_clk_50M), .cpu_rst_n(cpu_rst_n),
.pc(pc), .ice(ice), .iaddr(iaddr));
ifid_reg ifid_reg0(.cpu_clk_50M(cpu_clk_50M), .cpu_rst_n(cpu_rst_n),
.if_pc(pc), .id_pc(id_pc_i)
);
id_stage id_stage0(.cpu_rst_n(cpu_rst_n), .id_pc_i(id_pc_i),
.id_inst_i(inst),
.rd1(rd1), .rd2(rd2),
.rreg1(re1), .rreg2(re2),
.ra1(ra1), .ra2(ra2),
.id_aluop_o(id_aluop_o), .id_alutype_o(id_alutype_o),
.id_src1_o(id_src1_o), .id_src2_o(id_src2_o),
.id_wa_o(id_wa_o), .id_wreg_o(id_wreg_o),
.id_whilo_o(id_whilo_o),
.id_mreg_o(id_mreg_o), .id_din_o(id_din_o)
);
regfile regfile0(.cpu_clk_50M(cpu_clk_50M), .cpu_rst_n(cpu_rst_n),
.we(wb_wreg_o), .wa(wb_wa_o), .wd(wb_wd_o),
.re1(re1), .ra1(ra1), .rd1(rd1),
.re2(re2), .ra2(ra2), .rd2(rd2)
);
idexe_reg idexe_reg0(.cpu_clk_50M(cpu_clk_50M), .cpu_rst_n(cpu_rst_n),
.id_alutype(id_alutype_o), .id_aluop(id_aluop_o),
.id_src1(id_src1_o), .id_src2(id_src2_o),
.id_wa(id_wa_o), .id_wreg(id_wreg_o), .id_whilo(id_whilo_o),
.id_mreg(id_mreg_o), .id_din(id_din_o),
.exe_alutype(exe_alutype_i), .exe_aluop(exe_aluop_i),
.exe_src1(exe_src1_i), .exe_src2(exe_src2_i),
.exe_wa(exe_wa_i), .exe_wreg(exe_wreg_i), .exe_whilo(exe_whilo_i),
.exe_mreg(exe_mreg_i), .exe_din(exe_din_i)
);
exe_stage exe_stage0(.cpu_rst_n(cpu_rst_n),
.exe_alutype_i(exe_alutype_i), .exe_aluop_i(exe_aluop_i),
.exe_src1_i(exe_src1_i), .exe_src2_i(exe_src2_i),
.exe_wa_i(exe_wa_i), .exe_wreg_i(exe_wreg_i),
.exe_mreg_i(exe_mreg_i), .exe_din_i(exe_din_i),
.hi_i(exe_hi_i), .lo_i(exe_lo_i), .exe_whilo_i(exe_whilo_i),
.exe_aluop_o(exe_aluop_o),
.exe_wa_o(exe_wa_o), .exe_wreg_o(exe_wreg_o), .exe_wd_o(exe_wd_o),
.exe_mreg_o(exe_mreg_o), .exe_din_o(exe_din_o),
.exe_whilo_o(exe_whilo_o), .exe_hilo_o(exe_hilo_o)
);
exemem_reg exemem_reg0(.cpu_clk_50M(cpu_clk_50M), .cpu_rst_n(cpu_rst_n),
.exe_aluop(exe_aluop_o),
.exe_wa(exe_wa_o), .exe_wreg(exe_wreg_o), .exe_wd(exe_wd_o),
.exe_mreg(exe_mreg_o), .exe_din(exe_din_o),
.exe_whilo(exe_whilo_o), .exe_hilo(exe_hilo_o),
.mem_aluop(mem_aluop_i),
.mem_wa(mem_wa_i), .mem_wreg(mem_wreg_i), .mem_wd(mem_wd_i),
.mem_mreg(mem_mreg_i), .mem_din(mem_din_i),
.mem_whilo(mem_whilo_i), .mem_hilo(mem_hilo_i)
);
mem_stage mem_stage0(.cpu_rst_n(cpu_rst_n), .mem_aluop_i(mem_aluop_i),
.mem_wa_i(mem_wa_i), .mem_wreg_i(mem_wreg_i), .mem_wd_i(mem_wd_i),
.mem_mreg_i(mem_mreg_i), .mem_din_i(mem_din_i),
.mem_whilo_i(mem_whilo_i), .mem_hilo_i(mem_hilo_i),
.mem_wa_o(mem_wa_o), .mem_wreg_o(mem_wreg_o), .mem_dreg_o(mem_dreg_o),
.mem_mreg_o(mem_mreg_o), .dre(mem_dre_o),
.mem_whilo_o(mem_whilo_o), .mem_hilo_o(mem_hilo_o),
.dce(dce), .daddr(daddr), .we(we), .din(din)
);
memwb_reg memwb_reg0(.cpu_clk_50M(cpu_clk_50M), .cpu_rst_n(cpu_rst_n),
.mem_wa(mem_wa_o), .mem_wreg(mem_wreg_o), .mem_dreg(mem_dreg_o),
.mem_mreg(mem_mreg_o), .mem_dre(mem_dre_o),
.mem_whilo(mem_whilo_o), .mem_hilo(mem_hilo_o),
.wb_wa(wb_wa_i), .wb_wreg(wb_wreg_i), .wb_dreg(wb_dreg_i),
.wb_mreg(wb_mreg_i), .wb_dre(wb_dre_i),
.wb_whilo(wb_whilo_i), .wb_hilo(wb_hilo_i)
);
wb_stage wb_stage0(.cpu_rst_n(cpu_rst_n),
.wb_mreg_i(wb_mreg_i), .wb_dre_i(wb_dre_i),
.wb_wa_i(wb_wa_i), .wb_wreg_i(wb_wreg_i), .wb_dreg_i(wb_dreg_i),
.wb_whilo_i(wb_whilo_i), .wb_hilo_i(wb_hilo_i),
.dm(dm),
.wb_wa_o(wb_wa_o), .wb_wreg_o(wb_wreg_o), .wb_wd_o(wb_wd_o),
.wb_whilo_o(wb_whilo_o), .wb_hilo_o(wb_hilo_o)
);
hilo hilo0(.cpu_clk_50M(cpu_clk_50M), .cpu_rst_n(cpu_rst_n),
.we(wb_whilo_o),
.hi_i(wb_hilo_o[63:32]), .lo_i(wb_hilo_o[31:0]),
.hi_o(exe_hi_i), .lo_o(exe_lo_i)
);
endmodule
`include "defines.v"
module MiniMIPS32_SYS(
input wire sys_clk_100M,
input wire sys_rst_n
);
wire cpu_clk_50M;
wire [`INST_ADDR_BUS] iaddr;
wire ice;
wire [`INST_BUS ] inst;
wire dce;
wire [`INST_ADDR_BUS] daddr;
wire [`BSEL_BUS ] we;
wire [`INST_BUS ] din;
wire [`INST_BUS ] dout;
clk_wiz_0 clocking
(
// Clock out ports
.clk_out1(cpu_clk_50M), // output clk_out1
// Clock in ports
.clk_in1(sys_clk_100M)
); // input clk_in1
inst_rom inst_rom0 (
.clka(cpu_clk_50M), // input wire clka
.ena(ice), // input wire ena
.addra(iaddr[12:2]), // input wire [10 : 0] addra
.douta(inst) // output wire [31 : 0] douta
);
MiniMIPS32 minimips32 (
.cpu_clk_50M(cpu_clk_50M),
.cpu_rst_n(sys_rst_n),
.iaddr(iaddr),
.ice(ice),
.inst(inst),
.dce(dce),
.daddr(daddr),
.we(we),
.din(din),
.dm(dout)
);
data_ram data_ram0 (
.clka(cpu_clk_50M), // input wire clka
.ena(dce), // input wire ena
.wea(we), // input wire [3 : 0] wea
.addra(daddr[12:2]), // input wire [10 : 0] addra
.dina(din), // input wire [31 : 0] dina
.douta(dout) // output wire [31 : 0] douta
);
endmodule
之后的测试和仿真,建立工程整合起来等等我们下一章再说…看着有些复杂