zoukankan      html  css  js  c++  java
  • 基于简单DUT的UVM验证平台的搭建(一)

    最近一个月在实习公司做回归测试,对公司的UVM平台用的比较熟练,就想着自己做一个DUT,然后搭建一个UVM验证平台。

    首先,DUT是一个简单的32位的加法器,代码如下:alu.v

    module adder32_cla(
      input         clk     ,
      input         rst     ,
      input         enable  ,
      input  [31:0] a       ,
      input  [31:0] b       ,
      input         cin     ,
      output [31:0] sum_r   ,
      output        cout_r  
    );
    
      reg [31:0] sum_r  = 32'h00000000 ;
      reg        cout_r = 1'h0  ;
    
    always @(posedge clk or negedge rst) 
      begin
        if (!rst)
          begin
            sum_r  = 32'h00000000  ;
            cout_r =  1'h0          ;
          end
        else if(enable)
          begin 
            {cout_r,sum_r} <= a + b + cin;              
          end
        else
          begin               
            sum_r  <= sum_r  ;
            cout_r <= cout_r ;
          end                                   
      end    
      
    endmodule
    View Code

    UVM验证组件:

    1、top.sv

    `timescale 1ns/1ns
    
    `include "pkg.sv"
    `include "alu.v"
    
    module top();
    
    import uvm_pkg::*;
    `include "uvm_macros.svh"    
    my_if my_my_if();
         
    adder32_cla inst1 
    (
      .clk    (my_my_if.clk    ),
      .rst    (my_my_if.rst    ),
      .enable (my_my_if.enable ),
      .a      (my_my_if.a      ),
      .b      (my_my_if.b      ),
      .cin    (my_my_if.cin    ),
      .sum_r  (my_my_if.sum_r  ),
      .cout_r (my_my_if.cout_r )
    );
    initial  
      begin
             my_my_if.cin    = 1'b0         ;
             my_my_if.a      = 32'h00000000 ;
             my_my_if.b      = 32'h00000000 ;
             my_my_if.enable = 1'b1         ;
             my_my_if.rst    = 1'b0         ;
        #100 my_my_if.rst    = 1'b1         ;
      end
    initial
      begin
             my_my_if.clk   <= 1'b0        ;
            
        #500 my_my_if.clk   <= 1'b0        ;
        #500 my_my_if.clk   <= 1'b1        ;
    forever 
        #50  my_my_if.clk   = ~my_my_if.clk;
    end
    initial
      begin
        uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.env.i_agt.drv","my_if",my_my_if);
        uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.env.o_agt.mon","my_if",my_my_if);                         
        run_test();
      end    
    
    endmodule 
    View Code

    top.sv主要的作用是实例化DUT,和输入输出的interface,并且定义了时钟频率,传递了接口一连接TB,和run_test();用于启动UVM phase。

    2、my_test.sv

    `ifndef MY_TEST__SV
    `define MY_TEST__SV
    import uvm_pkg::*;
    `include "uvm_macros.svh"    
    `include "my_env.sv"
    class my_test extends uvm_test;
      my_env env;
      extern function new(string name="my_test",uvm_component parent=null);
      extern virtual function void build_phase(uvm_phase phase);
    `uvm_component_utils(my_test)
    endclass
    
    function my_test::new (string name="my_test",uvm_component parent=null);
      super.new(name,parent);
    endfunction
    function void my_test::build_phase(uvm_phase phase);
       super.build_phase(phase);
       env = my_env::type_id::create("env");
    endfunction
    
    `endif
    View Code

    my_test.sv派生于uvm_test,属于基类。在项目中,主要采用基类构建框架,继承类实现具体动作的方式来增加灵活性。

    3、my_env.sv

    `ifndef MY_ENV__SV
    `define MY_ENV__SV
    import uvm_pkg::*;
    `include "uvm_macros.svh"    
    `include "my_agent.sv"
    `include "my_scoreboard.sv"
    `include "my_model.sv"
    
    class my_env extends uvm_env;
      my_agent       i_agt ;              //my_driver functional
      my_agent       o_agt ;              //my_monitor functional
      my_model       mdl   ;
      my_scoreboard  scb   ;
      
      uvm_tlm_analysis_fifo  #(my_transaction) agt_scb_fifo;   //my_monitor
      uvm_tlm_analysis_fifo  #(my_transaction) agt_mdl_fifo;   //my_driver
      uvm_tlm_analysis_fifo  #(my_transaction) mdl_scb_fifo;   //my_model
      extern function new (string name,uvm_component parent);
      extern virtual function void build_phase(uvm_phase phase);
      extern virtual function void connect_phase(uvm_phase phase);
    `uvm_component_utils(my_env);
    endclass
    
    function my_env::new (string name,uvm_component parent);
    super.new(name,parent);
    endfunction
    
    function void my_env::build_phase(uvm_phase phase);
    super.build_phase(phase);
      i_agt = my_agent::type_id::create("i_agt",this);
      o_agt = my_agent::type_id::create("o_agt",this);
      i_agt.is_active = UVM_ACTIVE;
      o_agt.is_active = UVM_PASSIVE;
      mdl = my_model::type_id::create("mdl",this);
      scb = my_scoreboard::type_id::create("scb",this);
      agt_scb_fifo = new("agt_scb_fifo",this);
      agt_mdl_fifo = new("agt_mdl_fifo",this);
      mdl_scb_fifo = new(" mdl_scb_fifo",this);
    endfunction
    
    function void my_env::connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      i_agt.ap.connect(agt_mdl_fifo.analysis_export);
      mdl.port.connect(agt_mdl_fifo.blocking_get_export);
      mdl.ap.connect(mdl_scb_fifo.analysis_export);
      scb.exp_port.connect(mdl_scb_fifo.blocking_get_export);
      o_agt.ap.connect(agt_scb_fifo.analysis_export);
      scb.act_port.connect(agt_scb_fifo.blocking_get_export);
    
    endfunction
    `endif
    View Code

    my_env.sv的主要是agent scoreboard reference_model模块的实例化,和三个tlm_analysis_fifo,然后build_phase中type_id::create()模块,配置i/oagent,scoreboard model和new三个FIFO,之后connect_phase中,connect analysis_export和blocking_get_export。

    4、my_transaction.sv

    要传送的transaction

    import uvm_pkg::*;
    `include "uvm_macros.svh"    
    `ifndef MY_TRANSACTION__SV
    `define MY_TRANSACTION__SV
    class my_transaction extends uvm_sequence_item;
        rand bit        cin;
        rand bit [31:0] a,b;
        bit      [31:0] sum_r;
        bit             cout_r;
        
    extern function new (string name="my_transaction");
        `uvm_object_utils_begin(my_transaction)
          `uvm_field_int(cin,UVM_ALL_ON)
          `uvm_field_int(a,UVM_ALL_ON)
          `uvm_field_int(b,UVM_ALL_ON)
          `uvm_field_int(sum_r,UVM_ALL_ON)
          `uvm_field_int(cout_r,UVM_ALL_ON)
        `uvm_object_utils_end
    
    endclass
    
    function my_transaction::new (string name="my_transaction");
      super.new(name);
    endfunction
    `endif
    View Code

    主要包括各种内容以及uvm_object_utils,uvm_field_int的注册,参数约束和随机化。

    5、my_sequencer.sv

    发送transaction的sequencer

    `ifndef MY_SEQUENCER__SV
    `define MY_SEQUENCER__SV
    import uvm_pkg::*;
    `include "uvm_macros.svh"    
    `include "my_transaction.sv"
    `include "my_sequence.sv"
    
    class my_sequencer extends uvm_sequencer #(my_transaction);
      extern function new (string name,uvm_component parent);
      extern virtual function void build_phase(uvm_phase phase); 
    `uvm_component_utils(my_sequencer)
    endclass
    
    function my_sequencer::new (string name,uvm_component parent);
      super.new(name,parent);
    endfunction
    
    function void my_sequencer::build_phase(uvm_phase phase);
      super.build_phase(phase);
    endfunction
    
    `endif
    View Code

    负责把transaction类型的数据传送给sequence,里面内容很简单就一个注册和new函数。

    6、my_sequence.sv

    `ifndef MY_SEQUENCE__SV
    `define MY_SEQUENCE__SV
    import uvm_pkg::*;
    `include "uvm_macros.svh"    
    `include "my_transaction.sv"
    class  my_sequence extends uvm_sequence #(my_transaction); 
      my_transaction m_trans;
      `uvm_object_utils(my_sequence)
       extern function new(string name="my_sequence");
         virtual task body();
           if(starting_phase!=null)
             starting_phase.raise_objection(this);
             repeat(10)
             begin
               `uvm_do(m_trans)
             end
             #10000;
           if(starting_phase!=null)
             starting_phase.drop_objection(this);
         endtask
    endclass
    
    function  my_sequence::new(string name="my_sequence");
      super.new(name);
    endfunction
    
    `endif
    View Code

    sequence,用于产生激励,里面的objection机制用来控制验证平台的打开与关闭,需要在drop_objection之前先raise_objection。task_body中uvm_do(my_trans),在之前和之后要starting_phase.raise_objection(this)和starting_phase.drop_objection(this);

    7、my_driver.sv

    `ifndef MY_DRIVER__SV
    `define MY_DRIVER__SV
    import uvm_pkg::*;
    `include "uvm_macros.svh"    
    `include "my_transaction.sv"
    
    class my_driver extends uvm_driver #(my_transaction);     
          
         virtual my_if vif;//to DUT
    uvm_analysis_port #(my_transaction) ap;//the data is to reference model
    
    `uvm_component_utils(my_driver)
    
    
    extern function new (string name,uvm_component parent);
    extern virtual function void build_phase(uvm_phase phase);
    
    extern virtual task  main_phase(uvm_phase phase);
    extern virtual task  drive_one_pkt(my_transaction req);
    endclass
    
    function my_driver::new (string name,uvm_component parent);
    super.new(name,parent);
    endfunction
      
    function  void my_driver::build_phase(uvm_phase phase);
    super.build_phase(phase);
    if(!uvm_config_db#(virtual my_if)::get(this,"","my_if",vif))
    `uvm_fatal("my_driver","Error in Getting interface");
    ap=new("ap",this);
    
    endfunction
    
    task my_driver::main_phase(uvm_phase phase);
     my_transaction req;
    super.main_phase(phase);
      
        while(1)
           begin 
           seq_item_port.get_next_item(req);
                         
            drive_one_pkt(req);
             ap.write(req);
          seq_item_port.item_done();
        
        end
    endtask
    
    task my_driver::drive_one_pkt(my_transaction req);
    @vif.drv_cb;
        @vif.drv_cb
        begin
            vif.drv_cb.enable<=1'b1;
            vif.drv_cb.cin<=req.cin;
            vif.drv_cb.a<=req.a;
            vif.drv_cb.b<=req.b;
        end
            @vif.drv_cb;
        @vif.drv_cb vif.drv_cb.enable<=1'b0;
    
    endtask
    
    `endif
    View Code

    首先是virtual inf和build_phase中接受接口,否则fatal,然后main_phase中while(1)循环的get_next_item(req),调用发送函数发送req,返回item_done()。

    8、my_monitor.sv

    `ifndef MY_MONITOR__SV
    `define MY_MONITOR__SV
    import uvm_pkg::*;
    `include "uvm_macros.svh"
    `include "my_transaction.sv"    
    class my_monitor extends uvm_monitor;
      virtual my_if vif;
      uvm_analysis_port #(my_transaction) ap; //  to scoreboard
      extern function new (string name,uvm_component parent);
      extern virtual function void build_phase(uvm_phase phase);
      extern virtual task  main_phase(uvm_phase phase);
      extern virtual task  receive_one_pkt(ref my_transaction get_pkt);
    `uvm_component_utils(my_monitor)
    endclass
    
    function my_monitor::new (string name,uvm_component parent);
      super.new(name,parent);
    endfunction
    
    function  void my_monitor::build_phase(uvm_phase phase);
      super.build_phase(phase);
      if(!uvm_config_db#(virtual my_if)::get(this,"","my_if",vif))
      `uvm_fatal("my_monitor","Error in Getting interface");
      ap=new("ap",this);
    endfunction
    
    task my_monitor::main_phase(uvm_phase phase);
      my_transaction tr;
      super.main_phase(phase);
      while(1) begin
        tr=new();
        receive_one_pkt(tr);
        ap.write(tr);
      end
    endtask
    
    task my_monitor::receive_one_pkt(ref my_transaction get_pkt);
      @(negedge vif.drv_cb.enable);
        get_pkt.cout_r=vif.mon_cb.cout_r;
        get_pkt.sum_r=vif.mon_cb.sum_r;
    endtask
               
    `endif
    View Code

    主要是virtual inf和analysis_port,在build_phase中接受接口,mian_phase中while(1)的调用接受函数和和ap.write(tr)。

    9、my_agent.sv

    `ifndef MY_AGENT__SV
    `define MY_AGENT__SV
    import uvm_pkg::*;
    `include "uvm_macros.svh"    
    `include "my_transaction.sv"
    `include "my_sequence.sv"
    `include "my_sequencer.sv"
    `include "my_driver.sv"
    `include "my_monitor.sv"
    
    class my_agent extends uvm_agent;
      my_sequencer sqr;
      my_driver drv;
      my_monitor mon;
      extern function new (string name,uvm_component parent);
      extern virtual function void build_phase(uvm_phase phase);
      extern virtual function void connect_phase(uvm_phase phase);
    uvm_analysis_port #(my_transaction) ap;
    `uvm_component_utils_begin(my_agent)
      `uvm_field_object(sqr,UVM_ALL_ON)
      `uvm_field_object(drv,UVM_ALL_ON)
      `uvm_field_object(mon,UVM_ALL_ON)
    `uvm_component_utils_end
    endclass
    
    function my_agent::new (string name,uvm_component parent);
      super.new(name,parent);
    endfunction
    
    function void my_agent::build_phase(uvm_phase phase);
      super.build_phase(phase);
      if(is_active==UVM_ACTIVE )
        begin 
          sqr=my_sequencer::type_id::create("sqr",this);
          drv=my_driver::type_id::create("drv",this);
        end
        else begin
          mon=my_monitor::type_id::create("mon",this);
        end
    endfunction          
     
    function void my_agent::connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      
    if(is_active==UVM_ACTIVE )
        begin 
          drv.seq_item_port.connect(sqr.seq_item_export);
          this.ap=drv.ap;
        end
        else begin
          this.ap=mon.ap;
        end
    endfunction
    
    `endif
    View Code

    含有driver和monitor,在build_phase中如果UVM_ACTIVE,就例化drv和sqr,但都例化mon,在build_phase中如果UVM_ACTIVE就链接drv和sqr的接口。

    10、interface.sv

    `ifndef MY_INTERFACE__SV
    `define MY_INTERFACE__SV
    
    interface my_if;
        logic             clk,rst   ;
        logic             cin        ; 
        logic [31:0]   a          ; 
        logic [31:0]   b          ;
        logic             enable   ; 
        wire              cout_r   ;
        wire  [31:0]   sum_r   ;
          
    clocking drv_cb @(posedge clk);
        output enable,cin,a,b;
    endclocking
    
    clocking mon_cb @(posedge clk);
        input cout_r,sum_r;      
    endclocking
          
    endinterface
    `endif
    View Code

    interface里面就是接口,用于连接DUT和driver和monitor。

    12、my_model.sv

    就是参考模型(reference_model)

    `ifndef MY_MODEL__SV
    `define MY_MODEL__SV
    import uvm_pkg::*;
    `include "uvm_macros.svh"    
    `include "my_transaction.sv"
    class my_model extends uvm_component;
      uvm_blocking_get_port #(my_transaction)  port;   //from my_driver
      uvm_analysis_port  #(my_transaction)   ap;       //to scoreboard
      extern function new (string name,uvm_component parent);
      extern virtual function void build_phase(uvm_phase phase);
      extern virtual task  main_phase(uvm_phase phase);
      extern virtual task one_pkt(ref my_transaction pkt,ref my_transaction pkt2);
    `uvm_component_utils(my_model)
    endclass
    
    function my_model::new (string name,uvm_component parent);
      super.new(name,parent);
    endfunction
    
    function void my_model::build_phase(uvm_phase phase);
      super.build_phase(phase);
      port=new("port",this);
      ap=new("ap",this);
    endfunction
    
    task my_model::main_phase(uvm_phase phase);
      my_transaction tr,tr2;
      super.main_phase(phase);
      while(1) begin
        tr2=new();
        port.get(tr);
        one_pkt(tr2,tr);
        ap.write(tr2);
      end
    endtask
    
    task my_model::one_pkt(ref my_transaction pkt,ref my_transaction pkt2);
      bit [32:0] sum_total;
      begin
        sum_total=pkt2.a+pkt2.b+pkt2.cin;
        pkt.sum_r=sum_total[31:0];
        pkt.cout_r=sum_total[32];
      end
    endtask
    `endif
    View Code

    需要进的blocking_get_port和出的analysis_port两个,在build_phase中连接他们,在main_phase中则while1的,如果get_port.get()收到,就ana_port.write()这个tr。

    13、my_scoreboard.sv

    `ifndef MY_SCOREBOARD__SV
    `define MY_SCOREBOARD__SV
    import uvm_pkg::*;
    `include "uvm_macros.svh"    
    `include "my_transaction.sv"
    class my_scoreboard extends uvm_scoreboard;
    int pre_number=0;
    my_transaction expect_queue[$];
    uvm_blocking_get_port #(my_transaction) exp_port;//from my_reference
    uvm_blocking_get_port #(my_transaction) act_port;//from my_monitor
    `uvm_component_utils(my_scoreboard)
    extern function new (string name,uvm_component parent);
    extern virtual function void build_phase(uvm_phase phase);
    extern virtual task  main_phase(uvm_phase phase);
    endclass
    
    function my_scoreboard::new (string name,uvm_component parent);
      super.new(name,parent);
    endfunction
    
    function void my_scoreboard::build_phase(uvm_phase phase);
      super.build_phase(phase);
      exp_port=new("exp_port",this);
      act_port=new("act_port",this);
    endfunction
    task my_scoreboard::main_phase(uvm_phase phase);
      my_transaction get_expect,get_actual,tmp_tran;
      bit result;
      super.main_phase(phase);
    fork
      while (1)
      begin
          exp_port.get(get_expect);
          expect_queue.push_back(get_expect);
      end
      while (1) 
      begin                   
        act_port.get(get_actual);
        if(expect_queue.size>0)begin
          tmp_tran=expect_queue.pop_front();
          result=get_actual.compare(tmp_tran);
          if(result)  begin
            pre_number=pre_number+1;
            $display("compare SUCCESSFULLy:%0d",pre_number);
          end
          else begin
            $display("compare FAILED");
            $display("the expect pkt is");  
            tmp_tran.print();
            $display("the actual pkt is");  
            get_actual.print();
          end
        end
        else  begin
          $display("ERROR::Received from DUT,while Expect Queue is empty");
          get_actual.print();
         end
      end
    join
    
    endtask                
                            
    `endif
    View Code

    是两个get_port,在build_phase中new它们,首先对于exp端,while(1)的,只要get一个tr,就要在tr_queue中push进去一个,同时对于act端,while(1)的。只要get一个tr,就要从tr_queue中pop一个出来,进行比较。这个两个是fork join的,互不影响。

    14、my_case0.sv

    `ifndef MY_CASE0__SV
    `define MY_CASE0__SV
    import uvm_pkg::*;
    `include "uvm_macros.svh"    
    `include "my_test.sv"
      
    class my_case0 extends  my_test;
      `uvm_component_utils(my_case0) 
      extern function new(string name="my_case0",uvm_component parent=null);
      extern virtual function void build_phase(uvm_phase phase);  
    endclass
    
    function my_case0::new (string name="my_case0",uvm_component parent=null);
      super.new(name,parent);
    endfunction
    
    function void my_case0::build_phase(uvm_phase phase);
      super.build_phase(phase);
      uvm_config_db#(uvm_object_wrapper)::set(this,"env.i_agt.sqr.main_phase","default_sequence",my_sequence::type_id::get());
    endfunction
    
    `endif
    View Code

    my_case0.sv扩展于my_test.sv,在my_case0.sv的build_phase中传递sequence。

    至此,本UVM验证平台的各个组件就已经完成了,接下来有时间把Makefile脚本完善好上传,并在装有vcs和Verdi的虚拟机去运行,查看波形和查看覆盖率报告。

    目前还差的东西是在top.sv里面加上生成波形的语句。

    附录:pkt.v   导入整个验证平台

    `ifndef MY_INTERFACE__SV
    `define MY_INTERFACE__SV

    `include "interface.sv"
    `include "my_env.sv"
    `include "my_test.sv"
    `include "my_case0.sv"

    `endif

    欢迎讨论:QQ:447574829

  • 相关阅读:
    extern "C" 分析 转
    Python标准库3.4.3webbrowser21.1
    rxtx java lib /var/lock issue
    list installed package & remove installed package & find package from repository Archlinux
    eclipse initialize
    add repo for CentOS
    RPMforge for CentOS
    Install Core Development Tools
    [Qtcreator] CMake + Multiple Build configuration
    list installed package & remove installed package & find package from repository
  • 原文地址:https://www.cnblogs.com/zhangxianhe/p/11279583.html
Copyright © 2011-2022 走看看