zoukankan      html  css  js  c++  java
  • Testbench

    作者:桂。

    时间:2017-08-15  07:11:50

    链接:http://www.cnblogs.com/xingshansi/p/7363048.html 


    前言

      Testbench主要用于module的测试,这里仅记录一般的操作流程。

    〇、verilog与C的区别

      本段文字出处

      RTL级的verilog其实就是常说的verilog语言中可综合的那部分,它是verilog语言的一个子集。所谓的RTL级建模,其实也就是用verilog语言去描述实际电路的行为,比如用verilog语言去描述一个ram或者是一个移位寄存器。 
      对于这个层次上的建模,最重要的思想就是“硬件意识”,也就是说,我们在写代码的时候,大脑里要先知道我们设计的是什么样的电路,然后再用verilog语言将它描述出来。其实,在出现硬件描述语言之前,电路设计都是用原理图去搭的,这种设计过程也是我们心中先有了电路,然后用一种计算机能够识别的方式进行描述,现在的verilog HDL只是相当于我们有了更先进的描述方法,但是在RTL级的设计过程还是不变----心中先有电路,再将它翻译成verilog代码。 
    刚才我们说了,RTL的设计其实就是用verilog去描述电路的行为,那么,我们要怎么去理解这个“电路的行为”呢? 
    第一,电路在物理上是并行工作的。 
      我们知道,在写C程序的时候,由于CPU在任一时刻最多只能执行一条指令,程序都是顺序执行的,即使是我们平时说的多任务操作系统,所谓的并行任务也只是从宏观上来说的(人感觉不到而已),但从微观上来说它仍然是顺序执行的。所以我们设计C程序时思维是串行或者说是并行的。所以看语言不能从上往下看调用逻辑,而是要抓住数据的流向,各个In/out的调用顺序。
      但是数字电路设计则不然,数字电路芯片无非是由一些门电路和触发器组成,从物理上看,在任何时刻,芯片内部各部分电路都是同时工作的。所以在设计的时候,我们需要以一种并发的思维去思考。以下面例子为例: 
    例3: 

    always @(posedge clk or negedge rst_n) 
    begin 
    if ( rst_n == 1'b0 ) 
    begin 
    wren <= 1’b0; 
    rden <= 1’b0; 
    end 
    else 
    begin 
    wren <= a&b; 
    rden <= c&d; 
    end 
    end 
    

      在综合成实际电路之后,wren和rden的赋值是同时进行的,并不会因为wren在rden前面就先对wren赋值(其实将赋值理解高低电平的传递会更好一些)。事实上,当综合成网表文件后,这种赋值的先后顺序也就不存在了。 

    第二,电路行为的先后顺序是通过时钟节拍去完成的。 
      在一些时候,我们需要完成这样的功能:在STATE0状态下,当事件A来了后,去触发状态STATE1;在状态STATE1下,外部总线上读取数据;在处理完后,又进入到状态STATE2,将数据突发传送到背板总线上,并回到STATE0。 
      这就有了一种逻辑上的先后关系,但是由于我们的电路都是并行工作的,我们不能像C语言的方式将代码在一个进程中按先后顺序排放后就可以完成功能。那我们要怎么去实现呢?我们知道,我们人类去判别先后顺序是按通过时间完成的,也就是说,如果我们是基于时钟节拍去设计的话----即在节拍n事件A到来,在节拍B进入状态STATE0,在n+1到n+k节拍中每x个节拍从外部总线上取一个数,在n+k节拍后,每一个节拍将缓存在FIFO的数据放到背板总线上,就可以实现这种逻辑的先后顺序。总结起来,就是电路行为的逻辑先后顺序是通过时钟节拍去体现的,设计时可以通过将事件安排在不同的时钟节拍去完成。

      二、Testbench的基本操作

      A-基本操作

      对于testbench而言,端口与被测module一一对应,input声明为reg,output声明为wire,如果是systemveriog,则可以统一定义为logic。

    1-直接赋值

    initial操作:

    module exam();
    reg rst_n;
    reg clk;
    reg data;
    
    initial
    begin
           clk=1'b0;
           rst=1'b1;
           #10
           rst=1'b0;
           #500
           rst=1'b1;
    end
    
    always
    begin
           #10
                clk=~clk;
    end
    

      注意到有个#符号,该符号的意思是指延迟相应的时间单位。该时间单位由timscale决定.一般在testbench的开头定义时间单位和仿真 精度,比如`timescale 1ns/1ps,前面一个是代表时间单位,后面一个代表仿真时间精度。以上面的例子而言,一个时钟周期是20个单位,也就是20ns。而仿真时间精度的概 念就是,你能看到1.001ns时对应的信号值,而假如timescale 1ns/1ns,1.001ns时候的值就无法看到。对于一个设计而言,时间刻度应该统一,如果设计文件和testbench里面的时间刻度不一致,仿真 器默认以testbench为准。一个较好的办法是写一个global.v文件,然后用include的办法,可以防止这个问题。

    2-重复操作

    例如写某一个字符串,需要重复操作且字符串内容有变更,这个时候可以借助task来完成。

    task load_count;
           input [3:0] load_value;
           begin
                @(negedge clk_50);
                         $display($time, " << Loading the counter with %h >>", load_value);
                load_l = 1’b0;
                count_in = load_value;
                @(negedge clk_50);
                load_l = 1’b1;
           end
    endtask //of load_count
    
    initial 
    begin
       load_count(4’hA);   // 调用task
    end
    

      其他像forever,for,function等等语句用法类似,虽然不一定都能综合,但是用在testbench里面即使是偏高级的语言也不要紧,写testbench与写普通的module思路略有差异。

    3-文件输入

    有时候,需要大量的数据输入,直接赋值的话比较繁琐,可以先生成数据,再将数据读入到寄存器中,需要时取出即可。用 $readmemb系统任务从文本文件中读取二进制向量(可以包含输入激励和输出期望值)。$readmemh 用于读取十六进制文件。例如:

    reg [7:0]   mem[1:256]   //   a 8-bit, 256-word 定义存储器mem
    initial   $readmemh ( "E:/readhex/mem.dat", mem ) // 将.dat文件读入寄存器mem中
    initial   $readmemh ( "E:/readhex/mem.dat", mem, 128, 1 ) // 参数为寄存器加载数据的地址始终
    

      B-查看仿真结果

      对于简单的module来说,要在modelsim的仿真窗口里面看波形,就用add wave ..命令,比如,testbench的顶层module名叫tb,要看时钟信号,就用add wave tb.clk。要查看所有信号的时候,就用 add wave /*。当然,也可以在workspace下的sim窗口里面右键单击instance来添加波形。
      对于复杂的仿真,免不了要记录波形和数据到文件里面去。 这一块的资料可搜索关键词:$dumpvar

      C-testbench编写技巧

    1).如果激励中有一些重复的项目,可以考虑将这些语句编写成一个task,这样会给书写和仿真带来很大方便。例如,一个存储器的testbench的激励可以包含write,read等task。

    2).如果DUT中包含双向信号(inout),在编写testbench时要注意。需要一个reg变量来表示其输入,还需要一个wire变量表示其输出。

    3).如果initial块语句过于复杂,可以考虑将其分为互补相干的几个部分,用数个initial块来描述。在仿真时,这些initial块会并发运行。这样方便阅读和修改。

    4).每个testbench都最好包含$stop语句,用以指明仿真何时结束。
    5).加载测试向量时,避免在时钟的上下沿变化,比如数据最好在时钟上升沿之前变化,这也符合建立时间的要求。

      三、常用的时钟/复位

    时钟clk以及复位rst是除了测试模块外,必须要考虑的信息。

      A-产生时钟的几种方式
    (a)使用initial方式产生占空比50%的时钟
    initial
    begin
    Clk = 0 ;
    # delay ;
    forever
    # (period/2)   Clk = ~ Clk ;
    end
    

      注意一定要给时钟赋初始值,因为信号的缺省值为z,如果不赋初值,则反相后还是z,时钟就一直处于高阻z状态。产生的时钟信号如下

    testbench <wbr>产生激励的一些描述方式
    (b)使用always方式
    initial
    Clk= 0 ;
    always
    # (period/2)  Clk = ~ Clk ;
    (c)使用repeat产生确定数目的时钟脉冲
    initial
    begin
    Clk = 0 ;
    repeat ( 6 ) 
    # (period/2)   Clk = ~ Clk ;
    end
    

      该例使用repeat产生 3个时钟脉冲,产生的波形如下:

    testbench <wbr>产生激励的一些描述方式
    (d)产生占空比非50%的时钟
    initial
    Clk = 0 ;
    always
    begin
    # 3 Clk=~Clk;
    #2 Clk=~Clk;
    end

    testbench <wbr>产生激励的一些描述方式

      B-产生复位信号的几种形式

    (a)异步复位
    initial
    begin
    Rst = 1 ;
    # 100 ;
    Rst = 0 ;
    # 500 ;
    Rst = 1 ;
    end
    testbench <wbr>产生激励的一些描述方式
    (b)同步复位信号
    initial
    begin
    Rst = 1 ;
    @( negedge Clk) ; //等待时钟下降沿
    Rst = 0 ;
    # 30 ;
    repeat(3)@( negedge Clk) ; //等待3个时钟下降沿
    Rst = 1 ;
    end
    testbench <wbr>产生激励的一些描述方式
     
    参考
    • http://blog.csdn.net/wordwarwordwar/article/details/53885209
  • 相关阅读:
    Ubuntu 18.04.3 更改系统语言为简体中文
    Centos7.3、nginx环境下部署hugo博客
    Centos7.3 卸载 Nginx(彻底卸载) 并重新安装 Nginx(RPM源yum安装)
    Centos7.3、nginx环境下部署hexo博客(非git推送方式)
    使用阿里云对象存储OSS+PicGo搭建图床
    Hexo博客添加LiveRe评论系统
    使用 jsDelivr CDN加速Github 仓库的图片
    解决win10一开机占用内存就飙到70%的问题
    [Andriod官方训练教程]管理Activity的生命活动之停止和重启一个Activity
    [Andriod官方训练教程]支持不同的设备之支持不同的语言
  • 原文地址:https://www.cnblogs.com/xingshansi/p/7363048.html
Copyright © 2011-2022 走看看