在描述完电路之后,我们需要进行对代码进行验证,主要是进行功能验证。现在验证大多是基于UVM平台写的systemverilog,然而我并不会sv,不过我会使用verilog进行简单的验证,其实也就是所谓的仿真。这里就来记录一下一些验证的基础吧。

一、验证基础与仿真原理

  ①综合中的语法,都适用于仿真,在仿真中,Verilog语句是串行的,其面向硬件的并行特性则是通过其语义(语言含义)来实现的,因此并不会丢失代码的并行含义和特征。

  ②仿真的关键元素有:仿真时间、事件驱动、队列、调度等。

  ③仿真时间:指由仿真器维护的时间值,用来对仿真电路所用的真实时间进行建模。0时刻被称为仿真起始时刻。当仿真时间推进到某一个时间点时,该时间点就被称为当前仿真时间,而以后的任何时刻都被称为未来的仿真时间。

本质上,仿真时间是没有时间单位的,由于代码中有`timescale语句的定义,就出现了xxxns。

仿真事件都是严格按照仿真时间向前推进的,如果在同一个仿真时刻有多个事件要执行,那么首先需要根据他们之间的优先级来判定谁先执行。优先级相同,可能随机执行,也可能按照代码的顺序来执行。

  ④事件驱动:仿真时间只能被下面事件中的一种来推进:

    ·定义过的门级或者线传输延时;

    ·更新时间;

    ·“#”的事件控制;

    ·“always”关键字引入的事件控制

    ·“wait”的等待语句

  ⑤事件队列与调度:事件队列与调度可以简单地理解为:它决定了verilog在某个时刻先完成哪些语句。

VerilogHDL的分层事件队列为:

当前仿真时间事件

活跃事件(顺序随机或者按照代码出现的顺序)

阻塞赋值;

连续赋值;

非阻塞赋值的右式计算;

原语输入计算和输出改变;

系统任务:$display

非活跃事件

显示0延时赋值;

Verilog的PLI call back例程

非阻塞赋值更新时间

非阻塞赋值产生一个非阻塞更新时间,被调度到当前仿真时间

监控事件

$monitor和$strobe系统任务,监控时间不能生成任何其他的事件,这是也要注意的。

将来仿真时间事件

将来事件

被调度到将来仿真时间的时间。

  ⑥关于forever、force和release、wait、UDP、PLI等具体语法我就不想记录了,没那个心思...

  ⑦系统任务的使用:

  在Verilog HDL 语言中,以“$”字符开始的标识符表示系统任务或系统函数。系统任务和函数即在语言中预定义的任务和函数。和用户自定义任务和函数类似,系统任务可以返回0 个或多个值,且系统任务可以带有延迟。系统任务的功能非常强大,主要分为以下几类:

A、显示任务(display task);

B、文件输入/输出任务(File I/O task);

C、时间标度任务(timescale task);

D、仿真控制任务(simulation control task);

E、时序验证任务(timing check task);

F、仿真时间函数 (simulation time function)

G、实数变换函数(conversion functions for real);

H、概率分布函数(probabilistic distribution function)

由于时间关系,我不进行详述记录了,用到的时候再进行记录。

二、测试文件的激励

(1)信号的初始化问题

  主要有三种产生激励的方法:一种是直接编辑测试激励波形(这种基本上被淘汰了),一种是用Verilog测试代码的时序控制功能,产生测试激励。还有就是利用Verilog HDL 语言的读文件功能,从文本文件中读取数据(该数据可以通过C/C++、MATLAB 等软件语言生成)。

  ①代码中的变量的初始化可以用initial进行初始化,也可以在定义的时候进行初始化。

  ②在硬件系统中,当系统上电之后,信号电平不是0就是1,不会存在x或者z,这是就会根据EDA的默认状态进行默认的设置。由于上电的默认性,导致这个默认信号不一定是我们想要的信号,因此我们需要进行复位进行初始化。

  ③在Verilog HDL 中,有两种不同的原因可能导致信号值为x。第一种原因是,有两个不同的信号源用相同的强度驱使同一个节点,并试图驱动成不同的逻辑值,这一般是由设计错误造成的。第二种原因是信号值没有初始化。所以在设计组合逻辑时,需要将不确定的输入转化成确定输入,然后再完成组合逻辑。

2)时钟信号的生成

  ①普通时钟信号

  所谓的普通时钟信号就指的是占空比为50%的时钟信号,也是最常用的时钟信号,其波形下图所示:

            

                占空比为50%的时钟信号

普通时钟信号可通过initial 语句和always 语句产生,其代码如下:

----基于initial 语句的方法:

parameter clk_period = ;
reg clk;
initial begin
  clk = ;
  forever
    # (clk_period/) clk = ~clk;
end

---基于always 语句的方法:

parameter  clk_period = ;
reg clk;
initial
clk = ; always # (clk_period/) clk = ~clk;

在这里的initial 语句用于初始化clk 信号,否则就会出现对未知信号取反的情况,因而造成clk信号在整个仿真阶段都为未知状态。

  ②自定义占空比的时钟信号

自定义占空比信号通过always 模块可以快速实现,下面给出一个占空比为40%的时钟信号代码:

parameter High_time = ,
Low_time = ; //占空比为High_time/( High_time+ Low_time)
reg clk;
always begin
clk = ;
#High_time;
clk = ;
#Low_time;
end

这里由于直接对clk 信号赋值,所以不需要initial 语句初始化clk 信号。当然,这种方法也可以用于产生普通时钟信号,只是代码行数较多而已。

  ③相位偏移的时钟信号

  相位偏移是两个时钟信号之间的相对概念,下图所示,其中clk_a 为参考信号,clk_b为偏移信号:

  首先通过一个always 模块产生参考时钟clk_a,然后通过延迟赋值得到clk_b 信号,其偏移的相位可通过360*pshift_time%(High_time+Low_time)来计算,其中%为取模运算。

下面代码的相位偏移为72 度:

parameter High_time = ,
Low_time = ,
pshift_time = ; reg clk_a;
wire clk_b; always begin
clk_a = ;
# High_time;
clk_b = ;
# Low_time;
end assign # pshift_time clk_b = clk_a;

  ④固定数目的时钟信号

上述语句产生的时钟信号都是无限个周期的,也可以通过repeat 语句来产生固定个数的时钟脉冲,下面的代码产生了5 个周期的时钟:

parameter clk_cnt = ,
clk_period = ;
reg clk; initial begin
clk = ;
repeat (clk_cnt)
# clk_period/ clk = ~clk;
end

(3)复位信号的产生

  ①异步复位信号

异步复位信号的实现代码如下,代码将产生低有效的复位信号rst_n,其复位时间为100 个仿真单位:

parameter rst_repiod = ;
reg rst_n; initial begin
rst_n = ;
# rst_repiod;
rst_n = ;
end

  ②同步复位

同步复位信号的实现代码如下:

parameter rst_repiod = ;
reg rst_n; initial begin
rst_n = ;
@( posedge clk);
rst_n = ;
# rst_repiod;
@( posedge clk);
rst_n = ; end

上述代码首先将复位信号rst_n 初始化为1,然后等待时钟信号clk 的上升沿,将rst_n拉低,进入有效复位状态;然后经过100 个仿真周期,等待下一个上升沿到来后,将复位信号置为1。在仿真代码中,是不存在逻辑延迟的,因此在上升沿对rst_n 的赋值,能在同一个沿送到测试代码逻辑中。

  在需要复位时间为时钟周期的整数倍时,可以将rst_repiod 修改为时钟周期的3 倍来实现,也可以通过下面的代码来完成。

parameter rst_num = ;
initial begin
rst_n = ;
@(posedge clk);
rst_n = ;
repeat(rst_num) @(posedge clk);
rst_n = ;
end

上述代码在clk 的第一个上升沿开始复位,然后经过5 个时钟上升沿后,在第5 个时钟上升沿撤销复位信号,进入有效工作状态。

(4)数据的产生

数据的产生这里就不进行描述了,在以后关于常用的仿真模块中进行记录。

三、提高仿真时间的注意点

  ①减少层次结构

  仿真代码的层次越少,执行时间就越短。这主要是由于参数在模块端口之间传递需要消耗仿真器的执行时间。

②减少门级代码的使用

  由于门级建模属于结构级建模,自身参数建模已经比较复杂了,还需要通过模块调用的方式来实现,因此建议仿真代码尽量使用行为级语句,建模层次越抽象,执行时间就越短。引申一点,在行为级代码中,尽量使用面向仿真的语句。例如,延迟两个仿真时间单位,最好通过“#2”来实现,而不是通过深度为2 的移位寄存器来实现。

  ③仿真精度越高,效率越低

  例如包含`timescale 1ns / 1ps 定义的代码执行时间就比包含`timescale 1ns / 1ns 定义的代码执行时间长。

  ④进程越少,效率越高

  代码中的语句块越少仿真越快,例如将相同的逻辑功能分布在两个always 语句块中,其仿真执行时间就比利用一个always 语句来实现的代码短。这是因为仿真器在不同进程之间进行切换也需要时间。

  ⑤减少仿真器的输出显示

  Verilog HDL 语言包含一些系统任务,可以在仿真器的控制台显示窗口输出一些提示信息。虽然其对于软件调试是非常有用的,但会降低仿真器的执行效率。因此,在代码中这一类系统任务不能随意使用。本质上来讲,减少代码执行时间并不一定会提高代码的验证效率。

  关于仿真的其他入门知识,比如一些无规律信号的生成、测试结果的存储和显示等问题,我会在后面进行记录,主要是以代码模块的形式记录。

VerilogHDL常用的仿真知识的更多相关文章

  1. Ext常用开发基础知识

    Ext常用开发基础知识 组件定义 //这种方法可以缓存所需要的组件 调用起来比较方便(方法一 ) Ext.define('MySecurity.view.home.HomePanel', { //添加 ...

  2. Web测试的常用测试用例与知识

    1. Web测试中关于登录的测试 2. 搜索功能测试用例设计 3. 翻页功能测试用例 4. 输入框的测试 5. Web测试的常用的检查点 6. 用户及权限管理功能常规测试方法 7. Web测试之兼容性 ...

  3. VCS使用学习笔记(1)——Verilog相关的仿真知识

    本文主要学习Verilog的仿真特性,以及仿真器对Verilog的处理,算是对Verilog知识的增量学习.本文内容与我的另一篇博文(http://www.cnblogs.com/IClearner/ ...

  4. 开发中常用的es6知识

    结合实际开发,开发中常用的es6的知识: 1.新增let和const命令: ES6 新增了let命令,用来声明变量.它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效: cons ...

  5. linux运维常用命令及知识

    1.查找当前目录下所有以.tar结尾的文件然后移动到指定目录: find . -name “*.tar” -exec mv {} ./backup/ ; 查找当前目录30天以前大于100M的LOG文件 ...

  6. AIX 常用命令和知识

      BOOTLIST:#bootlist -m normal -o (查看bootlist)#bootlist -m normal (设置bootlist为空,谁要在我机器上执行我就要哭了)#boot ...

  7. 常用的flex知识 ,比起float position 好用不少

      flex布局具有便捷.灵活的特点,熟练的运用flex布局能解决大部分布局问题,这里对一些常用布局场景做一些总结. web页面布局(topbar + main + footbar) 示例代码   要 ...

  8. 那些你常用的JSP知识

    脚本程序 <> 或者,您也可以编写与其等价的XML语句,就像下面这样: <jsp:scriptlet> 代码片段 </jsp:scriptlet>任何文本.HTML ...

  9. 10个MCU常用的基础知识

    转自:http://bbs.21ic.com/icview-2659278-1-1.html 1.MCU有串口外设的话,在加上电平转换芯片,如MAX232.SP3485就是RS232和RS485接口了 ...

随机推荐

  1. Unity3D常用 API 之 Invoke 函数调用

    1.金钱副本细节完善 1.1.宝箱自动掉落 给宝箱预制体添加刚体组件即可. 1.2.实现按键宝箱批量掉落 ①将实例化生成宝箱的代码单独封装成一个函数: ②使用 for 循环,批量生成宝箱. 按一次z键 ...

  2. cp的用法

    1.cp的功能 拷贝一个或多个文件(或目录)到目的地 2.例子 1)一次拷贝多个源文件到目的地#cp /mnt/hgfs/DOC/{1,2,3,4,5}.txt /root/ldj 2)只拷贝链接文件 ...

  3. find的用法

    find在Linux系统中和其它工具,如sed.awk.grep等结合起来用,非常有用. 1.列出系统中所有属于root用户的“set uid”文件 #find / -perm 4755 –uid 0 ...

  4. 几个常用的linux命令(操作服务器时会用到)

    目录 tmux 背景 安装 使用 启动一个tmux session 暂时离开当前session 回到之前的session 重命名session 创建window 创建pane ps scp 参考 tm ...

  5. App测试札记

    App测试札记 测试应该收集信息 测试应该问问题 测试应该扮演不同角色 测试应该如实反馈 初学者 有哪些可以利用的信息?需求,技术方案,测试设计,现有功能,相关人员 App会在哪些环境下运行 App会 ...

  6. Index Scans 索引扫描

    官方文档链接地址 http://docs.oracle.com/cd/E11882_01/server.112/e40540/indexiot.htm#CNCPT1170 Index Scans 在索 ...

  7. Reflect(反射)

    反射.反射,程序员的快乐.反射是无处不在的. 那么什么是反射:通过反射,可以在运行时获得程序或程序集中每一个类型(包括类.结构.委托.接口和枚举等)的成员和成员的信息.有了反射,即可对每一个类型了如指 ...

  8. [leetcode-515-Find Largest Value in Each Tree Row]

    You need to find the largest value in each row of a binary tree. Example: Input:    1   / \  3 2 / \ ...

  9. 【LeetCode】98. Validate Binary Search Tree

    题目: Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is define ...

  10. 【CC2530入门教程-06】CC2530的ADC工作原理与应用

    第6课  CC2530的ADC工作原理与应用 广东职业技术学院  欧浩源 一.A/D转换的基本工作原理 将时间上连续变化的模拟量转化为脉冲有无的数字量,这一过程就叫做数字化,实现数字化的关键设备是AD ...