代码模块
1. 宏定义defines.v
相对于ORI指令新添加与具体指令相关的宏定义
// 宏定义模块
// ***************** 全局的宏定义 ***************
`define RstEnable 1'b1 // 复位信号有效
`define RstDisable 1'b0 // 复位信号无效
`define ZeroWord 32'h00000000 // 32位的数值0
`define WriteEnable 1'b1 // 使能写
`define WriteDisable 1'b0 // 禁止写
`define ReadEnable 1'b1 // 使能读
`define ReadDisable 1'b0 // 禁止读
`define AluOpBus 7:0 // 译码阶段的输出aluop_o的宽度 子类型
`define AluSelBus 2:0 // 译码阶段的输出alusel_o的宽度 类型
`define InstValid 1'b0 // 指令有效
`define InstInvalid 1'b1 // 指令无效
`define True_v 1'b1 // 逻辑真
`define False_v 1'b0 // 逻辑假
`define ChipEnable 1'b1 // 芯片使能
`define ChipDisable 1'b0 // 芯片禁止
// *************** 与具体指令有关的宏定义 ***************
`define EXE_NOP 6'b000000
`define EXE_ORI 6'b001101 // 指令ori的指令码
`define EXE_ANDI 6'b001100
`define EXE_XORI 6'b001110
`define EXE_LUI 6'b001111
`define EXE_SPECIAL_INST 6'b000000
`define EXE_AND 6'b100100 // 指令的功能码
`define EXE_OR 6'b100101
`define EXE_XOR 6'b100110
`define EXE_NOR 6'b100111
`define EXE_SLL 6'b000000
`define EXE_SLLV 6'b000100
`define EXE_SRL 6'b000010
`define EXE_SRLV 6'b000110
`define EXE_SRA 6'b000011
`define EXE_SRAV 6'b000111
//AluOp 子类型
`define EXE_OR_OP 8'b00100101 // or_func
`define EXE_NOP_OP 8'b00000000
`define EXE_AND_OP 8'b00100100
`define EXE_XOR_OP 8'b00100110
`define EXE_NOR_OP 8'b00100111
`define EXE_SLL_OP 8'b00000000
`define EXE_SLLV_OP 8'b00000100
`define EXE_SRL_OP 8'b00000010
`define EXE_SRLV_OP 8'b00000110
`define EXE_SRA_OP 8'b00000011
`define EXE_SRAV_OP 8'b00000111
//AluSel 类型
`define EXE_RES_LOGIC 3'b001 // 逻辑运算
`define EXE_RES_SHIFT 3'b010 // 移位运算
`define EXE_RES_NOP 3'b000
// *************** 与指令存储器ROM有关的宏定义 ***************
`define InstAddrBus 31:0 // ROM的地址总线宽度
`define InstBus 31:0 // ROM数的据总线宽度
`define InstMemNum 131071
`define InstMemNumLog2 17
// ************** 与通用寄存器Regfile有关的宏定义 ***************
`define RegAddrBus 4:0 // Regfile模块的地址线宽度
`define RegBus 31:0 // Regfile模块的数据线宽度
`define RegWidth 32
`define DoubleRegWidth 64
`define DoubleRegBus 63:0
`define RegNum 32 // 通用寄存器的数量
`define RegNumLog2 5 // 寻址通用寄存器使用的地址位数
`define NOPRegAddr 5'b00000
2. 译码id
添加逻辑指令OR、AND、XOR、NOR、ANDI、XORI、LUI和移位指令SLL、SRL、SRA、SLLV、SRLV、SRAV的译码分支
// 对指令进行译码,得到最终运算的类型、子类型、源操作数1、源操作数2、要写入的目的寄存器地址信息等
`include "defines.v"
module id (
// 输出到regfile的信息
output reg reg1_read_o, // regfile模块第一个读寄存器端口的读使能信号
output reg reg2_read_o, // regfile模块第二个读寄存器端口的读使能信号
output reg [`RegAddrBus] reg1_addr_o, // regfile模块第一个读寄存器端口的读地址信号
output reg [`RegAddrBus] reg2_addr_o, // regfile模块第二个读寄存器端口的读地址信号
// 送到执行阶段的信息
output reg [`AluOpBus] aluop_o, // 译码阶段的指令要进行的运算的子类型
output reg [`AluSelBus] alusel_o, // 译码阶段的指令要进行的运算的类型
output reg [`RegBus] reg1_o, // 译码阶段的指令要进行的运算的源操作数1
output reg [`RegBus] reg2_o, // 译码阶段的指令要进行的运算的源操作数2
output reg [`RegAddrBus] wd_o, // 译码阶段的指令要写入的目的寄存器地址
output reg wreg_o, // 译码阶段的指令是否有要写入的目的寄存器
input wire rst, // 复位
input wire [`InstAddrBus] pc_i, // 译码阶段指令对应的地址
input wire [`InstBus] inst_i, // 译码阶段的指令
// 读取的regfile的值
input wire [`RegBus] reg1_data_i, // 从regfile输入的第一个读寄存器端口的输出
input wire [`RegBus] reg2_data_i // 从regfile输入的第一个读寄存器端口的输出
);
// 取得指令的指令码,功能码
// 对于ori指令只需通过判断26-31bits的值,即可判断是否是ori指令,6'b00_1101
wire [5:0] op = inst_i[31:26]; // 指令码
wire [4:0] op2 = inst_i[10:6];
wire [5:0] op3 = inst_i[5:0]; // 操作码
wire [4:0] op4 = inst_i[20:16];
// 保存指令执行需要的立即数
reg [`RegBus] imm;
// 指令指示是否有效
reg instvalid;
// ****************************************************************************
// **************************第一阶段:对指令进行译码******************************
// ****************************************************************************
always @ (*) begin
if (rst == `RstEnable) begin
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
wd_o <= `NOPRegAddr; // 写入地址赋值为无效
wreg_o <= `WriteDisable; // 没有要写入的目的寄存器
instvalid <= `InstValid; // 指令有效
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= `NOPRegAddr;
reg2_addr_o <= `NOPRegAddr;
imm <= 32'h0;
end else begin
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
wd_o <= inst_i[15:11]; // 默认目的寄存器地址
wreg_o <= `WriteDisable;
instvalid <= `InstValid; // 指令有效
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= inst_i[25:21]; // 默认通过regfile读端口1读取的寄存器地址,rs,源寄存器地址
reg2_addr_o <= inst_i[20:16]; // 默认通过regfile读端口2读取的寄存器地址,rt,源寄存器地址
imm <= `ZeroWord;
case (op)
`EXE_SPECIAL_INST: begin
case (op2) // [10:6]sa为0
5'b00000: begin
case (op3) // [5:0]func
`EXE_OR: begin // or指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_OR_OP; // 8'b 0010_0101
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_AND: begin // and指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_AND_OP; // 8'b 0010_0100
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_XOR: begin // xor指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_XOR_OP ; // 8'b 0010_0110
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_NOR: begin // nor指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_NOR_OP; // 8'b 0010_0111
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SLLV: begin // sllv指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SLL_OP; // 8'b 0000_0100
alusel_o <= `EXE_RES_SHIFT;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SRLV: begin // srlv指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SRL_OP; // 8'b 0000_0110
alusel_o <= `EXE_RES_SHIFT;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
`EXE_SRAV: begin // srav指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SRA_OP; // 8'b 0000_0111
alusel_o <= `EXE_RES_SHIFT;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
instvalid <= `InstValid;
end
default: begin
end
endcase // case (op3)
end
default: begin
end
endcase // case(op2)
end
`EXE_ORI: begin // 依据op的值判断指令是否为ori指令
// ori指令需要将结果写入目的寄存器
wreg_o <= `WriteEnable;
// 运算子类型是逻辑'or'运算
aluop_o <= `EXE_OR_OP;
// 运算类型是逻辑运算
alusel_o <= `EXE_RES_LOGIC;
// 需要通过regfile的读端口1读取寄存器
reg1_read_o <= `WriteEnable;
// 不需要通过regfile的读端口2读取寄存器
reg2_read_o <= `WriteDisable;
// 指令执行需要的立即数,无符号扩展
imm <= {16'h0, inst_i[15:0]};
// 指令执行要写的目的寄存器地址
wd_o <= inst_i[20:16];
// ori指令是有效指令
instvalid <= `InstValid;
end
`EXE_ANDI: begin // andi指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_AND_OP; // 8'b 0000_1100
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
imm <= {16'h0, inst_i[15:0]};
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_XORI: begin // xori指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_XOR_OP; // 8'b 0010_0100
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
imm <= {16'h0, inst_i[15:0]};
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
`EXE_LUI: begin // lui指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_OR_OP; // 8'b 0010_0101
alusel_o <= `EXE_RES_LOGIC;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
imm <= {inst_i[15:0], 16'h0};
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
default: begin
end
endcase // case(op)
if (inst_i[31:21] == 11'b000_0000_0000) begin
if (op3 == `EXE_SLL) begin // sll指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SLL_OP; // 8'b 0000_0000
alusel_o <= `EXE_RES_SHIFT;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b1;
imm[4:0] <= inst_i[10:6];
wd_o <= inst_i[15:11];
instvalid <=`InstValid;
end else if (op3 == `EXE_SRL) begin // srl指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SRL_OP; // 8'b 0000_0010
alusel_o <= `EXE_RES_SHIFT;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b1;
imm[4:0] <= inst_i[10:6];
wd_o <= inst_i[15:11];
instvalid <= `InstValid;
end else if (op3 == `EXE_SRA) begin // sra指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SRA_OP; // 8'b 0000_0011
alusel_o <= `EXE_RES_SHIFT;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b1;
imm <= inst_i[10:6];
wd_o <= inst_i[15:11];
instvalid <= `InstValid;
end
end
end //else
end //always
// ****************************************************************************
// ********************第二阶段:确定进行运算的操作数1 ******************************
// ****************************************************************************
always @ (*) begin
if (rst == `RstEnable) begin
reg1_o <= `ZeroWord;
end else if (reg1_read_o == 1'b1) begin
reg1_o <= reg1_data_i; // regfile读端口1的输出值
end else if (reg1_read_o == 1'b0) begin
reg1_o <= imm; // 立即数
end else begin
reg1_o <= `ZeroWord;
end
end
// ****************************************************************************
// ********************第三阶段:确定进行运算的操作数2 ******************************
// ****************************************************************************
always @ (*) begin
if (rst == `RstEnable) begin
reg2_o <= `ZeroWord;
end else if (reg2_read_o == 1'b1) begin
reg2_o <= reg2_data_i; // regfile读端口2的输出值
end else if (reg2_read_o == 1'b0) begin
reg2_o <= imm; // 立即数
end else begin
reg2_o <= `ZeroWord;
end
end
endmodule
3. 执行ex
为逻辑运算和移位运算添加新的分支
// 从ID模块得到运算类型alusel_i、运算子类型aluse_i、源操作数reg1_i、源操作数reg2_i、要写入的目的寄存器地址wd_i
// EX模块依据这些数据进行运算
`include "defines.v"
module ex (
// 执行的结果
output reg [`RegAddrBus] wd_o, // 最终要写入的目的寄存器地址
output reg wreg_o, // 最终是否有要写入的目的寄存器
output reg [`RegBus] wdata_o, // 最终要写入目的寄存器的值
input wire rst,
// 译码阶段送到执行阶段的信息
input wire [`AluOpBus] aluop_i, // 执行阶段运算子类型
input wire [`AluSelBus] alusel_i, // 执行阶段运算类型
input wire [`RegBus] reg1_i, // 执行阶段源操作数1
input wire [`RegBus] reg2_i, // 执行阶段源操作数2
input wire [`RegAddrBus] wd_i, // 执行阶段要写入的目的寄存器地址
input wire wreg_i // 执行阶段是否有要写入的目的寄存器
);
// 保存逻辑运算的结果
reg [`RegBus] logicout;
reg [`RegBus] shiftres;
// ****************************************************************************
// ***************第一阶段:依据aluop_i指示的运算子类型进行运算*********************
// ****************************************************************************
// ********** 逻辑运算 **********
always @ (*) begin
if (rst == `RstEnable) begin
logicout <= `ZeroWord;
end else begin
case (aluop_i)
`EXE_OR_OP: begin
logicout <= reg1_i | reg2_i;
end
`EXE_AND_OP: begin
logicout <= reg1_i & reg2_i;
end
`EXE_NOR_OP: begin
logicout <= ~(reg1_i | reg2_i);
end
`EXE_XOR_OP: begin
logicout <= reg1_i ^ reg2_i;
end
default: begin
logicout <= `ZeroWord;
end
endcase // case
end // if
end // always
// ********** 移位运算 **********
always @ (*) begin
if (rst == `RstEnable) begin
shiftres <= `ZeroWord;
end else begin
case (aluop_i)
`EXE_SLL: begin
shiftres <= reg2_i << reg1_i[4:0];
end
`EXE_SRL: begin
shiftres <= reg2_i >> reg1_i[4:0];
end
`EXE_SRA: begin
shiftres <= ({32{reg2_i[31]}} << (6'd32-{1'b0, reg1_i[4:0]})) | reg2_i >> reg1_i[4:0];
end
endcase
end
end
// ****************************************************************************
// **********第二阶段:依据alusel_i指示的运算类型,选择一个运算结果作为最终结果********
// ****************************************************************************
always @ (*) begin
wd_o <= wd_i; // 要写入的目的寄存器地址
wreg_o <= wreg_i; // 是否要写入目的寄存器
case (alusel_i)
`EXE_RES_LOGIC: begin
wdata_o <= logicout; // wdata_o中存放运算结果
end
`EXE_RES_SHIFT: begin
wdata_o <= shiftres;
end
default: begin
wdata_o <= `ZeroWord;
end
endcase // case
end // always
endmodule
4. 通用寄存器RegFile
重新修改读端口2的取数顺序,解决取操作数2不准确的情况
// 寄存器堆栈,实现了32个32位通用整数寄存器,可以同时进行两个寄存器的读操作和一个寄存器的写操作
`include "defines.v"
module regfile (
// 写端口
input wire we, // 写使能信号
input wire [`RegAddrBus] waddr, // 要写入的寄存器地址
input reg [`RegBus] wdata, // 要写入的数据
// 读端口1
input wire re1, // 第一个读寄存器端口读使能信号
input wire [`RegAddrBus] raddr1, // 第一个读寄存器端口要读取的寄存器的地址
output reg [`RegBus] rdata1, // 第一个读寄存器端口输出的寄存器值
// 读端口2
input wire re2, // 第二个读寄存器端口读使能信号
input wire [`RegAddrBus] raddr2, // 第二个读寄存器端口要读取的寄存器的地址
output reg [`RegBus] rdata2, // 第二个读寄存器端口输出的寄存器值
input wire clk,
input wire rst
);
// **********************第一阶段:定义32个32位寄存器************************
reg [`RegBus] regs[0:`RegNum-1];
// **************************第二阶段:写操作******************************
always @ (posedge clk) begin
if (rst == `RstDisable) begin // 复位信号无效
if ((we ==`WriteEnable) && (waddr != `RegNumLog2'h0)) begin // 写使能&&写目的寄存器≠$0
regs[waddr] <= wdata;
end
end
end
// *************************第三阶段:读端口1的读操作******************************
always @ (*) begin
if (rst == `RstEnable) begin
rdata1 <= `ZeroWord; // 复位信号有效,输出0
end else if (raddr1 == `RegNumLog2'h0) begin // 读目的寄存器为$0,输出0
rdata1 <= `ZeroWord;
end else if (re1 == `ReadEnable) begin // 输出要读取的目的寄存器地址对应的值
rdata1 <= regs[raddr1];
end else if ((raddr1 == waddr) && (we == `WriteEnable) && (re1 == `ReadEnable)) begin // 读目的寄存器是写目的寄存器&&写使能&&读使能,输出写入的值
rdata1 <= wdata;
end else begin
rdata1 <= `ZeroWord;
end
end
// *************************第四阶段:读端口2的读操作******************************
always @ (*) begin
if (rst == `RstEnable) begin
rdata2 <= `ZeroWord;
end else if (raddr2 == `RegNumLog2'h0) begin
rdata2 <= `ZeroWord;
end else if (re2 == `ReadEnable) begin
rdata2 <= regs[raddr2];
end else if ((raddr2 == waddr) && (we == `WriteEnable) && (re2 == `ReadEnable)) begin
rdata2 <= wdata;
end else begin
rdata2 <= `ZeroWord;
end
end
endmodule
测试模块
测试逻辑指令
仿真波形
测试移位指令
仿真波形