一、概述

1.1、什么是Testbench

Testbench是测试平台,如下图所示。

verilog testbench verilog testbench 对比结果_fpga开发

这个平台的作用就是产生测试激励给待测的设计DUV(Design Under Verification)。灌激励的时候可以写入预期值,检查被灌激励后的DUV的输出是否与预期值一致,从而实现验证RTL设计功能的目的。

1.2、Testbench的六功能以及四要素

六功能

①产生激励:Generate stimulus;
②将激励输入到待测设计:DUV-Design Under Verification);
③产生预期响应:Generate Expectation;
④获取响应:Caputure response;
⑤检查响应的正确性:Check the response for correctness;
⑥根据验证目标评估验证进度:Measure the progress against the overall verification goals。

四要素

①灌激励:产生输入信号给RTL文件;
②做预期:产生预期结果;
③收集响应:收集输出结果,实例化;
④做比较:比较结果。

3、为什么需要Testbench?

保证设计在功能上的正确性需要验证,而testbench就是验证的一种形式。从设计规格到RTL代码阶段,由于RTL的代码是手动设计的,需要我们进行功能验证。而RTL代码到门级网表阶段则是由逻辑中和工具自动完成的。

实现仿真通常是:使用HDL(硬件描述语言)编制的testbench(仿真文件)通过波形或自动比较工具去分析设计的正确性。同时也分析testbench自身的覆盖率和准确性

二、建立Testbench

使用过程语句:initital语句、always语句

initial 和 always 是 2 个基本的过程结构语句,在仿真的一开始即开始相互并行执行。通常来说,被动的检测响应使用 always 语句,而主动的产生激励使用 initial 语句。

initial语句和always语句被赋值的信号类型必须定义为reg类型
而过程连续赋值语句assign被赋值的信号类型必须为wire类型,而且不能出现在过程块中。过程块(initial语句和always语句)通常包含很多语句块(串行语句块begin-end、并行语句块为fork-join、循环语句等)

initial 和 always 的区别是:
①initial 语句只执行一次,而 always 语句不断地重复执行。
②如果希望在 initial 里多次运行一个语句块,可以在 initial 里嵌人循环语句(while、repeat、for 和 forever 等)。
③always语句通常在一些条件发生时完成操作。如下嵌入串行语句块begin-end(并行语句块为fork-join),用always语句描述同步置数、同步清零计数器

module counter1(out, data, load, rst, clk);
     output [7:0] out;
     input   [7:0] data;
     input   load, rst, clk
     reg      [7:0] out;
     always @(posedg clk)                //上升沿触发,执行 always 操作
         begin                           //begin... end 中的语句块顺序执行。
             if(!rst) out = 8'b00;       //同步清零,低电平有效
             else if(load) out = data;   //同步置数 
             else out = out +1;
         end
 endmodule

④always语句具有可综合性(可综合性就是可以编译成网表文件,也就是可以生成电路。而不可综合性只能在仿真阶段用)

Testbench的书写格式

以全加器full_adder的testbench代码为例
由于gvim的注释只能英文,所以保持了用英文注释哈。

module full_adder_tb;

  reg ain, bin, cin;  // drive the input port with the reg type
  wire sumout, cout;  // sample the output port with the wire type

  full_adder u0_full_adder(
    // task 1. how to create an instance
    // module head: verillog-2001 format
    /*input  wire */ .a_in   (ain),
    /*input  wire */ .b_in   (bin),
    /*input  wire */ .c_in   (cin),     // carry in
    /*output wire */ .sum_out(sumout),
    /*output wire */ .c_out  (cout)   // carry out
  );

  // behavior of the adder can be synthesizable
  // "assign" means connectivity
  // assign {c_out, sum_out} = a_in + b_in + c_in;


  //task 2. clock and reset generator
  parameter CLK_PERIOD = 20;
  reg clk, reset_n; // reset_n : active low

  initial begin
    clk = 0;
    forever begin
      #(CLK_PERIOD/2) clk = ~clk;
    end
  end

  initial begin
    reset_n = 0;
    #100 
    reset_n = 1;
  end

  //task 3. drive the stimulus and capture the response
  //here is a testcase
  initial begin
    #110 ain = 0;
         bin = 0;
         cin = 0;  // 00
    #20  ain = 0;
         bin = 1;
         cin = 0;  // 01
    #20  ain = 1;
         bin = 0;
         cin = 0;  // 01
    #20  ain = 1;
         bin = 1;
         cin = 0;  // 10
    #20  ain = 0;
         bin = 0;
         cin = 1;  // 01
    #20  ain = 0;
         bin = 1;
         cin = 1;  // 10
    #20  ain = 1;
         bin = 0;
         cin = 1;  // 10
    #20  ain = 1;
         bin = 1;
         cin = 1;  // 11
    //#20  ain = 1;
    //     bin = 1;
    //     cin = 0;  // 10
    #50  $finish;  // here is a system task which can stop the simulation 
    // $stop
  end

  //task 4. check the result
  always @ (posedge clk) begin
    if (!reset_n) begin
      $display("%t:%m: resetting ...",$time); // counter 5 clock
    end
    else begin
      $display("%t:%m: resetting finish!", $time); // the 6th clock
    end
  end

  initial begin
    #115 if ({cout,sumout} != 2'b00) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
    #20  if ({cout,sumout} != 2'b01) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
    #20  if ({cout,sumout} != 2'b01) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
    #20  if ({cout,sumout} != 2'b10) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
    #20  if ({cout,sumout} != 2'b01) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
    #20  if ({cout,sumout} != 2'b10) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
    #20  if ({cout,sumout} != 2'b10) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
    #20  if ({cout,sumout} != 2'b11) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
    #20  if ({cout,sumout} != 2'b10) $display("Error: {cout,sumout}=%b,ain=%b, bin=%b, cin=%b",{cout,sumout}, ain, bin, cin);
  end

  // task 5. dump waveform with the compile option -debug_all
  initial begin
    //$vcdpluson;
  end
endmodule