美文网首页UVM
1 UVM学习笔记--phase机制(转)

1 UVM学习笔记--phase机制(转)

作者: 嬉笑的皮皮虾 | 来源:发表于2020-07-09 18:56 被阅读0次

    先来看一看,UVM的phase有哪些?

    phase 函数/任务 执行顺序 功能 典型应用
    build 函数 自顶向下 创建和配置测试平台的结构 创建组件和寄存器模型,设置或者获取配置
    connect 函数 自底向上 建立组件之间的连接 连接TLM/TLM2的端口,连接寄存器模型和adapter
    end_of_elaboration 函数 自底向上 测试环境的微调 显示环境结构,打开文件,为组件添加额外配置
    start_of_simulation 函数 自底向上 准备测试环境的仿真 显示环境结构,设置断点,设置初始运行的配置值
    run 任务 自底向上 激励设计 提供激励、采集数据和数据比较,与OVM兼容
    extract 函数 自底向上 从测试环境中收集数据 从测试平台提取剩余数据,从设计观察最终状态
    check 函数 自底向上 检查任何不期望的行为 检查不期望的数据
    report 函数 自底向上 报告测试结果 报告测试结果,将结果写入到文件中
    final 函数 自顶向下 完成测试活动结束仿真 关闭文件,结束联合仿真引擎

    1.UVM phase 概览

    UVM采用phase机制来自动化运行testbench各个仿真过程。UVM phase支持显示或隐式的同步方案,运行过程中的线程控制和跳转。用户只要把代码填入对应的phase,这些代码就会自动在正确的时间执行。各个phase执行顺序如下图所示:

    image.png

    相较于OVM,UVM新增了12个小的task phase,如下图:

    image.png

    其中run_phase和uvm新增加的12个小phase是并行执行的。

    2 按是否消耗仿真时间,所有phase可以分成两大类

    <1>  function phase:不消耗仿真时间,而其也可分成两大类:
          a. 继承自uvm_bottomup_phase, 在UVM component树中,自下而上的执行, 如connect_phase
          b. 继承自uvm_topdown_phase, 在UVM component树中,自上而下执行, 如build_phase
    <2>  task phase:消耗仿真时间的,也称动态运行(run-time)phase. 
    

    下图是各个phase的继承关系,从中可以看出

    image.png

    自上而下(top-down) function phase:build和final phase。

    自下而上(bottom-up)f unction phase: connect, end_of_elaboration,start_of_simulation, extract, check, report。

    task phase: run_phase以及其他12个小phase: pre_reset, reset_phase, post_reset, pre_configure, configure, post_configure,
    pre_main, main, post_main, pre_shutdown, shutdown, post_shutdown, 如下图:


    image.png

    3 task phase的同步

    一个UVM验证平台有许多component组成,每个component都有自己的run_phase,以及从pre_reset 到post_shuddown的12个小phase。只有所有component的一个小task phase 完成,整个仿真平台才开始下一个小task phase的执行。 各个component的run_phase之间,以及run_phase于最后一个小phase--post_shutdown_phase之间,都有这样的同步。


    image.png

    4. super.xxx_phase

    除了super.build_phase,其他super.xxx_phase几乎没做任何事情,因此,除了build_phase,其他phase可不必加super.xxx_phase.

    在super.build_phase中,主要完成自动获取config_db中参数的功能,如果自定义的component无需获取任何参数,也可省略。

    5. phase的跳转

    默认情况下各phase是从上到下按时间顺序执行,但可以自定义做必要的跳转,如在main_phase执行过程中,突然遇到reset信号被置起,可以用jump()实现从mian_phase到reset_phase的跳转:

    phase.jump(uvm_reset_phase::get())
    
    task my_driver::main_phase(uvm_phase phase);
      `uvm_info("driver", "main phase", UVM_LOW)
      fork
        while(1) begin
          seq_item_port.get_next_item(req);
          drive_one_pkt(req);
          seq_item_port.item_done();
        end
        begin
          @(negedge vif.rst_n);
          phase.jump(uvm_reset_phase::get());
        end
      join
    endtask
    

    跳转的限制

    不能跳转到build到start_of_function的function phase, 也不能跳转到run_phase. 从pre_reset_phase后的所有phase都可以作为jump()的参数。 除了向前跳转,也可向后跳转。除了向run-time phase跳转,甚至可以向final_phase等function phase跳转。

    phase的调试

    在命令行加UVM_PHASE_TRACE,可以将进入和退出个phase的信息打印到log中。
    <sim command> +UVM_PHASE_TRACE

    在指定phase设置verbosity:
    <sim command> +uvm_set_verbosity=<comp>,<id>,<verbosity>,<phase>

      例如:simv   +uvm_set_verbosity=uvm_test_top.env.mdl,my_model,UVM_NONE,main  +UVM_TESTNAME=my_case0 +UVM_PHASE_TRACE  -l tmp.log
    
       注意,其中参数<phase> 不需要phase后缀,如上面例子将uvm_test_top.env.mdl的main_phase中打印的信息屏蔽掉,命令行里用的是+uvm_set_verbosity=uvm_test_top.env.mdl,my_model,UVM_NONE,main 。
    

    设置timeout时间
    1. 通过命令行:<sim command> +UVM_TIMEOUT=<timeout>,<overridable>
    如<sim command> +UVM_TIMEOUT="300ns, YES"

      2.通过在base test中使用set_timeout(): uvm_top.set_timeout(500ns,0);
    
          必要时需要修改宏定义:`define UVM_DEFAULT_TIMEOUT 9200s
    

    ————————————————
    版权声明:本文为CSDN博主「wonder_coole」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/wonder_coole/java/article/details/90443373

    code

    关于执行的顺序,可以从下面这段简单的例码中得到佐证:

    module common_phase_order;
    import uvm_pkg::*;
    `include "uvm_macros.svh"
     
    class subcomp extends uvm_component;
    `uvm_component_utils(subcomp)
     
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
     
    function void build_phase(uvm_phase phase);
        `uvm_info("build_phase", "", UVM_LOW)
    endfunction
     
    function void connect_phase(uvm_phase phase);
        `uvm_info("connect_phase", "", UVM_LOW)
    endfunction
     
    function void end_of_elaboration_phase(uvm_phase phase);
        `uvm_info("end_of_elaboration_phase", "", UVM_LOW)
    endfunction
     
    function void start_of_simulation_phase(uvm_phase phase);
        `uvm_info("start_of_simulation_phase", "", UVM_LOW)
    endfunction
     
    task run_phase(uvm_phase phase);
        `uvm_info("run_phase", "", UVM_LOW)
    endtask
     
    function void extract_phase(uvm_phase phase);
        `uvm_info("extract_phase", "", UVM_LOW)
    endfunction
     
    function void check_phase(uvm_phase phase);
        `uvm_info("check_phase", "", UVM_LOW)
    endfunction
     
    function void report_phase(uvm_phase phase);
        `uvm_info("report_phase", "", UVM_LOW)
    endfunction
     
    function void final_phase(uvm_phase phase);
        `uvm_info("final_phase", "", UVM_LOW)
    endfunction
    endclass
     
    class topcomp extends subcomp;
    subcomp c1, c2;
    `uvm_component_utils(topcomp)
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
     
    function void build_phase(uvm_phase phase);
        `uvm_info("build_phase", "", UVM_LOW)
        c1 = subcomp::type_id::create("c1", this);
        c2 = subcomp::type_id::create("c2", this);
    endfunction
    endclass
     
    class test1 extends uvm_test;
    topcomp t1;
    `uvm_component_utils(test1)
     
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
     
    function void build_phase(uvm_phase phase);
        t1 = topcomp::type_id::create("t1", this);
    endfunction
    endclass
     
    initial begin
    //t1 = new("t1", null);
    run_test("test1");
    end
     
    endmodule
    

    输出结果:

    UVM_INFO @ 0: reporter [RNTST] Running test test1...
    UVM_INFO @ 0: uvm_test_top.t1 [build_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c1 [build_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c2 [build_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c1 [connect_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c2 [connect_phase]
    UVM_INFO @ 0: uvm_test_top.t1 [connect_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c1 [end_of_elaboration_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c2 [end_of_elaboration_phase]
    UVM_INFO @ 0: uvm_test_top.t1 [end_of_elaboration_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c1 [start_of_simulation_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c2 [start_of_simulation_phase]
    UVM_INFO @ 0: uvm_test_top.t1 [start_of_simulation_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c1 [run_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c2 [run_phase]
    UVM_INFO @ 0: uvm_test_top.t1 [run_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c1 [extract_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c2 [extract_phase]
    UVM_INFO @ 0: uvm_test_top.t1 [extract_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c1 [check_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c2 [check_phase]
    UVM_INFO @ 0: uvm_test_top.t1 [check_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c1 [report_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c2 [report_phase]
    UVM_INFO @ 0: uvm_test_top.t1 [report_phase]
    UVM_INFO @ 0: uvm_test_top.t1 [final_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c1 [final_phase]
    UVM_INFO @ 0: uvm_test_top.t1.c2 [final_phase]
    

    从这个例子可以看出,上面的九个phase,对于一个测试环境的声明周期而言,是有固定的执行先后顺序的;同时,对于处于同一个phase的组件之间,执行也会按照层次的顺序或者自顶向下、或者自底向上来执行。这个简单的环境中,顶层测试组件test1中,例化了一个t1组件,而t1组件内又进一步例化了c1和c2组件。从执行的打印结果来看,需要注意的地方有:

    对于build phase,执行顺序按照自顶向下,这符合验证结构建设的逻辑。因为只有先创建高层的组件,才会创建空间来容纳低层的组件。
    只有uvm_component及其继承与uvm_component的子类,才会按照phase机制将上面九个phase先后执行完毕。
    上面介绍的九个phase中,常用的phase包括build、connect、run和report,它们分别完成了组件的建立、连接、运行和报告。这些phase在uvm_component中通过_phase的后缀完成了虚方法的定义,比如build_phase()中可以定义一些例化组件和配置的任务。在这九个phase中,只有run_phase方法是一个可以耗时的任务,这意味着该方法中可以完成一些等待、激励、采样的任务。对于其它phase对应的方法,都是函数,必须即时返回(0耗时)。

    在run_phase中,用户如果要完成测试,则通常需要经历下面的激励序列:

    • 上电
    • 复位
    • 寄存器配置
    • 主要测试内容
    • 等待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_phase
    • main_phase
    • post_main_phase
    • pre_shutdown_phase
    • shutdown_phase
    • post_shutdown_phase

    上面的12个phase的执行顺序也是前后排列的。那么这12个phase与run_phase是什么关系呢?我们通过这段例码来看看:

    module uvm_phase_order;
    import uvm_pkg::*;
    `include "uvm_macros.svh"
    class test1 extends uvm_test;
    `uvm_component_utils(test1)
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void start_of_simulation_phase(uvm_phase phase);
        `uvm_info("start_of_simulation", "", UVM_LOW)
    endfunction
    task run_phase(uvm_phase phase);
        phase.raise_objection(this);
        `uvm_info("run_phase", "entered ..", UVM_LOW)
        #1us;
        `uvm_info("run_phase", "exited ..", UVM_LOW)
        phase.drop_objection(this);
    endtask
    task reset_phase(uvm_phase phase);
        `uvm_info("reset_phase", "", UVM_LOW)
    endtask
    task configure_phase(uvm_phase phase);
        `uvm_info("configure_phase", "", UVM_LOW)
    endtask
    task main_phase(uvm_phase phase);
        `uvm_info("main_phase", "", UVM_LOW)
    endtask
    task shutdown_phase(uvm_phase phase);
        `uvm_info("shutdown_phase", "", UVM_LOW)
    endtask
    function void extract_phase(uvm_phase phase);
        `uvm_info("extract_phase", "", UVM_LOW)
    endfunction
    endclass
    initial begin
        run_test("test1");
    end
    endmodule
    

    输出结果:

    UVM_INFO @ 0: reporter [RNTST] Running test test1...
    UVM_INFO @ 0: uvm_test_top [start_of_simulation]
    UVM_INFO @ 0: uvm_test_top [run_phase] entered ..
    UVM_INFO @ 0: uvm_test_top [reset_phase]
    UVM_INFO @ 0: uvm_test_top [configure_phase]
    UVM_INFO @ 0: uvm_test_top [main_phase]
    UVM_INFO @ 0: uvm_test_top [shutdown_phase]
    UVM_INFO @ 1000000: uvm_test_top [run_phase] exited ..
    UVM_INFO @ 1000000: uvm_test_top [extract_phase]
    

    从这个例子可以看到,实际上,run_phase任务和上面细分的12个phase是并行进行的。在start_of_simulation_phase任务执行以后,run_phase和reset_phase开始执行,而在shutdown_phase执行完之后,需要等待run_phase执行完以后,才能进入extract_phase。关于执行的关系,可以从下面这张图中得出:


    image.png

    这里需要提醒用户的是,虽然run_phase与细分的12个phase是并行执行的,而12个phase也是按照先后顺序执行的。为了避免不必要的干扰,用户可以选择run_phase,或者12个phase中的若干来完成激励,但是请不要将它们混合起来使用,因为这样容易导致执行关系的不明确。

    如果要进一步深入phase机制的话,我们首先需要清晰下面的概念:phase、schedule和domain。

    phase即上面介绍的部分,特定的phase会完成特定的功能。
    schedule包含phase的关联数组,即若干个phase会由schedule按照安排的顺序先后执行。
    domain则内置一个schedule。
    schedule类uvm_schedule和domain类uvm_domain均继承于uvm_phase。
    上面首先介绍的9个phase,共同构成了common domain;而另外12个phase,则共同构成了uvm domain。无论是domain、还是phase,它们在UVM环境中都只生成一个唯一的对象。关于common domain和uvm domain的联系和区别是:

    common domain无法被扩展或者取代,这是UVM phase机制的核心。也就是说,构成它的9个phase的顺序不能更改,也无法添加新的phase。同时,这一核心的domain也是为了与OVM的phase机制保持兼容,方便从OVM代码转换到UVM代码。
    uvm domain则包含了上面的12个phase,其调度也是按照先后顺序执行的。对于这一部分,与common domain不同的是,它们的执行是与run_phase同时开始,并且最后共同结束的。同时,用户还可以自定义一些phase,添加到uvm domain中,设置好与其他phase执行的先后关系。

    上面的common domain和uvm domain中包含的phase在uvm_pkg中例化的唯一phase实例群如下:

    image

    在详细介绍完UVM的各个phase,以及它们之间执行的顺序之后,读者可以结合之前硬件和软件的编译和例化部分,来统一理解UVM世界中的编译和运行顺序:

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

    对于使用phase机制,这里有一些建议:

    • 避免使用reset_phase()、configure_phase()、main_phase()、shutdown_phase()和其它pre_/post_ phase。这12个phase尽管细化了run_phase(),但是也使得phase的跳转过为冗余,在将来的UVM版本中,这些phase将考虑被废除。为了控制reset、configure、main和shutdown各个阶段的任务调度和同步,用户可以考虑fork-join语句块,或者高级的同步方式,例如uvm_barrier和uvm_event。
    • 避免phase的跳跃。实际上,用户可以指定个别组件的phase执行中,从phaseA跳跃到phaseC,而忽略了phaseB。但是这种方式不容易理解和调试,所以不建议使用这一特性。
    • 避免自定义phase的使用。尽管uvm domain中允许用户自定义phase,并且规定新添加phase的执行顺序,但是目前的这一机制还不方便调试。用户应该尽量将置于新phase中的任务,剥离到新的任务中,并且在已有的phase中调用它们,完成任务调用的模块化。

    从之前的例子和上面的图中,读者可以看到,UVM的环境建立和各个phase的先后调用的入口,都是从run_test()进入的。默认情况下,如果run_test()方法执行完毕,那么系统函数$finish则会被调用,来终止仿真。然而,有更多的方法来分别控制UVM仿真的开始和结束,我们接下来则分别介绍这些方法的应用。

    相关文章

      网友评论

        本文标题:1 UVM学习笔记--phase机制(转)

        本文链接:https://www.haomeiwen.com/subject/ybnscktx.html