目录
引言
本篇学习书本的第四章,实现 ori 指令。
致谢
感谢书籍《自己动手写CPU》及其作者雷思磊。一并感谢开源精神。
平台
开发环境:Vivado IDE 2018.3
FPGA芯片型号:xc7a35tfgg484-2
ori 指令
指令格式:
指令用法:
ori rt, rs,immediate
其作用是将指令中的16位立即数进行无符号数扩展至 32 位,然后与索引号为 rt 的通用寄存器的值进行逻辑或运算,运算的结果保存到索引号为 rs 的寄存器内。
符号扩展示例:
此处书本有误:
书本中给出的指令用法为:
但是随后举的例子却是:
所以正确的指令使用格式为:(上面写过了,再强调一遍)
ori rt, rs,immediate
流水线结构建立
模型
简单的MIPS五级流水线结构
设计
声明:
设计思路基本借鉴书本,但是具体的模块代码编写,与作者有较大不同。
宏定义
此文件主要定义了一些与设计相关的宏,当设计的参数需要更改时,在此文件修改即可,不需要改动内部设计文件。方便代码维护,也增强了代码的可读性。
// |------------------------------ ================================== ------------------------------
// |============================== MIPS32 CPU SYSTEM ALL MACRO DEFINE ==============================
// |------------------------------ ================================== ------------------------------
// |Create Date : 2022-12-06
// |Finish Date : 2022-
// |Edited by : Xu Y. B. (CSDN USER NAME :在路上,正出发)
// |Reference : 《自己动手写CPU》——第4章
// |
// |
// |------------------------------------------------------------------------------------------------
// |Change History:
// |
// |-------------------------------------- 系统级全局宏定义 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 0字
`define DEF_ZERO_WORD 32'd0 // 0字
// | 关于译码
`define DEF_ALU_OPR_BUS 7:0 // 译码输出 O_ALU_OPR 总线
`define DEF_ALU_SEL_BUS 2:0 // 译码输出 O_ALU_SEL 总线
// | 逻辑 0 1
`define DEF_LOG_TRUE 1'b1 // 逻辑 真
`define DEF_LOG_FALSE 1'b0 // 逻辑 假
// | 芯片使能
`define DEF_CHIP_EN 1'b1 // 芯片使能
`define DEF_CHIP_DIS 1'b0 // 芯片不使能
// |-------------------------------------- 指令相关的宏定义 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 指令码
`define DEF_ISTC_ORI 6'b001101 // ori
`define DEF_ISTC_NOP 6'd0 // nop
// | ALU操作码
`define DEF_ALU_OR_OPR 8'b00100101 // ALU 或 操作码
`define DEF_ALU_NOP_OPR 8'd0 // ALU 空 操作码
// | ALU 选择
`define DEF_ALU_SEL_LOGIC 3'b001
`define DEF_ALU_SEL_NOP 3'b000
// | 操作数
`define DEF_SRC_OPR_DATA_BUS 31:0
`define DEF_IMM_DATA_BUS 15:0
// |-------------------------------------- 指令存储器宏定义 --------------------------------------
// |------------------------------------------------------------------------------------------------
`define DEF_ISTC_ADDR_BUS 31:0 // 地址线总线
`define DEF_ISTC_DATA_BUS 31:0 // 数据线总线
`define DEF_ISTC_CACH_DEPTH 2**17-1 // 缓存深度/地址最大值
`define DEF_ISTC_ADDR_WIDTH_ACTUAL 17 // 实际使用的缓存地址线宽度
// |-------------------------------------- 通用寄存器宏定义 --------------------------------------
// |------------------------------------------------------------------------------------------------
`define DEF_GPR_ADDR_WIDTH 5 // 通用寄存器地址位宽(32个)
`define DEF_GPR_DATA_WIDTH 32 // 通用寄存器数据位宽
`define DEF_GPR_NUM 32 // 通用寄存器数目
`define DEF_GPR_ADDR_NOP 5'd0 // 空操作 GPR 地址
程序计数器
该模块在时钟节拍下,给出程序指令寄存器要读取的地址,以及读使能信号。输出接口对接指令存储器ROM模块。
// |------------------------------ ================================== ------------------------------
// |============================== 程序计数寄存器模块 ==============================
// |------------------------------ ================================== ------------------------------
// |Create Date : 2022-12-06
// |Finish Date : 2022-12-06
// |Edited by : Xu Y. B. (CSDN USER NAME :在路上,正出发)
// |Reference : 《自己动手写CPU》——第4章
// |
// |
// |------------------------------------------------------------------------------------------------
// |Change History:
// |
`include "MIPS_SYS_DEFINES.v"
`timescale 1ns / 1ps
module PC_REG_MDL(
// |-------------------------------------- 输入输出端口声明 --------------------------------------
// |------------------------------------------------------------------------------------------------
input I_CPU_CLK,
input I_CPU_RSTN,
output reg [`DEF_ISTC_ADDR_BUS] O_PC,
output reg O_ISTC_ROM_CE
);
// |-------------------------------------- 模块内部逻辑设计 --------------------------------------
// |------------------------------------------------------------------------------------------------
// CE
always @ (posedge I_CPU_CLK)
begin
if(~I_CPU_RSTN)
begin
O_ISTC_ROM_CE <= `DEF_CHIP_DIS;
end
else
begin
O_ISTC_ROM_CE <= `DEF_CHIP_EN;
end
end
// PC
always @ (posedge I_CPU_CLK)
begin
if(O_ISTC_ROM_CE == `DEF_CHIP_DIS)
begin
O_PC <= 32'd0;
end
else
begin
O_PC <= O_PC + 32'd4;
end
end
endmodule
译码
此模块主要接收来自指令存储器ROM模块输出的指令,并且按照指令编码规则进行指令译码,为后续的指令执行模块提供必要的计算信息。
// |------------------------------ ================================== ------------------------------
// |============================== 指令-译码模块 ==============================
// |------------------------------ ================================== ------------------------------
// |Create Date : 2022-12-07
// |Finish Date : 2022-12-07
// |Edited by : Xu Y. B. (CSDN USER NAME :在路上,正出发)
// |Reference : 《自己动手写CPU》——第4章
// |
// |
// |------------------------------------------------------------------------------------------------
// |Change History:
// |
`include "MIPS_SYS_DEFINES.v"
`timescale 1ns / 1ps
module ID_MDL(
// |-------------------------------------- 输入输出端口声明 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 时钟、复位
input I_CPU_CLK,
input I_CPU_RSTN,
// | 指令
input [`DEF_ISTC_DATA_BUS] I_ISTC,
// | GPR读写控制
output reg O_GPR_RD_EN_A,
output reg [`DEF_GPR_ADDR_WIDTH-1:0] O_GPR_RD_ADDR_A,
input [`DEF_GPR_DATA_WIDTH-1:0] I_GPR_RD_DATA_A,
output reg O_GPR_RD_EN_B,
output reg [`DEF_GPR_ADDR_WIDTH-1:0] O_GPR_RD_ADDR_B,
input [`DEF_GPR_DATA_WIDTH-1:0] I_GPR_RD_DATA_B,
// | 译码输出相关 对接 ALU 运算单元
// output reg [`DEF_ALU_SEL_BUS] O_ALU_SEL,
output reg [`DEF_ALU_OPR_BUS] O_ALU_OP_TYPE,
output reg [`DEF_SRC_OPR_DATA_BUS] O_SRC_OPR_DATA_A,
output reg [`DEF_SRC_OPR_DATA_BUS] O_SRC_OPR_DATA_B,
output reg O_DST_GPR_WR_EN,
output reg [`DEF_GPR_ADDR_WIDTH-1:0] O_DST_GPR_WR_ADDR
);
// |-------------------------------------- 模块内部信号声明 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 指令分解相关
wire [5:0] W_ISTC_TYPE;
wire [`DEF_GPR_ADDR_WIDTH-1:0] W_SRC_GPR_ADDR;
wire [`DEF_GPR_ADDR_WIDTH-1:0] W_DST_GPR_ADDR;
wire [`DEF_IMM_DATA_BUS] W_ISTC_IMM_DATA;
// | 32位立即数
reg [31:0] R_IMM_DATA_32BIT;
// | 指令有效信号
reg R_ISTC_VAL;
// |-------------------------------------- 模块内部逻辑设计 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 指令分解相关
assign W_ISTC_TYPE = I_ISTC[31:26];
assign W_SRC_GPR_ADDR = I_ISTC[25:21];
assign W_DST_GPR_ADDR = I_ISTC[20:16];
assign W_ISTC_IMM_DATA = I_ISTC[15:0] ;
// | 指令译码
always @ (posedge I_CPU_CLK)
begin
if(~I_CPU_RSTN)
begin
O_GPR_RD_EN_A <= 1'b0;
O_GPR_RD_ADDR_A <= `DEF_GPR_ADDR_NOP;
O_GPR_RD_EN_B <= 1'b0;
O_GPR_RD_ADDR_B <= `DEF_GPR_ADDR_NOP;
O_ALU_OP_TYPE <= `DEF_ALU_NOP_OPR;
// O_ALU_SEL <= `DEF_ALU_SEL_NOP;
O_DST_GPR_WR_EN <= 1'b0;
O_DST_GPR_WR_ADDR <= `DEF_GPR_ADDR_NOP;
R_ISTC_VAL <= 1'b0;
R_IMM_DATA_32BIT <= 32'd0;
end
else
begin
case(W_ISTC_TYPE)
`DEF_ISTC_ORI:// ori指令
begin
O_GPR_RD_EN_A <= 1'b1;
O_GPR_RD_ADDR_A <= W_SRC_GPR_ADDR;
O_GPR_RD_EN_B <= 1'b0;
O_GPR_RD_ADDR_B <= `DEF_GPR_ADDR_NOP;
O_ALU_OP_TYPE <= `DEF_ALU_OR_OPR;
// O_ALU_SEL <= `DEF_ALU_SEL_LOGIC;
O_DST_GPR_WR_EN <= 1'b1;
O_DST_GPR_WR_ADDR <= W_DST_GPR_ADDR;
R_ISTC_VAL <= 1'b1;
R_IMM_DATA_32BIT <= {16'd0,W_ISTC_IMM_DATA};
end
default:
begin
O_GPR_RD_EN_A <= 1'b0;
O_GPR_RD_ADDR_A <= `DEF_GPR_ADDR_NOP;
O_GPR_RD_EN_B <= 1'b0;
O_GPR_RD_ADDR_B <= `DEF_GPR_ADDR_NOP;
O_ALU_OP_TYPE <= `DEF_ALU_NOP_OPR;
// O_ALU_SEL <= `DEF_ALU_SEL_NOP;
O_DST_GPR_WR_EN <= 1'b0;
O_DST_GPR_WR_ADDR <= `DEF_GPR_ADDR_NOP;
R_ISTC_VAL <= 1'b0;
R_IMM_DATA_32BIT <= 32'd0;
end
endcase
end
end
// | 数据输出
always @ (posedge I_CPU_CLK)
begin
if(~I_CPU_RSTN)
begin
O_SRC_OPR_DATA_A <= `DEF_ZERO_WORD;
end
else if(O_GPR_RD_EN_A)
begin
O_SRC_OPR_DATA_A <= I_GPR_RD_DATA_A;
end
else if(~O_GPR_RD_EN_A)
begin
O_SRC_OPR_DATA_A <= R_IMM_DATA_32BIT;
end
else
begin
O_SRC_OPR_DATA_A <= `DEF_ZERO_WORD;
end
end
always @ (posedge I_CPU_CLK)
begin
if(~I_CPU_RSTN)
begin
O_SRC_OPR_DATA_B <= `DEF_ZERO_WORD;
end
else if(O_GPR_RD_EN_B)
begin
O_SRC_OPR_DATA_B <= I_GPR_RD_DATA_B;
end
else if(~O_GPR_RD_EN_B)
begin
O_SRC_OPR_DATA_B <= R_IMM_DATA_32BIT;
end
else
begin
O_SRC_OPR_DATA_B <= `DEF_ZERO_WORD;
end
end
endmodule
通用寄存器
此模块主要负责32位通用寄存器的初始化、读写等操作。可当作RAM理解。
// |------------------------------ ================================== ------------------------------
// |============================== 取指-译码接口模块 ==============================
// |------------------------------ ================================== ------------------------------
// |Create Date : 2022-12-06
// |Finish Date : 2022-12-06
// |Edited by : Xu Y. B. (CSDN USER NAME :在路上,正出发)
// |Reference : 《自己动手写CPU》——第4章
// |
// |
// |------------------------------------------------------------------------------------------------
// |Change History:
// |
`include "MIPS_SYS_DEFINES.v"
`timescale 1ns / 1ps
module GPR_WR_RD_MDL(
// |-------------------------------------- 输入输出端口声明 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 时钟、复位
input I_CPU_CLK,
input I_CPU_RSTN,
// | 写
input I_GPR_WR_EN,
input [`DEF_GPR_ADDR_WIDTH-1:0] I_GPR_WR_ADDR,
input [`DEF_GPR_DATA_WIDTH-1:0] I_GPR_WR_DATA,
// |读
input I_GPR_RD_EN_A,
input [`DEF_GPR_ADDR_WIDTH-1:0] I_GPR_RD_ADDR_A,
output reg [`DEF_GPR_DATA_WIDTH-1:0] O_GPR_RD_DATA_A,
input I_GPR_RD_EN_B,
input [`DEF_GPR_ADDR_WIDTH-1:0] I_GPR_RD_ADDR_B,
output reg [`DEF_GPR_DATA_WIDTH-1:0] O_GPR_RD_DATA_B
);
// |-------------------------------------- GPR-寄存器组定义 --------------------------------------
// |------------------------------------------------------------------------------------------------
reg [`DEF_GPR_DATA_WIDTH-1:0] R_GPR [`DEF_GPR_NUM-1:0];
// |-------------------------------------- GPR-寄存器初始化 --------------------------------------
// |------------------------------------------------------------------------------------------------
initial $readmemh("D:/VIVADO_WORK_SPACE/CPU_MIPS32/DATA_FILE/GPR_INIT.txt",R_GPR);
// |-------------------------------------- GPR-寄存器写操作 --------------------------------------
// |------------------------------------------------------------------------------------------------
always @ (posedge I_CPU_CLK)
begin
if(I_CPU_RSTN)
begin
if(I_GPR_WR_EN && I_GPR_WR_ADDR != `DEF_GPR_ADDR_WIDTH'd0)
begin
R_GPR[I_GPR_WR_ADDR] <= I_GPR_WR_DATA;
end
end
end
// |-------------------------------------- PORT A 读操作 --------------------------------------
// |------------------------------------------------------------------------------------------------
always @ (*)
begin
if(~I_CPU_RSTN)
begin
O_GPR_RD_DATA_A = `DEF_ZERO_WORD;
end
else if(I_GPR_WR_EN && I_GPR_RD_EN_A && (I_GPR_RD_ADDR_A == I_GPR_WR_ADDR))
begin
O_GPR_RD_DATA_A = I_GPR_WR_DATA;
end
else if(I_GPR_RD_EN_A)
begin
O_GPR_RD_DATA_A = R_GPR[I_GPR_RD_ADDR_A];
end
else
begin
O_GPR_RD_DATA_A = `DEF_ZERO_WORD;
end
end
// |-------------------------------------- PORT B 读操作 --------------------------------------
// |------------------------------------------------------------------------------------------------
always @ (*)
begin
if(~I_CPU_RSTN)
begin
O_GPR_RD_DATA_B = `DEF_ZERO_WORD;
end
else if(I_GPR_WR_EN && I_GPR_RD_EN_B && (I_GPR_RD_ADDR_B == I_GPR_WR_ADDR))
begin
O_GPR_RD_DATA_B = I_GPR_WR_DATA;
end
else if(I_GPR_RD_EN_B)
begin
O_GPR_RD_DATA_B = R_GPR[I_GPR_RD_ADDR_B];
end
else
begin
O_GPR_RD_DATA_B = `DEF_ZERO_WORD;
end
end
endmodule
初始化文件:
说明:此处文中的示例代码存在如下问题:
1、模块输出一般不选择组合逻辑输出;
2、always快描述组合逻辑时,要用阻塞赋值 “=”
指令执行
此模块是CPU的核心运算模块,将译码阶段传递的运算指令执行,并且完成数据的写请求。
// |------------------------------ ================================== ------------------------------
// |============================== 指令-执行模块 ==============================
// |------------------------------ ================================== ------------------------------
// |Create Date : 2022-12-07
// |Finish Date : 2022-12-07
// |Edited by : Xu Y. B. (CSDN USER NAME :在路上,正出发)
// |Reference : 《自己动手写CPU》——第4章
// |
// |
// |------------------------------------------------------------------------------------------------
// |Change History:
// |
`include "MIPS_SYS_DEFINES.v"
`timescale 1ns / 1ps
module EXE_MDL(
// |-------------------------------------- 输入输出端口声明 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 时钟、复位
input I_CPU_CLK,
input I_CPU_RSTN,
// | 操作指令
// input [`DEF_ALU_SEL_BUS] I_ALU_SEL,
input [`DEF_ALU_OPR_BUS] I_ALU_OP_TYPE,
// | 源操作数
input [`DEF_SRC_OPR_DATA_BUS] I_SRC_OPR_DATA_A,
input [`DEF_SRC_OPR_DATA_BUS] I_SRC_OPR_DATA_B,
// | 目的寄存器写
input I_DST_GPR_WR_EN,
input [`DEF_GPR_ADDR_WIDTH-1:0] I_DST_GPR_WR_ADDR,
// | 输出
output reg O_DST_GPR_WR_EN, //输入打一拍
output reg [`DEF_GPR_ADDR_WIDTH-1:0] O_DST_GPR_WR_ADDR, //输入打一拍
output reg [`DEF_GPR_DATA_WIDTH-1:0] O_DST_GPR_WR_DATA
);
// |-------------------------------------- 模块内部信号声明 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | I_ALU_OP_TYPE 打拍
reg [`DEF_ALU_OPR_BUS] R_I_ALU_OP_TYPE;
reg R_I_DST_GPR_WR_EN;
reg [`DEF_GPR_ADDR_WIDTH-1:0] R_I_DST_GPR_WR_ADDR;
// |-------------------------------------- 模块内部逻辑设计 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | I_ALU_OP_TYPE 打拍对齐时序
always @ (posedge I_CPU_CLK)
begin
if(~I_CPU_RSTN)
begin
R_I_ALU_OP_TYPE <= 0;
end
else
begin
R_I_ALU_OP_TYPE <= I_ALU_OP_TYPE;
end
end
// | 计算单元
always @ (posedge I_CPU_CLK)
begin
if(~I_CPU_RSTN)
begin
O_DST_GPR_WR_DATA <= `DEF_ZERO_WORD;
end
else
begin
case(R_I_ALU_OP_TYPE)
`DEF_ALU_OR_OPR:
begin
O_DST_GPR_WR_DATA <= I_SRC_OPR_DATA_A | I_SRC_OPR_DATA_B;
end
default:
begin
O_DST_GPR_WR_DATA <= `DEF_ZERO_WORD;
end
endcase
end
end
// | 写操作打拍
always @ (posedge I_CPU_CLK)
begin
if(~I_CPU_RSTN)
begin
O_DST_GPR_WR_EN <= 1'b0;
O_DST_GPR_WR_ADDR <= 5'd0;
R_I_DST_GPR_WR_EN <= 1'b0;
R_I_DST_GPR_WR_ADDR <= 5'd0;
end
else
begin
R_I_DST_GPR_WR_EN <= I_DST_GPR_WR_EN;
R_I_DST_GPR_WR_ADDR <= I_DST_GPR_WR_ADDR;
O_DST_GPR_WR_EN <= R_I_DST_GPR_WR_EN;
O_DST_GPR_WR_ADDR <= R_I_DST_GPR_WR_ADDR;
end
end
endmodule
内存访问
此模块主要是一些内存访问的操作,由于 ori 指令暂时不需要访存,所以该模块目前只有写使能传递功能。
// |------------------------------ ================================== ------------------------------
// |============================== -执行模块 ==============================
// |------------------------------ ================================== ------------------------------
// |Create Date : 2022-12-07
// |Finish Date : 2022-12-07
// |Edited by : Xu Y. B. (CSDN USER NAME :在路上,正出发)
// |Reference : 《自己动手写CPU》——第4章
// |
// |
// |------------------------------------------------------------------------------------------------
// |Change History:
// |
`include "MIPS_SYS_DEFINES.v"
`timescale 1ns / 1ps
module MEM_ACS_MDL(
// |-------------------------------------- 输入输出端口声明 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 时钟、复位
input I_CPU_CLK,
input I_CPU_RSTN,
// | 前接指令执行模块输出
input I_DST_GPR_WR_EN,
input [`DEF_GPR_ADDR_WIDTH-1:0] I_DST_GPR_WR_ADDR,
input [`DEF_GPR_DATA_WIDTH-1:0] I_DST_GPR_WR_DATA,
// | 后接写回模块
output reg O_DST_GPR_WR_EN,
output reg [`DEF_GPR_ADDR_WIDTH-1:0] O_DST_GPR_WR_ADDR,
output reg [`DEF_GPR_DATA_WIDTH-1:0] O_DST_GPR_WR_DATA
);
// |-------------------------------------- 模块内部逻辑设计 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 目的寄存器写操作传递/打拍
always @ (posedge I_CPU_CLK)
begin
if(~I_CPU_RSTN)
begin
O_DST_GPR_WR_EN <= 1'b0;
O_DST_GPR_WR_ADDR <= 5'd0;
O_DST_GPR_WR_DATA <= 32'd0;
end
else
begin
O_DST_GPR_WR_EN <= I_DST_GPR_WR_EN ;
O_DST_GPR_WR_ADDR <= I_DST_GPR_WR_ADDR;
O_DST_GPR_WR_DATA <= I_DST_GPR_WR_DATA;
end
end
endmodule
指令ROM
此模块存放程序指令。
// |------------------------------ ================================== ------------------------------
// |============================== 程序计数寄存器模块 ==============================
// |------------------------------ ================================== ------------------------------
// |Create Date : 2022-12-08
// |Finish Date : 2022-12-08
// |Edited by : Xu Y. B. (CSDN USER NAME :在路上,正出发)
// |Reference : 《自己动手写CPU》——第4章
// |
// |
// |------------------------------------------------------------------------------------------------
// |Change History:
// |
`include "MIPS_SYS_DEFINES.v"
`timescale 1ns / 1ps
module ROM_ISTC_MDL(
// |-------------------------------------- 输入输出端口声明 --------------------------------------
// |------------------------------------------------------------------------------------------------
input I_CPU_CLK,
input I_CPU_RSTN,
input I_RD_EN,
input [`DEF_ISTC_ADDR_BUS] I_RD_ADDR,
output reg [`DEF_ISTC_DATA_BUS] O_ISTC
);
// |-------------------------------------- 模块内部信号声明 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | ROM空间开辟
reg [`DEF_ISTC_DATA_BUS] R_ROM_DATA [`DEF_ISTC_CACH_DEPTH:0];
// |-------------------------------------- 模块内部逻辑设计 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | ROM初始化
initial $readmemh("D:/VIVADO_WORK_SPACE/CPU_MIPS32/DATA_FILE/ISTC_ROM.txt",R_ROM_DATA);
// | 数据读取
always @ (posedge I_CPU_CLK)
begin
if(~I_CPU_RSTN)
begin
O_ISTC <= `DEF_ZERO_WORD;
end
else
begin
if(I_RD_EN)
begin
O_ISTC <= R_ROM_DATA[I_RD_ADDR[`DEF_ISTC_ADDR_WIDTH_ACTUAL+1:2]];
end
else
begin
O_ISTC <= `DEF_ZERO_WORD;
end
end
end
endmodule
程序代码示例:(此处借鉴书本的方法,仅为了完成仿真)
顶层文件
处理器顶层
// |------------------------------ ================================== ------------------------------
// |============================== 指令-执行模块 ==============================
// |------------------------------ ================================== ------------------------------
// |Create Date : 2022-12-07
// |Finish Date : 2022-12-07
// |Edited by : Xu Y. B. (CSDN USER NAME :在路上,正出发)
// |Reference : 《自己动手写CPU》——第4章
// |
// |
// |------------------------------------------------------------------------------------------------
// |Change History:
// |
`include "MIPS_SYS_DEFINES.v"
`timescale 1ns / 1ps
module TOP_MIPS32(
// |-------------------------------------- 输入输出端口声明 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 时钟、复位
input I_CPU_CLK,
input I_CPU_RSTN,
// | 指令取
input [`DEF_ISTC_DATA_BUS] I_ISTC_FROM_ROM,
output [`DEF_ISTC_ADDR_BUS] O_ISTC_ADDR_2_ROM,
// | 指令存储器使能
output O_ISTC_ROM_CE
);
// |-------------------------------------- 模块内部信号声明 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | PC_REG_MDL 输出
wire [`DEF_ISTC_ADDR_BUS] W_PC;
// | IF_ID_MDL 输出
wire [`DEF_ISTC_ADDR_BUS] W_ID_ADDR;
wire [`DEF_ISTC_DATA_BUS] W_ID_DATA;
// ID_MDL 端口信号(GPR相关)
wire W_GPR_RD_EN_A;
wire [`DEF_GPR_ADDR_WIDTH-1:0] W_GPR_RD_ADDR_A;
wire [`DEF_GPR_DATA_WIDTH-1:0] W_GPR_RD_DATA_A;
wire W_GPR_RD_EN_B;
wire [`DEF_GPR_ADDR_WIDTH-1:0] W_GPR_RD_ADDR_B;
wire [`DEF_GPR_DATA_WIDTH-1:0] W_GPR_RD_DATA_B;
// ID_MDL 端口信号(EXE 相关)
wire [`DEF_ALU_OPR_BUS] W_ALU_OP_TYPE;
wire [`DEF_SRC_OPR_DATA_BUS] W_SRC_OPR_DATA_A;
wire [`DEF_SRC_OPR_DATA_BUS] W_SRC_OPR_DATA_B;
// GPR_WR_RD_MDL 写操作端口(EXE 相关)
wire W_DST_GPR_WR_EN_EXE_MDL_IN;
wire [`DEF_GPR_ADDR_WIDTH-1:0] W_DST_GPR_WR_ADDR_EXE_MDL_IN;
// GPR_WR_RD_MDL 写操作端口(MEM 相关)
wire W_DST_GPR_WR_EN_MEM_MDL_IN;
wire [`DEF_GPR_ADDR_WIDTH-1:0] W_DST_GPR_WR_ADDR_MEM_MDL_IN;
wire [`DEF_GPR_DATA_WIDTH-1:0] W_DST_GPR_WR_DATA_MEM_MDL_IN;
// GPR_WR_RD_MDL 写操作端口(GPR 相关)
wire W_DST_GPR_WR_EN_GPR_MDL_IN;
wire [`DEF_GPR_ADDR_WIDTH-1:0] W_DST_GPR_WR_ADDR_GPR_MDL_IN;
wire [`DEF_GPR_DATA_WIDTH-1:0] W_DST_GPR_WR_DATA_GPR_MDL_IN;
// |-------------------------------------- 模块内部逻辑设计 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 输出
assign O_ISTC_ADDR_2_ROM = W_PC;
// |-------------------------------------- 子模块例化 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 程序计数器模块
PC_REG_MDL INST_PC_REG_MDL
(
.I_CPU_CLK (I_CPU_CLK),
.I_CPU_RSTN (I_CPU_RSTN),
.O_PC (W_PC),
.O_ISTC_ROM_CE (O_ISTC_ROM_CE)
);
// | 译码模块例化
ID_MDL INST_ID_MDL
(
.I_CPU_CLK (I_CPU_CLK),
.I_CPU_RSTN (I_CPU_RSTN),
.I_ISTC (I_ISTC_FROM_ROM),
.O_GPR_RD_EN_A (W_GPR_RD_EN_A),
.O_GPR_RD_ADDR_A (W_GPR_RD_ADDR_A),
.I_GPR_RD_DATA_A (W_GPR_RD_DATA_A),
.O_GPR_RD_EN_B (W_GPR_RD_EN_B),
.O_GPR_RD_ADDR_B (W_GPR_RD_ADDR_B),
.I_GPR_RD_DATA_B (W_GPR_RD_DATA_B),
.O_ALU_OP_TYPE (W_ALU_OP_TYPE),
.O_SRC_OPR_DATA_A (W_SRC_OPR_DATA_A),
.O_SRC_OPR_DATA_B (W_SRC_OPR_DATA_B),
.O_DST_GPR_WR_EN (W_DST_GPR_WR_EN_EXE_MDL_IN),
.O_DST_GPR_WR_ADDR (W_DST_GPR_WR_ADDR_EXE_MDL_IN)
);
// | 通用寄存器模块例化
GPR_WR_RD_MDL INST_GPR_WR_RD_MDL
(
.I_CPU_CLK (I_CPU_CLK),
.I_CPU_RSTN (I_CPU_RSTN),
.I_GPR_WR_EN (W_DST_GPR_WR_EN_GPR_MDL_IN),
.I_GPR_WR_ADDR (W_DST_GPR_WR_ADDR_GPR_MDL_IN),
.I_GPR_WR_DATA (W_DST_GPR_WR_DATA_GPR_MDL_IN),
.I_GPR_RD_EN_A (W_GPR_RD_EN_A),
.I_GPR_RD_ADDR_A (W_GPR_RD_ADDR_A),
.O_GPR_RD_DATA_A (W_GPR_RD_DATA_A),
.I_GPR_RD_EN_B (W_GPR_RD_EN_B),
.I_GPR_RD_ADDR_B (W_GPR_RD_ADDR_B),
.O_GPR_RD_DATA_B (W_GPR_RD_DATA_B)
);
// | 执行模块例化
EXE_MDL INST_EXE_MDL
(
.I_CPU_CLK (I_CPU_CLK),
.I_CPU_RSTN (I_CPU_RSTN),
.I_ALU_OP_TYPE (W_ALU_OP_TYPE),
.I_SRC_OPR_DATA_A (W_SRC_OPR_DATA_A),
.I_SRC_OPR_DATA_B (W_SRC_OPR_DATA_B),
.I_DST_GPR_WR_EN (W_DST_GPR_WR_EN_EXE_MDL_IN),
.I_DST_GPR_WR_ADDR (W_DST_GPR_WR_ADDR_EXE_MDL_IN),
.O_DST_GPR_WR_EN (W_DST_GPR_WR_EN_MEM_MDL_IN),
.O_DST_GPR_WR_ADDR (W_DST_GPR_WR_ADDR_MEM_MDL_IN),
.O_DST_GPR_WR_DATA (W_DST_GPR_WR_DATA_MEM_MDL_IN)
);
// | 存储器存取模块例化
MEM_ACS_MDL INST_MEM_ACS_MDL
(
.I_CPU_CLK (I_CPU_CLK),
.I_CPU_RSTN (I_CPU_RSTN),
.I_DST_GPR_WR_EN (W_DST_GPR_WR_EN_MEM_MDL_IN),
.I_DST_GPR_WR_ADDR (W_DST_GPR_WR_ADDR_MEM_MDL_IN),
.I_DST_GPR_WR_DATA (W_DST_GPR_WR_DATA_MEM_MDL_IN),
.O_DST_GPR_WR_EN (W_DST_GPR_WR_EN_GPR_MDL_IN),
.O_DST_GPR_WR_ADDR (W_DST_GPR_WR_ADDR_GPR_MDL_IN),
.O_DST_GPR_WR_DATA (W_DST_GPR_WR_DATA_GPR_MDL_IN)
);
endmodule
SOPC顶层
// |------------------------------ ================================== ------------------------------
// |============================== MIPS32 SOPC 系统 ==============================
// |------------------------------ ================================== ------------------------------
// |Create Date : 2022-12-07
// |Finish Date : 2022-12-07
// |Edited by : Xu Y. B. (CSDN USER NAME :在路上,正出发)
// |Reference : 《自己动手写CPU》——第4章
// |
// |
// |------------------------------------------------------------------------------------------------
// |Change History:
// |
`include "MIPS_SYS_DEFINES.v"
`timescale 1ns / 1ps
module TOP_MIPS32_SOPC(
// |-------------------------------------- 输入输出端口声明 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 时钟、复位
input I_CPU_CLK,
input I_CPU_RSTN
);
// |-------------------------------------- 模块内部信号声明 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | TOP_MIPS32 模块端口
// | 指令取
wire [`DEF_ISTC_DATA_BUS] W_ISTC_FROM_ROM;
wire [`DEF_ISTC_ADDR_BUS] W_ISTC_ADDR_2_ROM;
// | 指令存储器使能
wire W_ISTC_ROM_CE;
// |-------------------------------------- 子模块例化 --------------------------------------
// |------------------------------------------------------------------------------------------------
// | 处理器模块
TOP_MIPS32 INST_TOP_MIPS32
(
.I_CPU_CLK (I_CPU_CLK),
.I_CPU_RSTN (I_CPU_RSTN),
.I_ISTC_FROM_ROM (W_ISTC_FROM_ROM),
.O_ISTC_ADDR_2_ROM (W_ISTC_ADDR_2_ROM),
.O_ISTC_ROM_CE (W_ISTC_ROM_CE)
);
// | 指令存储模块
ROM_ISTC_MDL INST_ROM_ISTC_MDL
(
.I_CPU_CLK (I_CPU_CLK),
.I_CPU_RSTN (I_CPU_RSTN),
.I_RD_EN (W_ISTC_ROM_CE),
.I_RD_ADDR (W_ISTC_ADDR_2_ROM),
.O_ISTC (W_ISTC_FROM_ROM)
);
endmodule
功能仿真
TestBench
// |------------------------------ ================================== ------------------------------
// |============================== 顶层模块仿真平台 ==============================
// |------------------------------ ================================== ------------------------------
// |Create Date : 2022-12-08
// |Finish Date : 2022-12-08
// |Edited by : Xu Y. B. (CSDN USER NAME :在路上,正出发)
// |Reference : 《自己动手写CPU》——第4章
// |
// |
// |------------------------------------------------------------------------------------------------
// |Change History:
// |
`timescale 1ns / 1ps
`define CLK_PERIOD 20
module TB_TOP_MIPS32();
reg I_CPU_CLK;
reg I_CPU_RSTN;
// 产生时钟
initial I_CPU_CLK = 0;
always #(`CLK_PERIOD/2) I_CPU_CLK = ~I_CPU_CLK;
// 产生复位
initial
begin
I_CPU_RSTN <= 0;
#(`CLK_PERIOD*3);
@(posedge I_CPU_CLK);
I_CPU_RSTN <= 1;
#(`CLK_PERIOD*10);
$finish;
end
// 顶层例化
TOP_MIPS32_SOPC INST_TOP_MIPS32_SOPC
(.I_CPU_CLK(I_CPU_CLK),
.I_CPU_RSTN(I_CPU_RSTN)
);
endmodule
仿真结果
执行时间
从取到第一条指令,到第一条指令完成并写入寄存器,共耗费100纳秒=5个时钟周期。