文章目录
- 引言
- 1. Verilog HDL 电路仿真和验证概述
- 2. Verilog HDL测试程序设计基础
- 2.1 Testbench及其结构
- 2.2 测试平台举例
- 2.2.1 组合电路仿真环境搭建
- 2.2.2 时序电路仿真环境搭建
- 2.3 Verilog HDL仿真结果确认
- 2.4 Verilog HDL仿真效率
- 3. 与仿真相关的系统任务
- 3.1 $display和\$write
- 3.2 $monitor和\$strobe
- 3.2.1 $monitor
- 3.2.2 $strobe
- 3.3 $time和\$realtime
- 3.4 $finish和\$stop
- 3.5 $readmemh和\$readmemb
- 3.6 $random
引言
- 前面的第三章,第四章要求非常准确,非常严格地按照想法做,但是到了测试,怎么写都可以
- 从硬件描述语言的作用,可以分为:可综合设计和测试仿真。对于设计岗位,要对前面的知识非常了解,同时对仿真测试也要很了解。对于验证岗位,要对测试仿真非常了解,尽可能用能想到的语法去做
- 第三章第四章讲的实际上是针对于电路的具体设计,最后是想的怎么把管子,触发器,与非门等连接到一起,操纵的是电路。而在测试和仿真,它的性质和C语言是完全一样的,操纵的是计算机的CPU
1. Verilog HDL 电路仿真和验证概述
- 验证在 Verilog HDL 设计的整个流程中分为4个阶段:
(1)功能验证
(2)综合后验证
(3)时序验证
(4)板级验证 - 其中和Verilog有关系的是前三种,最后一个就是电路板级的验证了
- 这个课程只讲了功能验证,综合后验证和时序验证就是对EDA工具的使用(后面会有介绍)
- !!功能验证是没有经过综合,在计算机层面上的验证,是最基本的验证,换句话来讲,就是对语法的检验过程
- 综合后验证就是把所有原件连接在一起,它是实实在在的电路,是对电路的连接关系,基本逻辑的考察
- 时序验证就是在综合后电路加上一个延迟文件(.sdf等),贴近于实际电路的情况,把实际中的物理器件变成数学模型放在这里,所以,基本上时序通过,板级就不会有问题,除非对信号的调理没调理到合适的位置,对信号的容差没有考虑
- 目前常用的功能验证有三种:黑盒法、白盒法和灰盒法
2. Verilog HDL测试程序设计基础
2.1 Testbench及其结构
-
通常采用测试平台(Testbench)方式进行仿真和验证。在仿真时,Testbench 用来产生测试激励给待验证设计(Design Under Verification,DUV),或者称为待测试设计(Design Under Test,DUT),同时检查DUV/DUT 的输出是否与预期的一致,从而达到验证设计功能的目的
-
在数字信号的设计中,有一些输入信号,一些输出信号,用示波器或LED或其他东西看一下输出电位。这时候就有三个东西:信号源、被测电路、测试仪器(对输出结果的观察)
-
对于硬件描述语言,一定还是一个模块,然后对输入输出变量和参数进行定义
-
三个部分:调用待测试的模块,定义测试向量(激励向量产生,显示输出结果),显示格式定义(可以不写,因为定义不是很准确)
-
!!其实我们就是在写如何产生激励向量
-
例:T触发器测试程序
-
输出打印可以不这么写,后面会讲
-
产生的结果
-
仿真流程:
-
从图中可以清晰地看出Testbench 的主要功能:
(1)DUT提供励信号
(2)正确实例化DUT
(3)将仿真数据显示在终端或者存为文件,也可以显示在波形窗口中供分析检查
(4)复杂设计可以使用 EDA 工具,或者通过用户接口自动比较仿真结果与理想值,实现结果的自动检查 -
前两点功能主要和 Testbench 的编写方法和风格(Coding Style)相关,后两点功能主要和仿真工具的功能特性及支持的用户接口相关。仿真工具将在后面章节中详细介绍,本章所要论述的重点是如何编写规范、高效、合理的测试程序
-
编写Testbench 时需要注意的问题:
(1)Testbench代码不需要可综合性。Testbench 代码只是硬件行为描述而不是硬件设计
(2)!!行为级描述效率高。Verilog HDL 语言具备 5个描述层次,分别为开关级、门级、RTL级、算法级和系统级。电路设计希望用门级,RTL级,测试仿真最好是算法级和系统级。所以,讲设计的时候强调电路一定要准确,讲仿真的时候强调效率,语法要非常高级
(3)掌握结构化、程式化的描述方式。结构化的描述有利于设计维护。由于在 Testbench中,所有的initial、always 以及 assign 语句都是同时执行的,其中每个描述事件都是基于时间“0”点开始的,因此可通过这些语句将不同的测试激励划分开来。一般不要将所有的测试都放在一个语句块中。
2.2 测试平台举例
- 测试平台需要产生时钟信号、复位信号和一系列的仿真向量,观察 DUT 的响应,确认仿真结果
2.2.1 组合电路仿真环境搭建
- 组合电路就是输入输出信号之间的关系,没有时钟信号。主要是检查设计结果是否符合该电路真值表的功能
- 例:搭建全加器的仿真环境
(1)真值表
(2)全加器代码
(3)根据全加器真值表,编写全加器测试程序
(4)结果
2.2.2 时序电路仿真环境搭建
- 时序逻辑电路仿真环境的搭建要求与组合逻辑电路基本相同,主要区别在于时序逻辑电路仿真环境中,需要考虑时序、定时信息和全局复位、置位等信号要求,并定义这些信号。
- 例:搭建十进制加法计数器的仿真环境
(1)加法计数器代码
(2)测试代码
(3)结果
2.3 Verilog HDL仿真结果确认
- 上面2.1的T触发器测试程序例子输出写的很不好,但是刚开始写这个东西,是因为更加贴合C语言的学习方式(屏幕打印)
- !!!!为什么说不好?如果不打印,在某个软件把值显示出来,这是没问题的。但是屏幕打印要求CPU包含指令集,总线包含显卡再到打印的过程。当显示存储一个东西只需要一个指令就完了(在内存的某个地址写一个数),但是如果是内存中的某个数拿出来通过打印的话,可能几百条指令的时间就没有了,这个浪费是巨大的
- !!!!在任何设计中,包括软件设计,硬件仿真,都不要用屏幕打印。屏幕打印是当你出错的时候,需要看中间某个特殊量的时候,没有办法,只能打印。在C语言或者其他语言也不会这么干,通常会打印到一个file,日志文件
- 1.观察波形:适合电路规模中等
- 2.观察文本输出
(1)$display
(2)$ monitor
打印语句也是有帮助的,但是帮助不是特别大,建议只打印很重要的信号,或者在小规模设计中可以用 - 3.自动检查仿真结果:不看波形,只比较判断,假如判断出错了就打印出来,不出错就不管。这种方式是最快的,但是不直观
- 4.使用VCD文件:把信号全部打印到文件中,再对文件进行处理
- !!对超大规模或者极大规模的设计,唯一的解决办法一定是VCD文件
2.4 Verilog HDL仿真效率
-
希望仿真速度很快
-
1.减小层次结构
(仿真代码的层次越少,执行时间就越短。这主要是因为参数在模块端口之间传递时需要消耗仿真器的执行时间) -
2.减少门级代码的使用
(由于门级建模属于结构级建模,自身参数建模较复杂,需要通过建模调用的方式来实现,因此建议仿真代码尽量使用行为级语句,建模层次越抽象,执行时间就越短) -
3.仿真精度越高,效率越低
(计时单位值与计时精度值的差距越大,则模拟时间越长。`timescale 仿真时间标度将在第 593节中详细讲述)
!!要合理选择仿真精度,比如1ps/1ps需要的时间就是1ns/1ns的1000倍,而超大规模的电路仿真是以天为单位的 -
4.进程越少,效率越高
-
5.减少仿真器的输出显示
-
!!!!为了提高仿真效率:第一是选取合适的仿真精度,第二十减少仿真器的输出显示,第三是尽可能用高层次的语言进行设计,同时减少层数
3. 与仿真相关的系统任务
3.1 $display和$write
- VerilogHDL中有两种主要的标准输出任务:display和$write。这两个系统函数都用于输出信息且语法格式相同
- 区别:$display自动的在输出后进行换行;$write输出特定信息时不自动换行
- 在仿真和任务中间,$display和$write相当于C语言的printf
- 语法格式:
“<format_specifiers>”通常称为“格式控制”,而“<signal1,signal2,signaln>”则为“信号输出列表” - 输出格式说明由“%”和格式字符组成,其作用是将输出的数据转换成指定的格式输出。格式说明总是由“%”字符开始的。对于不同类型的数据用不同的格式输出。表5.3-1 中给出了常用的几种输出格式,无论何种格式都以较短的结果输出。
- 特殊的是有等级层次的打印选项%m
- 特殊字符:
3.2 $monitor和$strobe
- !!对于$display和$write,一旦出现就要打印,而$monitor和$strobe在信号变化的时候打印
- $monitor和$strobe又不完全一样
3.2.1 $monitor
- 语法格式
- 与$display最大的区别是$monitor可以独立成一个指令,指令中信号列表的任意一个信号发生变化就打印($strobe也是)
- 提供了两个任务$monitoron和$monitoroff
- $monitoron 和$monitoroff任务的作用是通过打开和关闭监控标志来控制监控任务$monitor 的启动和停止,这样,程序员可以很容易地控制$monitor 的发生时间
- 例子
(1) $monitor语句可以完全独立于信号赋值语句,完全是时间上相关的、
(2)当a发生变化或者b发生变化的时候,打印
3.2.2 $strobe
- 语法格式:
- 有四种语法,b,o,h实际上是把$strobe语句打印的格式固定下来了
- $strobe的特点:在被调用时刻所有的赋值语句都完成后,才输出相应的文字信息。
- 例子
- HDL其实并不是完全并行的,因为计算机扫描肯定是从上开始的
- $strobe 任务提供了另一种数据显示机制可以保证数据只在所有赋值语句执行完毕后才被显示。
3.3 $time和$realtime
- 系统时间任务是一个非常重要的任务,它表征了在时间上面具体的值是多少
(1)$time 可以返回一个 64 比特整数表示的当前仿真时刻值,该时刻是以模块的开始仿真时间为基准的
(应该是0,2,4,6,8) - 一看到$time就是一个当前时间的概念
(2)系统函数$realtime。$realtime 和$time 的作用是一样的,只是$reatime 返回的时间数字是一个实数,该数字也是以时间尺度为基准的
- `timescale 1ns/100ps
- #2.1 set = 0;
- 这时候用$realtime就是实数类型的时间
- !!但是这时候如果用$time就会四舍五入
- !!!!但是在实际系统中,用的最多的$time,$realtime用的非常非常少
3.4 $finish和$stop
- 用于仿真控制
- $finish是结束仿真并推出仿真环境,也就是整个仿真都停掉了(图形之类的都看不到,直接退出了)
- $stop就是停在这,但是不退出仿真,还是可以继续
- 语法格式:
- n可以是0,1,2
- !!n=2是非常重要的一个使用。在以后的设计中,为了满足设计要求,通常会做一小段仿真程序(比如原来要做10s,但是只做了10ms),让着一小段程序先跑一下系统和cpu和仿真时间能不能满足要求,然后再跑大仿真
3.5 $readmemh和$readmemb
- 这两个语句是内存访问语句,但是再学习过程中,这两个语句已经落后非常多了,大概了解就可以了
3.6 $random
-
$random 是产生随机数的系统函数,每次调用该函数将返回一个 32 位的随机数,该随机数是一个带符号的整型数。
-
语法:
-
!!产生:当随机数去调用系统函数的时候,这时候是从内存空间取出来了一个数来做你的随机数
-
通常会用随机出产生一个范围的数,比如-5到5。用法:
-
其中 b为一常数且要求大于零,它给出了一个范围在(-b+1)到(b-1)之间的随机数