SV的testbench例子-加法器

下图是一个加法器的框图:

输入管脚包括a、b、valid和clk、reset,以及一个输出管脚c。

TestBench Without Monitor, Agent and Scoreboard 

不带monitor、agent和scoreboard的结构如下:

(1)定义transaction 

transaction的作用主要是定义随机化的输入输出管脚,并输出显示.

class transaction;
   
  //declaring the transaction items
  rand bit [3:0] a;
  rand bit [3:0] b;
       bit [6:0] c;
  function void display(string name);
    $display("-------------------------");
    $display("- %s ",name);
    $display("-------------------------");
    $display("- a = %0d, b = %0d",a,b);
    $display("- c = %0d",c);
    $display("-------------------------");
  endfunction
endclass

(2)定义generator

generator的作用是实例化transaction,并且随机化trasaction中的随机变量,将随机化后的数据发送到driver.要实现与driver的通信,这就需要用到mailbox.在发送数据完成后,需要触发事件表明发送数据的完成,这需要定义事件.

注意,这里的mailbox是generator和driver之间共有的!

class generator;
   
  //declaring transaction class
  rand transaction trans;
   
  //定义信箱
  mailbox gen2driv;
   
  //定义transaction的实例化次数,每实例化一次就发送一组随机数
  int  repeat_count; 
 
  //在实例化次数完成后需要触发事件表示发送数据结束
  event ended;
 
  //构造方法
  function new(mailbox gen2driv);//
    //getting the mailbox handle from env
    this.gen2driv = gen2driv;
  endfunction
   
  //main task, generates(create and randomizes) the repeat_count number of transaction packets and puts into mailbox
  task main();
 
    repeat(repeat_count) begin
      trans = new();
      if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");   
      gen2driv.put(trans);
    end
    -> ended; //实例化次数完成后触发事件
  endtask 
endclass

(3)定义interface

实现testbench与DUT之间的通信. 

interface intf(input logic clk,reset);//这个接口中并没有声明采样时序
   
  //declaring the signals
  logic       valid;
  logic [3:0] a;
  logic [3:0] b;
  logic [6:0] c;
   
endinterface

(4)定义driver

driver的作用是接收来自generator的随机数据,并将随机数据发送到DUT.

class driver;
  
  //记录transaction实例化的数目
  int no_transactions;
   
  //创建虚接口句柄
  virtual intf vif;//只有虚接口才可以在类中实例化
   
  //创建信箱句柄
  mailbox gen2driv;
   
  //构造方法
  function new(virtual intf vif,mailbox gen2driv);
    //getting the interface
    this.vif = vif;
    //getting the mailbox handles from  environment
    this.gen2driv = gen2driv;
  endfunction
   
  //复位任务
  task reset;
    wait(vif.reset);
    $display("[ DRIVER ] ----- Reset Started -----");
    vif.a <= 0;
    vif.b <= 0;
    vif.valid <= 0;
    wait(!vif.reset);
    $display("[ DRIVER ] ----- Reset Ended   -----");
  endtask
   
  //主任务,将从信箱获得的数据发送到接口,同时将接口中获得的DUT数据存入到信箱
  task main;
    forever begin
      transaction trans;
      gen2driv.get(trans);
      @(posedge vif.clk);//顺序执行,第一个时钟上升沿
      vif.valid <= 1;
      vif.a     <= trans.a;
      vif.b     <= trans.b;
      @(posedge vif.clk);//第二个时钟上升沿
      vif.valid <= 0;//将接口中的vif.c赋给tran.c,vif.c来自DUT
      trans.c   <= vif.c;
      @(posedge vif.clk);
      trans.display("[ Driver ]");
      no_transactions++;
    end
  endtask
           
endclass

 (5)定义environment

environment包括mailbox、generator、diver,它通过实例化信箱实现driver和gennerator之间的通信.

`include "transaction.sv"
`include "generator.sv"
`include "driver.sv"//将文件导入
class environment;
   
  //generator and driver instance
  generator gen;
  driver    driv;
   
  //信箱句柄
  mailbox gen2driv;
   
  //虚接口
  virtual intf vif;
   
  //构造方法
  function new(virtual intf vif);
    //get the interface from test
    this.vif = vif;
     
    //creating the mailbox (Same handle will be shared across generator and driver)
    gen2driv = new();
     
    //实例化generator和driver的对象
    gen  = new(gen2driv);
    driv = new(vif,gen2driv);
  endfunction
   
  //
  task pre_test();//测试前任务,用于复位
    driv.reset();
  endtask
   
  task test();//测试任务
    fork
    gen.main();
    driv.main();
    join_any
  endtask
   
  task post_test();//测试后任务
    wait(gen.ended.triggered);//等待generator的事件触发
    wait(gen.repeat_count == driv.no_transactions);//transaction随机化次数
  endtask 
   
  //主任务
  task run;
    pre_test();
    test();
    post_test();
    $finish;
  endtask
   
endclass

(6)定义test

test模块是使用program块编写的,这是与前面的不同.其作用主要是实例化environment、初始化激励、配置testbench如产生多少transaction的对象数量.

`include "environment.sv"//导入environment
program test(intf intf);
   
  //declaring environment instance
  environment env;
   
  initial begin
    //creating environment
    env = new(intf);
     
    //setting the repeat count of generator as 10, means to generate 10 packets
    env.gen.repeat_count = 10;
     
    //calling run of env, it interns calls generator and driver main tasks.
    env.run();
  end
endprogram

(7)testbench的顶层模块

主要包括interface、DUT和test.用prgram块编写test可以实现test与DUT之间的无竞争交互!

