ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

(6)UVM phase机制

2021-11-10 10:59:20  阅读:390  来源: 互联网

标签:info run test UVM phase 机制 uvm


phase机制

文章目录

一、前言

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

二、执行机制

  • 如果暂时抛开phase的机制剖析,对于UVM组件的开发者而言,它们主要关心各个phase执行的先后顺序。
  • 在定义了各个phase虚方法后,UVM环境会按照phase的顺序分别调用这些方法。
    在这里插入图片描述
  • 九个phase对应九种方法,这九种方法只有component具备
  • 上面的九个phase对于一个测试环境的声明周期而言,是有固定的先后执行顺序的;同时对于同一个phase中的组件,执行也会按照层次的顺序或者自顶向下、或者自底向上来执行。
  • 对于build phase,执行顺序按照自顶向下,这符合验证结构建设的逻辑。因为只有先例化高层组件,才会创建空间来容纳底层组件。
  • 只有uvm_component及其继承于uvm_component的子类,才会按照phase机制将上面的九个phase先后执行完毕。
  • 常用的phase包括build、connect、run和report,它们分别完成了组件的建立、连接、运行和报告。这些phase在uvm_component中通过_phase的后缀完成了虚方法的定义,比如build_phase()可以定义一些组件例化和配置的任务。
  • 在所有phase中,只有run_phase方法是一个可以耗时的任务,这意味着该方法可以完成一些等待、激励、采样的任务。对于其它phase对应的方法都是函数,必须立即返回(0耗时)。
  • 在run_phase中,用户如果要完成测试,通常需要组织下面的激励序列:
  1. 上电
  2. 复位
  3. 寄存器配置
  4. 发送主要测试内容
  5. 等待DUT完成测试

三、 phase例子

