很多FPGA/IC开发工具都提供设计例子,方便使用者学习和练习,例如,Xilinx ISE提供了很多设计实例,放在ISE5.X的安装目录下的ISEexamples目录下,例如CDMA匹配滤波器、Johnson计数器、PN码发生器、频率计等,这些例子是经验丰富的工程师写的,我们可以学到编程思想、代码风格等方面的知识和经验,这些东西可能从学校老师或一般书籍都学习不到。
如果你用的不是Xilinx的FPGA,也就是说不使用ISE,那也没关系,HDL代码和testbench的设计思想和方法是一样的,你照样可以从中学到很多东西。下面以其中一个例子――同步FIFO为例,分析一下我们的第一个testbench,设计的源代码可以在ISEexamples目录下找到,Xilinx还提供了Application Note详细介绍了该FIFO的细节,
1.511x8同步FIFO功能简介
为了对这个511x8同步FIFO进行功能验证,首先要清楚它的功能,只有这样才能知道需要验证什么功能,以及如何进行验证,图1为该同步FIFO的原理框图。
与异步FIFO相比,同步FIFO的读、写时钟是同一个时钟,简化了FIFO的设计,Empty和Full标志的产生也比较容易,同步FIFO内部使用二进制计数器记录读地址和写地址。在异步FIFO中,由于读写使用不同的时钟,也就是说设计存在两个时钟域,为了减少出现亚稳态时产生的错误,记录读写地址的计数器要使用格雷码,Empty和Full标志的产生也比较复杂。511x8同步FIFO(以下简称FIFO)的工作时序如图2所示。
读FIFO数据时,首先read_allow信号置高,时钟上升沿到来时read_addr地址处的数据将出现在read_data处,同时read_addr加1。让read_allow信号持续为高可以完成burst read操作。如果读出的数据是FIFO的最后一个数据,那么读操作完成后Empty信号变高。Empty信号为高时读出来的数据是无效的。
写FIFO数据时,首先write_allow信号置高,同时准备好输入数据write_data,时钟上升沿到来时,数据将写入write_addr所指向的地址中,同时write_addr加1。让write_allow信号持续为高可以完成burst write操作。如果某一个时钟上升沿时写入第511个数,那么下一个时钟沿到来的时候Full信号变高,表示FIFO已经写满。
我们再详细分析FIFO的工作时序图。在图2中,开始时FIFO的读写指针均为0,Empty为高表示FIFO处于空的状态,然后write_allow置高,时钟上升沿到来时写入第一个数据,Empty变低;一个CLK之后,read_allow置高,时钟上升沿到来时,读出数据,由于是最后一个数据,所以Empty信号又变为有效(高电平)。在时序图的右半部分,写入509个数据之后,再写入两个数据,Full信号变为有效,表示FIFO为满。
这个FIFO还有一个名为fifo_count_out的输出,从4’b0000~4’b1111,分别表示FIFO满的程度从不足1/16到15/16,为某些应用提供方便。
2.验证
清楚FIFO的功能之后,我们就可以开始验证工作了。验证工作的第一步是整理出FIFO需要验证的功能点,这些功能点一般直接来源于FIFO应该具有的功能,或者来源于它的使用方法。FIFO需要验证的功能点包括:
1)FIFO复位后,read_addr和write_addr为0,Full为0,Empty为1。
2)读FIFO数据时,read_allow信号必须置高,时钟上升沿到来时read_addr地址处的数据将出现在read_data处,同时read_addr加1。
3)读出FIFO的最后一个数据后,Empty信号变高。
4)写FIFO数据时,write_allow信号必须置高,时钟上升沿到来时,输入数据write_data将写入write_addr所指向的地址中,同时write_addr加1。
5)如果某一个时钟上升沿时写入第511个数,那么下一个时钟沿到来的时候Full信号变高,表示FIFO已经写满。
6)fifo_count_out端能正确的指示FIFO满的程度。
分析Xilinx提供的testbench可以为我们编写自己的testbench提供很好的参考。FIFO的RTL代码和testbench代码放在ISEexamplesfifo_ver_131和fifo_vhd_131下。以verilog代码为例,fifo_ver_131中包括了两个testbench文件,一个是功能仿真testbench文件fifoctlr_cc_tb.tf,另一个是时序仿真(后仿真)testbench文件fifoctlr_cc_tb_timing.tf,这里我们主要分析功能仿真文件,为了方便大家理解,以下(下一帖)为注释过的功能仿真testbench。大家看testbench的代码时,对照FIFO需要验证的功能点,检查是不是所有功能点都经过了验证。
FIFO的testbench主要包括初始化、验证initial块、读写task等内容,初始化部分主要完成复位信号、CLK信号等的初始化工作,读写task把读写、delay等操作模块化,方便使用。这里主要介绍一下验证initial块,也可以说是验证的主程序,如下所示。
initial begin
delay; //保证验证环境正确复位
writeburst128; //写入512个数,Full信号应该在写入511个数后变高
writeburst128;
writeburst128;
writeburst128;
read_enable = 1; //读出一个数,Full信号应该变低
writeburst128; //同时读写,检查FIFO操作是否正确
read_enable = 0; //读操作结束
endwriteburst; //写操作结束
delay;
readburst128; //连续读512次,Empty信号应在读出511个数后变高
readburst128;
readburst128;
readburst128;
endreadburst;
end
这段程序首先延迟5个时钟周期,等初始化完成之后再开始验证工作。验证时,首先写入512个数,使用波形观察器可以检查写入的过程是否正确,以及Full信号在写入511个数后是否变高;然后read_enable = 1,读出一个数,Full信号应该变低,这样写操作和Full信号的验证就基本完成了;程序接着也启动了写操作,由于此时read_enable仍然为高,即读写同时进行,这是对实际情况的模拟,可以对FIFO的功能进行更严格的验证;最后,连续读FIFO 512次,用波形观察器检查读操作是否正确,Empty信号是否在读出511个数后变高,如果这些操作都是正确的,那么FIFO的功能就基本正确了。
需要注意的一点是,以上的程序是不可综合的,因为不是RTL级描述,而是行为级描述(Behavioral Description)。行为级描述的特点是直接描述对象的功能,具有比较高的抽象层次,开发、运行速度都比RTL代码要快,因此testbench都是用行为级描述写的。关于行为级描述的特点、写法以后将有专门的章节论述。
这个testbench的特点是,输入激励由testbench产生,输出响应的检查人工完成,这样的testbench编写相对容易,可以加快开发速度,作为开发人员自己验证是非常好的选择。有些testbench能完成输入激励和输出检查,不用观察波形也能完成验证工作,这样的testbench具有更高的自动化程度,使用方便,可重复性好,当设计比较复杂而且团队中有专门的验证工程师时,一般会有验证工程师建立一套这样的testbench,用于验证开发工程师的RTL级代码,如果发现问题,开发工程师修改后在testbench再运行一次所花的时间非常少,开发复杂项目时这样做可以比用波形观察器节省很多时间。
3.总结
验证一般要通过写testbench实现,testbench要完成向DUT施加激励和检查DUT相应是否正确的功能,这就要求我们非常清楚待验证模块(DUT)的功能,这样才知道需要验证什么、如何施加激励和如何检查响应是否正确。写testbench时, 首先要列出需要验证的功能,让后再编写testbench,这样可以做到有的放矢,避免遗漏。
编辑:jq
-
IC
+关注
关注
36文章
5882浏览量
175129 -
fifo
+关注
关注
3文章
387浏览量
43506 -
CLK
+关注
关注
0文章
127浏览量
17109
原文标题:FPGA开发分析一个testbench
文章出处:【微信号:gh_9d70b445f494,微信公众号:FPGA设计论坛】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论