简介
本系列文章主要针对FPGA初学者编写,包括FPGA的模块书写、基础语法、状态机、RAM、UART、SPI、VGA、以及功能验证等。将每一个知识点作为一个章节进行讲解,旨在更快速的提升初学者在FPGA开发方面的能力,每一个章节中都有针对性的代码书写以及代码的讲解,可作为读者参考。
第三章:仿真验证
模块功能是否正确,可以通过验证求得。每一个模块都有一些输入及输出端口,输入端口传递数据到该模块,输出端口将通过内部逻辑得到的结果传递出该模块。当输出的数据与输入的数据满足模块内部的逻辑时,则说明该模块的功能满足我们的要求。
输入端口的数据由模块外部传入,不能由本模块内部产生,可以这么理解,input 的数据是由外部传递到本模块的,如果在本模块内部再产生数据给该端口,在电路上就会造成一根线上有两个方向的数据传输,那么硬件上是不允许的,因此 input 端口数据只能由外部产生。
由此可知我们的输入数据只能通过外部单元产生,外部单元包括本模块以外的模块和具体开发板上面的硬件,由于我们现在要做仿真验证,不会下板验证,因此我们可以用 Verilog HDL语言描述一个模块,该模块只是为我们的功能模块(a_and_b)产生输入的数据,这样的模块我们称之为测试模块(testbench)。为了方便管理,我们可以为测试模块新建一个文件夹,该文件夹可与design、 ise_prj 文件夹同路径,我们为之取名为 sim。在 sim 文件夹下新建一个文本文档, 与 a_and_b.v文件一样,我们将该文本文档改名为 tb_a_and_b.v(tb 指的是 testbench),通过该文件的名字我们也可以看成,该文件是a_and_b 文件的测试文件。
测试模块模板:
图1 测试模块代码模板
按照模板格式,编写tb_a_and_b 模块的测试代码如下:
图2 测试模块代码示意
代码解析:
①第 2 行给出了模块的名称,该名称需要跟文件名一致。此处小括号内没有描述端口,因为每一个端口都需要跟开发板上引脚相连,但是测试文件只是用来验证功能文件是否满足逻辑功能,而不需要下载到开发板,所以不需要描述出端口,当然描述出端口也不会错。
②3、4 行定义了两个 reg(寄存器)型变量,为了跟功能模块中的a、b 相连接做准备,此处名字也可以定义成跟功能模块的端口名不一致。定义成 reg 型的原因,是由于在 7、8 行使用了 always对该两个变量进行赋值,Verilog HDL 语言中,所有在 always 中被赋值的变量必须为 reg 型。
③第 5 行定义了 wire 型变量c,是为了接收由功能模块输出的数据。此处也可以不定义该变量,即功能模块的输出不需要传递到测试文件,但是若想传递到测试文件,那么接收的变量的类型必须是wire 型,也就是说接收外模块端口传递的数据的变量必须为 wire 型。
④7、8 行是通过 always 语句对两个 reg型变量进行赋值,always 同 assign一样,都是 Verilog HDL 的赋值方式,它们两个在Verilog HDL的赋值方式中是用的最多的。always 中被赋值的变量必须为 reg 型,它既可以描述组合逻辑,也可以描述时序逻辑,但是 assign只能描述组合逻辑。always 在英文中是总是的意思, 顾名思义,当满足条件时,always 语句是可以实现循环的。第 7行中(always#10) 就是一种循环方式,其表达的意思是每当延时 10 个时间单位,就会执行一次后面的语句,具体的时间单位可以由我们来设定,只需要在module 的上一行写上 `timescale 时间单位/时间精度即可,由此可知测试代码中的 8、9 行分别延时为 10ns 和20ns,用#所标注的延时信息在功能文件中是无的。random 是 Verilog HDL 中的随机数函数,其取值范围-2^32+到+2^32-1,加上大括号表明对其取绝对值,绝对值的取值范围为 0到+2^32-1。在 Verilog HDL 中,函数都是以**开头的。 ⑤10 到15 行是对功能模块的实例化,可以将功能文件理解成已经定义好的函数,在此调用该函数,只是在 Verilog HDL描述的功能模块是可以生产具体电 路的。从中可以看出首先写出功能文件的名字,在该名字后面重新写一个名字,重新写的名字指的是功能文件被调到测试文件中的名字,小括号内部描述功能文 件端口(注意只能是端口)与测试文件中变量(可以是端口也可以是内部变量)的连线,点后面的变量名为且必须为功能文件的端口名,小括号内部的变量为测试模块中的变量。这种做法在 Verilog HDL中叫做模块实例化,被实例化到其他模块的模块叫做被实例化模块,在举得例子中功能模块即为被实例化模块,被实例化模块的端口才可以与其他模块进行连接。了解了大概的程序之后,我们就可以打开仿真软件—Modelsim进行仿真了, 使用 Modelsim 进行仿真可以用两种方法,第一种是直接打开 Modelsim 软件建立工程,第二种是直接通过 ISE软件调取 Modelsim,这次我们先使用第一种方法。
Modelsim的使用
打开Modelsim软件:
图 3 Modelsim 主界面
如图 3 所示,点击左上角 File 选项,选择 New>>Project…新建一个工程。会弹出如图 4所示的界面,在第一行填写工程名,可以随意取名,但是不要出现中文字符和特殊字符,在第二行选择工程存放的路径,尽量工程路径存放到我们建立的 sim文件夹下,其他默认即可,然后点击 OK。
图 4 新建工程
图 5 填写工程信息
填写完图5信息后,在弹出的如图 6 所示的界面中,点击 Add Existing File 选项添加已经存在的文件,即我们已经写好的功能文件和仿真文件。Create New File 为创建新的文件, Create
Simulation 为创建仿真,Create New Folder 为创建新的文件夹。
图 6 添加文件
点击 Add Existing File 选项后,弹出如图 7 所示的界面,选中不同路径下的两个文件(功能文件和仿真文件),分为两次将两个文件加入到该工程,然后点击 Close 关闭该界面。
图 7 选中文件
之后会弹出如图 8 所示的界面,此时两个文件后面分别有一个蓝色的问号, 表明两个文件的语法正确与否无法确定。
图 8 工程界面
右键空白处,或者右键某个文件,选择 Compile>>Compile All 对所有文件进行编译,若想单独对某个文件进行编译,可选择 Compile>>Compile Selected。
图 9 编译工程文件
若文件编译正确,蓝色的问号将会变成绿色的对勾,表示编译成功。若编译结果有错,蓝色的问号将会变成红色的叉号,可以双击 ,查看错误信息,然后修改,再次重新编译, 直到正确为止。
建立仿真,切换到图 10 中的 Library,点开 work 前面的加号,右键测试文件(红圈),选择 Simulate without Optimization。
图 10 开始仿真波形
若是成功,则会出现如图 11 所示的界面,若是错误,可在 Transcript 对话框中找到对应的错误,修改之后,然后重新建立仿真。
图 11 仿真界面
按照图 12 所示,右键所要加载波形的模块,尽量加功能文件的变量,也可以将功能文件和测试文件变量同时加入到仿真窗口。
图 12 添加波形
添加波形后会出现如图 13 所示的界面,设置仿真的时间,点击运行所设置的时间,或者点击直接运行,但若是点击持续运行,则需要点击停止运行。运行仿真后,则可以看到如图所示的仿真波形。
从图中我们看到如图所示的正确波形,查看仿真的波形验证代码是否满足我们的逻辑功能,在黄线处,a = 1,b = 0,则a & b = 0,通过观察波形,c 的结果为 0,实际结果与预期结果一致。若是不一致则需要修改逻辑,重新仿真,直到正确为止。