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
  • 相关阅读:
    安装VMtools vim编辑器的使用 压缩包命令 Linux下的用户管理 (第三天)
    VM虚拟机安装 常用Linux命令 网卡配置 (第二天)
    数据库的交互模式 常用的dos命令 (第一天)
    Validate US Telephone Numbers FreeCodeCamp
    Arguments Optional FreeCodeCamp
    Everything Be True FreeCodeCamp
    Binary Agents FreeCodeCamp
    Steamroller FreeCodeCamp
    Drop it FreeCodeCamp
    Smallest Common Multiple FreeCodeCamp
  • 原文地址:https://www.cnblogs.com/xingshansi/p/7363048.html
Copyright © 2011-2022 走看看