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




目录

引言

致谢

平台

ori 指令

流水线结构建立

模型

简单的MIPS五级流水线结构

设计

宏定义

程序计数器

译码

通用寄存器

指令执行

内存访问

指令ROM

顶层文件

处理器顶层

SOPC顶层 

功能仿真

TestBench

仿真结果

执行时间

时序细节



引言

本篇学习书本的第四章,实现 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个时钟周期。

时序细节



  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
引言 说起计算器,值得我们骄傲的是,最早的计算工具诞生在中国。中国古代最早采用的 一种计算工具叫筹策,又被叫做算筹。这种算筹多用竹子制成,也有用木头,兽骨充当 材料的。约二百七十枚一束,放在布袋里可随身携带。直到今天仍在使用的珠算盘,是 中国古代计算工具领域中的另一项发明,明代时的珠算盘已经与现代的珠算盘几乎相同 。17世纪初,西方国家的计算工具有了较大的发展,英国数学家纳皮尔发明的"纳皮尔算 筹",英国牧师奥却德发明了圆柱型对数计算尺,这种计算尺不仅能做加减乘除、乘方、 开方运算,甚至可以计算三角函数,指数函数和对数函数,这些计算工具不仅带动了计 算器的发展,也为现代计算器发展奠定了良好的基础,成为现代社会应用广泛的计算工 具。 近年来随着科技的飞速发展,单片机的应用正在不断深入,同时带动传统控制检测技 术日益更新。在实时检测和自动控制的单片机应用系统中,单片机往往作为一个核心部 件来使用,仅单片机方面知识是不够的,还应根据具体硬件结构软硬件结合,加以完善 。 本任务是个简易的两位数的四则运算,程序都是根据教材内和网络中的程序参考编 而成,在功能上还并不完善,限制也较多。本任务重在设计构思与团队合作,使得我们 用专业知识、专业技能分析和解决问题全面系统的锻炼。 随着半导体集成工艺的不断发展,单片机的集成度将更高、体积将更小、功能将更强 。在单片机家族中,80C51系列是其中的佼佼者,加之Intel公司将其MCS –51系列中的80C51内核使用权以专利互换或出售形式转让给全世界许多著名IC制造厂商 ,如Philips、 NEC、Atmel、AMD、华邦等,这些公司都在保持与80C51单片机兼容的基础上改善了80C5 1的许多特性。这样,80C51就变成有众多制造厂商支持的、发展出上百品种的大家族, 现统称为80C51系列。80C51单片机已成为单片机发展的主流。专家认为,虽然世界上的 MCU品种繁多,功能各异,开发装置也互不兼容,但是客观发展表明,80C51可能最终形 成事实上的标准MCU芯片。 事实上单片机是世界上数量最多的计算机。现代人类生活中所用的几乎每件电子和 机械产品中都会集成有单片机。手机、电话、计算器、家用电器、电子玩具、掌上电脑 以及鼠标等电脑配件中都配有1- 2部单片机。而个人电脑中也会有为数不少的单片机在工作。汽车上一般配备40多部单片 机,复杂的工业控制系统上甚至可能有数百台单片机在同时工作!单片机的数量不仅远 超过PC机和其他计算的总和,甚至比人类的数量还要多。 单片机内部也用和电脑功能类似的模块,比如CPU,内存,并行总线,还有和硬盘作 用相同的存储器件,不同的是它的这些部件性能都相对我们的家用电脑弱很多,不过价 钱也是低的,一般不超过10元即可......用它来做一些控制电器一类不是很复杂的工作 足矣了。我们现在用的全自动滚筒洗衣机、排烟罩、VCD等等的家电里面都可以看到它的 身影!......它主要是作为控制部分的核心部件。 设计方案及原理 1.首先赋予显示缓冲初始值'00 00',并把数据存储单元清零。 2.主程序调用键盘扫描子程序,判断键值,是数字第一次直接赋予23H单元,如是第二次 输入数字,则把第一次值乘十后与第二次值相加,结果存储到23H单元,并R4计数2次, 表示已输入两位,扫描键值时就不在赋值和显示。等待运算符号的按下,按下等于号就 直接与零计算并显示,如加减乘除就R5加一 并把R4清零,表示可以输入下一操作数,与第一次相同,并等待等于键按下。清零键则 不管在任何情况下都清零,相当与软复位。 3.在扫描完键盘后,调用数据显示转换子程序,并选择,由于本任务是两位四则运算, 只有乘法中结果会超出FFH的范围,在此就是选择处理方式,超出范围则跳过数据显示转 换子程序,未超出则调用数据显示转换子程序,两种选择都是要把值转换为七段码。 4.调用数码管动态显示子程序,显示数值,重新循环。 硬件系统设计 1、LED接口电路 简易计算器需要2位8段码LED显示电路。用8031单片机经8255A扩展2位8段码LED显示 器,用8255A的A口作为段码(字形代码)数据口,PB0和PB1作为位控制端口。在位控制 口加集电极开路的反相高压驱动器74LS06以提供驱动LED显示器所需的足够大的电流,然 后接至各数码显示器的共阴极端。同理,在段码数据口集电极开路的正相高压驱动器74 LS07提供足够大的电流,然后接到数码显示器的各段。逻辑电路结构如下: 2、键盘接口电路 简易计算器需要4*4的行列式键盘。用8031单片机经8255A扩展4*4行列式键盘,8255A的 B口和C口用于扩展键盘接口,B口高4位作为输出口,C口低4位作为输入口。逻辑电路结 构如下: 3、计算器逻辑电路图 将LED接口电路和键盘接口
数码管显示与键盘扫描系统 摘要: 现如今已经跨越了三个"电"的时代,即电气时代、电子时代和现已进入的电脑时代。 不过,这种电脑,通常是指个人计算机,简称PC机。它由主机、键盘、显示器等组成。 还有一类计算机,这种计算机就是把智能赋予各种机械的单片机(亦称微控制器)。这 种计算机的最小系统只用了一片集成电路,就可进行简单运算和控制。因为它体积小, 通常都是放置在一个机械装置的内部。它在整个装置中,起着有如人类头脑的作用,它 出了毛病,整个装置就瘫痪了。各种产品一旦用上了单片机,就能起到使产品升级换代 的功效,常在产品名称前冠以形容词——"智能型",如智能型洗衣机等。现在有些工厂的 技术人员或其它业余电子开发者搞出来的某些产品,不是电路太复杂,就是功能太简单 且极易被仿制。究其原因,可能就卡在产品未使用单片机或其它可编程逻辑器件上。数 码管显示与键盘扫描系统是单片机系统中十分典型的应用,可将4×4键盘的按键对应显示 在数码管上。 关键词:单片机 数码管 一、绪论 1. 研究意义 用单片机驱动LED数码管有很多方法,按显示方式可分静态显示和动态(扫描)显示 ;按译码方式可分硬件译码和软件译码。静态显示数据稳定,占用很少的CPU时间。动态 显示需要CPU时刻对显示器件进行数据刷新,显示数据有闪烁感,占用的CPU时间多。LE D数码管的外围电路一般需要一个限流电阻和加大驱动电流的晶体管。 LED数码管是由发光二级管显示字段组成的显示器,有"8"字段和"米"字段之分,这种 显示器有共阳极和共阴极两种。实际上不用驱动电路即可达到正常亮度,为了可靠性设 计可采用晶体管构成驱动电路。 2. 设计目的 在单片机的产品设计中,人机界面是非常重要的部分,而且随着系统的日益复杂,以 及人们对产品的人机交互能力的要求不断提升,常握单片机系统中的人机界面基础设计 能力成为了学习单片机的基础课程,而4X4键盘的操作和LED数码管的动态显示是人机界 面设计的基础内容,掌握这些基础设计能力,加深对人机界面的认识,同时提高人机界 面系统设计能力。 3.研究范围及技术要求 半导体数码管,4×4键盘。做此实验用51板,因为51板提供了相应的LED模组,LED显 示器是单片机应用中常用的输出器件,4×4键盘的扩展显示也是在MCS- 51上进行实验。给 4×4 键盘的每个键定义一个功能,其中把定义为 0~9 的键盘称为数 字键,把定义成 DEL 的键称为删除键,把定义成 ENT 的键成为确认键,其他键称为保 留键。如果是数字键按下,把代表数字显示在数码管上:按键按下时,6 位数码管靠右 边显示该键的代表数字;继续按键时,已经显示在数码管上的数字左移一位,按键代表 的数字显示在最右边的数码管上。 4.发展及指导思想 单片机在开发过程中 ,常常会因为资源不足而不得不大量扩展接口芯片以满足应用系统的需要。国外主要采用 串行接口的键盘显示专用芯片,如BC7280/81、HD7279、CH451等。这类芯片占用CPU的资 源少,传输速度较快,外围器件要求也较少,在中小系统中都可得到广泛的应用。指导 思想主要是用串行接口使得键盘的扩展在LED上显示。 二、方案论述 1.数码管工作原理 数码管由 8 个发光二极管(以下简称字段)构成,通过不同的组合可用来显示数字 0~ 9,字符 A~F、H、L、P、R、U、Y 等符号及小数点"."。 数码管又分为共阴极和共阳极两种类型。共阳极数码管中 8 个发光二极管的阳极(二极管正端)连接一起,即为共阳极接法,简 称共阳数码管。通常,公共阳极接高电平(一般接电源),其它管脚接段驱动电路输出端 。 当某段驱动电路的输入端为低电平时,该端所连接的字段导通并点亮。根据发光字段的 不 同组合可显示出各种数字或字符。此时,要求段驱动电路能吸收额定的段导通电流,还 需 根据外接电源及额定段导通电流来确定相应的限流电阻。 共阴极数码管中 8 个发光二极管的阴极(二极管负端)连接在一起,即为共阴极接法, 简称共阴数码管。通常,共阴极接低电平(一般接地),其它管脚接段驱动电路输出端 。 当某段驱动电路的输出端为高电平时,该端所连接的字符导通并点亮,根据发光字段的 不 同组合可显示出各种数字或字符。同样,要求段驱动电路能提供额定的段导通电流,还 需 根据外接电源及额定段导通电流来确定相应的限流电阻。 单片机数码管显示控制程序设计仿真图 2. 4×4行列式键盘的工作原理 行列式键盘的工作方式是先用列线发送扫描字,然后读取行线的状态,查看是否有按 键按下。键盘部分提供一种扫描的工作方式,可以和具有64个按键的矩阵键盘相连接, 能对键盘不断扫描、自动消抖、自动识别按下的键,并给出编码,能对双键或n个键同时 按下的情况实行保护。   在显示部分,它可以为发光二极管、荧光管及其他

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

在路上-正出发

哈哈,多少是个心意

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值