精华内容
下载资源
问答
  • 本人学习过程中整理的各项UVM相关资料,包括:UVM primer, UVM实战及笔记,UVM Cookbook,SV_UVM_Debug,UVM初级开发指南,UVM快速学习教程等。
  • UVM 在 VCS 中的详细教程,但是对于初学者就是一 道屏障,我探索了几天,下文将一步一步的举例子说明 UVM+VCS+Verdi 的 liunx 平台搭建过 程(假设你已经安装好 VCS 和 verdi )、和 Questa sim+UVM 的 window 平台...
  • UVM实战教程

    2017-07-06 10:54:15
    UVM实战教程
  • UVM经典教程.7z

    2020-07-13 20:17:00
    经典UVM教程,很好的学习资料,高清,有标签,内涵经典代码,非常适合初学者及多年工作经验的人,很好的资料
  • UVM极简教程

    万次阅读 多人点赞 2019-06-21 17:05:38
    UVM(universal verification method)作为通用验证方法学,解决了什么问题? 验证平台的规范化 验证环境包括激励输入和输出数据的比对。UVM将激励、比对、reference model分别实现为不同的类。在子IP验证环境中使用...

    UVM(universal verification method)作为通用验证方法学,解决了什么问题?

    • 验证平台的规范化

    验证环境包括激励输入和输出数据的比对。UVM将激励、比对、reference model分别实现为不同的类。在子IP验证环境中使用的组件,可以在IP验证环境中复用。

    • 对System Verilog的封装

    System Verilog引入了类似C++,java的语法,可以实现为类。类给我们带来的好处就是封装、继承、多态。

    https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1561024690522&di=38ecd78c14567e7c2e4c9ad0a6225b8f&imgtype=0&src=http%3A%2F%2Fimage.mamicode.com%2Finfo%2F201605%2F20180110193019695430.png

    展开说一下,

    • UVM环境

    UVM的整体环境封装在uvm_env中,这可以看作是与真正实例化module的tb同级的环境。

    • UVM Sequence

    UVM Sequence是负责发送transaction包的激励来源。

    比如axi总线的一个transaction,在真正的verilog中,是按照clock的一拍一拍的数据和地址,在UVM中,可以定义一个uvm_sequence_item,将这一整包的数据和地址封装为成员为int address, int data[]的类;sequence就是负责产生各种各样的transaction。

    注意:

    1)并不是验证环境中只有一个sequence,而是可能会存在多个sequence。

    比如存在以下三种sequence:

            专门发送长包的sequence

            专门发送短包的sequence

            发送存在错包的sequence

    将这三种sequence包装为一个virtual sequence,即可复用其他的验证环境,发送随机间隔的长包、短包、错包sequence。

    2)sequence和transaction都是uvm_sequencet_item类型,都是object,而非component。

    • UVM Sequencer

    UVM Sequencer主要负责将Sequence产生的transaction,转发给driver。

    在case中可以通过设置*sequence.start(*sequencer)指明特定sequence是运行在哪个sequencer上的。

    在sequence中包括m_sequencer成员,在调用sequence的start方法时,会调用set_item_context方法,而这个方法又进一步调用了set_sequencer方法,将uvm_sequence_item中的m_sequencer成员指定了sequencer。

     

    通过m_sequencer这个方式,sequence可以明确当前运行在哪个sequencer上,获取sequencer的信息(需要将m_sequencer通过cast转换为扩展类sequencer类型)。

    • UVM Driver

    UVM Driver的作用:

    1. 将transaction类分解为具体的时序信号,传递到interface接口上;
    2. 完成与sequencer的握手,即来自sequencer的request,driver将会返回response;

     

    张强的《UVM实战》上打了比分:sequence好比是弹夹,driver好比是手枪,这个比喻

    比较形象,sequence里面装满了一颗一颗的子弹,子弹的上膛,发射都是driver作用的,driver如果通用性实现的比较好,是可以发送不同类型的子弹的,这也体现了验证平台的复用。

     

           需要注意的是,driver一般可以实现成forever的,持续的发送transaction,直到结束。这也可以理解成一个只要有子弹,就一直发射的手枪。

    • UVM Reference Model

    UVM Reference Model一般是用C/C++完成的,用于与DUT进行比对的算法,在平台中的作用:

    1. 接收设计DUT的输入transaction,根据算法计算,产生期望比对transaction
    2. 将transaction传出给scoreboard
    • UVM Monitor

    UVM Monitor用于检测interface接口上的数据,其作用:

    1. 将输入DUT interface接口上的数据接收,打包,传递给UVM Reference Model,作为其输入;
    2. 将输出DUT interface接口上的数据接收,打包,传递给UVM Scoreboard,作为其输入
    3. 可以进行protocol的检查;是否interface不满足protocol协议。
    • UVM Scoreboard

    UVM Scoreboard用于将uvm reference model的期望输出与monitor接收到的dut实际输出进行比对。

    • UVM Agent

    UVM Agent负责封装sequencer、driver、monitor;之所以封装这三者,存在以下原因:

    1. 由于在DUT的输入端存在sequencer、driver、monitor;DUT输出端存在monitor;封装为agent后,在env环境中封装两个agent,其中一个为active的agent,其成员为sequencer、driver、monitor;非active的agent仅包括monitor。
    2. 考虑nic的情况,在验证nic时,如果存在多路输入输出,每一路都需要driver、sequencer,如果实例化多套driver,sequencer,monior那么会十分繁琐;在封装后,只需要实例化多套agent即可。

    • Interface

    UVM 环境是基于SystemVerilog类的,设计代码在tb中是使用verilog的方式例化的,如何将设计代码的接口与driver连接呢?通过interface。

    在tb_top中例化interface,与dut的接口连接;

    在driver 类中例化virtual interface;使用uvm_config_db的方式即可将driver中的接口与dut的接口连接。

    此外,介绍一些UVM中比较关键的概念,UVM借鉴了设计模式中的工厂模式,代理模式,单例模式等,使得UVM更加便于使用。本质上这些模式也是基于继承、多态;通过指针或者引用的形式,使基类指针可以指向各式各样的继承类。

    1.Factory 工厂

    在声明继承类型,如uvm_driver的子类my_driver,可以通过uvm_component_utils的形式,来对通过工厂进行注册。在创建类时,也是通过my_driver::type_id::create()进行创建,type_id本质上是uvm_object_registry的别名,通过registry,在单例factory中,对该类进行了创建。

    利用工厂的一个关键好处在于可以使用重载。比如,目前使用的是my_driver,如果想批量的替换成your_driver,可以调用set_override_by_type的方法,将my_driver直接重载掉。

    2.uvm_config_db方法

    uvm_config_db方法可以实现在不同的component之间传递信息,即set、get。

    即比如之前介绍的sequence,如果负责产生axi_transaction,而具体是怎么类型的transaction,burst的大小,地址的大小,是可以通过配置sequence而改变的。那么如何配置sequence呢?testcase是class,sequence也是class,这些component通过什么进行信息交互呢?

    就是uvm_config_db,可以在使用uvm_config_db#(**)::set(*,*,*)来配置,那么uvm_config_db是如何具体工作的呢?

    uvm_config_db继承自uvm_resource_db,在其内部存在静态的uvm_pool类型的关联数组m_rsc。m_rsc的索引为component,值为对应component需要set的uvm_pool。

    uvm_pool的索引为loopup = {inst_name,”_M_UVM_”,filed_name},其值为uvm_resource类型的r。

    如果是set:

    通过r.write(value,cntxt)对r赋值,通过r.set_overide(),向全局静态的uvm_resource_pool写入这个r。

    如果是get:

    通过uvm_resource_pool::get()获得uvm_resouce_pool类型的rp,通过rp.lookup_regex_names获得uvm_reource_types::rsrc_q_t类型的rq,在rq中存放包括优先级的resource 队列,获取优先级最高的uvm_resource,即为返回值。

     

    说了好多,总之,uvm_config_db的set,get是通过静态的uvm_resource_pool进行信息传递的。

    uvm_resource_pool理解可以参考源码:./src/base/uvm_resource.svh

     

    3.::type_id::create::

    UVM约定俗成的要求在class中使用`uvm_component_utils(****_driver)进行注册,在创建时使用my_driver = ***_driver::type_id::create(“my_driver”, );进行创建。

    这背后的机制是什么呢?

    `uvm_component_utils(T)使用宏定义,其进一步调用了`m_uvm_component_regsitery_internal(T,T),在这个宏中,定义了uvm_component_registry #(T,`“S`”)类型的type_id,在uvm_component_registry中实现了create()函数。Create()函数是创建的重头戏:

               Create()函数首先调用uvm_factory::get()方法,得到全局的uvm_factory类型的f,通过obj=f.create_component_by_type(get(),contxt,name,parent())创建这个obj。

                                                                

     

    4.Phase机制

    Phase机制理解起来比较简单,如果把验证平台中的不同的component理解成一个班级里上课的同学,那么虽然不同的同学每节课写作业有快有慢,但是他们都是整齐划一的按照上课的时间,下课的时间,共享一个作息表。

                                                                                   

                                                                                                                               

                                                                                                                              ————欢迎关注我的公众号《处理器与AI芯片》 

    展开全文
  • 我的第一批UVM教程(#1至#6)已于三年前发布。 从那以后,UVM(以及我的知识)已经发展,我一直想更新我的文章和代码。 但这并不容易,因为我的文章和代码紧密结合,读者的一些评论甚至提到了特定的代码行。UVM ...

    我的第一批UVM教程(#1至#6)已于三年前发布。 从那以后,UVM(以及我的知识)已经发展,我一直想更新我的文章和代码。 但这并不容易,因为我的文章和代码紧密结合,读者的一些评论甚至提到了特定的代码行。

    UVM 1.2

    大约两个月前,Accellera发布了新的UVM 1.2,它激励我更新我的 jelly bean taster。 我认为是时候进行大修了。 有趣的是,我的代码几乎不需要修改。 我做的唯一改变是利用了uvm_sequence_base类的新自动phase objection。 set_automatic_phase_objection函数在执行序列之前自动提出objection,并在结束序列(第25行)后丢弃objection。

    class jelly_bean_test extends uvm_test;
      `uvm_component_utils( jelly_bean_test )
     
      jelly_bean_env jb_env;
     
      function new( string name, uvm_component parent );
        super.new( name, parent );
      endfunction: new
     
      function void build_phase( uvm_phase phase );
        super.build_phase( phase );
     
        jelly_bean_transaction::type_id::set_type_override( 
          sugar_free_jelly_bean_transaction::get_type() );
        jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ), .parent( this ) );
      endfunction: build_phase
     
      task main_phase( uvm_phase phase );
        gift_boxed_jelly_bean_sequence jb_seq;
     
        jb_seq = gift_boxed_jelly_bean_sequence::type_id::create( .name( "jb_seq" ) );
        assert( jb_seq.randomize() );
        `uvm_info( "jb_seq", jb_seq.convert2string(), UVM_NONE )
        jb_seq.set_starting_phase( phase );
        jb_seq.set_automatic_phase_objection( .value( 1 ) );
        jb_seq.start( jb_env.jb_agent.jb_seqr );
      endtask: main_phase
    endclass: jelly_bean_test

    借此机会,我还简化了记分板,因为环境中使用的分层记分板总是让新手感到困惑。以下是验证组件的简化层次结构。请注意,jelly_bean_sb_subscriber周围的包装已被删除。如果您有兴趣,可以将此图与“概述”中的图形进行比较。


    jelly_bean_sb_subscriber已经相应地被简化了。write函数自己检查口味是否具有期望值。

    class jelly_bean_sb_subscriber extends uvm_subscriber#( jelly_bean_transaction );
      `uvm_component_utils( jelly_bean_sb_subscriber )
     
      function new( string name, uvm_component parent );
        super.new( name, parent );
      endfunction: new
     
      function void write( jelly_bean_transaction t );
        if (     t.flavor == CHOCOLATE && t.sour   && t.taste == YUMMY ||
             ! ( t.flavor == CHOCOLATE && t.sour ) && t.taste == YUCKY ) begin
          `uvm_error( get_name(), { "You lost sense of taste!", t.convert2string() } )
        end else begin
          `uvm_info( get_name(), { "You have a good sense of taste.", t.convert2string() }, UVM_LOW )
        end
      endfunction: write  
    endclass: jelly_bean_sb_subscriber

    jelly_bean_env直接实例化没有包装的jelly_bean_sb_subscriber(第16行)。

    class jelly_bean_env extends uvm_env;
      `uvm_component_utils( jelly_bean_env )
     
      jelly_bean_agent         jb_agent;
      jelly_bean_fc_subscriber jb_fc;
      jelly_bean_sb_subscriber jb_sb;
     
      function new( string name, uvm_component parent );
        super.new( name, parent );
      endfunction: new
     
      function void build_phase( uvm_phase phase );
        super.build_phase( phase );
        jb_agent = jelly_bean_agent        ::type_id::create( .name( "jb_agent" ), .parent( this ) );
        jb_fc    = jelly_bean_fc_subscriber::type_id::create( .name( "jb_fc"    ), .parent( this ) );
        jb_sb    = jelly_bean_sb_subscriber::type_id::create( .name( "jb_sb"    ), .parent( this ) );
      endfunction: build_phase
     
      function void connect_phase( uvm_phase phase );
        super.connect_phase( phase );
        jb_agent.jb_ap.connect( jb_fc.analysis_export );
        jb_agent.jb_ap.connect( jb_sb.analysis_export );
      endfunction: connect_phase  
    endclass: jelly_bean_env


    您现在可以在EDA Playground上查看并运行新代码!对于那些不了解EDA Playground的人来说,它是由Victor EDA,Inc.开发的基于网络的仿真器。大约两周前,他们添加了Aldec的Riviera-PRO EDU仿真器,这使我们能够使用SystemVerilog的全部功能,包括随机化,SVA和功能覆盖率。多么美妙!谢谢Victor EDA和Aldec!

    展开全文
  • 这篇文章揭示了糖果爱好者系列UVM教程的预期成果。使用创建的验证组件并写出测试用例,然后实际软件仿真运行。Test这个特殊的例子将告诉你如何开发一系列各种无糖的jelly_beans。测试类如下所述。class jelly_bean_...

    这篇文章揭示了糖果爱好者系列UVM教程的预期成果。使用创建的验证组件并写出测试用例,然后实际软件仿真运行。

    Test

    这个特殊的例子将告诉你如何开发一系列各种无糖的jelly_beans。测试类如下所述。

    class jelly_bean_test extends uvm_test;
       `uvm_component_utils(jelly_bean_test)
     
       jelly_bean_env jb_env;
     
       function new(string name, uvm_component parent);
          super.new(name, parent);
       endfunction: new
     
       function void build_phase(uvm_phase phase);
          super.build_phase(phase);
          begin
             jelly_bean_configuration jb_cfg;
     
             jb_cfg = new;
             assert(jb_cfg.randomize());
             uvm_config_db#(jelly_bean_configuration)::set
               (.cntxt(this), .inst_name("*"), .field_name("config"), .value(jb_cfg));
             jelly_bean_transaction::type_id::set_type_override(sugar_free_jelly_bean_transaction::get_type());
             jb_env = jelly_bean_env::type_id::create(.name("jb_env"), .parent(this));
          end
       endfunction: build_phase
     
       task run_phase(uvm_phase phase);
          gift_boxed_jelly_beans_sequence jb_seq;
     
          phase.raise_objection(.obj(this));
          jb_seq = gift_boxed_jelly_beans_sequence::type_id::create(.name("jb_seq"), .contxt(get_full_name()));
          assert(jb_seq.randomize());
          `uvm_info("jelly_bean_test", { "\n", jb_seq.sprint() }, UVM_LOW)
          jb_seq.start(jb_env.jb_agent.jb_seqr);
          #10ns ;
          phase.drop_objection(.obj(this));
       endtask: run_phase
    endclass: jelly_bean_test

    build_phase执行三个动作:

    • 将配置类注册到config数据库(第17行)
    • 使用sugar_free_jelly_bean事务,而不是jelly_bean_transaction。用set_type_override实现(第19行)
    • 创建jelly_bean_env(第20行)

    第13行至第18行为将来添加更丰富的配置使用,并添加为占位符。由于缺乏配置需求,本系列中的配置解释被省略。如果您对此主题感兴趣,请参阅以后的configuration章节。

    在run_phase中,创建gift_boxed_jelly_beans_sequence,并调用start开始运行。

    Configuration

    作为参考,下面插入jelly_bean_configuration类的源代码。

    class jelly_bean_configuration extends uvm_object;
       `uvm_object_utils(jelly_bean_configuration)
     
       function new(string name = "");
          super.new(name);
       endfunction: new
    endclass: jelly_bean_configuration

    Top

    在继续进行仿真之前,需要编写实例化DUT模块的顶层模块,将jelly_bean_if注册到资源数据库中,并运行测试。顶级模块也负责时钟生成。

    更新(2014年4月2日):在本教程中,我们使用了uvm_resource_db来存储jelly_bean_if(第15和16行)。 UVM建议使用uvm_config_db而不是uvm_resource_db,因为前者更健壮。你可以用15行和16行代替:

    module top;
       import uvm_pkg::*;
     
       reg clk;
       jelly_bean_if     jb_slave_if(clk);
       jelly_bean_taster jb_taster(jb_slave_if);
     
       initial begin // clock generation
          clk = 0;
          #5ns ;
          forever #5ns clk = ! clk;
       end
     
       initial begin
          uvm_resource_db#(virtual jelly_bean_if)::set
            (.scope("ifs"), .name("jelly_bean_if"), .val(jb_slave_if));
          run_test();
       end
    endmodule: top

    Simulation

    作为压轴的一部分,现在是编译和运行仿真的时候了。下面发布的是仿真结果。

    UVM_INFO @ 0: reporter [RNTST] Running test jelly_bean_test...
    UVM_INFO jb.sv(481) @ 0: uvm_test_top [jelly_bean_test]
    -----------------------------------------------------------------------
    Name                      Type                             Size  Value
    -----------------------------------------------------------------------
    jb_seq                    gift_boxed_jelly_beans_sequence  -     @772
      num_jelly_bean_flavors  integral                         32    'h2
      req                     object                           -
      rsp                     object                           -
    -----------------------------------------------------------------------
     
    UVM_INFO jb.sv(406) @ 40: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
    ----------------------------------------------------------------
    Name          Type                               Size  Value
    ----------------------------------------------------------------
    jb_tx         sugar_free_jelly_bean_transaction  -     @818
      flavor      flavor_e                           3     CHOCOLATE
      color       color_e                            2     RED
      sugar_free  integral                           1     'h1
      sour        integral                           1     'h1
      taste       taste_e                            2     YUCKY
    ----------------------------------------------------------------
     
    UVM_INFO jb.sv(406) @ 60: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
    ----------------------------------------------------------------
    Name          Type                               Size  Value
    ----------------------------------------------------------------
    jb_tx         sugar_free_jelly_bean_transaction  -     @834
      flavor      flavor_e                           3     CHOCOLATE
      color       color_e                            2     GREEN
      sugar_free  integral                           1     'h1
      sour        integral                           1     'h0
      taste       taste_e                            2     YUMMY
    ----------------------------------------------------------------
     
    UVM_INFO jb.sv(406) @ 80: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
    ------------------------------------------------------------
    Name          Type                               Size  Value
    ------------------------------------------------------------
    jb_tx         sugar_free_jelly_bean_transaction  -     @842
      flavor      flavor_e                           3     APPLE
      color       color_e                            2     GREEN
      sugar_free  integral                           1     'h1
      sour        integral                           1     'h1
      taste       taste_e                            2     YUMMY
    ------------------------------------------------------------
     
    UVM_INFO jb.sv(406) @ 100: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
    ------------------------------------------------------------
    Name          Type                               Size  Value
    ------------------------------------------------------------
    jb_tx         sugar_free_jelly_bean_transaction  -     @850
      flavor      flavor_e                           3     APPLE
      color       color_e                            2     RED
      sugar_free  integral                           1     'h1
      sour        integral                           1     'h0
      taste       taste_e                            2     YUMMY
    ------------------------------------------------------------

    有了这六篇文章,基本的UVM教程就完成了。虽然这种jelly-bean机器是假想的,但它有能力与其他DUT一起改写。如果有机会,我想回顾这些帖子中省略的其他主题,例如virtual sequence。我希望本教程能帮助你进一步理解UVM。


    展开全文
  • UVM的基本教程

    2020-12-01 14:02:31
    文章目录一、基本介绍二、在实践中学习1.接口interface2.待测设计DUT3.传输数据包transaction4.序列sequence5.序列器sequencer6.驱动器driver7....通用验证方法学UVM(Universal Verification Met.


    一、基本介绍

    通用验证方法学UVM(Universal Verification Methodology)已经成为集成电路设计的验证标准。UVM类构建的库促进了测试用例的搭建,UVM测试用例的每一个元素都是从现有的UVM类派生出来的。
    每个类都有仿真阶段,这些仿真阶段作为类的方法,按照一定的顺序执行。其中较为常用的几个UVM阶段如下:

    • build_phase负责测试用例结构的创建和配置,构建组件的层次结构。
    • connect_phase用于连接类中的不同子组件。
    • run_phase是主要阶段,在其中执行仿真。
    • report_phase可用于显示仿真的结果

    为了在类和变量中实现一些重要的方法,UVM提供了UVM宏。最常见的UVM宏如下:

    • uvm_component_utils: 当类从uvm_component类派生时,注册一个新的类的类型。
    • uvm_object_utils: 类似于uvm_component_utils,但类是从类uvm_object派生的类。
    • uvm_field_int: 在UVM工厂注册一个变量。这个宏提供了像copy()、compare()和print()这样的函数.
    • uvm_info:在环境中仿真期间打印消息。
    • uvm_error:这个宏发送带有错误日志的消息。
    • uvm_fatal: 仿真发生致命错误时,这个宏可以强制结束仿真并打印消息。

    为了解释一个UVM环境的结构,在测试用例中,将使用一个简单的加法器作为测试设计(DUT)。UVM测试平台如图所示。DUT是与测试台交互以验证其功能的硬件实现。
    图1 UVM 测试平台
    为了实现DUT的仿真, sequencer产生一系列数据送入DUT。由于sequencer发送高抽象级别的数据包,并且DUT只接受来自接口的数据,所以,driver用来将来自sequencer的数据包,转换成信号送入DUT。
    经过接口的数据需要被捕获,以便后续的仿真验证。由于driver只能实现数据包到信号的转换,所以,另外还需要一个模块实现与driver相反的功能,将信号转换成数据包。monitor就是这样的一个模块,收集driver与DUT之间通信的接口信号,并将其转换成数据包,最后送入参考模型中去进行比较。
    一个agent通常包含三个组件:一个sequencer序列发生器,一个driver驱动,一个monitor监控。有两种类型的agent:Active Agent包含上述三个组件;Passive Agent只包含上述的monitor监控和driver驱动。agent包含build phase函数,用于去创建层次结果,以及connect phase用于连接模块。
    参考模型(refmod)是在RTL实现之前,早期阶段构思的理想的模型。在抽象的高级别上模拟DUT。
    comparator类主要用于比较参考模型和DUT之间的数据。参考模型和比较器组成了记分牌,用于检查DUT产生的传输是否正确。一个或者多个agent加上记分牌构成了env类。测试类负责执行测试,创建环境,并将序列连接到序列发生器上。最后,top中实现DUT和testbench的例化。

    二、在实践中学习

    下面通过对一个加法器验证环境的搭建,来进一步理解UVM中的各个组件。加法器模块的功能验证平台分为以下的模块和类。

    1.接口interface

    接口的构造是专门为封装块之间的通信信号而创建的,modport为模块端口提供方向信息,并控制特定模块内任务和功能的使用。

    interface input_if(input clk, rst);
        logic [31:0] A, B;
        logic valid, ready;
        
        modport port(input clk, rst, A, B, valid, output ready);
    endinterface
    
    
    interface output_if(input clk, rst);
        logic [31:0] data;
        logic valid, ready;
        
        modport port(input clk, rst, output valid, data, ready);
    endinterface
    
    

    2.待测设计DUT

    待测设计DUT(design under test)是需要被验证的设计代码。

    module adder(input_if.port inter, output_if.port out_inter, output state);
        enum logic [1:0] {INITIAL,WAIT,SEND} state;
        
        always_ff @(posedge inter.clk)
            if(inter.rst) begin
                inter.ready <= 0;
                out_inter.data <= 'x;
                out_inter.valid <= 0;
                state <= INITIAL;
            end
            else case(state)
                    INITIAL: begin
                        inter.ready <= 1;
                        state <= WAIT;
                    end
                    
                    WAIT: begin
                        if(inter.valid) begin
                            inter.ready <= 0;
                            out_inter.data <= inter.A + inter.B;
                            out_inter.valid <= 1;
                            state <= SEND;
                        end
                    end
                    
                    SEND: begin
                        if(out_inter.ready) begin
                            out_inter.valid <= 0;
                            inter.ready <= 1;
                            state <= WAIT;
                        end
                    end
            endcase
    endmodule: adder
    

    3.传输数据包transaction

    传输数据包的构造是专门为封装块之间通信的数据而创建的,其中的field机制能够将数据包中的数据注册到UVM工厂中,为数据提供copy()、compare()、print()、colne()等函数。

    class packet_in extends uvm_sequence_item;
        rand integer A;
        rand integer B;
    
        `uvm_object_utils_begin(packet_in)
            `uvm_field_int(A, UVM_ALL_ON|UVM_HEX)
            `uvm_field_int(B, UVM_ALL_ON|UVM_HEX)
        `uvm_object_utils_end
    
        function new(string name="packet_in");
            super.new(name);
        endfunction: new
    endclass: packet_in
    
    
    class packet_out extends uvm_sequence_item;
        integer data;
    
        `uvm_object_utils_begin(packet_out)
            `uvm_field_int(data, UVM_ALL_ON|UVM_HEX)
        `uvm_object_utils_end
    
        function new(string name="packet_out");
            super.new(name);
        endfunction: new
    endclass: packet_out
    

    4.序列sequence

    UVM序列是一个对象,它用于生成仿真的行为。UVM序列不是组件层次结构的一部分。每个UVM序列最终被绑定到一个UVM序列器。多个UVM序列实例可以绑定到同一个UVM序列器。

    class sequence_in extends uvm_sequence #(packet_in);
        `uvm_object_utils(sequence_in)
    
        function new(string name="sequence_in");
            super.new(name);
        endfunction: new
    
        task body;
            packet_in tx;
    
            forever begin
                tx = packet_in::type_id::create("tx");
                start_item(tx);
                assert(tx.randomize());
                finish_item(tx);
            end
        endtask: body
    endclass: sequence_in
    

    5.序列器sequencer

    UVM序列器作为仲裁器,用于控制来自多个仿真序列的事务流。更具体地说,UVM序列器控制,由一个或多个UVM序列生成的事务流。

    class sequencer extends uvm_sequencer #(packet_in);
        `uvm_component_utils(sequencer)
    
        function new (string name = "sequencer", uvm_component parent = null);
            super.new(name, parent);
        endfunction
    endclass: sequencer
    
    

    6.驱动器driver

    驱动器(driver)从序列器(sequencer),接收序列(sequence)产生的传输数据包(transaction),并在DUT接口上应用(驱动)它。因此,驱动器driver将传输级别的数据包仿真,转换成管脚信号级别的仿真。它还有一个TLM接口,用于接收来自序列器sequencer的传输数据包transaction,去驱动DUT上的接口信号。

    typedef virtual input_if input_vif;
    
    class driver extends uvm_driver #(packet_in);
        `uvm_component_utils(driver)
        input_vif vif;
        event begin_record, end_record;
    
        function new(string name = "driver", uvm_component parent = null);
            super.new(name, parent);
        endfunction
    
        virtual function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            assert(uvm_config_db#(input_vif)::get(this, "", "vif", vif));
        endfunction
    
        virtual task run_phase(uvm_phase phase);
            super.run_phase(phase);
            fork
                reset_signals();
                get_and_drive(phase);
                record_tr();
            join
        endtask
    
        virtual protected task reset_signals();
            wait (vif.rst === 1);
            forever begin
                vif.valid <= '0;
                vif.A <= 'x;
                vif.B <= 'x;
                @(posedge vif.rst);
            end
        endtask
    
        virtual protected task get_and_drive(uvm_phase phase);
            wait(vif.rst === 1);
            @(negedge vif.rst);
            @(posedge vif.clk);
            
            forever begin
                seq_item_port.get(req);
                -> begin_record;
                drive_transfer(req);
            end
        endtask
    
        virtual protected task drive_transfer(packet_in tr);
            vif.A = tr.A;
            vif.B = tr.B;
            vif.valid = 1;
    
            @(posedge vif.clk)
            
            while(!vif.ready)
                @(posedge vif.clk);
            
            -> end_record;
            @(posedge vif.clk); //hold time
            vif.valid = 0;
            @(posedge vif.clk);
        endtask
    
        virtual task record_tr();
            forever begin
                @(begin_record);
                begin_tr(req, "driver");
                @(end_record);
                end_tr(req);
            end
        endtask
    endclass
    
    
    typedef virtual output_if output_vif;
    
    class driver_out extends uvm_driver #(packet_out);
        `uvm_component_utils(driver_out)
        output_vif vif;
    
        function new(string name = "driver_out", uvm_component parent = null);
            super.new(name, parent);
        endfunction
    
        virtual function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            assert(uvm_config_db#(output_vif)::get(this, "", "vif", vif));
        endfunction
    
        virtual task run_phase(uvm_phase phase);
            super.run_phase(phase);
            fork
                reset_signals();
                drive(phase);
            join
        endtask
    
        virtual protected task reset_signals();
            wait (vif.rst === 1);
            forever begin
                vif.ready <= '0;
                @(posedge vif.rst);
            end
        endtask
    
        virtual protected task drive(uvm_phase phase);
            wait(vif.rst === 1);
            @(negedge vif.rst);
            forever begin
              @(posedge vif.clk);
              vif.ready <= 1;
            end
        endtask
    endclass
    

    7.监视器monitor

    监视器(monitor)对DUT接口进行采样,并捕获事务中的信息,构成传输数据包(transaction),这些传输数据包被发送到UVM测试台以进行进一步分析。因此,与驱动器(driver)类似,但过程相反,它实现的是将管脚信号上的信号收集转换成传输数据包。
    UVM监视器可以在内部对所产生的事务执行一些处理(例如覆盖率收集、检查、日志记录、记录等等)。

    class monitor extends uvm_monitor;
        input_vif  vif;
        event begin_record, end_record;
        packet_in tr;
        uvm_analysis_port #(packet_in) item_collected_port;
        `uvm_component_utils(monitor)
       
        function new(string name, uvm_component parent);
            super.new(name, parent);
            item_collected_port = new ("item_collected_port", this);
        endfunction
    
        virtual function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            assert(uvm_config_db#(input_vif)::get(this, "", "vif", vif));
            tr = packet_in::type_id::create("tr", this);
        endfunction
    
        virtual task run_phase(uvm_phase phase);
            super.run_phase(phase);
            fork
                collect_transactions(phase);
                record_tr();
            join
        endtask
    
        virtual task collect_transactions(uvm_phase phase);
            wait(vif.rst === 1);
            @(negedge vif.rst);
            
            forever begin
                do begin
                    @(posedge vif.clk);
                end while (vif.valid === 0 || vif.ready === 0);
                -> begin_record;
                
                tr.A = vif.A;
                tr.B = vif.B;
                item_collected_port.write(tr);
    
                @(posedge vif.clk);
                -> end_record;
            end
        endtask
    
        virtual task record_tr();
            forever begin
                @(begin_record);
                begin_tr(tr, "monitor");
                @(end_record);
                end_tr(tr);
            end
        endtask
    endclass
    
    
    class monitor_out extends uvm_monitor;
        `uvm_component_utils(monitor_out)
        output_vif  vif;
        event begin_record, end_record;
        packet_out tr;
        uvm_analysis_port #(packet_out) item_collected_port;
       
        function new(string name, uvm_component parent);
            super.new(name, parent);
            item_collected_port = new ("item_collected_port", this);
        endfunction
    
        virtual function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            assert(uvm_config_db#(output_vif)::get(this, "", "vif", vif));
            tr = packet_out::type_id::create("tr", this);
        endfunction
    
        virtual task run_phase(uvm_phase phase);
            super.run_phase(phase);
            fork
                collect_transactions(phase);
                record_tr();
            join
        endtask
    
        virtual task collect_transactions(uvm_phase phase);
            wait(vif.rst === 1);
            @(negedge vif.rst);
            
            forever begin
                do begin
                    @(posedge vif.clk);
                end while (vif.valid === 0 || vif.ready === 0);
                -> begin_record;
                
                tr.data = vif.data;
                item_collected_port.write(tr);
    
                @(posedge vif.clk);
                -> end_record;
            end
        endtask
    
        virtual task record_tr();
            forever begin
                @(begin_record);
                begin_tr(tr, "monitor_out");
                @(end_record);
                end_tr(tr);
            end
        endtask
    endclass
    

    8.代理agent

    代理(agent)是一个分层组件,它将处理特定DUT接口的其他验证组件组合在一起。一个典型的代理包括:一个sequencer,用于管理仿真流的UVM序列器;一个driver,用于在DUT接口上驱动仿真的驱动器;一个monitor,用于监视DUT的接口。代理可能包括其他组件,如覆盖率收集器、协议检查器、TLM模型等。
    在这里插入图片描述

    class agent extends uvm_agent;
        sequencer sqr;
        driver    drv;
        monitor   mon;
    
        uvm_analysis_port #(packet_in) item_collected_port;
    
        `uvm_component_utils(agent)
    
        function new(string name = "agent", uvm_component parent = null);
            super.new(name, parent);
            item_collected_port = new("item_collected_port", this);
        endfunction
    
        virtual function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            mon = monitor::type_id::create("mon", this);
            sqr = sequencer::type_id::create("sqr", this);
            drv = driver::type_id::create("drv", this);
        endfunction
    
        virtual function void connect_phase(uvm_phase phase);
            super.connect_phase(phase);
            mon.item_collected_port.connect(item_collected_port);
            drv.seq_item_port.connect(sqr.seq_item_export);
        endfunction
    endclass: agent
    
    
    class agent_out extends uvm_agent;
        driver_out    drv;
        monitor_out   mon;
    
        uvm_analysis_port #(packet_out) item_collected_port;
    
        `uvm_component_utils(agent_out)
    
        function new(string name = "agent_out", uvm_component parent = null);
            super.new(name, parent);
            item_collected_port = new("item_collected_port", this);
        endfunction
    
        virtual function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            mon = monitor_out::type_id::create("mon_out", this);
            drv = driver_out::type_id::create("drv_out", this);
        endfunction
    
        virtual function void connect_phase(uvm_phase phase);
            super.connect_phase(phase);
            mon.item_collected_port.connect(item_collected_port);
        endfunction
    endclass: agent_out
    

    9.记分板scoreboard

    记分板(scoreboard)的主要功能是检查DUT的行为是否正确。记分板接收的实际数据包,来自代理(agent)送入DUT的输入输出,接收的期望数据包,来自参考模型,最后在记分板中比较实际的数据包和期望的数据包。

    class comparator #(type T = packet_out) extends uvm_scoreboard;
      typedef comparator #(T) this_type;
      `uvm_component_param_utils(this_type)
    
      const static string type_name = "comparator #(T)";
    
      uvm_put_imp #(T, this_type) from_refmod;
      uvm_analysis_imp #(T, this_type) from_dut;
    
      typedef uvm_built_in_converter #( T ) convert; 
    
      int m_matches, m_mismatches;
      T exp;
      bit free;
      event compared, end_of_simulation;
    
      function new(string name, uvm_component parent);
        super.new(name, parent);
        from_refmod = new("from_refmod", this);
        from_dut = new("from_dut", this);
        m_matches = 0;
        m_mismatches = 0;
        exp = new("exp");
        free = 1;
      endfunction
    
      virtual function string get_type_name();
        return type_name;
      endfunction
    
      task run_phase(uvm_phase phase);
        phase.raise_objection(this);
        @(end_of_simulation);
        phase.drop_objection(this);
      endtask
    
      virtual task put(T t);
        if(!free) @compared;
        exp.copy(t);
        free = 0;
        
        @compared;
        free = 1;
      endtask
    
      virtual function bit try_put(T t);
        if(free) begin
          exp.copy(t);
          free = 0;
          return 1;
        end
        else return 0;
      endfunction
    
      virtual function bit can_put();
        return free;
      endfunction
    
      virtual function void write(T rec);
        if (free)
          uvm_report_fatal("No expect transaction to compare with", "");
        
        if(!(exp.compare(rec))) begin
          uvm_report_warning("Comparator Mismatch", "");
          m_mismatches++;
        end
        else begin
          uvm_report_info("Comparator Match", "");
          m_matches++;
        end
        
        if(m_matches+m_mismatches > 100)
          -> end_of_simulation;
        
        -> compared;
      endfunction
    endclass
    

    10.仿真环境env

    仿真环境env是一个分层组件,它将其他相互关联的验证组件组合在一起。通常在仿真环境env中实例化的典型组件有代理(agent)、记分牌(scoreboard),甚至其他仿真环境。
    顶层仿真环境封装了所有针对DUT的验证组件。

    class env extends uvm_env;
        agent       mst;
        refmod      rfm;
        agent_out   slv;
        comparator #(packet_out) comp;  
        uvm_tlm_analysis_fifo #(packet_in) to_refmod;
    
        `uvm_component_utils(env)
    
        function new(string name, uvm_component parent = null);
            super.new(name, parent);
            to_refmod = new("to_refmod", this); 
        endfunction
    
        virtual function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            mst = agent::type_id::create("mst", this);
            slv = agent_out::type_id::create("slv", this);
            rfm = refmod::type_id::create("rfm", this);
            comp = comparator#(packet_out)::type_id::create("comp", this);
        endfunction
    
        virtual function void connect_phase(uvm_phase phase);
            super.connect_phase(phase);
            // Connect MST to FIFO
            mst.item_collected_port.connect(to_refmod.analysis_export);
            
            // Connect FIFO to REFMOD
            rfm.in.connect(to_refmod.get_export);
            
            //Connect scoreboard
            rfm.out.connect(comp.from_refmod);
            slv.item_collected_port.connect(comp.from_dut);
        endfunction
    
        virtual function void end_of_elaboration_phase(uvm_phase phase);
            super.end_of_elaboration_phase(phase);
        endfunction
      
        virtual function void report_phase(uvm_phase phase);
            super.report_phase(phase);
            `uvm_info(get_type_name(), $sformatf("Reporting matched %0d", comp.m_matches), UVM_NONE)
            if (comp.m_mismatches) begin
                `uvm_error(get_type_name(), $sformatf("Saw %0d mismatched samples", comp.m_mismatches))
            end
        endfunction
    endclass
    
    
    

    11.测试用例test

    测试用例是测试台中最顶层的组件。测试用例通常执行三个主要功能:实例化顶层环境、配置环境(通过工厂覆盖或配置数据库),以及通过在环境中调用序列来进行仿真。
    通常,有一个带有UVM环境实例化和其他公共项的基本UVM测试。然后,其他单独的测试将扩展这个基本测试,并以不同的方式配置环境或选择不同的序列运行。
    测试用例是在运行时动态实例化的,允许UVM测试台只编译一次,并与许多不同的测试一起运行。

    class simple_test extends uvm_test;
      env env_h;
      sequence_in seq;
    
      `uvm_component_utils(simple_test)
    
      function new(string name, uvm_component parent = null);
        super.new(name, parent);
      endfunction
    
      virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        env_h = env::type_id::create("env_h", this);
        seq = sequence_in::type_id::create("seq", this);
      endfunction
     
      task run_phase(uvm_phase phase);
        seq.start(env_h.mst.sqr);
      endtask: run_phase
    
    endclass
    

    12.顶层top

    顶层主要实例化DUT模块和测试类,并配置它们之间的连接。

    import uvm_pkg::*;
    `include "uvm_macros.svh"
    `include "./input_if.sv"
    `include "./output_if.sv"
    `include "./adder.sv"
    `include "./packet_in.sv"
    `include "./packet_out.sv"
    `include "./sequence_in.sv"
    `include "./sequencer.sv"
    `include "./driver.sv"
    `include "./driver_out.sv"
    `include "./monitor.sv"
    `include "./monitor_out.sv"
    `include "./agent.sv"
    `include "./agent_out.sv"
    `include "./refmod.sv"
    `include "./comparator.sv"
    `include "./env.sv"
    `include "./simple_test.sv"
    
    //Top
    module top;
      logic clk;
      logic rst;
      
      initial begin
        clk = 0;
        rst = 1;
        #22 rst = 0;
        
      end
      
      always #5 clk = !clk;
      
      logic [1:0] state;
      
      input_if in(clk, rst);
      output_if out(clk, rst);
      
      adder sum(in, out, state);
    
      initial begin
        `ifdef INCA
           $recordvars();
        `endif
        `ifdef VCS
            //$vcdpluson;
            $fsdbDumpfile("test.fsdb");
        	$fsdbDumpSVA();
        	$fsdbDumpvars();
            $fsdbDumpMDA();
        `endif
        `ifdef QUESTA
           $wlfdumpvars();
           set_config_int("*", "recording_detail", 1);
        `endif
        
        uvm_config_db#(input_vif)::set(uvm_root::get(), "*.env_h.mst.*", "vif", in);
        uvm_config_db#(output_vif)::set(uvm_root::get(), "*.env_h.slv.*",  "vif", out);
        
        run_test("simple_test");
      end
    endmodule
    

    13.参考模型reference mode和直接编程接口(DPI)

    SystemVerilog直接编程接口(DPI)是SystemVerilog调用外部语言(如C、c++等)函数的接口。DPI由两个层组成:SystemVerilog层和外语层,它们彼此隔离。下面给出了refmod的代码,以说明DPI的用法。sum()函数在文件external.cpp中定义,一旦在refmod中调用它,就应该在sum()函数的定义之前添加关键字“external C”

    #include <stdio.h>
    #include <math.h>
    
    extern "C" int sum(int a, int b){
      return a+b;
    }
    
    
    import "DPI-C" context function int sum(int a, int b);
    
    class refmod extends uvm_component;
        `uvm_component_utils(refmod)
        
        packet_in tr_in;
        packet_out tr_out;
        integer a, b;
        uvm_get_port #(packet_in) in;
        uvm_put_port #(packet_out) out;
        
        function new(string name = "refmod", uvm_component parent);
            super.new(name, parent);
            in = new("in", this);
            out = new("out", this);
        endfunction
        
        virtual function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            tr_out = packet_out::type_id::create("tr_out", this);
        endfunction: build_phase
        
        virtual task run_phase(uvm_phase phase);
            super.run_phase(phase);
            
            forever begin
                in.get(tr_in);
                tr_out.data = sum(tr_in.A, tr_in.B);
                out.put(tr_out);
            end
        endtask: run_phase
    endclass: refmod
    
    

    14.简单的makefile

    comp: clean
    	g++ -c external.cpp -o external.o
    	vcs -full64  -sverilog top.sv -dpi -ntb_opts uvm -debug_pp -timescale=1ns/10ps  external.o -debug_access+cbk -lca -cm tgl+line+fsm+cond+branch
    
    sim:
    	./simv +UVM_TR_RECORD +UVM_VERBOSITY=HIGH +UVM_TESTNAME=simple_test -lca -cm tgl+line+fsm+cond+branch
    
    all: comp sim
    
    dbg: clean
    	g++ -c external.cpp -o external.o
    	vcs -full64  -sverilog top.sv -dpi -ntb_opts uvm -debug_pp -timescale=1ns/10ps  external.o -debug_access+cbk -debug_access+all -kdb -lca
    	$ ./simv +UVM_TR_RECORD +UVM_VERBOSITY=HIGH +UVM_TESTNAME=simple_test -gui=verdi
    
    clean:
    	rm -rf DVEfiles csrc simv simv.daidir ucli.key .vlogansetup.args .vlogansetup.env .vcs_lib_lock simv.vdb AN.DB vc_hdrs.h *.diag *.vpd *tar.gz external.o inter.fsdb novas.conf novas_dump.log novas.rc test.fsdb verdiLog
    
    view_waves:
    	dve &
    
    verdi:
    	verdi -sverilog -ntb_opts uvm-1.2 -timescale=1ns/10ps top.sv -f filelist.f +incdir+./ -ssf ./test.fsdb &
    
    urg_gui:
    	urg -dir *.vdb -format both -report urgReportALL; \
    firefox urgReportALL/tests.html &
    
    

    comp函数先通过g++编译C程序,再通过VCS进行编译,其中-debug_access+cbk选项对静态网络、寄存器和变量启用基于PLI的回调。-cm tgl+line+fsm+cond+branch选项开启代码翻转覆盖率、行覆盖率、状态机覆盖率、条件覆盖率、分支覆盖率。
    sim函数执行VCS仿真。
    dbg函数用于VCS和verdi联调,编译里边加入了-debug_access+all -kdb –lca选项,仿真里边加入了-gui=verdi选项。
    verdi函数用于启动verdi查看波形。
    urg_gui函数用于查看代码覆盖率。


    代码附件

    上述完整代码下载链接如下:
    https://github.com/AFEI1100/easyUVM-master.git

    展开全文
  • UVM 快速学习教程

    2012-07-10 14:18:54
    The UVM (Universal Verification Methodology) Class Library provides the building blocks needed to quickly develop well-constructed and reusable verification components and test environments in ...
  • 本文介绍如何使用UVM 1.2自定义消息格式。步骤0 - 默认格式在更改消息格式之前,让我们看看在使用uvm_info宏时默认输出的外观:UVM_INFO testbench.sv(86) @ 0: reporter [top] This is a message from top. UVM_...
  • 推荐使用gitbash,使用makefile之前要安装makefile,教程在这里->https://www.eemaker.com/git-bash-make.html 本节代码下载链接:链接:https://pan.baidu.com/s/1-HEJPsiSYM4oDnCgTnK-2A 提取码:qgyy ...
  • 推荐使用gitbash,使用makefile之前要安装makefile,教程在这里->https://www.eemaker.com/git-bash-make.html 本节代码下载链接:链接:https://pan.baidu.com/s/1JOJDUyBLSkXA67OsaDCLFg 提取码:c8n4 ...
  • 推荐使用gitbash,使用makefile之前要安装makefile,教程在这里->https://www.eemaker.com/git-bash-make.html 本节代码下载链接:链接:https://pan.baidu.com/s/1GBhvpGaoG_BNvRZqh-4kPA 提取码:4rcr ...
  • 推荐使用gitbash,使用makefile之前要安装makefile,教程在这里->https://www.eemaker.com/git-bash-make.html 本节代码下载链接:链接:https://pan.baidu.com/s/1GsljDwXgzC53M-C5_J2iXg 提取码:1iul ...
  • UVM糖果爱好者教程 - 1.概述

    千次阅读 2018-03-16 14:52:57
    Accellera最近发布的UVM可能会改变验证的未来,因为验证方法似乎在这个UVM中得到巩固。本专栏将提供关于这种新的验证方法的简单教程。本教程不是关注AXI,OCP或其他系统总线,而是基于jelly-bean生成器(DUT)的假设...
  • 当我们在Agent中创建jelly_bean_driver时,我们编写了build_phase函数和run_phase任务,但是实际调用它们的人是谁?答案是uvm_phase类。...这些phase类是从uvm_topdown_phase,uvm_bottomup_phase或uvm_task...

空空如也

空空如也

1 2 3 4 5
收藏数 98
精华内容 39
关键字:

uvm教程