zoukankan      html  css  js  c++  java
  • UVM Primer

    tinyalu_pkg.sv

    package tinyalu_pkg;
       import uvm_pkg::*;
    `include "uvm_macros.svh"
       
          typedef enum bit[2:0] {no_op  = 3'b000,
                              add_op = 3'b001, 
                              and_op = 3'b010,
                              xor_op = 3'b011,
                              mul_op = 3'b100,
                              rst_op = 3'b111} operation_t;
    
       virtual tinyalu_bfm bfm_g;
       
    
    `include "coverage.svh"
    `include  "random_tester.svh"
    `include "add_tester.svh"   
    `include "scoreboard.svh"
    `include "random_test.svh"
    `include "add_test.svh"
       
       
    endpackage : tinyalu_pkg

     tinyalu_bfm.sv

    interface tinyalu_bfm;
       import tinyalu_pkg::*;
    
       byte         unsigned        A;
       byte         unsigned        B;
       bit          clk;
       bit          reset_n;
       wire [2:0]   op;
       bit          start;
       wire         done;
       wire [15:0]  result;
       operation_t  op_set;
    
       assign op = op_set;
    
       task reset_alu();
          reset_n = 1'b0;
          @(negedge clk);
          @(negedge clk);
          reset_n = 1'b1;
          start = 1'b0;
       endtask : reset_alu
       
    
    
       task send_op(input byte iA, input byte iB, input operation_t iop, shortint alu_result);
          if (iop == rst_op) begin
             @(negedge clk);
             op_set = iop;
             @(posedge clk);
             reset_n = 1'b0;
             start = 1'b0;
             @(posedge clk);
             #1;
             reset_n = 1'b1;
          end else begin
             @(negedge clk);
             op_set = iop;
             A = iA;
             B = iB;
             start = 1'b1;
             if (iop == no_op) begin
                @(posedge clk);
                #1;
                start = 1'b0;           
             end else begin
                do
                  @(negedge clk);
                while (done == 0);
                alu_result = result;
                start = 1'b0;
             end
          end // else: !if(iop == rst_op)
          
       endtask : send_op
    
       initial begin
          clk = 0;
          forever begin
             #10;
             clk = ~clk;
          end
       end
    
    
    endinterface : tinyalu_bfm

    tb_classes

    coverage.svh(和面向对象的验证平台那一节一样)

    class coverage;
    
       virtual tinyalu_bfm bfm;
       
       
       byte         unsigned        A;
       byte         unsigned        B;
       operation_t  op_set;
    
       covergroup op_cov;
    
          coverpoint op_set {
             bins single_cycle[] = {[add_op : xor_op], rst_op,no_op};
             bins multi_cycle = {mul_op};
    
             bins opn_rst[] = ([add_op:no_op] => rst_op);
             bins rst_opn[] = (rst_op => [add_op:no_op]);
    
             bins sngl_mul[] = ([add_op:xor_op],no_op => mul_op);
             bins mul_sngl[] = (mul_op => [add_op:xor_op], no_op);
    
             bins twoops[] = ([add_op:no_op] [* 2]);
             bins manymult = (mul_op [* 3:5]);
    
    
          }
    
       endgroup
    
       covergroup zeros_or_ones_on_ops;
    
          all_ops : coverpoint op_set {
             ignore_bins null_ops = {rst_op, no_op};}
    
          a_leg: coverpoint A {
             bins zeros = {'h00};
             bins others= {['h01:'hFE]};
             bins ones  = {'hFF};
          }
    
          b_leg: coverpoint B {
             bins zeros = {'h00};
             bins others= {['h01:'hFE]};
             bins ones  = {'hFF};
          }
    
          op_00_FF:  cross a_leg, b_leg, all_ops {
             bins add_00 = binsof (all_ops) intersect {add_op} &&
                           (binsof (a_leg.zeros) || binsof (b_leg.zeros));
    
             bins add_FF = binsof (all_ops) intersect {add_op} &&
                           (binsof (a_leg.ones) || binsof (b_leg.ones));
    
             bins and_00 = binsof (all_ops) intersect {and_op} &&
                           (binsof (a_leg.zeros) || binsof (b_leg.zeros));
    
             bins and_FF = binsof (all_ops) intersect {and_op} &&
                           (binsof (a_leg.ones) || binsof (b_leg.ones));
    
             bins xor_00 = binsof (all_ops) intersect {xor_op} &&
                           (binsof (a_leg.zeros) || binsof (b_leg.zeros));
    
             bins xor_FF = binsof (all_ops) intersect {xor_op} &&
                           (binsof (a_leg.ones) || binsof (b_leg.ones));
    
             bins mul_00 = binsof (all_ops) intersect {mul_op} &&
                           (binsof (a_leg.zeros) || binsof (b_leg.zeros));
    
             bins mul_FF = binsof (all_ops) intersect {mul_op} &&
                           (binsof (a_leg.ones) || binsof (b_leg.ones));
    
             bins mul_max = binsof (all_ops) intersect {mul_op} &&
                            (binsof (a_leg.ones) && binsof (b_leg.ones));
    
             ignore_bins others_only =
                                      binsof(a_leg.others) && binsof(b_leg.others);
    
          }
    
    endgroup
       
       function new (virtual tinyalu_bfm b);
         op_cov = new();
         zeros_or_ones_on_ops = new();
         bfm = b;
       endfunction : new
    
       
    
       task execute();
          forever begin  : sampling_block
             @(negedge bfm.clk);
             A = bfm.A;
             B = bfm.B;
             op_set = bfm.op_set;
             op_cov.sample();
             zeros_or_ones_on_ops.sample();
          end : sampling_block
       endtask : execute
          
    endclass : coverage

    scoreboard.svh(和面向对象的验证平台那一节一样)

    class scoreboard;
          virtual tinyalu_bfm bfm;
    
       function new (virtual tinyalu_bfm b);
         bfm = b;
       endfunction : new
    
       task execute();
          shortint predicted_result;
          forever begin : self_checker
             @(posedge bfm.done) 
               case (bfm.op_set)
                 add_op: predicted_result = bfm.A + bfm.B;
                 and_op: predicted_result = bfm.A & bfm.B;
                 xor_op: predicted_result = bfm.A ^ bfm.B;
                 mul_op: predicted_result = bfm.A * bfm.B;
               endcase // case (op_set)
             
             if ((bfm.op_set != no_op) && (bfm.op_set != rst_op))
               if (predicted_result != bfm.result)
                 $error ("FAILED: A: %0h  B: %0h  op: %s result: %0h",
                         bfm.A, bfm.B, bfm.op_set.name(), bfm.result);
             
          end : self_checker
       endtask : execute
       
       
    endclass : scoreboard

    add_tester.svh

    class add_tester extends random_tester;
    
       function new (virtual tinyalu_bfm b);
          super.new(b);
       endfunction : new
    
       function operation_t get_op();
          bit [2:0] op_choice;
          return add_op;
       endfunction : get_op
    
    endclass : add_tester

    ramdom_tester.svh

    class random_tester;
    
       virtual tinyalu_bfm bfm;
    
       function new (virtual tinyalu_bfm b);
           bfm = b;
       endfunction : new
    
    
       protected function operation_t get_op();
          bit [2:0] op_choice;
          op_choice = $random;
          case (op_choice)
            3'b000 : return no_op;
            3'b001 : return add_op;
            3'b010 : return and_op;
            3'b011 : return xor_op;
            3'b100 : return mul_op;
            3'b101 : return no_op;
            3'b110 : return rst_op;
            3'b111 : return rst_op;
          endcase // case (op_choice)
       endfunction : get_op
    
       protected function byte get_data();
          bit [1:0] zero_ones;
          zero_ones = $random;
          if (zero_ones == 2'b00)
            return 8'h00;
          else if (zero_ones == 2'b11)
            return 8'hFF;
          else
            return $random;
       endfunction : get_data
       
       task execute();
          byte         unsigned        iA;
          byte         unsigned        iB;
          operation_t                  op_set;
          shortint     result;
          bfm.reset_alu();
          repeat (1000) begin : random_loop
             op_set = get_op();
             iA = get_data();
             iB = get_data();
             bfm.send_op(iA, iB, op_set, result);
          end : random_loop
          #500;
       endtask : execute
       
    endclass : random_tester

    定义并注册UVM test

    random_test.svh

    首先,random_test继承了uvm_test类。这个uvm_test是uvm_component类的子类,所以random_test也是uvm_component的子类
    从uvm_component继承的子类的new()方法要遵从限制条件

    • 构造器必须按顺序定义name和parent两个参数,name 在前,parent在后
    • name参数必须是字符串。parent参数必须是uvm_component类型
    • 构造器的第一个可执行必须调用super.new(name, parent),然后UVM才能正常工作
    class random_test extends uvm_test;
       `uvm_component_utils(random_test);   //我们在 class 表达式之后就使用了这个宏,这个宏在 Factory 里注册了random_test 类。现在 Factory 可以创建 random_test 对象了
    
     virtual tinyalu_bfm bfm;
       
       function new (string name, uvm_component parent);
          super.new(name,parent);
          if(!uvm_config_db #(virtual tinyalu_bfm)::get(null, "*","bfm", bfm))  //我们调用uvm_config_db的get()方法来取得bfm的句柄。注意我们传入了set()方法中用过的bfm字符串和BFM句柄变量。
            $fatal("Failed to get BFM");
       endfunction : new
    
    
       task run_phase(uvm_phase phase);
          random_tester    random_tester_h;
          coverage  coverage_h;
          scoreboard scoreboard_h;
    
          phase.raise_objection(this);  //提出objection
    
          random_tester_h    = new(bfm);
          coverage_h  = new(bfm);
          scoreboard_h = new(bfm);
          
          fork
             coverage_h.execute();
             scoreboard_h.execute();
          join_none
    
          random_tester_h.execute();
          phase.drop_objection(this);  //撤销objection
       endtask : run_phase
    
    endclass

    定义类还需要做最后一件事情,注册到 Factory。回顾我们的动物 Factory 的话你会发现我们是用 Hardcoding 编写出来的 , Factory 能处理的动物种类。要增加动物到 Factory 的话只能改Factory 的源码
    UVM开发者不希望我们改他们的代码,于是他们创建了用 uvm_component_utils 宏的机制来解决这个问题。
    我们在 class 表达式之后就使用了这个宏,这个宏在 Factory 里注册了random_test 类。现在 Factory 可以创建 random_test 对象了

    我们在顶层module调用 run_test(),UVM 用 Factory 创建并通过调用run_phase() 方法来启动 Test 对象。UVM 的 uvm_test 定义里有 run_phase() 方法,UVM 用run_phase() 来执行我们的 Test

    我们的Test 要工作的话必须重载 run_phase()方法。这个方法名必须是 run_phase() 然后必须有一个 uvm_phase 类型的phase参数。

    add_test.svh

    class add_test extends uvm_test;  //add_test继承了uvm_test类
       `uvm_component_utils(add_test);  //将add_test注册到Factory
    
       virtual tinyalu_bfm bfm;
       
       function new (string name, uvm_component parent);
          super.new(name,parent);
          if(!uvm_config_db #(virtual tinyalu_bfm)::get(null, "*","bfm", bfm))
            $fatal("Failed to get BFM");
       endfunction : new
       
    
       task run_phase(uvm_phase phase);
          add_tester   add_tester_h;
          coverage  coverage_h;
          scoreboard scoreboard_h;
    
          phase.raise_objection(this);
    
          add_tester_h = new(bfm);
          coverage_h   = new(bfm);
          scoreboard_h   = new(bfm);
          
          fork
             coverage_h.execute();
             scoreboard_h.execute();
          join_none
    
          add_tester_h.execute();
          phase.drop_objection(this);
       endtask : run_phase
    
    endclass

    用UVM启动仿真

    top.sv

    module top;
       import uvm_pkg::*;
    `include "uvm_macros.svh"
    
       import   tinyalu_pkg::*;
    `include "tinyalu_macros.svh"
    
       tinyalu_bfm       bfm();
       tinyalu DUT (.A(bfm.A), .B(bfm.B), .op(bfm.op), 
                    .clk(bfm.clk), .reset_n(bfm.reset_n), 
                    .start(bfm.start), .done(bfm.done), .result(bfm.result));
    
    initial begin
      uvm_config_db #(virtual tinyalu_bfm)::set(null, "*", "bfm", bfm);
      run_test();  //run_test()方法从命令行得到一个类名字符串,然后用UVM Factory来创建这个类的测试对象,然后这个测试对象开始运行测试
    end
    
    endmodule : top

    基础知识

    uvm_config_db

    第一部分:uvm_config_db的使用方法

    uvm_config_db作用是把验证环境的一些资源配置为类似全局变量一样,使得它对于整个验证环境来说是可见的。最常见的是在验证环境顶层通过它对一些组件进行配置,组件可以在建立的阶段读取这些配置实现组件不同工作模式的切换,下面是使用uvm_config_db的语法:

    uvm_config_db#(T)::set(uvm_component cntxt, string inst_name, string field_name,      T value);

    uvm_config_db#(T)::get(uvm_component cntxt, string inst_name, string field_name, ref T value);

    例子:

    uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input_if)

    uvm_config_db#(virtual my_if)::get(this,                     "", "vif",        vif)

    在以上两个函数set和get是使用它时要调用的函数,set表示把要处理的资源放进全局可见的数据库,get表示从全局可见的数据库输出需要的资源,使用set和get函数时有五个参数需要制定,第一个是uvm_config_db类的参数#(T),T表示要set或get的资源的类型,它可以是虚拟接口,sequencer等等,第二个cntxt和第三个参数inst_name一起定义了uvm_config_db中set或get函数的作用范围。第四个参数决定了是对作用范围中的哪个对象或变量进行操作,第五个参数value会存储当前操作对象的句柄或者操作变量的值。需要注意的是,当制定第二个参数是一个uvm组件时,uvm会用它的全局名字取替换它,而全局名字会通过uvm的get_full_name来获取。

    第二部分:uvm_config_db的作用对象

    在uvm验证环境中并不是所有的资源我们都会用uvm_config_db去配置,这样会让我们的验证环境变得不可维护,下面是一些典型的在验证环境中使用uvm_config_db去配置的资源:

    • 虚拟接口,虚拟接口提供了一个访问真实虚接口的入口,我们会把虚拟接口的句柄放到全局的数据库中,uvm中的这些组件会通过get函数拿到虚拟接口的句柄对接口数据进行操作。

    • 配置数据,负责配置环境的类中包含许多可以控制验证环境的变量,它会改变这些变量,并且通过set函数把它放到我们全局的数据库中,其他的组件通过get函数来取到这些变量的值,再根据这些值去改变工作模式

    • sequencers,在uvm中sequencers负责把我们写的sequence进行排队送到driver上去,所以sequence需要有对sequencer的访问入口,我们同样通过uvm_config_db的方式来把sequencer的句柄传给sequence

    run_test()

    run_test() task从仿真器的命令行+UVM_TESTNAME里读出参数,然后对这个类名例化对象

  • 相关阅读:
    学习tornado:介绍
    【Unity Shaders】Using Textures for Effects介绍
    高性能C++网络库libtnet实现:http
    理解WebKit和Chromium: 硬件加速之RenderLayer树到合成树
    【Unity Shaders】Diffuse Shading——使用2D ramp texture来创建一个假的BRDF(双向反射分布函数)
    【Unity Shaders】Diffuse Shading——漫反射光照改善技巧
    ③ 设计模式的艺术-16.访问者(Visitor)模式艺术
    ② 设计模式的艺术-15.观察者(Observer)模式
    ① 设计模式的艺术-14.职责链(Chain of Responsibility)模式
    ⑦ 设计模式的艺术-13.代理(Proxy)模式
  • 原文地址:https://www.cnblogs.com/yiyedada/p/12371086.html
Copyright © 2011-2022 走看看