package phase_order_pkg;
  import uvm_pkg::*;
  `include "uvm_macros.svh"

  class comp2 extends uvm_component;
    `uvm_component_utils(comp2)
    function new(string name = "comp2", uvm_component parent = null);
      super.new(name, parent);
      `uvm_info("CREATE", $sformatf("unit type [%s] created", name), UVM_LOW)
    endfunction
    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      `uvm_info("BUILD", "comp2 build phase entered", UVM_LOW)
      `uvm_info("BUILD", "comp2 build phase exited", UVM_LOW)
    endfunction
    function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      `uvm_info("CONNECT", "comp2 connect phase entered", UVM_LOW)
      `uvm_info("CONNECT", "comp2 connect phase exited", UVM_LOW)
    endfunction
    task run_phase(uvm_phase phase);
      super.run_phase(phase);
      `uvm_info("RUN", "comp2 run phase entered", UVM_LOW)
      `uvm_info("RUN", "comp2 run phase entered", UVM_LOW)
    endtask
    function void report_phase(uvm_phase phase);
      super.report_phase(phase);
      `uvm_info("REPORT", "comp2 report phase entered", UVM_LOW)
      `uvm_info("REPORT", "comp2 report phase exited", UVM_LOW)   
    endfunction
  endclass
  
  class comp3 extends uvm_component;
    `uvm_component_utils(comp3)
    function new(string name = "comp3", uvm_component parent = null);
      super.new(name, parent);
      `uvm_info("CREATE", $sformatf("unit type [%s] created", name), UVM_LOW)
    endfunction
    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      `uvm_info("BUILD", "comp3 build phase entered", UVM_LOW)
      `uvm_info("BUILD", "comp3 build phase exited", UVM_LOW)
    endfunction
    function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      `uvm_info("CONNECT", "comp3 connect phase entered", UVM_LOW)
      `uvm_info("CONNECT", "comp3 connect phase exited", UVM_LOW)
    endfunction
    task run_phase(uvm_phase phase);
      super.run_phase(phase);
      `uvm_info("RUN", "comp3 run phase entered", UVM_LOW)
      `uvm_info("RUN", "comp3 run phase entered", UVM_LOW)
    endtask
    function void report_phase(uvm_phase phase);
      super.report_phase(phase);
      `uvm_info("REPORT", "comp3 report phase entered", UVM_LOW)
      `uvm_info("REPORT", "comp3 report phase exited", UVM_LOW)   
    endfunction
  endclass
  
  class comp1 extends uvm_component;
    comp2 c2;
    comp3 c3;
    `uvm_component_utils(comp1)
    function new(string name = "comp1", uvm_component parent = null);
      super.new(name, parent);
      `uvm_info("CREATE", $sformatf("unit type [%s] created", name), UVM_LOW)
    endfunction
    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      `uvm_info("BUILD", "comp1 build phase entered", UVM_LOW)
      c2 = comp2::type_id::create("c2", this);
      c3 = comp3::type_id::create("c3", this);
      `uvm_info("BUILD", "comp1 build phase exited", UVM_LOW)
    endfunction
    function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      `uvm_info("CONNECT", "comp1 connect phase entered", UVM_LOW)
      `uvm_info("CONNECT", "comp1 connect phase exited", UVM_LOW)
    endfunction
    task run_phase(uvm_phase phase);
      super.run_phase(phase);
      `uvm_info("RUN", "comp1 run phase entered", UVM_LOW)
      `uvm_info("RUN", "comp1 run phase entered", UVM_LOW)
    endtask
    function void report_phase(uvm_phase phase);
      super.report_phase(phase);
      `uvm_info("REPORT", "comp1 report phase entered", UVM_LOW)
      `uvm_info("REPORT", "comp1 report phase exited", UVM_LOW)   
    endfunction
  endclass

  class phase_order_test extends uvm_test;
    comp1 c1;
    `uvm_component_utils(phase_order_test)
    function new(string name = "phase_order_test", uvm_component parent = null);
      super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      `uvm_info("BUILD", "phase_order_test build phase entered", UVM_LOW)
      c1 = comp1::type_id::create("c1", this);
      `uvm_info("BUILD", "phase_order_test build phase exited", UVM_LOW)
    endfunction
    function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      `uvm_info("CONNECT", "phase_order_test connect phase entered", UVM_LOW)
      `uvm_info("CONNECT", "phase_order_test connect phase exited", UVM_LOW)
    endfunction
    task run_phase(uvm_phase phase);
      super.run_phase(phase);
      `uvm_info("RUN", "phase_order_test run phase entered", UVM_LOW)
      phase.raise_objection(this);
      #1us;
      phase.drop_objection(this);
      `uvm_info("RUN", "phase_order_test run phase exited", UVM_LOW)
    endtask
    function void report_phase(uvm_phase phase);
      super.report_phase(phase);
      `uvm_info("REPORT", "phase_order_test report phase entered", UVM_LOW)
      `uvm_info("REPORT", "phase_order_test report phase exited", UVM_LOW)    
    endfunction
    
    task reset_phase(uvm_phase phase);
      `uvm_info("RESET", "phase_order_test reset phase entered", UVM_LOW)
      phase.raise_objection(this);
      #1us;
      phase.drop_objection(this);
      `uvm_info("RESET", "phase_order_test reset phase exited", UVM_LOW)
    endtask
    
    task main_phase(uvm_phase phase);
      `uvm_info("MAIN", "phase_order_test main phase entered", UVM_LOW)
      phase.raise_objection(this);
      #1us;
      phase.drop_objection(this);
      `uvm_info("MAIN", "phase_order_test main phase exited", UVM_LOW)
    endtask 
  endclass
endpackage

