• uvm核心


    核心机制

    uvm_object

    • UVM世界中的类最初都是从一个uvm_void根类(root class)继承来的,而实际上这个类并没有成员变量和方法。(extends----注册---new)
    • uvm_void只是一个虚类(virtual class),还在等待将来继承于它的子类去开垦。在继承于uvm_void的子类中,有两个类,一个为uvm_object类,另外一个为uvm_port_base类。
    • 在UVM世界的类库地图中除过事务接口(transaction interface)类继承于uvm_port_base,其它所有的类都是从uvm_object类—步步继承而来的。
    • 从uvm_object提供的方法和相关的宏操作来看,它的核心方法主要提供与数据操作的相关服务: Copy、Clone、Compare、Print、Pack/Unpack
    • 在SV模块的学习中,我们懂得了什么是句柄拷贝和对象拷贝。因此,无论是copy或者clone,都需要确保在操作的过程中,需要有source object和target objecto

    域的自动化(field_automation)

    • 从UVM通过域的自动化,使得用户在注册UVM类的同时也可以声明今后会参与到对象拷贝、克隆、打印等操作的成员变量
    • 域的自动化解放了verifier的双手,这使得在使用uvm_object提供的一些预定义方法时,非常便捷,而无需再实现自定义方法。
    • 在了解了域的自动化常用的宏之后,用户需要考虑哪些成员变量在注册UVM类(`uvm_{component, object}_utils)的时候,也一并将它们归置到对应的域列表中,以便为稍后的域方法提供可以自动实现的基础。
    1. ·uvm_object_utils_begin(box) //域的自动化声明,之后类的句柄可以直接对声明了的变量之间调用方法
    2. `uvm_field_int(volume,UVM_ALL_ON) //UVM_ALL_ON表示与变量对应的数据操作
    3. `uvm_field_emun(colot_t,color,UVM_ALL_ON)
    4. `uvm_object_utils_end
    • 域的自动化的相关的宏都是`uvm_field_{int,object,string,enum,event,real……}(ARG,FLAG)。ARG表示成员变量,FLAG表示用来标记的数据操作。

    拷贝(copy)

    • 在UVM的数据操作中,需要对copy和clone加以区分。前者默认已经创建好了对象,只需要对数据进行拷贝;后者则会自动创建对象并对source object进行数据拷贝,再返回target object句柄。(uvm的clone与sv的copy一样)
    • 无论是copy或者clone,都需要对数据进行复制。
    • 但是如果数据成员包括句柄,那么拷贝的时候,是否只拷贝该成员句柄本身,还是也额外创建新的对象,拷贝该句柄指向的对象?从示例可以看到,在进行copy时,默认进行的是深拷贝(deepcopy),即会执行copy()和do_copy()。(do_copy是copy的回调函数,只要对do_copy进行定义,调用了copy后会自动调用do_copy)
    1. class ball extends uvm_object;
    2. int diameter=10;
    3. color_t color=RED;
    4. `uvm_object_utils_begin(ball)
    5. `uvm_field_int(diameter,UVM_DEFAULT)
    6. `uvm_field_enum(color_t,color,UVM_NOCOPY) //不参与copy
    7. `uvm_object_utils_end
    8. ……
    9. function void do_copy(uvm_object rhs);
    10. ball b;
    11. cast(b,rhs);
    12. if(b.diameter<=20)begin
    13. diameter=20;
    14. end
    15. endfunction
    16. endclass
    17. class box extends uvm_object;
    18. int volume=120
    19. color_t color=WHITE;
    20. string name="box";
    21. ball b;
    22. ·uvm_object_utils_begin(box)
    23. ……
    24. uvm_field_object(b,UVM_ALL_ON)
    25. box b1,b2;
    26. initial begin
    27. b1=new("box1");
    28. b1.volume=80;
    29. b1.b.color=WHITE;
    30. b2=new();
    31. b2.copy(b1); //对b进行了深拷贝,拷贝指向的对象,这里理论拷贝diameter和color
    32. b2.name("b2");

     通过b的值为337和340可以知道指向两个不同的实例,进行了深拷贝

    比较(compare)

    function bit compare (usm object rhs,uvm_comparer comparer=null) ;

    • 默认情况下,如果不对比较的情况作出额外配置,用户可以在调用compare()方法时,省略第二项参数,即采用默认的比较配置。
    • 比较方法经常会在两个数据类中进行。例如从generator产生的一个transaction(数据类),和在设计输出上捕捉的transaction(数据类),如果它们为同一种类型,除了可以自定义数据比较之外,也可以直接使用uvm_object::compare()函数来实现数据比较和消息打印。
    • 在上面的两个对象比较中,会将每一个自动化的域进行比较,所以在执行compare()函数时,内置的比较方法也会将比较错误输出。
    • 从结果来看,比较发生了错误,返回0值。那么,b1.color和b2.color虽然不相同,为什么没有比较错误的信息呢?
      原因在于,(全局)默认的比较器,即uvm_package::uvm_default_comparer最大输出的错误比较信息是1,也就是说当比较错误发生时,不会再进行后续的比较。
    • 实际上,在uvm_object使用到的方法compare()、print()和pack(),如果没有指定数据操作配置对象作为参数时,会使用在uvm_pkg中例化的全局数据操作配置成员。

    全局对象

    • 在uvm_pkg中例化了不少全局对象,而在本节中我们会使用到的全局配置对象包括有uvm_default__comparer,uvm_default_printer和uvm_default_packer。
    • 如果用户不想使用默认的比较配置,而是想自己对比较进行设定,可以考虑创建—个uvm_comparer对象,或者修改全局的uvm_comparer对象。但是factory不可以修改,只有一个。

    以上@1表示全局例化第一次。可以看到那些都与uvm_top并列,表明各个全局对象不在环境层次里,不在层次结构下面。 uvm_pkg已经对其进行了例化,不需要再自己例化,直接指定就行。参考下面打印代码。

    打印(print)

    • 打印方法是核心基类提供的另外—种便于开发和调试的功能。通过field automation,使得声明之后的各个成员域会在调用uvm_objectgprint()函数时自动打印出来。
    • 相比于在仿真中设置断点,逐步调试,打印是另外—种调试方式。它的好处在于可以让仿真继续进行,会在最终回顾执行过程中,从全局理解执行的轨迹和逻辑。
    1. uvm_table_printer local_printer; //参考前面的表,默认table_printer
    2. local_printer=new();
    3. uvm_default_printer=uvm_default_line_printer; //更改打印机
    4. uvm_default_printer=uvm_default_tree_printer; //uvm_pkg已经例化,不用再对default_tree_printer
    5. 例化
    6. local_printer.knobs.full_name=1;
    7. b1.print.(local_print); //按自己要求,配置,设定了打印规则

    打包和解包(pack&unpack)

    function int pack (ref bit bitstream[ ],input uvm_packer packer=null) ;   //输出动态数组?

    function int unpack (ref bit bitstream[ ], input uvm_packer packer=null) ;

    • pack是为了将自动化声明后的域(标量)打包为比特流(bitstream) 。即将各个散乱的数据,整理到bit数据串中,类似于struct packed的整理方式,但又能充分利用数据空间,也更容易与硬件之间进行数据传递和比对。
    • unpack与pack相反,即将串行数据解包变为原有的各自域。该操作适用于从硬件—侧接收串行数据,进行校验之后,还原为软件—侧对象中各自对应的成员变量。
    • pack与unpack在通常的UVM环境中使用较少,但是当与外界环境,例如SystemC发生大规模数据传递,该方法是首选,因为可以通过简单数据流实现精确的数据传输,另外,在UVM与FPGA、emulator之间进行数据交换时,该方法也由于简便得到了青睐。

    phase机制

    • SV的验证环境构建中,我们可以发现,传统的硬件设计模型在仿真开始前,已经完成例化和连接了;而SV的软件部分对象例化则需要在仿真开始后执行。
    • 虽然对象例化通过调用构建函数new()来实现,但是单单通过new()函数无法解决一个重要问题,那就是验证环境在实现层次化时,如何保证例化的先后关系,以及各个组件在例化后的连接。
    • 此外如果需要实现高级功能,例如在顶层到底层的配置时,SV也无法在底层组件例化之前完成对底层的配置逻辑。(sv里需要例化后才能配置)
    • 因此UVM在验证环境构建时,引入了phase机制,通过该机制我们可以很清晰地将UVM仿真阶段层次化。
    • 这里的层次化,不单单是各个phase的先后执行顺序,而且处于同—phase中的层次化组件之间的phase也有先后关系。

    执行机制

    • 如果暂时抛开phase的机制剖析,对于UVM组件的开发者而言,他们主要关心各个phase执行的先后顺序。
    • 在定义了各个phase虚方法后,UVM环境会按照phase的顺序分别调用这些方法。

    运行过程至上到下,除了run都是函数,函数一般要立即做返回。(实际phase设置为void,不返回)

    9个主要phase

    • 在所有phase中,只有run_phase方法是一个可以耗时的任务,这意味着该方法可以完成一些等待、激励、采样的任务。对于其它phase对应的方法都是函数,必须立即返回(0耗时)。在
    • run_phae中,用户如果要完成测试,通常需要组织下面的激励序列:上电--复位--寄存器配置--发送主要测试内容--等待DUT完成测试
    • 在用户发送激励的一种简单方式是,在run phase中完成上面所有的激励;另外一种方式是,如果用户可以将上面几种典型序列划分到不同区间,让对应的激励按区间顺序发送的话,可以让测试更有层次。因此run_phase又可以分为下面12个phase:
      pre_reset_phase      reset_phase    post_reset_phase  pre_configure_phase configure_phase   post_configure_phase                       pre_main_phas         main_phase         post_main_phase                      pre_shutdown_phase           shutdown_phase       post_shutdown_phase
    • 实际上run_phase任务和上面细分的12个phase是并行的,即在start_of_simulation_phase任务执行以后,run_phase和reset_phase开始执行,而在shutdown_phase执行完成之后,需要等待
      run_phase执行完才可以进入extract_phase。

    UVM编译和运行顺序

    • 首先在加载硬件模型调用仿真器之前,需要完成编译和建模阶段。
    • 接下来在开始仿真之前,会分别执行硬件的always/initial语句,以及UVM的调用测试方法run_test和几个phase,分别是build、connect、end_of_elaboration和start_of_simulation。
    • 在开始仿真后,将会执行run_phase或者对应的12个细分phaseo在仿真结束后,将会执行剩余的phase,分别是extract、check、report和final。

    激励全部发送完毕,仿真结束

    uvm仿真开始

    • 要在仿真开始时建立验证环境,用户可以考虑选择下面几种方式:
    1. 可以通过全局函数(由uvm_pkg提供) run_test()来选择性地指定要运行哪一个uvm_test。这里的test类均继承于uvm_test。这样的话,指定的test类将被例化并指定为顶层的组件。一般而言,run_test()函数可以在合适module/program中的initial进程块中调用。
    2. 如果没有任何参数传递给run_test(),那么用户可以在仿真时通过传递参数+UVM_TESTNAME=,来指定仿真时调用的uvm_test。当然,即便run_test()函数在调用时已经有test名称传递,在仿真时+UVM_TESTNAME=也可以从顶层覆盖已指定的test。这种方式使得仿真不需要通过再次修改run_test()调用的test名称和重复编译,就可以灵活选定test。
    • 无论上面哪一种方式,都必须在顶层调用全局函数run_test(),用户可以考虑不传递test名称作为参数,而在仿真时通过传递参数+UVM_TESTNAME=来选择test。
    • 全局函数run_test()的重要性,正是从uvm_root创建了一个UVM世界。
    1. task run_test(string test_name="");
    2. uvm_root top;
    3. uvm_coreserverice_t cs;
    4. cs=uvm_coreservice_t::get();
    5. top=cs.get_root(); //拿到顶层
    6. top.run_test(test_name);
    7. endtask
    • UVM顶层类uvm_root。该类也继承于uvm_component,它也是UVM环境结构中的一员,而它可以作为顶层结构类。
    • 它提供了一些像run_test()的这种方法,来充当了UVM世界中的核心角色。
    • 在uvm_pkg中,有且只有一个顶层类uvm_root所例化的对象,即uvm_top。
    • uvm_top承担的核心职责包括:
    1. 作为隐形的UVM世界顶层,任何其它的组件实例都在它之下,通过创建组件时指定parent来构成层次
    2. 如果parent设定为null,那么它将作为uvm_top的子组件
    3.  phase控制。控制所有组件的phase顺序。
    4. 索引功能。通过层次名称来索引组件实例。(建议使用字符串索引)
    5. 报告配置。通过uvm_top来全局配置报告的繁简度(verbosity) 。
    6. 全局报告设备。由于可以全局访问到uvm_top实例,因此UVM报告设备在组件内部和组件外部(例如module和sequence)都可以访问。
    • 通过uvm_top调用方法run_test(test_name),uvm_top做了如下的初始化:
    1. 得到正确的test_nameo。
    2. 初始化objection机制。 (控制仿真退出)
    3. 创建uvm_test_top实例。
    4. 调用phase控制方法,安排所有组件的phase方法执行顺序。
    5. 等待所有phase执行结束,关闭phase控制进程。
    6. 报告总结和结束仿真。

    uvm仿真结束

    • UVM-1.1之后,结束仿真的机制有且只有一种,那就是利用objection挂起机制来控制仿真结束。
    • uvm_objection类提供了一种供所有component和sequence共享的计数器。如果有组件来挂起objection,那么它还应该记得落下objection。
    • 参与到objection机制中的参与组件,可以独立的各自挂起objection,(run phase阶段,必须有组件挂起objection)来防止run phase退出,但是只有这些组件都落下objection后,uvm_objection共享的counter才会变为0,这意味run phase退出的条件满足,因此可以退出run phase。
    • 对于uvm_objection类,用来反停止的控制方法包括:
      raise_objection ( uvm_object obj = null, string description ="" , int count= 1)挂起objection
      drop_objection ( uvm_object obj= null, string description = "" , int count= 1)落下objection
      set_drain_time ( uvm_object obj= null, time drain)设置退出时间
    • 对这几种方法,在实际应用中的建议有:
    1. 对于component()而言,用户可以在run_phase()中使用phase.raise_objection() /phase.drop_objection()来控制run phase退出。(phase.raise_objection(this)  表示当前组件在当前phase挂起objection,并且须在耗时前)run phase中,没有挂起objection,那么里面任何语句都不会执行,会退出???
    2. 用户最好为description字符串参数提供说明,这有利于后期的调试。
    3. 应该使用默认count值。
    4. 对于uvm_top或者uvm_test_top应该尽可能少地使用set_drain_time()。
    • 看起来挂起objection已经晚了,因为run phase还是立即退出了。这是因为在挂起objection之前已经运行了1ps,而处于fork-join_none的run_phase任务在0时刻被调用后,如果run phase退出机制在0时刻没有发现任何挂起的objection,那么就会终止所有的run_phase()任务,继而转入了extract phaseo
    • 所以如果要在component中挂起objection,建议在一进入run_phase()后就挂起,保证objection counter及时被增加。另外,用户需要习惯在sequence中挂起objection,由于sequence不是uvm_component类,而是uvm_object类,因此它只有body()方法,而没有run_phase()方法。
    • 所以在sequence中使用objection机制时,可以在body()中的首尾部分挂起和落下objection。

    config机制

    • 在验证环境的创建过程build phase中,除了组件的实例化,配置也是必不可少的
    • 为了验证环境的复用性,通过外部的参数配置,使得环境在创建时可以根据不同参数来选择创建的组件类型、组件实例数目、组件之间的连接以及组件的运行模式等。
    • 在更细致的环境调节(environment tuning)中有更多的变量需要配置,例如for-loop的阈值、字符串名称、随机变量的生成比重等。
    • 比起重新编译来调节变量,如果在仿真中可以通过变量设置来修改环境,那么就更灵活了,而UVM config机制正提供了这样的便捷。
    • 在UVM提供了uvm_config _db配置类以及几种方便的变量设置方法来实现仿真时的环境控制,常见的uvm_config_db类的使用方式包括:(从任何层次传递到其他层次)
    1. 传递virtual interface到环境中
    2. 设置单一变量值,例如int、string、enum等
    3. 传递配置对象(config object)到环境

    T表示传递的类型,前三个参数构成层次

    • uvm_config_db#(T) : :set(uvm_uvm_component cntxt,string inst_name,string fieid_ name , T value) ;   一般在顶层set                实例                   实例下的名称              某一个变量
    • uvm_config_db#(T) : :get(uvm_component cntxt, string inst_name,string fieid_name , inout T value) ;   底部get                  component句柄               实例的名称        变量名称

    interface传递

    • interface传递可以很好地解决了连接硬件世界和软件世界。
    • 而在之前SV验证模块中,虽然SV可以通过层次化的interface的索引来完成了传递,但是这种方式不利于软件环境的封装和复用
    • UVM的uvm_config_db使得接口的传递和获取彻底分离开来。在实现接口传递的过程中需要注意:
    1. 接口传递应该发生在run_test()之前。这保证了在进入build phase之前,virtual interface已经被传递到uvm_config _db中。(set发生在get之前,run_test要执行build)
    2. 用户应当把interface与virtual interface的声明区分开来,在传递过程中的类型应当为virtual interface,即实际接口的句柄。
    1. class comp1 extends uvm_component;
    2. virtual intf1 vif;
    3. ……
    4. uvm_config_db#(virtual intf1)::get(this,"","vif",vif) //comp1.vif具体为
    5. //root.test.comp1.vif
    6. ……
    7. endclass
    8. intf1 intf();
    9. initial begin root句柄 这个参数为接口指针
    10. uvm_config_db#(virtual intf1)::set(uvm_root::get(),"uvm_test_top.c1","vif",intf);
    11. run_test("test1");
    12. end

    config_db里为关联数组(path value),可以理解为中间的存放。

    变量设置

    • 在各个test中,可以在build phase对底层组件的变量加以配置,进而在环境例化之前完成配置,使得环境可以按照预期运行。先set,后创建。
      1. uvm_config_db#(int)::get(this,"","vall",val)
      2. uvm_config_db#(int)::set(this,"c1","vall",100);
      3. c1=comp1::type_id::create("c1",this); //set,再create,因为创建会执行build_phase,也就会
      4. //执行里面的get

    object传递

    • 在test配置中,需要配置的参数不只是数量多,而且可能还分属于不同的组件。
    • 那么如果对这么多层次中的变量做出类似上面的变量设置,那会需要更多的代码,容易出错还不易于复用,甚至底层组件的变量被删除后,也无法通过uvm_config_db:set()得知配置是否成功。
    • 然而如果将每个组件中的变量加以整合,首先放置到一个uvm_object中,再对中心化的配置对象进行传递,那么将会更有利于整体环境的修改维护。

    set和get类型(括号内的)必须一致,不能一个子类一个父类

    1. class config1 extends uvm_object;
    2. ……
    3. endclass
    4. class comp1 extends uvm_component;
    5. config cfg;
    6. function void build_phase(uvm_phase phase);
    7. uvm_object tmp;
    8. uvm_config_db(uvm_object)::get(this,"","cfg",tmp); //注意这里get到的父类句柄,所以
    9. void!($cast(cfg,tmp); //进行cast 更方便的是传递子类
    10. endfunction
    11. endclass
    12. config1 cfg1;
    13. uvm_config_db(uvm_object)::set(this,"c1","cfg",cfg1); //子类句柄按照父类句柄传递

    总结

    在使用uvm_config_db:set()/ge()t时,实际发生了这些后台操作:

    • uvm_config_db:set()通过层次和变量名,将这些信息放置到uvm_pkg唯一的全局变量uvm_pkg::uvm_resources
    • 全局变量uvm_resources用来存储和释放配置资源信息(resourceinformation)。uvm_resources是uvm_resource_pool类的全局唯一实例,该实例中有两个resource数组用来存放配置信息,这两个数组中一个由层次名字索引,一个由类型索引,通过这两个关联数组可以存放通过层次配置的信息。
    • 同时,底层的组件也可以通过层次或者类型来取得来自高层的配置信息。这种方式使信息的配置和获取得到剥离,便于调试复用。
    • 在使用uvm_config_db:.get()方法时,通过传递的参数构成索引层次,然后在uvm_resource已有的配置信息池中索引该配置,如果索引到,方法返回1,否则返回0。

    建议

    • 在使用set()/get()方法时,传递的参数类型应当上下保持一致。对于uvm_obiect等实例的传递,如果get类型与set类型不一致,应当首先通过$cast()完成类型转换,再对类型转换后的对象进行操作。
    • Set()/get()方法传递的参数可以使用通配符“*”来表示任意的层次,类似于正则表达式的用法。尚时用户需要懂得“*.compi”“*compl”的区别,前者表示在自前层次以下所有名称为“compl”的组件,而后者表示包括当前层次及当前层次以下所有名为“comp1”的组件。
    • module环境中如果要使用uvm_config db:set(),则传递的第一个参数uvm_component cntxt参数一般用来表示当前的层次。如果当前层次为最高层,用户可以设置为null,也可以设置为uvm_root:get()来表示uvm_root的全局顶层实例
    • 在使用配置变量时,应当确保先进行uvm_config_db:get()操作,在获得了正确的配置值以后再使用。
    • 应当尽量确保uvn_config_db::set()方法在相关配置组件创建前调用。这是因为只有先完成配置,相关组件在例化前才可以得到配置值继而正确地例化
    • 在set()方法第一个参数使用当前层次的前提下;对于同一组件的同一个变量,如果有多个高层组件对该变量进行设置,那么较高层组件的配置会覆盖较低层的配置;但是如果是同一层次组件对该变量进行多次配置时,应该遵循后面的配置会覆盖前面的配置。
    • 用户应该在使用uvm_config dbget()方法时,添加便于调试的语句,例如通过UVM报告信息得知get()方法中的配置变量是杏从uvm_config_db获取到,如果没有获取,是否需要采取其它措施。

    消息管理

    • 在一个好的验证系统应该具有消息管理特性,它们是:
    1. 通过—种标准化的方式打印信息
    2. 过滤(重要级别)信息
    3. 打印通道
    • 这些特性在UVM中均有支持,UVM提供了一系列丰富的类和方法来生成和过滤消息︰
    1. 消息方法
    2. 消息处理
    3. 消息机制

    消息方法

    • 在UVM环境中或者环境外,只要有引入uvm_pkg,均可以通过下面的方法来按照消息的严重级别和冗余度来打印消息。

    function void uvm _report_info(string id,string message,int verbosity = UVM_MEDIUM,string filename = "", int line = 0) ;
    function void uvm _report_warning(string id, string message, int verbosity = UVM_MEDIUM,string filename = "" , int line = 0);                                                                                            function void uvm_report_error(string id, string message,intverbosity = UVM_LOW, string filename = "", int line = 0);
    function void uvm_report_fatal(string id, string message,int verbosity = UVM_NONE,string filename = "", int line = 0) ;
    id:标记,表明消息属于什么类型;message:消息主体,message body;verbosity:重要不重要,冗余程度;filename、line:消息来自哪个文件哪一行?系统会自动填好。

    四个消息函数有若干共同的信息,它们是严重级别(severity)、冗余度(verbosity) 、消息ID、消息、文件名和行号:

    • 严重级别:从函数名本身也可以得出,这四个严重级别分别是UVM_INFO、UVM_WARNING、UVM_ERROR、UVM_FATAL。不同的严重级别在打印的消息中也会有不同的指示来区别,同时仿真器对不同严重级别消息的处理方式也不一样。例如对于UVM_FATAL的消息,默认情况下仿真会停止。
    • 消息ID:该ID可以是任意的字符串,用来标记该消息。这个标记会同消息本身打印出来,同时不同的标记也可以用来进行消息处理。
    • 消息:即消息文本的主体。
    • 冗余度:冗余度与消息处理中的过滤直接相关。冗余度的设置如果低于过滤的开关,那么该消息会打印出来,否则不会被打印出来。但是无论信息是否会被打印出来,这都与对消息采取的其它措施没有关系,例如仿真停止。(重要性:NONE(最重要)、LOW、MEDIUM、HIGH、FULL、DEBUG)
    • 文件名和行号:这些信息用来提供消息发生时所在的文件和行号。用户可以使用默认值,而UVM后台会自动填补它们原本的文件名和行号,同时也在打印时将文件名和行号输出。

    消息处理

    与每一条消息对应的是如何处理这些消息。通常情况下,消息处理的方式是同消息的严重级别对应的。如果用户有额外的需求,也可以修改对各个严重级别的消息处理方式。

    不同的严重级别信息,用户可以使用默认的消息处理方式

    uvm_error里面有uvm_count,错误进行计数,当达到一定数值时,会退出仿真。uvm_fatal一定会退出仿真

    消息宏

    • 如果要做自定义的消息处理方式,用户可以通过uvm_report_object类提供的方法进行配置。
    • uvm_report_object类是间于uvm_object类与uvm_component类之间的中间类,它的主要功能是完成消息打印和管理。
    • UVM也提供了一些宏来对应上面的消息方法,用户也可以使用这些宏来处理消息(建议)

    uvm_void-----uvm_object-----uvm_report_object-----uvm_component

     消息机制

    • 消息处理是由uvm_report_handler类来完成的,而每一个uvm_report_object类中都有一个uvm_report_handler实例
    • 上面的uvm_report_object消息处理方法或者uvm_component消息处理方法,都是针对于这些uvm_report_handler做出的配置。
    • 除了上面的常见使用方法,用户还可以做出更高级的消息控制。例如,当UVM_ERROR出现之后,仿真默认会停止,这是由于设置了UVM_ERROR的处理方式是UVM_COUNT数量达到上限(默认为1),即停止仿真。可以通过set_max_quit_count来修改UVM_COUNT值。

    回调函数

    消息用户在处理信息时还希望做出额外的处理,这时回调函数就显得很有必要了,uvm_report_object类提供了下面的回调函数满足用户更多的需求:
    function bit report_hook(string id,string message,int verbosity , string filename , int line) ;
    function bit report_info_hook (string id, string message,int verbosity , string filename , int line) ;
    function bit report_warning_hook(string id,string message,int verbosity , string filename , int line); 
    function bit report_error_hook(string id,string message,int verbosity , string filename , int line) ;
    function bit report_fatal_hook(string id, string message,int verbosity , string filename , int line) ;

    • report_hook()函数通过结合消息管理时的UVM_CALL_HOOK参数,结合用户自定义的回调函数,就可以实现更丰富的配置。
    • 这样用户在调用回调函数时,首先会调用report_hook()函数,接下来才按照severity级别来选择更细致的回调函数report_SEVERITY_hook()。
    • 默认情况下,report_hook()函数返回值为1,进而再转入severity hook函数
    • 如果report_hook()函数由用户自定义且返回0的话,那么后续report_SEVERITY_hook()函数不会执行。
    1. set_report_serverity_action(UVM_ERROR,UVM_DISPLAY|UVM_CALL_HOOK);//对于error,不仅要打印出来
    2. //还会调用回调函数
    3. set_report_verbosity_level(UVM_LOW); //只打印low以及重要程度更高的none,其余的过滤

    对于不同id,使用if、case判断进行不同处理(使用不同hook)。

    • 除了每一个uvm_report_object中都内置一个uvm_report_handler实例之外,所有的uvm_report_handler实例也都依赖于uvm_pkg中uvm_report_server的唯—实例,但是该实例并没有作为全局变量,直接暴露给用户,需要用户自行调用uvm_report_server::get_server()方法来获取。(每一个component都有消息处理的实例,可以处理消息。消息最终是全局打印、处理的。report_handler要从report_server进行申请处理)
    • uvm_report_server(同factory一样也在coreservice下)是一个全局的消息处理设备,用来处理从所有uvm_report_hanlder中产生的消息。这个唯一的report server之所以没有暴露在uvm_pkg中供用户使用,一个原因在于对消息的处理方式。

    uvm_void----uvm_object---uvm_report_server---uvm_default_report_server

  • 相关阅读:
    自然语言处理(七):来自Transformers的双向编码器表示(BERT)
    计算机毕业设计之java+ssm 精准扶贫系统
    wifi6 feature 详解
    基于Debian搭建Hyperledger Fabric 2.4开发环境及运行简单案例
    day44((VueJS)路由的懒加载使用 路由的元信息(meta) 路由守卫函数 vant组件库的应用)
    Codeforces Round 894 div3 题解 | JorbanS
    聪明人和傻子和程序员
    文心一言 VS 讯飞星火 VS chatgpt (92)-- 算法导论8.3 5题
    Leetcode165. 比较版本号
    90、Redis 的 value 所支持的数据类型(String、List、Set、Zset、Hash)---->Hash 相关命令
  • 原文地址:https://blog.csdn.net/qq_44455456/article/details/126530186