《自己动手写CPU》学习记录(3)——第4章/Part 1

时间:2022-12-09 19:56:48

《自己动手写CPU》学习记录(3)——第4章/Part 1



《自己动手写CPU》学习记录(3)——第4章/Part 1


目录

引言

致谢

平台

ori 指令

流水线结构建立

模型

简单的MIPS五级流水线结构

设计

宏定义

程序计数器

译码

通用寄存器

指令执行

内存访问

指令ROM

顶层文件

处理器顶层

SOPC顶层 

功能仿真

TestBench

仿真结果

执行时间

时序细节



引言

本篇学习书本的第四章,实现 ori 指令。

致谢

感谢书籍《自己动手写CPU》及其作者雷思磊。一并感谢开源精神。

平台

开发环境:Vivado IDE 2018.3

FPGA芯片型号:xc7a35tfgg484-2



ori 指令

指令格式:

《自己动手写CPU》学习记录(3)——第4章/Part 1

指令用法:

ori rt, rs,immediate

其作用是将指令中的16位立即数进行无符号数扩展至 32 位,然后与索引号为 rt 的通用寄存器的值进行逻辑或运算,运算的结果保存到索引号为 rs 的寄存器内。

符号扩展示例:

《自己动手写CPU》学习记录(3)——第4章/Part 1

此处书本有误

书本中给出的指令用法为:

《自己动手写CPU》学习记录(3)——第4章/Part 1

 但是随后举的例子却是:

《自己动手写CPU》学习记录(3)——第4章/Part 1

 所以正确的指令使用格式为:(上面写过了,再强调一遍)

ori rt, rs,immediate

流水线结构建立

模型

《自己动手写CPU》学习记录(3)——第4章/Part 1

简单的MIPS五级流水线结构

《自己动手写CPU》学习记录(3)——第4章/Part 1

设计

声明:

设计思路基本借鉴书本,但是具体的模块代码编写,与作者有较大不同

宏定义

此文件主要定义了一些与设计相关的宏,当设计的参数需要更改时,在此文件修改即可,不需要改动内部设计文件。方便代码维护,也增强了代码的可读性。

// |------------------------------ ================================== ------------------------------
// |============================== 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

初始化文件:

《自己动手写CPU》学习记录(3)——第4章/Part 1

说明:此处文中的示例代码存在如下问题:

《自己动手写CPU》学习记录(3)——第4章/Part 1

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

程序代码示例:(此处借鉴书本的方法,仅为了完成仿真)

《自己动手写CPU》学习记录(3)——第4章/Part 1

顶层文件

处理器顶层

// |------------------------------ ================================== ------------------------------
// |============================== 		     指令-执行模块  	      ==============================
// |------------------------------ ================================== ------------------------------
// |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

仿真结果

执行时间

《自己动手写CPU》学习记录(3)——第4章/Part 1

从取到第一条指令,到第一条指令完成并写入寄存器,共耗费100纳秒=5个时钟周期。

时序细节

《自己动手写CPU》学习记录(3)——第4章/Part 1

《自己动手写CPU》学习记录(3)——第4章/Part 1