简易CPU设计入门:验证系统初始化模块(一)

在上一节,我们讲解了系统初始化模块的代码。代码其实并不多,但我还是讲了不少的字数。本节,我们来讲解验证系统初始化模块的代码。

所谓的验证系统初始化模块,指的是,针对【sys_init】模块,单独地编写test bench文件,来观测它的执行过程。

在你自己去编写硬件逻辑代码的时候,你也可以说,编写好了某一个模块之后,接着去编写针对该模块的test bench文件,以测试这一个模块的运行。

test bench文件,可以对某一个模块进行测试验证。既可以测试小模块,也可以测试大的功能模块。可以测试单独的某一个功能模块,也可以对整个的项目进行测试。

在本节,我们要去测试验证的,是【sys_init.v】文件中的【sys_init】模块。这个代码文件,其实它本身是不需要测试的。因为它的逻辑太简单了。然而,我还是建议,大家在初学的时候,多去编写测试文件,养成测试的习惯。正因为代码简单,容易编写测试文件,所以我们才可以借着逻辑的简单性,而去编写测试代码,熟悉编写测试代码的方法与逻辑。

本次,我们编写系统初始化模块的测试代码,主要地,还是为了学习非阻塞赋值,观测非阻塞赋值的波形,以及分析非阻塞赋值的机制。

为啥要去学习呢?

如果可以的话,在学习基础知识的话,我们应该力求对一些个基础知识,有着精准的理解与掌握。有的时候,某一个知识理解得不精准,模模糊糊,这会给后续的代码编写带来不良影响。

一.   编写验证代码

本节的验证代码,是用来验证上一节的【sys_init】模块的。在你继续下面的学习之前,你需要首先学习过上一节的内容。如果还没有学习过,请点击下方链接,以学习上一节的内容。

系统初始化模块

在你确保了,已经理解了上一节的内容的基础上,你可以接着往下来学习。

我们来看一下验证代码。

`timescale 1ns/1ns
module tb_sys_init();
reg sys_clk;
reg sys_rst_n;
wire init_done;

wire [9:0] cnt;
assign cnt = sys_init_inst.cnt;

initial
begin
	sys_clk = 1'b1;
	sys_rst_n <= 1'b0;
#60
	sys_rst_n <= 1'b1;
end

always #10 sys_clk = ~sys_clk;

sys_init sys_init_inst
(
	.sys_clk(sys_clk),
	.sys_rst_n(sys_rst_n),
	.init_done(init_done)
);

endmodule

验证代码的内容不算多。接下来,我们慢慢地来学习它。

733518d2fd0542fe990f92be734ed302.png

图1,tb_sys_init模块

图1所示,为验证代码的起始部分。

代码的第1行,用于设置时间单位。在【1ns/1ns】这部分语句中,左边的【1ns】代表着,本模块的1个时间单位,是1ns。右边的【1ns】用于指定仿真过程中进位取整的精度为1ns。

如果你将第1行写作如下的代码块

`timescale 100ns/1ns

那么,这个时候,代码中的1个时间单位就是100ns了。而进位取整的精度则是1ns。

需要注意的是,如下的写法是错误的。

`timescale 60ns/3ns

这是因为,无论是左边的指定时间单位的数值,还是右边的指定时间精度的数值,只能采用1、10或者100,两边的数值可以相同,也可以不同。

我在学习FPGA开发板的配套教程的时候,一般地,在设定时间单位与时间精度的时候,都将其设定为1ns,如图1中的第1行所示。对于初学者来讲,我也建议你先采用这种通用的写法。以后接触实际项目的时候,再去考虑其他的写法。

图1中的第2行设定了验证模块的模块名。一般地,我们的验证模块的名称,都是【tb_xxx】的格式。如果你验证的是【add】模块,则验证模块名字就是【tb_add】。此处,我们要验证的模块是【sys_init】,所以,验证模块的名字就是【tb_sys_init】。

图1的3到5行,对应着【sys_init】模块的信号列表。在这里,我将【sys_init.v】中的【sys_init】模块的信号列表截图一下,大家再去看一看这一模块。

5d8434f8550f4228bf29f85594697ea1.png

图2,sys_init模块的信号列表

我们将图2和图1对比一下,发现图1的3到5行的变量名,它和图2的信号列表的信号名是一样的。只不过,图2中的input类型的变量,到了图1,就变成了reg类型。图2中的output类型的变量,到了图1中,就变成了wire型。

一般地,在编写针对某一模块的test bench文件的时候,直接将被测试模块的信号列表复制到test bench代码文件中即可。然后呢,再在test bench文件中,对变量的类型进行修改。将被测试文件中的input类型修改为test bench文件中的reg类型,将被测试文件中的output类型修改为test bench文件中的wire型。

如果被测试文件中存在着inout类型的变量,它到了test bench文件中,要修改为什么类型呢?依然是变为wire型。

看完了图1,我们再看一下【tb_sys_init】的下面的一个代码块。

e42152def4b24843ae90684a5db565bd.png

图3,tb_sys_init模块

20行到25行,是对【sys_init】模块的信号列表与【tb_sys_init】中对应信号的连接操作。

我们还可以在第20行中看到,我们将【sys_init】进行了实例化,实例对象的名字为【sys_init_inst】。

接下来呢,我们再来看看本测试模块中,对时钟信号与复位信号的设置情况。

e2b15f801d824c139e0e49021287820f.png

图4,tb_sys_init模块

在图4中,行号18的位置是说,让时钟信号,每10个时间单位翻转一次。这样一来,时钟信号的周期为20个时间单位。由图1知,我们将时间单位设置为1ns了。所以,在本测试模块中,1个时钟周期是20ns。

10到16行是对时钟信号与复位信号的设置。在开始,让时钟信号为1,让系统复位信号的值为0。然后呢,延迟60ns,将系统复位信号设置为高电平。

系统复位信号【sys_rst_n】中的【n】后缀,代表说,本复位信号是低电平有效也就是,本信号的值为0时,才进行系统复位操作。为1时,不进行复位操作。

除了以上所讲的代码行以外,测试模块还有一个很关键的地方。

d82e91b2eb9f4a61b1ffc3013ab7dcc5.png

图5,tb_sys_init模块

0919312a39354e3eb0b16ba787d9d2c4.png

图6,sys_init模块的cnt变量

在图5中,行号7和8的位置,它是申请了cnt变量,然后用连续赋值语句,将cnt设置为sys_init模块中的cnt变量。通过这种设置,就将【sys_init】模块中的cnt变量,导入到【tb_sys_init】模块中了。使得我们可以在【tb_sys_init】模块中,观测【sys_init】模块中的cnt变量的值。

【sys_init_inst】,它是【sys_init】模块的实例化,如图3中的行号20的代码行所示。

为啥要进行这种变量导入操作呢?因为我们在Quartus软件中启动ModelSim仿真的时候,我们需要运行的仿真文件是【tb_sys_init.v】。有了导入变量的代码,那么,我们就可以在运行【tb_sys_init.v】仿真文件的时候,观测被测试模块中的相应的变量的值了。

结束语

好了,本节的内容,就先讲这些了。接下来,我们还需要对测试代码与被测试代码的波形进行讲解的。这个任务,我们就在后面的文章中来讲解着了。

祝大家学习愉快。有不懂的,你也可以来联系我的。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值