简易CPU设计入门:系统初始化模块

在上一节,我粗略地讲了讲顶层模块代码。在前面的某一个文章里面,我讲了本CPU项目代码的整体运行流程,那一篇文章的链接如下所示。

项目的总体执行流程

在项目的总体执行流程里面,首先要去进行的,便是系统的初始化工作。

在系统的初始化阶段,要去做什么呢?在个人电脑上,系统加电以后,首先去运行的,是BIOS软件程序。BIOS,就是基本输入输出系统。这个系统,用来对系统的硬件作各种检测和初始化工作。比如说,系统里面是否有插入鼠标啊,是否有插入硬盘啊,还要对内存进行初始化设置啊,等等。

在我的这个简易的CPU项目代码里面,没有这种复杂的BIOS代码。因为我暂时也写不出来那么复杂的东西。但是呢,我还是觉得,要留有这么一个象征性的模块,表示说,在CPU正常运转之前,先要进行初始化设置。

初始化模块位于哪里呢?我们还是以【cpu_me01】文件夹作为起始,则系统初始化模块位于路径【cpu_me01\code\sys_init.v】里面。

我的这个CPU项目的名字,我将其取名为【cpu_me01】了。项目代码的下载方法,请参阅如下链接。

下载项目代码的方法

项目的下载方法,大家已经知道了,初始化模块的文件路径大家也知道了,接下来呢,请大家打开【sys_init.v】文件。

这个文件的篇幅并不大,我将这个代码文件的内容贴在下面的代码块里面。

module sys_init
(
	input wire sys_clk,
	input wire sys_rst_n,
	output reg init_done
);

reg [9:0] cnt;

always @(posedge sys_clk or negedge sys_rst_n)
	if (sys_rst_n == 1'b0)
		cnt <= 10'd0;
	else if (cnt < 10'd20)
		cnt <= cnt + 1'b1;
	else
		cnt <= cnt;

always @(posedge sys_clk or negedge sys_rst_n)
	if (sys_rst_n == 1'b0)
		init_done <= 1'b0;
	else if (cnt == 10'd9)
		init_done <= 1'b1;
	else
		init_done <= 1'b0;

endmodule

代码块的内容,是供大家浏览用的。不要求你将里面的代码全都给记住。浏览一下即可。

一.   输入输出信号列表

接下来,我们来学习这个模块的代码。

图1

在图1里面,第1行显示了本模块的名字,为【sys_init】。

第3行到第5行,显示了本模块的输入输出信号列表。其中,输入信号为系统时钟信号和系统复位信号。系统复位信号的名字为【sys_rst_n】,里面的【n】表示此信号为低电平有效。输出信号为初始化完成信号【init_done】。

init,这个东西,它是【initialization】的缩写,是【初始化】的意思。

在编程语言里面,申请了一个变量以后,第一次设置它的值,就是对这个变量的初始化。

对一个系统的某些变量,某些东西进行第一次的设置,这个是对系统的初始化。

在信号列表里面,两个输入信号,时钟信号与复位信号,都是从顶层模块【cpu_top】中传递过来的。我们来看一看顶层模块中的相关代码。

顶层模块图1
顶层模块图2
顶层模块图3

在顶层模块图1中,有两个输入信号,它们也是系统时钟信号和系统复位信号。然后呢,在顶层模块图2中,我们看到,顶层模块的系统时钟信号和系统复位信号分别与【sys_init】模块的系统时钟信号与系统复位信号连接起来了。然后呢,【sys_init】模块的初始化完成信号【init_done】还与顶层模块的同名信号连接起来了。在顶层模块图3中,我们看到,我是在顶层模块里面申请了一个wire型的【init_done】变量。

后面我们还会涉及许多的代码文件,它们也都有着系统时钟与系统复位信号。这些个代码文件中的这俩信号,都直接或间接地来自顶层模块中的系统时钟与系统复位信号,就像【sys_init.v】一样。

谈到时钟信号,就会有频率的问题。那么,在我们的系统中,时钟频率为多少呢?设计模块是没有讲的。但是呢,我在顶层模块的test bench文件里面,是有设置的。我是将整个的仿真CPU的时钟频率设置为50MHz。

以下几行关键代码,可以完成这一功能。

`timescale 1ns/1ns
reg sys_clk;
initial sys_clk = 1;
always #10 sys_clk = ~sys_clk ;

如代码块所示,我们将1个时间单位设置为1纳秒。时钟信号的初始值为1,每10纳秒,时钟信号反转一次。20纳秒,为一个时钟周期。

1 / 20ns = 1 / (20 * 10^(-9)) = 1000 / (20 * 10^(-6)) = 50 / 10^(-6) = 50 * 10^6 Hz = 50MHz

在这里,由于我列的不是数学的分式,而是用编程的数学符号来列式计算的,所以可能有点不好懂。以后,等我熟练了CSDN的公式编辑器的用法以后,我再去用标准的数学分式来写了。

在本项目代码里面,时钟频率,为50MHz。前面,我已经推导过了。大家稍微看一看,相信是可以看懂的。

二.   计数变量cnt

接下来,我们回到【sys_init】模块里面,接着看图1。

第8行里面,我是申请了一个10比特的向量变量,变量名为【cnt】。cnt,就是【count】的缩写。cnt,或者是【count】,一般都是用来计数的。

给什么计数呢?

我们接着往下看。

图2

在图2里面,这一块代码是用来设置cnt的执行逻辑的。

cnt变量在系统复位信号为0的时候为0值。当系统复位信号为非0的时候,cnt在每一个时钟信号的上升沿到来时加1。直到变为20的时候,cnt保持此值不变。

三.   初始化完成信号init_done

我们接着往下看。

图3

在图3里面,我们看到,当系统检测到cnt的值为9的时候,【init_done】变量被非阻塞赋值为1。其余时刻,【init_done】均为0值。也就是,【init_done】信号仅当系统检测到【cnt】为9的时候才会变为1,且仅维持一个时钟周期。其余时刻【init_done】信号均为0值。

这样一来,我们来看看系统初始化模块所做的事情。就是申请了一个变量【cnt】来查数。查到9的时候,让init_done被非阻塞赋值为1。当init_done变为1时,表示系统初始化完成。系统初始化完成的高电平有效信号,会通过顶层模块的同名信号,传递给其他模块,推动本仿真CPU继续往后执行着。

结束语

在这里,代码中开始涉及了非阻塞赋值的问题。

非阻塞赋值,我认为,它可以说是Verilog HDL语法中的一个难点。对此,我可能会花费一些个时间,用来探讨这一问题。

有可能,在学习本专栏的时候,你已经是明白了非阻塞赋值的概念了。也有可能,你尚未弄明白。那么,在本专栏里面,我将尝试着对非阻塞赋值的知识点,作一点讲解与梳理。

本节,就先到这里了。大家再见。

  • 30
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值