`include "interface.sv"
`include "random_test.sv"//导入文件
 
module tbench_top;
   
  //clock and reset signal declaration
  bit clk;
  bit reset;
   
  //clock generation
  always #5 clk = ~clk;
   
  //reset Generation
  initial begin
    reset = 1;
    #5 reset =0;
  end
   
   
  //实例化接口
  intf i_intf(clk,reset);
   
  //实例化test,接口句柄作为参数
  test t1(i_intf);
   
  //DUT instance, interface signals are connected to the DUT ports
  adder DUT (
    .clk(i_intf.clk),
    .reset(i_intf.reset),
    .a(i_intf.a),
    .b(i_intf.b),
    .valid(i_intf.valid),
    .c(i_intf.c)
   );
   
  //enabling the wave dump
  initial begin
    $dumpfile("dump.vcd"); $dumpvars;
  end
endmodule

DUT模块:

/*
            --------------
 valid ---->|            |
            |			 |
    a -/--->|       	 |
            |   adder    |---/-> c
    b -/--->|            |
            |            |
            --------------
               ^      ^ 
               |      |
              clk   reset

*/
module adder(
  input 	   clk	,
  input 	   reset,
  input  [3:0] a	,
  input  [3:0] b	,
  input        valid,
  output [6:0] c  		); 
  
  reg [6:0] tmp_c;
  
  //Reset 
  always @(posedge reset) 
    tmp_c <= 0;
   
  // Waddition operation
  always @(posedge clk) 
    if (valid)    tmp_c <= a + b;
  
  assign c = tmp_c;

endmodule

运行结果如下:

SV的testbench例子-加法器_第1张图片

SV的testbench例子-加法器_第2张图片

带monitor、agent和scoreboard的结构如下:

(1)定义monitor

monitor的作用是对来自接口的信号进行采样,并将信号级的活动转换为事务级,将它们发送到scoreboard.

class monitor;
   
  //creating virtual interface handle
  virtual intf vif;
   
  //creating mailbox handle
  mailbox mon2scb;
   
  //constructor
  function new(virtual intf vif,mailbox mon2scb);
    //getting the interface
    this.vif = vif;
    //getting the mailbox handles from  environment
    this.mon2scb = mon2scb;
  endfunction
   
  //Samples the interface signal and send the sample packet to scoreboard
  task main;
    forever begin
      transaction trans;
      trans = new();
      @(posedge vif.clk);
      wait(vif.valid);
      trans.a   = vif.a;//因为输出相对于输入延时为1个周期
      trans.b   = vif.b;
      @(posedge vif.clk);
      trans.c   = vif.c;
      @(posedge vif.clk);
      mon2scb.put(trans);//将总线上的数据存入信箱
      trans.display("[ Monitor ]");
    end
  endtask
   
endclass

(2)定义scoreboard

scoreboard主要是接收来自monitor存入信箱的数据包,并将它们与期望值相比较,如果不匹配则会报错.

由于这个例子中DUT的行为比较简单,所以添加了一行代码以生成预期结果,对于复杂的设计可以采用参考模型的方法来生成预期结果.

class scoreboard;
    
  //creating mailbox handle
  mailbox mon2scb;
   
  //used to count the number of transactions
  int no_transactions;
   
  //constructor
  function new(mailbox mon2scb);
    //getting the mailbox handles from  environment
    this.mon2scb = mon2scb;
  endfunction
   
  //Compares the Actual result with the expected result
  task main;
    transaction trans;
    forever begin
      mon2scb.get(trans);
        if((trans.a+trans.b) == trans.c)//左边为预期结果,右边为monitor传送过来的结果
          $display("Result is as Expected");
        else
          $error("Wrong Result.\n\tExpeced: %0d Actual: %0d",(trans.a+trans.b),trans.c);
        no_transactions++;
      trans.display("[ Scoreboard ]");
    end
  endtask
   
endclass

(3)定义environment

`include "transaction.sv"
`include "generator.sv"
`include "driver.sv"
`include "monitor.sv"
`include "scoreboard.sv"
class environment;
   
  //generator and driver instance
  generator  gen;
  driver     driv;
  monitor    mon;
  scoreboard scb;
   
  //mailbox handle's 两个信箱
  mailbox gen2driv;
  mailbox mon2scb;
   
  //virtual interface
  virtual intf vif;
   
  //constructor
  function new(virtual intf vif);
    //get the interface from test
    this.vif = vif;
     
    //creating the mailbox (Same handle will be shared across generator and driver)
    gen2driv = new();
    mon2scb  = new();
     
    //creating generator and driver
    gen  = new(gen2driv);
    driv = new(vif,gen2driv);
    mon  = new(vif,mon2scb);
    scb  = new(mon2scb);
  endfunction
   
  //
  task pre_test();
    driv.reset();
  endtask
   
  task test();
    fork
      gen.main();
      driv.main();
      mon.main();
      scb.main();
    join_any
  endtask
   
  task post_test();
    wait(gen.ended.triggered);
    wait(gen.repeat_count == driv.no_transactions); //Optional
    wait(gen.repeat_count == scb.no_transactions);
  endtask 
   
  //run task
  task run;
    pre_test();
    test();
    post_test();
    $finish;
  endtask
   
endclass

其余组件不变,运行结果如下:

SV的testbench例子-加法器_第3张图片

SV的testbench例子-加法器_第4张图片

SV的testbench例子-加法器_第5张图片

SV的testbench例子-加法器_第6张图片

SV的testbench例子-加法器_第7张图片

你可能感兴趣的:(systemverilog)