module phase_order;

  import uvm_pkg::*;
  `include "uvm_macros.svh"
  import phase_order_pkg::*;

  initial begin
    run_test(""); // empty test name
  end
endmodule

在这里插入图片描述

  • 在上例中可以看到,build phase自顶向下执行,connect phase和report phase自低向上执行。
  • 注意看,run phase,其似乎是自顶向下执行。这是因为run_phase是task,所以它并不是等到下面的phase执行完才执行上面的phase,而是将这些run_phase通过fork…join_none的形式全部启动。所以,更加正确的说法是自下而上的启动,同时在运行。

四、十二个分支phase

在用户发送激励的一种简单方式是,在run_phase中完成上面所有的激励;另外一种方式是,如果用户可以将上面几种典型序列划分到不同高层次,让对应的激励按区间顺序发送的化,可以让测试更有层次。因此run_phase又可以分为下面12个phase:
在这里插入图片描述

  • 实际上run_phase任务和上面细分的12个phase是并行的,即在start_of_simulation_phase任务执行之后,run_phase和reset_phase开始执行,而在shutdowm_phase执行完成之后,需要等待run_phase执行完才可以进入extract_phase。
    在这里插入图片描述

五、UVM编译和运行顺序

在这里插入图片描述

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

六、UVM仿真开始

要在仿真开始时建立验证环境,用户可以考虑选择下面几种方式:

  • 可以通过全局函数(由uvm_pkg提供)run_test()来选择性地指定要运行哪一个uvm_test。这里的test类均继承于uvm_test。这样的话,指定的test类将被例化并指定为顶层的组件。一般而言,run_test()函数可以在合适module/program中的initial进程中调用。
  • 如果没有任何参数传递给run_test(),那么用户可以在仿真时通过传递参数+UVM_TESTNAME=<test_name>,来指定仿真时调用的uvm_test。当然,即便run_test()函数在调用时已经有test名称传递,在仿真时+UVM_TESTNAME=<test_name>也可以从顶层覆盖已指定的test。这种方式使得仿真不需要通过再次修改的run_test()调用的test名称和重复编译,就可以灵活选定test。
  • 无论上面哪一种方式,都必须在顶层调用全局函数run_test(),用户可以考虑不传递test名称作为参数,而在仿真时通过传递参数+UVM_TESTNAME=<test_name>来选择test。
  • 全局函数run_test()的重要性,正是从uvm_root创建了一个UVM世界。
    在这里插入图片描述
    顶层类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_name。
  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后,uvm_objection共享的counter才会变为0,这意味run phase退出的条件满足,因此可以退出run phase。

对于uvm_objection类,用来反停止的控制方法包括:

  1. raise_objection(uvm_object obj=null,string description="",int count=1)挂起objection
  2. drop_objection(uvm_object obj=null,string description="",int count=1)落下objection
  3. set_drain_time(uvm_object obj=null,time drain)设置退出时间

对这几种方法,在实际应用中的建议有:

  1. 对于component(),用户可以在run_phase中使用phase.raise_objection()/phase.drop_objection()来控制run phase退出。
  2. 用户最好为description字符串参数提供说明,这有利于后期的调试。
  3. 应该使用默认count值。
  4. 对于uvm_top或者uvm_test_top应该尽可能少地使用set_drain_time()。

213

  • uvm_pkg::uvm_test_done实例会在test1的run_phase()执行完毕之后,才会退出run_phase。

  • 这得益于test1::run_phase()在仿真一开始就挂起了objection,而在执行完毕后才落下了objection。这时uvm_pkg:uvm_test_done认为run phase已经可以退出,进而转向下一个extract phase。

  • 直到退出所有phase之后,UVM进入了报告总结阶段。

  • 那么如果没有挂起objection,UVM仿真会怎么样?
    在这里插入图片描述
    objection一般在run_phase中使用到它

  • 看起来挂起objection已经晚了,因为run_phase还是立即退出了。这是因为在挂起objection之前已经运行了1ps,而处于fork…join_none的run_phase任务在0时刻被调用后,如果run_phase退出机制在0时刻没有发现任何挂起的objection,那么就会终止所有的run_phase()任务,继而转入了extract phase。

  • 所以如果要在component中挂起objection,建议在一进入run_phase()后就挂起,保证objection counter及时被增加。

  • 另外,用户需要习惯在sequence中挂起objection,由于sequence不是uvm_component类,而是uvm_object类,因此他只有body()方法,而没有run_phase方法。

  • 所以在sequence中使用objection机制时,可以在body()中的首位部分挂起和落下objection。

关注作者

  • 自述
    作者是一位中科大数字设计专业的研究生,水平有限,如有错误,请大家指正,想要与大家一同进步。
  • 经历
    曾获得国家奖学金,“高教社杯”数学建模国家二等奖等
  • 陆续更新:
    1.与UVM验证相关的system verilog后续内容;
    2.与verilog数字设计相关的一些基础模块设计,例如FIFO,UART,I2C等的书写。
    3.保研与竞赛经历等
  • 微信公众号
    欢迎大家关注公众号“数字IC小白的日常修炼”,期待与大家一同仗剑遨游数字IC世界。

标签:info,run,test,UVM,phase,机制,uvm
来源: https://blog.csdn.net/qq_42419590/article/details/121239092

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有