FPGA 高手养成记-【很重要】Testbenth仿真全过程
扫描二维码
随时随地手机看文章
文章目录
-
前言
-
测试模块设计
-
仿真全过程
模块包含
定义顶层模块
元件例化
测试激励的书写
波形仿真
系统任务仿真 -
总结
01. 前言
在FPGA 高手养成记-Test bench文件结构一览无余 只是简单的例举了常用的 testbench 写法,在工程应用中基本能够满足我们需求, 至于其他更为复杂的 testbench 写法, 大家可参考其他书籍或资料。
testbench没有像RTL代码设计那样严谨,我们可以在符合语法规则的前提下,随意编写我们的测试文件,有些在RTL代码中不可综合的语句,我们可以在testbench中实现。大体流程如下:
02. 测试模块设计
要测试我们的cpu需要ROM和RAM模块,这就需要我们先做好这两个模块
这里定义了一个 1024 x 8 的RAM
再定义一个8192 x 8 的ROM
ROM和RAM都还没有装入数据,等会我们会调用函数给他们装数据,接下来是地址译码器,来控制ROM和RAM的打开与关闭。
各模块建立好之后我们就开始仿真了。
03.仿真
这次教学我们用的是modelsim SE 10.0 版本进行教学,直接先在quartus II中建一个.v文件将其保存在原来的工程文件目录中,并命名为cpu_top.v,直接在这里写测试代码
下面大家可以来完成cpu 的仿真过程了
3.1,模块包含
首先,我们需要将我们刚写好的那几个模块包含进去,即CPU模块,ROM模块,RAM模块,地址译码器模块,并写好时间测量度,见下图
3.2,定义顶层模块
由于我们的设计只有两个输入,即时钟模块和复位模块,凡是输入信号在testbench中统一定义成reg型变量,凡是输出或者双向输入输出信号统一定义成wire型变量,我们的设计只有输入没有输出,故只定义输入和连线即可
下图便是我们要组成的测试顶层模块图,我们定义的wire型变量,实际就是我们顶层模块中,模块模块与模块间的连线。而这些连线就是我们cpu的输出,这样我们就可以用我们的测试模块来测试我们的cpu是否能正确工作
3.3. 元件例化
就是将各个模块连接起来即可,这里就不做太多的说明了,因为以前都写过很多次了
3.4.测试激励的书写
先写好时钟产生模块和复位模块.并将复位模块用task任务封装,这样我们在测试过程中就可以随时调用复位任务进行复位
时钟为50Mhz,复位时间为20ns
然后,我们再用task封装我们需要的模块,我们来想一下,上电后,CPU会从ROM中读两个时钟周期的数据是吧,但是我们的ROM现在还是空的,所以我们需要一个任务是往ROM中装入程序,给ROM中装数据我们可以用系统函数$readmemb,即打开一个文件,并将其中的数据送到我们之前定义的ROM中去
而test1.pro文件是需要我们自己定义的,我们可以在quartusII中再新建一个.v文件,在里面写上我们自己定义的程序,并将其保存为.pro文件即可,至于写什么程序,是我们随便定义的.
装完ROM和RAM的数据之后,按说就可以了进行波形仿真了,因为cpu是自动读取数据的,下面我们先来做第一步仿真,我先把之后的代码注释掉,大家先看没有被注释掉的代码
里面都是我们之前封装好的函数,刚开始进行复位,然后进行第一步测试,之后停止,将其保存之后,并默认为用其打开,打开后见下图
然后,file——new——library——ok即建好一个库
点击左上角的编译按钮,将我们之前写好的所有.v文件全部都编译进去
看到transcript一栏显示编译成功后即可,若没有transcript一栏,可以选择菜单中的view——transcript即可,若显示有红色错误,那就请读者按照它的要求进行修改代码,这说明你的代码有问题,一般是连接问题
3.5,波形仿真
编译成功后,双击cpu_top就可以开始波形仿真了
进入仿真页面后,我们右击cpu模块将其加入至波形
大家先看两个图,等会结合这两个图给大家细细讲解仿真过程


我们先来看第一个过程
上电后,cpu先从ROM中读回两个周期的数据,是从ROM的0地址开始的,再对比我们之前定义好的ROM,数据读取正确,读回的数据的前三位是111,即指令码JMP,后13位003c为地址码,JMP指令是将读回的数据作为新的地址码来读取相应地址的数据。那么,下一步,cpu应该是从ROM的003c地址处读数据才对,再看一下波形
对比波形后可知,cpu正好是从003c处读取数据,读到的数据指令码位111即JMP,地址码位0006,再到ROM的0006地址处看
这次读回的指令码位101,即LDA,也就是说将后13位地址码对应的RAM中的数据读回,送到累加器中,想一下,这时的RAM应该是打开的,而且双向输入输出口的数据总线上应该是来自RAM的8位数据,由于ROM0006地址处的地址码为1800是13位的,而RAM的地址是9位的,因此实际上我们从RAM中读回的数据是从RAM的0地址读回的,即我们之前给RAM写好的0000_0000,再看一下波形
正如我们所想的一样,数据总线上是0000_0000,RAM是打开的,地址为1800
就这样,读者可以自己再试一下,看看我们的cpu是不是按照我们之前给他的程序运行的,在这里我就不再给大家一一介绍了
虽然波形仿真很直观,但是看久了就会令人眼花缭乱,尤其是数据很多的时候,我们只能看其中一部分,不能讲所有数据看完整,这时候我们单单是用波形来仿真就远远不够了,下面介绍用系统任务仿真的过程
3.6,系统任务仿真
再回到我们的代码,注释掉了一些代码吧,我们把那些代码给加上,以其中一个过程为例
假设读回的指令码位101,即LDA,如果我在fentch_8的高电平期间且在cpu输出地址为奇数的时候记录一下此时的时间、指令、地址、目的地址、数据的话就可以不用看波形,让电脑来帮助我们来分析了,因此作如下处理
这里我延时60ns,是因为第一次记录的时候数据总线上还没有数据,只有延时一会才会有数据,即上面那张波形图右边那根黄色的线处记录一下数据,并将其显示。我们也可以加上一下标注,来帮助我们观察
这样我们再来仿真的时候就不用看波形了,直接打开transcript一栏观察记录即可
这样便可以为我们省下大量的仿真时间
04. 总结
这里提出以下几点建议供大家参考:
-
封装有用且常用的 testbench, testbench 中可以使用 task 或 function 对代码进行封装, 下次利用时灵活调用即可;
-
如果待测试文件中存在双向信号(inout)需要注意, 需要一个 reg 变量来表示输入, 一个 wire 变量表示输出;
-
单个 initial 语句不要太复杂,可分开写成多个 initial 语句, 便于阅读和修改;
-
Testbench 说到底是依赖 PC 软件平台, 必须与自身设计的硬件功能相搭配。





