zoukankan      html  css  js  c++  java
  • 3.小白学uvm验证

    1.phase机制

      uvm 验证环境通过 phase 机制,引入了一套自动化的运行流程,通过该机制我们可以清晰的了解 UVM 仿真阶段的层次化,因为 verilog 中有阻塞和非阻塞赋值,相应的仿真平台中,也要实现 NBA 区域 和 Actice 区域,在不同的 phase 区域中做不同的事情,可以避免竞争关系导致的变量值得不确定性; 用户只需要在需要添加用户代码的区别填充即可,降低了仿真平台的调试成本

      如下图所示,这是 UVM 验证环境中全部 phase,从上至下,依次顺序执行。其中有些 phase 为 task 函数类型,另一些为 function 函数类型。

    耗时 phase (图中灰色部分-task)
    • run_phase 和其十二个子 phase,推荐使用 run_phase,在使用子 phase 时尽量不要和 run_phase 同时使用,混用是可能导致 phase 直接的跳转环境混乱,但并不是混合使用一定会出现问题。其运行关系如下图折叠代码块
    run_phase 运行机制
    fork
        begin
            run_phase();
        end
        begin
            pre_reset_phase();
            reset_phase();
            post_reset_phase();
            pre_configure_phase();
            configure_phase();
            post_configure_phase();
            pre_main_phase();
            main_phase();
            post_main_phase();
            pre_shutdown_phase();
            shutdown_phase();
            post_shutdown_phase();
        end
    join

      稍微解释一下上述代码含义,fork-join 表示两个 begin-end 是同时开始运行的,当 run_phase() 和 pre_reset_phase() 同时存在时,run_phase() 和 pre_reset_phase() 在同一个时刻点开始运行,在 run_phase() 和 pre_reset_phase() 两个 phase 均运行结束以后,主 phase 才会继续执行 extract_phase()。耗时的 phase 的运行是在 uvm_tree 中从下往上运行。

    不耗时 phase (图中白色部分-function)
    • 包含 build_phase()、connect_phase()、end_of_elaboartion_phase()、start_of_simulation_phase()、extract_phase()、check_phase()、report_phase()、final_phase();
    • 不耗时的几个 phase 在 uvm_tree 中是从上往下执行的,比如说 build_phase,这个 phase 主要用来实例化各个组件环境,理论上为了 uvm_tree 的顺利构建应该从上往下执行。 并不一定上述所有的 phase 都会用到,考虑个小问题,已经有了 9 主 phase,为什么还要引入 12 子phase 呢,这是为了有时候需要更加细化的进行一些 driver 工作,比如对 DUT 进行 reset 复位,如果我们在 reset_phase 进行了复位相关的操作,我们在运行到后续 phase 时,只需要 jump 到 reset_phase, 后续 phase 会依次重复执行依次,相当于对 DUT 实现了复位操作。
    phase 机制-执行顺序

      前面只提到 phase 的执行顺序是从上而下执行,以 uvm-tree 的角度叙述则是,从树顶到数叶自上而下执行,如下图所示,uvm_top 是最先开始执行的,后续依次往下执行。

      似乎上述的执行过程又存在问题,下来我们来细说一下上述过程,首先执行的是 build_phase() ,该 phase 用于构建 uvm-tree 的结构,前面我们提到 uvm_component 组件类必须在 build_phase 中实例化,也是这个原因,因为 build_phase() 的主要用途就是实例化组件类,构建 tree; 不同层次所有组件build_phase() 从空间(uvm-tree)上从上而下执行从树根到树叶的全部组件类 build_phase(),而同层次的组件类则是按照 new/create 是自定的名字以字典序按照前后顺序自动执行 build_phase()其他不耗时的 function phase 的执行过程则相反,比如先执行 driver 这层记得 connect_phase(), 再执行 agent 这一级的 connect_phase()

      耗时 phase 的执行和上述两种又有一些区别,需要明确以下几个方面

     a. 对于同一组件,其 run_phase() 和 pre_reset_phase() 属于两个不同的线程,他们同一个时刻点启动, 但 12 个细分 phase 顺序执行;
     b. 对于同一层级的组件,其 run_phase() 和 pre_reset_phase() 属于两个不同的线程,他们同一个时刻点先后启动, 但 12 个细分 phase 顺序执行;
     c. 对于不同层级别的 component 在下一级别启动之后,才会启动;
     d. run_phase() 和 post_shutdown_phase() 需要都执行结束才会进入 extra_phase() 中。

      耗时 phase 的执行过程可以理解为自下而上启动,同时执行

      假设上图中 drv component 其 main_phase() 在 0 时刻开始执行,需要100个时刻才能结束;scb component 其 main_phase() 也从在 0 时刻开始执行, 且需要耗时最长,假设需要200个时刻才能结束(其他组件的 main_phase()也包含耗时,但均未超过 scb 组件的耗时),整个验证平台的 main_phase() 要在第200个时刻才能切换到 post_main_phase(),即 drv 在100时刻执行完毕后,不会马上跳到 post_main_phase(),需要等到所有组件中耗时最长的 mian_phase() 执行结束后,在 200 时刻,进入 post_main_phase(),其启动和执行的过程也是自下而上启动,同时执行

    2.config_db机制

      在 uvm_tree 树中,越上层就越接近“用户场景”,通常不同的场景公用一套 driver 和 monitor 函数,通过设置不同的参数,产生不同的场景驱动时序,假设我们的 uvm-tree 如下图所示,上层组件 env 要给“树叶”层的组件 drv 传递参数 pre_num,这个时候就需要用到 config_db 给我们传递信息。

    config_db两大函数-set() 和get()

      config_db 的主要用途就是进行全局参数的传递共享,主要包含 set() 和 get() 函数,其中 set()用于在 config 全局数据库(config database)中创建一个条目(entries),当然前提是没有数据库中未包含该条目,如果已经存在,则更新它,get() 则用于从数据库中取出最新的 entries

    uvm_config_db #(T)::set(...)
    uvm_config_db #(T)::get(...)
      注意点
        a.其中T,需要设置或者取出的 entries 的参数类型
        b.get() 必须在 set() 之后进行,通常 set() 一般在 build_phase()中进行,get() 一般在其他 phase 中,或者在以下级别的build_phase中完成。

      我们继续上述话题,我们要从 env 中给 drv 传递 pre_num 参数, 中间跨过了中间组件 i_agt。以下是 set()函数的一种写法:

    uvm_config_db set 示例
    function void my_env::build_phase();
        ... 
        int pre_num = 25;
        ...
        uvm_config_db#(int)::set(this,"env.i_agt.drv","pre_num",100)
        ...
    endfuction

      上述代码含义为,在组件 my_env 的 build_phase 中包含 pre_num = 25 的局部变量,同时通过 uvm_config_db 对 pre_num 进行设置,下面对立面的具体参数进行解释如下:

    int : T 的具体参数类型,该类型必须和第四个参数 100 ,的类型一致,否则会报错;
    this : 该参数必须为组件类型的指针, 当设置为 NULL 时,会默认 uvm_root 组件
    "env.i_agt_drv": 欲设置参数的绝对路径,其类型为字符串类型,这也是一个隐患,如果你拼写错了,你不仅无法取到该参数,而且编译器还不报错。
    100: 为欲设置的参数值。

      第二个参数+第三个参数=目标参数的绝对路径,以我个人的理解是,UVM在 全局 config_db 数据库中,创建一个名字为 pre_num 的config_db 数据库 entries,该条目的索引为“第二个参数+第三个参数组成的字符串”;第二个参数和第三个参数可以很灵活的设置,比如以下骚操作:

    uvm_config_db set 示例
    function void my_env::build_phase();
        ... 
        int pre_num = 25;
        ...
        uvm_config_db#(int)::set(null,"uvm_test_top.env.i_agt.drv","pre_num",100)
        ...
    endfuction

      你也可以在 drv 组件中正确的取到 pre_num 参数值。大家似乎绝对还不透彻,可能在以后的使用过程中造成错误,那我再按照我的理解说一条“潜规则”,目标参数的绝对路径对应的字符串的设置“规则”,在 get() 函数执行时,该字符串对应的各个组件均已经被实例化,且实例化名也字符串中对应的一致,你就可以 get() 到设置的参数。有一点忘了提了,uvm_config_db#(int)::set(null,"uvm_test_top.env.i_agt.drv","pre_num",100) 不会影响同名的参数的值,即在此次 set() 函数的执行不会将局部变量 pre_num 由 25 该成 100;

      既然已经完成 set 操作了,现在再来说说 get;在 dirver 中你可以通过以下代码来取 pre_num 参数:

    uvm_config_db get 示例
    function void my_drv::build_phase();
        ... 
        int pre_num_A;
        ...
        uvm_config_db#(int)::get(this,"","pre_num",pre_num_A)
        ...
    endfuction

      上述代码的含义是,通过 get() 函数将 config_db 数据中,索引为 this ,条目名为 pre_num的值,取出来给 pre_num_A;因为在 my_drv 组件的 build_phase 执行时,字符串 uvm_test_top.env.i_agt.drv 中的各个组件均已经实例化,而且在 my_drv 中 this 就是表示 uvm_test_top.env.i_agt.drv 对应的组件(uvm-tree绝对路径)。

    config_db中一大坑-只 set 不 get的情况

      一般情况下是 set 和 get 成对出现,但是有个特殊情况是,只 set 不 get 值,也能完成变量信息的传递,下来来简单说一下这个坑。假设 pre_num的 set() 还是如上述情况(情况一),而 drv 如下定义:

    config_db 特殊示例
    class my_driver extend uvm_driver;
        int pre_num;
    
    `uvm_component_utils_begin(my_driver)
        `uvm_field_int(pre_num, UVM_ALL_ON)
    `uvm_component_utils_end
    
    function void my_drv::new(string name = "my_driver", uvm_component parent = null);
        ...
        super.new(name,phase);
       ...
    endfuction
    function void my_drv::build_phase();
        super.build_phase(phase);
        ...
    endfuction
    

    endclass

       通过上述代码,就可以省去 get 操作,在上述代码中,首先定义了一个和entries中同名的变量 pre_num ,第二通过 uvm_field_int 对 pre_num 变量进行注册, 第三在 build_phase 中调用了 super.build_phase(),通过这操作者三步,你就可以省略 get()。但是我的建议,咱们还是老老实实的 get 省的给后续看你代码的人造成困惑。

       最后再提一点,当有多个 set 对同变量进行设置时,get 会取到那个值,比如我们同时在 i_agt 和 uvm_test_top,中对 pre_num 设置(目标还是给 drv),这个情况 uvm 已经考虑过了,因为 uvm_test_top 离用户进,所以它的权限最高,我们会取到 uvm_test_top 组件中设置到的值,即谁最权威,听谁的;第二在同一个层次对一个变量 进行多次 set, 这个时候就看 进行 get 操作时,那个 set 操作的时间离得最短,即谁时间最近,听谁的
       至于其他的什么内容,如 monitor 中 set 值,给 driver; 我奉劝一句,不要玩的太骚,小心走火。

  • 相关阅读:
    398. Random Pick Index
    382. Linked List Random Node
    645. Set Mismatch
    174. Dungeon Game
    264. Ugly Number II
    115. Distinct Subsequences
    372. Super Pow
    LeetCode 242 有效的字母异位词
    LeetCode 78 子集
    LeetCode 404 左叶子之和
  • 原文地址:https://www.cnblogs.com/dreamCll/p/11780938.html
Copyright © 2011-2022 走看看