UVM phase机制源码探微

打印 上一主题 下一主题

主题 638|帖子 638|积分 1914

1. uvm_phase

  UVM的phase机制承袭在差别时间做差别事情的计划哲学,重要提供了function和task两种类型的phase,在function phase中重要做一些实例化,各组件的连接以及仿真结束后的统计、report等工作,这些事情是不必要泯灭仿真时间的;重要的仿真过程是在task phase即run_phase中运行的,与run_phase同步运行的还有12个run-time task phase, 这些细分的phase可以实现对仿真行为更加精致的控制,比如在reset_phase对DUT做reset,configure_phase对DUT做一些设置, 在main_phase中灌输激励等等,各个function phase是严格按照下图中从上到下的顺序来执行的(图中蓝色为function phase,绿色为task phase):
                    

                      图1.1 uvm_phase graph

2. uvm_phase的运行和调度

  我们知道,UVM通过phase的调度来进行差别阶段的仿真,那当我们跑一个testcase的时候,验证平台是怎么开始仿真的呢?我们一般会在平台的顶层module TB里边来调用run_test()开始仿真:
  1. //uvm_root.svh
  2. task uvm_root::run_test(string test_name="");
  3.   ...
  4.   process phase_runner_proc;
  5.   ...
  6.     if (test_name != "") begin
  7.           uvm_coreservice_t cs = uvm_coreservice_t::get();                                                     
  8.           uvm_factory factory=cs.get_factory();
  9.           
  10.     if(m_children.exists("uvm_test_top")) begin
  11.       uvm_report_fatal("TTINST",
  12.           "An uvm_test_top already exists via a previous call to run_test", UVM_NONE);
  13.       #0; // forces shutdown because $finish is forked
  14.     end
  15.     $cast(uvm_test_top, factory.create_component_by_name(test_name,
  16.           "", "uvm_test_top", null));
  17.    ...
  18.   fork begin
  19.     // spawn the phase runner task
  20.     phase_runner_proc = process::self();
  21.     uvm_phase::m_run_phases();
  22.   end
  23.   join_none
  24.   #0; // let the phase runner start
  25.   
  26.   wait (m_phase_all_done == 1);
  27.   
  28.   // clean up after ourselves
  29.   phase_runner_proc.kill();
  30.   ...
  31.   if (finish_on_completion)
  32.     $finish;
  33. endtask
复制代码
  在这个函数中UVM会通过传入的下令行参数+UVM_TESTNAME=xxx来获取testcase的名字,然后创建一个uvm_test_top的component节点,这个uvm_test_top句柄指向当前仿真testcase class的实例,随后会在此中例化env及整个平台hirearchy。别的,SV中有process进程的用法,uvm会用fork…join_none来启动一个进程phase_runner_proc,在此中调用m_run_phases(),在这个task中完成进行对phase的部分调度(之所以说是部分调度,在于其实还有一个background process在同步运行,下文会提到),同时uvm不停等待全部phase全部完成的信号m_phase_all_done,一旦该信号被拉高则杀掉phase_runner_proc进程,调用$finish结束本次仿真。
  1. //uvm_phase.svh
  2. class uvm_phase extends uvm_object;
  3.    local static mailbox #(uvm_phase) m_phase_hopper = new();
  4.    extern static task m_run_phases();
  5.    ...
  6. endclass
  7. task uvm_phase::m_run_phases();
  8.   uvm_root top;
  9.   uvm_coreservice_t cs;
  10.   cs = uvm_coreservice_t::get();
  11.   top = cs.get_root();
  12.   // initiate by starting first phase in common domain
  13.   begin
  14.     uvm_phase ph = uvm_domain::get_common_domain();
  15.     void'(m_phase_hopper.try_put(ph));
  16.   end
  17.   forever begin
  18.     uvm_phase phase;
  19.     m_phase_hopper.get(phase);
  20.     fork
  21.       begin
  22.         phase.execute_phase();
  23.       end
  24.     join_none
  25.     #0;  // let the process start running
  26.   end
  27. endtask
复制代码
  m_run_phases()是界说在uvm_phase类的静态task,其调用get_common_domain()来获得一个uvm_phase类型的句柄,然后将其放入邮箱m_phase_hopper中,这个邮箱是static类型的,存放的对象是uvm_phase类型,UVM平台中全部的phase实例对象会共用这一个邮箱。随后在forever循环中该邮箱会不绝地被查抄,一旦有新的phase放入,则立即取出来用fork…join_none启动新线程调用该phase的execute_phase()。
2.1. get_common_domain()

  这个函数做的事情就是把全部的function phase和task phase按照先后顺序排排队,安排好先后顺序,按照图1.1中所示的那样编成一个domain,取个名字叫做"common",作为uvm default的domain。这个函数中先查抄m_common_domain是否为null,我们可以大致猜到这是一个common_domain的实例句柄,很明显这里是采用了单例模式,若系统中已经存在这个实例,则直接返回该实例句柄。若是第一次调用该函数,m_common_domain为null,则调用new()函数创建一个句柄为domain,名字为"common"的实例,然后调用add()函数将8个function phase和run_phase按顺序加入,add()函数做的其实就是给这些phase排排队加入名为"common_domain"的phase运行图,我们后面会详细来看add()函数的实现。之后把该domain句柄分别存入以"common"为索引的数组m_domains和赋给m_common_domain。get_uvm_domain()实际上就是获取12个run-time task phase的运行图,末了会再次调用add()将其加入m_common_domain的运行图中,with_phase参数传入的是之前run_phase的句柄,表明12个run-time phase是以跟run_phase并行运行的方式加入到m_common_domain中。
  1.   static function uvm_domain get_common_domain();
  2.     uvm_domain domain;
  3.     uvm_phase schedule;
  4.     if (m_common_domain != null)
  5.       return m_common_domain;
  6.     domain = new("common");
  7.     domain.add(uvm_build_phase::get());
  8.     domain.add(uvm_connect_phase::get());
  9.     domain.add(uvm_end_of_elaboration_phase::get());
  10.     domain.add(uvm_start_of_simulation_phase::get());
  11.     domain.add(uvm_run_phase::get());
  12.     domain.add(uvm_extract_phase::get());
  13.     domain.add(uvm_check_phase::get());
  14.     domain.add(uvm_report_phase::get());
  15.     domain.add(uvm_final_phase::get());
  16.     m_domains["common"] = domain;
  17.     ...
  18.     m_common_domain = domain;
  19.     domain = get_uvm_domain();
  20.     m_common_domain.add(domain,
  21.                      .with_phase(m_common_domain.find(uvm_run_phase::get())));
  22.    
  23.     return m_common_domain;
  24.   endfunction
复制代码
2.1.1. phase_type & phase_state

  为了更方便的对phase进行调度,UVM界说了差别的phase_type,如UVM_PHASE_NODE代表全部的(无论function/task phase)phase的一个实例(节点);UVM_PHASE_DOMAIN代表在此中界说了差别phase线性/并行运行关系的一组phase的运行图(雷同于从上海发往北京的高铁调度时刻表,phase就像是运行在其间的一列列班车,哪个时间段该发行哪班车,都会清清楚楚纪录在时刻表里,固然高铁不可能每一个时刻都会发出一班车,不像phase之间是无缝衔接的),此中可以包含许多的UVM_PHASE_NODE;而UVM_PHASE_SCHEDULE表示一个微缩版的UVM_PHASE_DOMAIN,它此中只界说了12个run-time task phase的运行图,等等。
  1. //uvm_object_globals.svh
  2. typedef enum { UVM_PHASE_IMP,
  3.                UVM_PHASE_NODE,
  4.                UVM_PHASE_TERMINAL,
  5.                UVM_PHASE_SCHEDULE,
  6.                UVM_PHASE_DOMAIN,
  7.                UVM_PHASE_GLOBAL
  8. } uvm_phase_type;
复制代码
  别的UVM还界说了phase_state,用来表示某phase当前的差别运行状态,如UVM_PHASE_SYNCING表示差别domain的run-time task phase正在进行同步,UVM_PHASE_EXECUTING表示当前phase正在执行,等等。
  1. //uvm_object_globals.svh
  2.    typedef enum { UVM_PHASE_UNINITIALIZED = 0,
  3.                   UVM_PHASE_DORMANT      = 1,
  4.                   UVM_PHASE_SCHEDULED    = 2,
  5.                   UVM_PHASE_SYNCING      = 4,
  6.                   UVM_PHASE_STARTED      = 8,
  7.                   UVM_PHASE_EXECUTING    = 16,
  8.                   UVM_PHASE_READY_TO_END = 32,
  9.                   UVM_PHASE_ENDED        = 64,
  10.                   UVM_PHASE_CLEANUP      = 128,
  11.                   UVM_PHASE_DONE         = 256,
  12.                   UVM_PHASE_JUMPING      = 512
  13.                   } uvm_phase_state;
复制代码
2.1.2. uvm_phase class

2.1.2.1. new()

  uvm_phase类继承自uvm_object,在此中界说了一些变量如m_phase_type和m_state, 分别是uvm_phase_type和uvm_phase_state类型的,uvm_phase类型的句柄m_parent指向的是该phase的上一个层级,此中还界说了uvm_phase类型的m_end_node,这个变量是用来表示一个uvm_domain和uvm_schedule的结束节点。别的,两个bit类型的联合数组以uvm_phase类型为索引,用来纪录各个phase间的顺序关系,此中m_predecessors[]用来纪录当前phase的全部前置phase,比如若当前phase是一个connect_phase,则build_phase在其之前运行,是它的一个前置phase,则connect_phase的实例中m_predecessors[build_phase] = 1;同理,m_successors[]用于纪录当前phase的全部后继phase,对于一个build_phase实例来说,其m_successors[connect_phase] = 1;
  1. //uvm_phase.svh
  2. class uvm_phase extends uvm_object;
  3.   protected uvm_phase_type m_phase_type;
  4.   protected uvm_phase      m_parent;     // our 'schedule' node [or points 'up' one level]
  5.   uvm_phase                m_imp;        // phase imp to call when we execute this node
  6.   local uvm_phase_state    m_state;
  7.   ...
  8.   protected bit  m_predecessors[uvm_phase];
  9.   protected bit  m_successors[uvm_phase];
  10.   protected uvm_phase m_end_node;
  11.   ...
  12. endclass
  13. function uvm_phase::new(string name="uvm_phase",
  14.                         uvm_phase_type phase_type=UVM_PHASE_SCHEDULE,
  15.                         uvm_phase parent=null);
  16.   super.new(name);
  17.   m_phase_type = phase_type;
  18.   // The common domain is the only thing that initializes m_state.  All
  19.   // other states are initialized by being 'added' to a schedule.
  20.   if ((name == "common") &&
  21.       (phase_type == UVM_PHASE_DOMAIN))
  22.     m_state = UVM_PHASE_DORMANT;
  23.    
  24.   m_parent = parent;
  25.   ...
  26.   if (parent == null && (phase_type == UVM_PHASE_SCHEDULE ||
  27.                          phase_type == UVM_PHASE_DOMAIN )) begin
  28.     //m_parent = this;
  29.     m_end_node = new({name,"_end"}, UVM_PHASE_TERMINAL, this);
  30.     this.m_successors[m_end_node] = 1;
  31.     m_end_node.m_predecessors[this] = 1;
  32.   end
  33. endfunction
复制代码
  来看uvm_phase类的new()函数,它有三个参数,在创建一个uvm_phase类型的实例时分别传入名字,phase_type和和parent,它分别把传入的phase_type和parent赋给m_phase_type和m_parent,若传入的phase_type是UVM_PHASE_SCHEDULE或UVM_PHASE_DOMAIN,表明当前正在创建一个多phase的运行图(domain/schedule),则调用new()创建一个m_end_node的实例对象,这个m_end_node的phase_type是UVM_PHASE_TERMINAL类型,parent就是这个运行图,然后把m_end_node作为这个运行图的后继phase纪录下来,这段代码的意图就是一旦我们想创建一个新的domain/schedule对象,就必要先给它安排一个UVM_PHASE_TERMINAL类型的节点,无论运行图内部怎么调度这些phase,最终运行完毕总是要结束的。
2.1.2.1. uvm_common_phases

  UVM的build_phase类和final_phase类继承自uvm_topdown_phase类,之所以叫"topdown"是因为在UVM平台hireachy各个component节点实例化的时候是自上而下执行的,与之相反其余的function phase类型都继承自uvm_bottomup_phase类,比如connect_phase的执行在UVM平台hireachy各个component节点间是自下而上执行。别的run_phase和12个run-time phase类都继承自uvm_task_phase类。这几种类型new()函数中phase_type参数都会传入UVM_PHASE_IMP来创建一个phase实例。此中的关键是traverse()函数,此中会根据当前phase执行的状态来调用当前component的差别函数,此中UVM预先界说了许多callback如phase_started()/phase_ended(),用户可以在component中来扩展这些callback,这些callback会在全部的phase(包括全部function和task phase)执行前后进行调用。我们留意到,在当前component的traverse()执行到末了的时候,会查抄它全部的子节点并调用traverse()来以此执行此phase的内容,所以我们说继承自uvm_bottomup_phase的phase如build_phase的执行是自上而下的。与之相反,uvm_bottomup_phase类的traverse()函数是先在其全部子节点中调用traverse()执行,末了执行当前component,因而继承自uvm_bottomup_phase的phase如connect_phase的执行是自下而上的。uvm_task_phase的执行顺序与uvm_bottomup_phase雷同,也是自下而上发起,但却是同时执行的,并不会等待子节点phase执行完毕才会执行上一级节点。别的,function phase在UVM_PHASE_EXECUTING状态,会调用execute()函数,这个函数会启动一个新进程并调用exec_func()。
  1. //uvm_topdown_phase.svh
  2. virtual class uvm_topdown_phase extends uvm_phase;
  3.   // Function: new
  4.   //
  5.   // Create a new instance of a top-down phase
  6.   //
  7.   function new(string name);
  8.     super.new(name,UVM_PHASE_IMP);
  9.   endfunction
  10.   
  11.     virtual function void traverse(uvm_component comp,
  12.                                  uvm_phase phase,
  13.                                  uvm_phase_state state);
  14.     string name;
  15.     uvm_domain phase_domain = phase.get_domain();
  16.     uvm_domain comp_domain = comp.get_domain();
  17.     if (m_phase_trace)
  18.     `uvm_info("PH_TRACE",$sformatf("topdown-phase phase=%s state=%s comp=%s comp.domain=%s phase.domain=%s",
  19.           phase.get_name(), state.name(), comp.get_full_name(),comp_domain.get_name(),phase_domain.get_name()),
  20.           UVM_DEBUG)
  21.     if (phase_domain == uvm_domain::get_common_domain() ||
  22.         phase_domain == comp_domain) begin
  23.         case (state)
  24.           UVM_PHASE_STARTED: begin
  25.             comp.m_current_phase = phase;
  26.             comp.m_apply_verbosity_settings(phase);
  27.             comp.phase_started(phase);
  28.             end
  29.           UVM_PHASE_EXECUTING: begin
  30.             if (!(phase.get_name() == "build" && comp.m_build_done)) begin
  31.               uvm_phase ph = this;
  32.               comp.m_phasing_active++;
  33.               if (comp.m_phase_imps.exists(this))
  34.                 ph = comp.m_phase_imps[this];
  35.               ph.execute(comp, phase);
  36.               comp.m_phasing_active--;
  37.             end
  38.             end
  39.           UVM_PHASE_READY_TO_END: begin
  40.             comp.phase_ready_to_end(phase);
  41.             end
  42.           UVM_PHASE_ENDED: begin
  43.             comp.phase_ended(phase);
  44.             comp.m_current_phase = null;
  45.             end
  46.           default:
  47.             `uvm_fatal("PH_BADEXEC","topdown phase traverse internal error")
  48.         endcase
  49.     end
  50.     if(comp.get_first_child(name))
  51.       do
  52.         traverse(comp.get_child(name), phase, state);
  53.       while(comp.get_next_child(name));
  54.   endfunction
  55.   
  56.   // Function: execute
  57.   //
  58.   // Executes the top-down phase ~phase~ for the component ~comp~.
  59.   //
  60.   virtual function void execute(uvm_component comp,
  61.                                           uvm_phase phase);
  62.     // reseed this process for random stability
  63.     process proc = process::self();
  64.     proc.srandom(uvm_create_random_seed(phase.get_type_name(), comp.get_full_name()));
  65.     comp.m_current_phase = phase;
  66.     exec_func(comp,phase);
  67.   endfunction
  68. endclass
  69. //uvm_common_phases.svh
  70. class uvm_build_phase extends uvm_topdown_phase;
  71.    virtual function void exec_func(uvm_component comp, uvm_phase phase);
  72.       comp.build_phase(phase);
  73.    endfunction
  74.    local static uvm_build_phase m_inst;
  75.    static const string type_name = "uvm_build_phase";
  76.    // Function: get
  77.    // Returns the singleton phase handle
  78.    //
  79.    static function uvm_build_phase get();
  80.       if(m_inst == null)
  81.          m_inst = new();
  82.       return m_inst;
  83.    endfunction
  84.    protected function new(string name="build");
  85.       super.new(name);
  86.    endfunction
  87.    virtual function string get_type_name();
  88.       return type_name;
  89.    endfunction
  90. endclass
复制代码
  以uvm_build_phase为例,其实exec_func()就是调用了我们界说在某component的build_phase来执行。此中界说了uvm_build_phase类型的静态变量m_inst,代表这个phase的一个实例,当调用静态函数get()时会返回该唯一实例对象句柄。而task phase在UVM_PHASE_EXECUTING状态,也会调用execute()函数,这个函数会用fork…join_none启动一个新进程并调用exec_task(),在exec_task()返回前用m_num_procs_not_yet_returned来纪录当前有多少个component在运行这个phase,以判断何时可以结束该phase。与function phase雷同,exec_task()也是界说在task phase中调用当前component的同名task phase执行,get()函数也会返回该phase唯一实例对象句柄m_inst。
  1. //uvm_task_phase.svh
  2. virtual class uvm_task_phase extends uvm_phase
  3.   ...
  4.   virtual function void execute(uvm_component comp,
  5.                                           uvm_phase phase);
  6.     fork
  7.       begin
  8.         process proc;
  9.         // reseed this process for random stability
  10.         proc = process::self();
  11.         proc.srandom(uvm_create_random_seed(phase.get_type_name(), comp.get_full_name()));
  12.         phase.m_num_procs_not_yet_returned++;
  13.         exec_task(comp,phase);
  14.         phase.m_num_procs_not_yet_returned--;
  15.       end
  16.     join_none
  17.   endfunction
  18. endclass
复制代码
2.1.3. uvm_domain class

  uvm_domain类继承自uvm_phase,此中界说了以字符串为索引的内容为uvm_domain类型的静态联合数组m_domains[],在uvm_domain的new()函数中首先调用super即uvm_phase类型的new()函数创建一个phase_type为UVM_PHASE_DOMAIN的实例,然后把传入的名字作为索引,将该实例对象存入m_domains[]。
  1. //uvm_domain.svh
  2. class uvm_domain extends uvm_phase;
  3.   static local uvm_domain m_common_domain;
  4.   static local uvm_domain m_domains[string];
  5.   ...
  6.   function new(string name);
  7.     super.new(name,UVM_PHASE_DOMAIN);
  8.     if (m_domains.exists(name))
  9.       `uvm_error("UNIQDOMNAM", $sformatf("Domain created with non-unique name '%s'", name))
  10.     m_domains[name] = this;
  11.   endfunction
  12.   ...
  13. endclass
复制代码
2.1.4. get_uvm_domain()

  这个函数就是将12个run-time phases的运行顺序排好然后返回一个uvm_domain类型的运行图句柄m_uvm_domain。
  1. //uvm_domain.svh
  2. class uvm_domain extends uvm_phase;
  3.     static local uvm_domain m_uvm_domain; // run-time
  4.     static local uvm_phase m_uvm_schedule;
  5.   ...
  6.   static function void add_uvm_phases(uvm_phase schedule);
  7.     schedule.add(uvm_pre_reset_phase::get());
  8.     schedule.add(uvm_reset_phase::get());
  9.     schedule.add(uvm_post_reset_phase::get());
  10.     schedule.add(uvm_pre_configure_phase::get());
  11.     schedule.add(uvm_configure_phase::get());
  12.     schedule.add(uvm_post_configure_phase::get());
  13.     schedule.add(uvm_pre_main_phase::get());
  14.     schedule.add(uvm_main_phase::get());
  15.     schedule.add(uvm_post_main_phase::get());
  16.     schedule.add(uvm_pre_shutdown_phase::get());
  17.     schedule.add(uvm_shutdown_phase::get());
  18.     schedule.add(uvm_post_shutdown_phase::get());
  19.   endfunction
  20.   // Function: get_uvm_domain
  21.   //
  22.   // Get a handle to the singleton ~uvm~ domain
  23.   //
  24.   static function uvm_domain get_uvm_domain();
  25.   
  26.     if (m_uvm_domain == null) begin
  27.       m_uvm_domain = new("uvm");
  28.       m_uvm_schedule = new("uvm_sched", UVM_PHASE_SCHEDULE);
  29.       add_uvm_phases(m_uvm_schedule);
  30.       m_uvm_domain.add(m_uvm_schedule);
  31.     end
  32.     return m_uvm_domain;
  33.   endfunction
  34. endclass
复制代码
  静态函数get_uvm_domain()首先分别创建了名为"uvm"和"uvm_sched"的对象并分别赋值给uvm_domain和uvm_phase类型的句柄m_uvm_domain和m_uvm_schedule,二者phase_type分别为UVM_PHASE_COMMON和UVM_PHASE_SCHEDULE,然后调用add_uvm_phases()函数,将12个run-time phases通过add()函数按先后顺序加入m_uvm_schedule中,此时m_uvm_schedule就是一个运行图,末了再将这个运行图schedule加入m_uvm_domain这个domain中,末了回到get_common_domain()中,把m_uvm_domain加入到m_common_domain中,同时设置with_phase为run_phase,就是把m_uvm_domain中的这12个run-time phase和run_phase同步运行。
2.2. add()

  我们在之前调用add()函数时无外乎是四种情况,分别为
1, domain.add(uvm_build_phase::get()); //向uvm_common_domain中添加funtion phase和run_phase
2, schedule.add(uvm_pre_reset_phase::get()); //向m_uvm_schedule中添加12个run-time phases
3, m_uvm_domain.add(m_uvm_schedule); //将m_uvm_schedule加入m_uvm_domain
4, m_common_domain.add(domain,
        .with_phase(m_common_domain.find(uvm_run_phase::get()))); //将包含12个run-time phases 的m_uvm_domain加入uvm_common_domain并使它们跟run_phase同步运行
  来看add()函数,我们隐去了此中关于debug的部分:
  1. function void uvm_phase::add(uvm_phase phase,
  2.                              uvm_phase with_phase=null,
  3.                              uvm_phase after_phase=null,
  4.                              uvm_phase before_phase=null);
  5.   uvm_phase new_node, begin_node, end_node, tmp_node;
  6.   uvm_phase_state_change state_chg;
  7.   if (phase == null)
  8.       `uvm_fatal("PH/NULL", "add: phase argument is null")
  9.   if (with_phase != null && with_phase.get_phase_type() == UVM_PHASE_IMP) begin
  10.     string nm = with_phase.get_name();
  11.     with_phase = find(with_phase);
  12.     if (with_phase == null)
  13.       `uvm_fatal("PH_BAD_ADD",
  14.          {"cannot find with_phase '",nm,"' within node '",get_name(),"'"})
  15.   end
  16.   if (before_phase != null && before_phase.get_phase_type() == UVM_PHASE_IMP) begin
  17.     string nm = before_phase.get_name();
  18.     before_phase = find(before_phase);
  19.     if (before_phase == null)
  20.       `uvm_fatal("PH_BAD_ADD",
  21.          {"cannot find before_phase '",nm,"' within node '",get_name(),"'"})
  22.   end
  23.   if (after_phase != null && after_phase.get_phase_type() == UVM_PHASE_IMP) begin
  24.     string nm = after_phase.get_name();
  25.     after_phase = find(after_phase);
  26.     if (after_phase == null)
  27.       `uvm_fatal("PH_BAD_ADD",
  28.          {"cannot find after_phase '",nm,"' within node '",get_name(),"'"})
  29.   end
  30.   if (with_phase != null && (after_phase != null || before_phase != null))
  31.     `uvm_fatal("PH_BAD_ADD",
  32.        "cannot specify both 'with' and 'before/after' phase relationships")
  33.   if (before_phase == this || after_phase == m_end_node || with_phase == m_end_node)
  34.     `uvm_fatal("PH_BAD_ADD",
  35.        "cannot add before begin node, after end node, or with end nodes")
  36.   // If we are inserting a new "leaf node"
  37.   if (phase.get_phase_type() == UVM_PHASE_IMP) begin
  38.     uvm_task_phase tp;
  39.     new_node = new(phase.get_name(),UVM_PHASE_NODE,this);
  40.     new_node.m_imp = phase;
  41.     begin_node = new_node;
  42.     end_node = new_node;
  43.     // The phase_done objection is only required
  44.     // for task-based nodes
  45.     if ($cast(tp, phase)) begin
  46.        if (new_node.get_name() == "run") begin
  47.          new_node.phase_done = uvm_test_done_objection::get();
  48.        end
  49.        else begin
  50.          new_node.phase_done = uvm_objection::type_id::create({phase.get_name(), "_objection"});
  51.        end
  52.     end
  53.   end
  54.   // We are inserting an existing schedule
  55.   else begin
  56.     begin_node = phase;
  57.     end_node   = phase.m_end_node;
  58.     phase.m_parent = this;
  59.   end
  60.   // If 'with_phase' is us, then insert node in parallel
  61.   /*
  62.   if (with_phase == this) begin
  63.     after_phase = this;
  64.     before_phase = m_end_node;
  65.   end
  66.   */
  67.   // If no before/after/with specified, insert at end of this schedule
  68.   if (with_phase == null && after_phase == null && before_phase == null) begin
  69.     before_phase = m_end_node;
  70.   end
  71.   // INSERT IN PARALLEL WITH 'WITH' PHASE
  72.   if (with_phase != null) begin
  73.     begin_node.m_predecessors = with_phase.m_predecessors;
  74.     end_node.m_successors = with_phase.m_successors;
  75.     foreach (with_phase.m_predecessors[pred])
  76.       pred.m_successors[begin_node] = 1;
  77.     foreach (with_phase.m_successors[succ])
  78.       succ.m_predecessors[end_node] = 1;
  79.   end
  80.   
  81.   
  82.   // INSERT BEFORE PHASE
  83.   else if (before_phase != null && after_phase == null) begin
  84.     begin_node.m_predecessors = before_phase.m_predecessors;
  85.     end_node.m_successors[before_phase] = 1;
  86.     foreach (before_phase.m_predecessors[pred]) begin
  87.       pred.m_successors.delete(before_phase);
  88.       pred.m_successors[begin_node] = 1;
  89.     end
  90.     before_phase.m_predecessors.delete();
  91.     before_phase.m_predecessors[end_node] = 1;
  92.   end
  93.   
  94.   // INSERT AFTER PHASE
  95.   else if (before_phase == null && after_phase != null) begin
  96.     end_node.m_successors = after_phase.m_successors;
  97.     begin_node.m_predecessors[after_phase] = 1;
  98.     foreach (after_phase.m_successors[succ]) begin
  99.       succ.m_predecessors.delete(after_phase);
  100.       succ.m_predecessors[end_node] = 1;
  101.     end
  102.     after_phase.m_successors.delete();
  103.     after_phase.m_successors[begin_node] = 1;
  104.   end
  105.   
  106.   // IN BETWEEN 'BEFORE' and 'AFTER' PHASES
  107.   else if (before_phase != null && after_phase != null) begin
  108.     if (!after_phase.is_before(before_phase)) begin
  109.       `uvm_fatal("PH_ADD_PHASE",{"Phase '",before_phase.get_name(),
  110.                  "' is not before phase '",after_phase.get_name(),"'"})
  111.     end
  112.     // before and after? add 1 pred and 1 succ
  113.     begin_node.m_predecessors[after_phase] = 1;
  114.     end_node.m_successors[before_phase] = 1;
  115.     after_phase.m_successors[begin_node] = 1;
  116.     before_phase.m_predecessors[end_node] = 1;
  117.     if (after_phase.m_successors.exists(before_phase)) begin
  118.       after_phase.m_successors.delete(before_phase);
  119.       before_phase.m_predecessors.delete(after_phase);
  120.     end
  121.   end // if (before_phase != null && after_phase != null)
  122.   // Transition nodes to DORMANT state
  123.   if (new_node == null)
  124.     tmp_node = phase;
  125.   else
  126.     tmp_node = new_node;
  127.   state_chg = uvm_phase_state_change::type_id::create(tmp_node.get_name());
  128.   state_chg.m_phase = tmp_node;
  129.   state_chg.m_jump_to = null;
  130.   state_chg.m_prev_state = tmp_node.m_state;
  131.   tmp_node.m_state = UVM_PHASE_DORMANT;
  132.   `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(tmp_node, state_chg))
  133. endfunction
复制代码
  对于第一种调用,函数首先会判断参数中phase是否为null,然后依次判断各个参数with_phase/after_phase/before_phase是否为null,我们可以略过这些语句。接下来判断传入的phase是否为UVM_PHASE_IMP类型,很明显uvm_build_phase::get()返回值符合这个条件,于是创建一个UVM_PHASE_NODE为phase_type的uvm_phase类型的新实例对象new_node,这个new_node的唯一对象句柄m_imp就指向当前传入的phase_type为UVM_PHASE_IMP的对象,这个phase_type为UVM_PHASE_NODE的实例句柄会更换参数传入的phase_type为UVM_PHASE_IMP的对象存入phase的运行图。接下来把new_node分别赋值给begin_node和end_node。接下来判断当前phase是否是一个task phase类型,若是,则在此中实例化uvm_objection的对象,这里临时按下不表。接下来判断若with_phase/after_phase/before_phase均为null,则before_phase指向m_end_node,这个m_end_node就是当前调用add()函数的phase_type为UVM_PHASE_DOMAIN大概UVM_PHASE_SCHEDULE的结束节点。这里before_phase指向某个运行图(domain/schedule)的结束节点,意图很明显,就是要把当前phase加入到这个运行图的m_end_node节点之前,作为运行图的末了一个phase。后面的代码判断若before_phase不为null,则
1, 把当前运行图的m_end_node.m_predecessors[]作为新加入phase的m_predecessors[];
2, 把m_end_node作为新加入phase的m_successors,即phase.m_successors[m_end_node]=1;
3, 对于m_end_node的全部m_predecessors,删除m_end_node作为他们的m_successors的纪录,并把新加入的phase作为它们的m_successors;
4, 把m_end_node的全部m_predecessors纪录删除,并把新加入的phase作为其唯一的m_predecessors。
  对于第一种情况,若调用domain.add(uvm_build_phase::get());之后,则运行图如下:
   
                    

  若依次调用add()添加全部function phase和run_phase到uvm_common_domain之后,所形成的运行图如如下:
                  

  第二种调用情况雷同,是把12个run-time task phases加入到m_uvm_schedule中形成顺序运行图,如下:
                  

  接下来看第三种调用add()的方式:m_uvm_domain.add(m_uvm_schedule);将m_uvm_schedule加入到m_uvm_domain的运行图中,由于传入的phase参数不是UVM_PHASE_IMP类型,所以函数会直接将before_phase指向m_uvm_domain的m_end_node,将m_uvm_schedule放在m_uvm_domain::m_end_node之前:
                  

  第四种调用方式给参数with_phase传入了find()返回值,这个find()函数是界说在uvm_phase类中的函数,它会调用m_find_predecessor()和m_find_successor()函数遍历当前domain中的全部前置和后置phase,返回要查找phase的唯一实例句柄m_imp。当with_phase传入run_phase的实例句柄时,add()函数做了以下事情:
1, 把run_phase的全部m_predecessors作为m_uvm_domain的m_predecessors;
2, 把run_phase的全部m_successors作为m_uvm_domain::m_end_node的m_successors;
3, 对于run_phase全部的m_predecessors,把m_uvm_domain作为它们的m_successors;
4, 对于run_phase全部的m_successors,把m_uvm_domain::m_end_node作为它们的m_predecessors。
  最终调用get_common_domain()所形成的m_common_domain的phase运行图如下:
       

2.3. execute_phase()

  在task m_run_phases()中调用uvm_domain::get_common_domain()拿到UVM default domain m_common_domain的句柄ph后,会把ph放入邮箱m_phase_hopper,forever循环从邮箱中拿到ph后会启动fork…join_none线程调用execute_phase()。该task代码如下:
  1. //uvm_phase.svh
  2. task uvm_phase::execute_phase();
  3.   uvm_task_phase task_phase;
  4.   uvm_root top;
  5.   uvm_phase_state_change state_chg;
  6.   uvm_coreservice_t cs;
  7.   cs = uvm_coreservice_t::get();
  8.   top = cs.get_root();
  9.   // If we got here by jumping forward, we must wait for
  10.   // all its predecessor nodes to be marked DONE.
  11.   // (the next conditional speeds this up)
  12.   // Also, this helps us fast-forward through terminal (end) nodes
  13.   foreach (m_predecessors[pred])
  14.     wait (pred.m_state == UVM_PHASE_DONE);
  15.   // If DONE (by, say, a forward jump), return immed
  16.   if (m_state == UVM_PHASE_DONE)
  17.     return;
  18.   ...
  19.   // If we're a schedule or domain, then "fake" execution
  20.   if (m_phase_type != UVM_PHASE_NODE) begin
  21.     state_chg.m_prev_state = m_state;
  22.     m_state = UVM_PHASE_STARTED;
  23.     `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
  24.     #0;
  25.     state_chg.m_prev_state = m_state;
  26.     m_state = UVM_PHASE_EXECUTING;
  27.     `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
  28.     #0;
  29.   end
  30.   else begin // PHASE NODE
  31.     //---------
  32.     // STARTED:
  33.     //---------
  34.     state_chg.m_prev_state = m_state;
  35.     m_state = UVM_PHASE_STARTED;
  36.     `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
  37.     m_imp.traverse(top,this,UVM_PHASE_STARTED);
  38.     m_ready_to_end_count = 0 ; // reset the ready_to_end count when phase starts
  39.     #0; // LET ANY WAITERS WAKE UP
  40.     //if (m_imp.get_phase_type() != UVM_PHASE_TASK) begin
  41.     if (!$cast(task_phase,m_imp)) begin
  42.       //-----------
  43.       // EXECUTING: (function phases)
  44.       //-----------
  45.       state_chg.m_prev_state = m_state;
  46.       m_state = UVM_PHASE_EXECUTING;
  47.       `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
  48.       #0; // LET ANY WAITERS WAKE UP
  49.       m_imp.traverse(top,this,UVM_PHASE_EXECUTING);
  50.     end
  51.     else begin
  52.         m_executing_phases[this] = 1;
  53.         state_chg.m_prev_state = m_state;
  54.         m_state = UVM_PHASE_EXECUTING;
  55.         `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
  56.         fork : master_phase_process
  57.           begin
  58.   
  59.             m_phase_proc = process::self();
  60.   
  61.             //-----------
  62.             // EXECUTING: (task phases)
  63.             //-----------
  64.             task_phase.traverse(top,this,UVM_PHASE_EXECUTING);
  65.   
  66.             wait(0); // stay alive for later kill
  67.   
  68.           end
  69.         join_none
  70.   
  71.         uvm_wait_for_nba_region(); //Give sequences, etc. a chance to object
  72.   
  73.         // Now wait for one of three criterion for end-of-phase.
  74.         fork
  75.           begin // guard
  76.          
  77.            fork
  78.              // JUMP
  79.              begin
  80.                 wait (m_premature_end);
  81.                 `UVM_PH_TRACE("PH/TRC/EXE/JUMP","PHASE EXIT ON JUMP REQUEST",this,UVM_DEBUG)
  82.              end
  83.   
  84.              // WAIT_FOR_ALL_DROPPED
  85.              begin
  86.                bit do_ready_to_end  ; // bit used for ready_to_end iterations
  87.                // OVM semantic: don't end until objection raised or stop request
  88.                if (phase_done.get_objection_total(top) ||
  89.                    m_use_ovm_run_semantic && m_imp.get_name() == "run") begin
  90.                  if (!phase_done.m_top_all_dropped)
  91.                    phase_done.wait_for(UVM_ALL_DROPPED, top);
  92.                  `UVM_PH_TRACE("PH/TRC/EXE/ALLDROP","PHASE EXIT ALL_DROPPED",this,UVM_DEBUG)
  93.                end
  94.                else begin
  95.                   if (m_phase_trace) `UVM_PH_TRACE("PH/TRC/SKIP","No objections raised, skipping phase",this,UVM_LOW)
  96.                end
  97.                
  98.                wait_for_self_and_siblings_to_drop() ;
  99.                do_ready_to_end = 1;
  100.                   
  101.                //--------------
  102.                // READY_TO_END:
  103.                //--------------
  104.                while (do_ready_to_end) begin
  105.                  uvm_wait_for_nba_region(); // Let all siblings see no objections before traverse might raise another
  106.                  `UVM_PH_TRACE("PH_READY_TO_END","PHASE READY TO END",this,UVM_DEBUG)
  107.                  m_ready_to_end_count++;
  108.                  if (m_phase_trace)
  109.                    `UVM_PH_TRACE("PH_READY_TO_END_CB","CALLING READY_TO_END CB",this,UVM_HIGH)
  110.                  state_chg.m_prev_state = m_state;
  111.                  m_state = UVM_PHASE_READY_TO_END;
  112.                  `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
  113.                  if (m_imp != null)
  114.                    m_imp.traverse(top,this,UVM_PHASE_READY_TO_END);
  115.                   
  116.                  uvm_wait_for_nba_region(); // Give traverse targets a chance to object
  117.                  wait_for_self_and_siblings_to_drop();
  118.                  do_ready_to_end = (m_state == UVM_PHASE_EXECUTING) && (m_ready_to_end_count < max_ready_to_end_iter) ; //when we don't wait in task above, we drop out of while loop
  119.                end
  120.              end
  121.   
  122.              // TIMEOUT
  123.              begin
  124.                if (this.get_name() == "run") begin
  125.                   if (top.phase_timeout == 0)
  126.                     wait(top.phase_timeout != 0);
  127.                   if (m_phase_trace)
  128.                     `UVM_PH_TRACE("PH/TRC/TO_WAIT", $sformatf("STARTING PHASE TIMEOUT WATCHDOG (timeout == %t)", top.phase_timeout), this, UVM_HIGH)
  129.                   `uvm_delay(top.phase_timeout)
  130.                   if ($time == `UVM_DEFAULT_TIMEOUT) begin
  131.                      if (m_phase_trace)
  132.                        `UVM_PH_TRACE("PH/TRC/TIMEOUT", "PHASE TIMEOUT WATCHDOG EXPIRED", this, UVM_LOW)
  133.                      foreach (m_executing_phases[p]) begin
  134.                         if ((p.phase_done != null) && (p.phase_done.get_objection_total() > 0)) begin
  135.                            if (m_phase_trace)
  136.                              `UVM_PH_TRACE("PH/TRC/TIMEOUT/OBJCTN",
  137.                                            $sformatf("Phase '%s' has outstanding objections:\n%s", p.get_full_name(), p.phase_done.convert2string()),
  138.                                            this,
  139.                                            UVM_LOW)
  140.                         end
  141.                      end
  142.                         
  143.                      `uvm_fatal("PH_TIMEOUT",
  144.                                 $sformatf("Default timeout of %0t hit, indicating a probable testbench issue",
  145.                                           `UVM_DEFAULT_TIMEOUT))
  146.                   end
  147.                   else begin
  148.                      if (m_phase_trace)
  149.                        `UVM_PH_TRACE("PH/TRC/TIMEOUT", "PHASE TIMEOUT WATCHDOG EXPIRED", this, UVM_LOW)
  150.                      foreach (m_executing_phases[p]) begin
  151.                         if ((p.phase_done != null) && (p.phase_done.get_objection_total() > 0)) begin
  152.                            if (m_phase_trace)
  153.                              `UVM_PH_TRACE("PH/TRC/TIMEOUT/OBJCTN",
  154.                                            $sformatf("Phase '%s' has outstanding objections:\n%s", p.get_full_name(), p.phase_done.convert2string()),
  155.                                            this,
  156.                                            UVM_LOW)
  157.                         end
  158.                      end
  159.                         
  160.                      `uvm_fatal("PH_TIMEOUT",
  161.                                 $sformatf("Explicit timeout of %0t hit, indicating a probable testbench issue",
  162.                                           top.phase_timeout))
  163.                   end
  164.                   if (m_phase_trace)
  165.                     `UVM_PH_TRACE("PH/TRC/EXE/3","PHASE EXIT TIMEOUT",this,UVM_DEBUG)
  166.                end // if (this.get_name() == "run")
  167.                else begin
  168.                   wait (0); // never unblock for non-run phase
  169.                end
  170.              end // if (m_phase_trace)
  171.   
  172.            join_any
  173.            disable fork;
  174.         
  175.           end
  176.   
  177.         join // guard
  178.     end
  179.   end
  180.   m_executing_phases.delete(this);
  181.   //---------
  182.   // JUMPING:
  183.   //---------
  184.   // If jump_to() was called then we need to kill all the successor
  185.   // phases which may still be running and then initiate the new
  186.   // phase.  The return is necessary so we don't start new successor
  187.   // phases.  If we are doing a forward jump then we want to set the
  188.   // state of this phase's successors to UVM_PHASE_DONE.  This
  189.   // will let us pretend that all the phases between here and there
  190.   // were executed and completed.  Thus any dependencies will be
  191.   // satisfied preventing deadlocks.
  192.   // GSA TBD insert new jump support
  193.   if (m_phase_type == UVM_PHASE_NODE) begin
  194.     if(m_premature_end) begin
  195.       if(m_jump_phase != null) begin
  196.         state_chg.m_jump_to = m_jump_phase;
  197.       
  198.         `uvm_info("PH_JUMP",
  199.               $sformatf("phase %s (schedule %s, domain %s) is jumping to phase %s",
  200.                get_name(), get_schedule_name(), get_domain_name(), m_jump_phase.get_name()),
  201.               UVM_MEDIUM);
  202.       end
  203.       else begin
  204.         `uvm_info("PH_JUMP",
  205.               $sformatf("phase %s (schedule %s, domain %s) is ending prematurely",
  206.                get_name(), get_schedule_name(), get_domain_name()),
  207.               UVM_MEDIUM);
  208.       end
  209.   
  210.   
  211.       #0; // LET ANY WAITERS ON READY_TO_END TO WAKE UP
  212.       if (m_phase_trace)
  213.         `UVM_PH_TRACE("PH_END","ENDING PHASE PREMATURELY",this,UVM_HIGH)
  214.     end
  215.     else begin
  216.       // WAIT FOR PREDECESSORS:  // WAIT FOR PREDECESSORS:
  217.       // function phases only
  218.       if (task_phase == null)
  219.         m_wait_for_pred();
  220.     end
  221.   
  222.     //-------
  223.     // ENDED:
  224.     //-------
  225.     // execute 'phase_ended' callbacks
  226.     if (m_phase_trace)
  227.       `UVM_PH_TRACE("PH_END","ENDING PHASE",this,UVM_HIGH)
  228.     state_chg.m_prev_state = m_state;
  229.     m_state = UVM_PHASE_ENDED;
  230.     `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
  231.     if (m_imp != null)
  232.       m_imp.traverse(top,this,UVM_PHASE_ENDED);
  233.     #0; // LET ANY WAITERS WAKE UP
  234.   
  235.   
  236.     //---------
  237.     // CLEANUP:
  238.     //---------
  239.     // kill this phase's threads
  240.     state_chg.m_prev_state = m_state;
  241.     if(m_premature_end) m_state = UVM_PHASE_JUMPING;
  242.     else m_state = UVM_PHASE_CLEANUP ;
  243.     `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
  244.     if (m_phase_proc != null) begin
  245.       m_phase_proc.kill();
  246.       m_phase_proc = null;
  247.     end
  248.     #0; // LET ANY WAITERS WAKE UP
  249.     if (phase_done != null)
  250.       phase_done.clear();
  251.   end
  252.   //------
  253.   // DONE:
  254.   //------
  255.   m_premature_end = 0 ;
  256.   if(m_jump_fwd || m_jump_bkwd) begin
  257.     if(m_jump_fwd) begin
  258.       clear_successors(UVM_PHASE_DONE,m_jump_phase);
  259.     end
  260.     m_jump_phase.clear_successors();
  261.     uvmkit_gettimeofday(phase_finish_time);
  262.     void'(uvmkit_timeval_subtract(phase_diff_time, phase_finish_time, phase_start_time));
  263.     `uvm_info("TIMED_PHASE",
  264.               $sformatf("'%s' phase took a total of %f seconds",
  265.                         this.get_full_name(),
  266.                         uvmkit_timeval_to_real(phase_diff_time)),
  267.               UVM_LOW)
  268.   end
  269.   else begin
  270.     if (m_phase_trace)
  271.       `UVM_PH_TRACE("PH/TRC/DONE","Completed phase",this,UVM_LOW)
  272.     state_chg.m_prev_state = m_state;
  273.     m_state = UVM_PHASE_DONE;
  274.     `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
  275.     m_phase_proc = null;
  276.     uvmkit_gettimeofday(phase_finish_time);
  277.     void'(uvmkit_timeval_subtract(phase_diff_time, phase_finish_time, phase_start_time));
  278.     `uvm_info("TIMED_PHASE",
  279.               $sformatf("'%s' phase took a total of %f seconds",
  280.                         this.get_full_name(),
  281.                         uvmkit_timeval_to_real(phase_diff_time)),
  282.               UVM_LOW)
  283.     #0; // LET ANY WAITERS WAKE UP
  284.   end
  285.   #0; // LET ANY WAITERS WAKE UP
  286.   if (phase_done != null)
  287.     phase_done.clear();
  288. //-----------
  289. // SCHEDULED:
  290. //-----------
  291.   if(m_jump_fwd || m_jump_bkwd) begin
  292.     void'(m_phase_hopper.try_put(m_jump_phase));
  293.     m_jump_phase = null;
  294.     m_jump_fwd = 0;
  295.     m_jump_bkwd = 0;
  296.     uvmkit_gettimeofday(phase_finish_time);
  297.     void'(uvmkit_timeval_subtract(phase_diff_time, phase_finish_time, phase_start_time));
  298.     `uvm_info("TIMED_PHASE",
  299.               $sformatf("'%s' phase took a total of %f seconds",
  300.                         this.get_full_name(),
  301.                         uvmkit_timeval_to_real(phase_diff_time)),
  302.               UVM_LOW)
  303.   end
  304.   // If more successors, schedule them to run now
  305.   else if (m_successors.size() == 0) begin
  306.     top.m_phase_all_done=1;
  307.   end
  308.   else begin
  309.     // execute all the successors
  310.     foreach (m_successors[succ]) begin
  311.       if(succ.m_state < UVM_PHASE_SCHEDULED) begin
  312.         state_chg.m_prev_state = succ.m_state;
  313.         state_chg.m_phase = succ;
  314.         succ.m_state = UVM_PHASE_SCHEDULED;
  315.         `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(succ, state_chg))
  316.         #0; // LET ANY WAITERS WAKE UP
  317.         void'(m_phase_hopper.try_put(succ));
  318.         if (m_phase_trace)
  319.           `UVM_PH_TRACE("PH/TRC/SCHEDULED",{"Scheduled from phase ",get_full_name()},succ,UVM_LOW)
  320.       end
  321.     end
  322.   end
  323. endtask
复制代码
  第一次调用这个task是句柄m_common_domain,首先我们必要等待调用这个task的phase的m_predecessors[]执行完毕,即它们的m_state 为UVM_PHASE_DONE,这里m_common_domain是没有任何前置phase的,所以略过。若当前phase的m_state是UVM_PHASE_DONE,表明已经执行过了,task会直接return。若当前phase_type不是UVM_PHASE_NODE,即对于domain/schedule这两张phase运行图来说,会进行"fake" execution,task只会将其m_state分别转换为UVM_PHASE_STARTED和UVM_PHASE_EXECUTING,而并不会真的做什么事情。在task的末了判断m_jump_fwd和m_jump_bkwd是否为1,这两个变量是跟domain的jump操纵有关的,这里它们的值都是0,则对于当前phase的全部后续phase m_successors,判断其m_state是否已经走到了UVM_PHASE_SCHEDULED这一步,若没有,则将其m_state置为UVM_PHASE_SCHEDULED并分别将后续phase的句柄放入m_phase_hopper邮箱。对于m_common_domain来说,它的后续phase只有一个即build_phase,于是这里就会将m_common_domain运行图中的build_phase唯一实例句柄放入m_phase_hopper。
  在task m_run_phases()中forever循环从邮箱中拿到build_phase的句柄,会立即用fork…join_none启动新线程调用build_phase的execute_phase()执行。由于这次调用execute_phase()的句柄类型的phase_type是UVM_PHASE_NODE,而且是function phase,所以会依次将其m_state进入UVM_PHASE_STARTED、UVM_PHASE_EXECUTING和UVM_PHASE_ENDED状态,并将这些状态作为参数调用traverse()。traverse()我们之前已经研究过,这里传入的第一个参数是top,即uvm_root的唯一实例,则执行这些phase的component是从uvm_root作为入口,对于build_phase是自上而下执行的。在后面的状态中执行一些清理的工作,这里不再赘述。依然是task末了,会查抄build_phase的后续phase m_successors[]并将其放入m_phase_hopper邮箱来执行,这样UVM中的各个function phase就这样按固定顺序依次执行。直到执行到末了m_common_domain的m_end_node不再有m_successors,则把变量m_phase_all_done置为1,UVM会调用$finish结束仿真。
在执行到start_of_simulation_phse的时候,该phase有两个后继phase,分别为run_phase和m_uvm_domain,他们会分别被放入m_phase_hopper邮箱并被取走调用fork…join_none分别启动并行的新线程来调用各自的execute_phase(),在m_uvm_domain执行execute_phase()时,并不会真的执行,所以也不会有任何仿真延时,它也会在task末了把后继phase句柄即m_uvm_schedule放入邮箱并被取走开启新线程调用execute_phase(),m_uvm_schedule同样也不会真的被执行,而是继承把后继phase即pre_reset_phase实例句柄放入邮箱,当新的并行线程被启动调用execute_phase()时,这个phase_type为UVM_PHASE_NODE的phase会被执行。全部这统统都是并行发生的,所以我们说run_phase和12个run-time phase是同时被并行启动运行的。
  在运行task phase时,execute_phase()中会在调用traverse()执行当前task phase的同时启动数个并行进程,同时监测当前phase的objection和TIMEOUT,一旦达到退出条件,则立即杀掉当前进程进入下一个task phase。因此,在运行task phase时设置合理的objection和timeout以保证phase正常运行非常重要。


3. objection机制

  UVM的objection机制是共同task phase来用的,在task phase如run_phase中若整个平台中没有raise任何objection,则UVM运行到这里会立即跳转到下一个phase,不会执行run_phase里的任何内容。
3.1. uvm_objection class

  uvm_objection类扩展自uvm_report_object类,此中界说了一个存储内容为uvm_objection类型的静态队列,在new()函数中会把当前调用该函数的component/object句柄存入队列m_objections。别的,在uvm_objection类中还界说了一个uvm_objection_events类型的索引为uvm_object的联合数组m_events[],uvm_objection_events类中界说了数个event变量。联合数组m_source_count[]和m_total_count[]均是以uvm_object类型为索引,纪录的是raise objection的sequence大概component节点的objection数量和总的objection数量,联合数组m_drain_time[]以uvm_object类型为索引,纪录的是当前sequence大概component节点的objection的drain_time。
  1. //uvm_objection.svh
  2. class uvm_objection_events;
  3.   int waiters;
  4.   event raised;
  5.   event dropped;
  6.   event all_dropped;
  7. endclass
  8. class uvm_objection extends uvm_report_object;
  9.   `uvm_register_cb(uvm_objection, uvm_objection_callback)
  10.   protected int     m_source_count[uvm_object];
  11.   protected int     m_total_count [uvm_object];
  12.   protected time    m_drain_time  [uvm_object];
  13.   protected uvm_objection_events m_events [uvm_object];
  14.   /*protected*/ bit     m_top_all_dropped;
  15.   protected uvm_root m_top;
  16.      
  17.   static uvm_objection m_objections[$];
  18.   ...
  19.   function new(string name="");
  20.     super.new(name);
  21.     ...
  22.     m_objections.push_back(this);
  23.   endfunction
  24.   ...
  25. endclass
复制代码
3.2. raise_objection

  一般我们在某个sequence大概test的run_phase中调用raise_objection(),如phase.raise_objection(),当前phase会调用phase_done的raise_objection()函数,phase_done是界说在uvm_phase中的uvm_objection类型的句柄,在我们调用add()函数添加各个task phase时,就会例化出各个task phase的uvm_objection类型句柄并赋值给phase_done。
  1. //uvm_phase.svh
  2. class uvm_phase extends uvm_object;  
  3.   uvm_objection phase_done;
  4.   ...
  5. endclass
  6. function void uvm_phase::add(uvm_phase phase,
  7.                              uvm_phase with_phase=null,
  8.                              uvm_phase after_phase=null,
  9.                              uvm_phase before_phase=null);
  10.   if (phase.get_phase_type() == UVM_PHASE_IMP) begin
  11.     uvm_task_phase tp;
  12.     ...
  13.     // The phase_done objection is only required
  14.     // for task-based nodes
  15.     if ($cast(tp, phase)) begin
  16.        if (new_node.get_name() == "run") begin
  17.          new_node.phase_done = uvm_test_done_objection::get();
  18.        end
  19.        else begin
  20.          new_node.phase_done = uvm_objection::type_id::create({phase.get_name(), "_objection"});
  21.        end
  22.     end
  23.   end
  24.   ...
  25. endfunction
  26. function void uvm_phase::raise_objection (uvm_object obj,
  27.                                                    string description="",
  28.                                                    int count=1);
  29.   if (phase_done != null)
  30.     phase_done.raise_objection(obj,description,count);
  31.   else
  32.     m_report_null_objection(obj, description, count, "raise");
  33. endfunction
复制代码
  界说在uvm_objection类中的raise_objection()函数有三个参数,第一个参数uvm_object类型obj,我们可以在一个sequence大概某component节点中使用this指针传入当前的sequence大概component,第二个参数可以输入一些字符串类型的描述,第三个参数是要raise的objection数量,默认是1。若参数obj为null,则obj为uvm_root的唯一实例m_top,然后把obj作为前两个参数调用m_raise()函数。
  1. //uvm_objection.svh
  2. class uvm_objection extends uvm_report_object;
  3.   virtual function void raise_objection (uvm_object obj=null,
  4.                                          string description="",
  5.                                          int count=1);
  6.     if(obj == null)
  7.       obj = m_top;
  8.     m_cleared = 0;
  9.     m_top_all_dropped = 0;
  10.     m_raise (obj, obj, description, count);
  11.   endfunction
  12.   function void m_raise (uvm_object obj,
  13.                          uvm_object source_obj,
  14.                          string description="",
  15.                          int count=1);
  16.     int idx;
  17.     uvm_objection_context_object ctxt;
  18.     // Ignore raise if count is 0
  19.     if (count == 0)
  20.       return;
  21.     if (m_total_count.exists(obj))
  22.       m_total_count[obj] += count;
  23.     else
  24.       m_total_count[obj] = count;
  25.     if (source_obj==obj) begin
  26.       if (m_source_count.exists(obj))
  27.         m_source_count[obj] += count;
  28.       else
  29.         m_source_count[obj] = count;
  30.     end
  31.   
  32.     if (m_trace_mode)
  33.       m_report(obj,source_obj,description,count,"raised");
  34.     raised(obj, source_obj, description, count);
  35.     ...
  36.     if (ctxt == null) begin
  37.         // If there were no drains, just propagate as usual
  38.         if (!m_prop_mode && obj != m_top)
  39.           m_raise(m_top,source_obj,description,count);
  40.         else if (obj != m_top)
  41.           m_propagate(obj, source_obj, description, count, 1, 0);
  42.     end
  43.     else begin
  44.         // Otherwise we need to determine what exactly happened
  45.         int diff_count;
  46.         // Determine the diff count, if it's positive, then we're
  47.         // looking at a 'raise' total, if it's negative, then
  48.         // we're looking at a 'drop', but not down to 0.  If it's
  49.         // a 0, that means that there is no change in the total.
  50.         diff_count = count - ctxt.count;
  51.         if (diff_count != 0) begin
  52.             // Something changed
  53.             if (diff_count > 0) begin
  54.                 // we're looking at an increase in the total
  55.                 if (!m_prop_mode && obj != m_top)
  56.                   m_raise(m_top, source_obj, description, diff_count);
  57.                 else if (obj != m_top)
  58.                   m_propagate(obj, source_obj, description, diff_count, 1, 0);
  59.             end
  60.             else begin
  61.                 // we're looking at a decrease in the total
  62.                 // The count field is always positive...
  63.                 diff_count = -diff_count;
  64.                 if (!m_prop_mode && obj != m_top)
  65.                   m_drop(m_top, source_obj, description, diff_count);
  66.                 else if (obj != m_top)
  67.                   m_propagate(obj, source_obj, description, diff_count, 0, 0);
  68.             end
  69.         end
  70.         // Cleanup
  71.         ctxt.clear();
  72.         m_context_pool.push_back(ctxt);
  73.     end
  74.         
  75.   endfunction
  76. endclass
复制代码
  在此中界说了一个uvm_objection_context_object类型的变量ctxt,在uvm_objection_context_object类中界说了uvm_object类型的变量obj和source_obj,字符串description以及数量count,其实就是纪录下我们每次raise_objection大概drop_objection的参数信息。
  1. //uvm_objection.svh
  2. class uvm_objection_context_object;
  3.     uvm_object obj;
  4.     uvm_object source_obj;
  5.     string description;
  6.     int    count;
  7.     uvm_objection objection;
  8.     // Clears the values stored within the object,
  9.     // preventing memory leaks from reused objects
  10.     function void clear();
  11.         obj = null;
  12.         source_obj = null;
  13.         description = "";
  14.         count = 0;
  15.         objection = null;
  16.     endfunction : clear
  17. endclass
复制代码
  回到m_raise()函数,若传入count为0,函数直接返回。若是第一次在my_seq中raise objection,则m_total_count[my_seq]=count,若不是,则以该obj为索引的m_total_count数量加上此次要raise的objection数量。由于传入的obj和source_obj是同一个参数m_top,所以m_source_count[my_seq]=count,若不是,则略过,所以m_source_count的索引只会纪录在此中raise objection的sequence大概component节点。接下来函数调用raised():
  1. //uvm_objection.svh
  2. class uvm_objection extends uvm_report_object;
  3.   virtual function void raised (uvm_object obj,
  4.                                 uvm_object source_obj,
  5.                                 string description,
  6.                                 int count);
  7.     uvm_component comp;
  8.     if ($cast(comp,obj))   
  9.       comp.raised(this, source_obj, description, count);
  10.     `uvm_do_callbacks(uvm_objection,uvm_objection_callback,raised(this,obj,source_obj,description,count))
  11.     if (m_events.exists(obj))
  12.        ->m_events[obj].raised;
  13.   endfunction
  14. endclass
复制代码
  若传入的obj参数是component类型,会调用component的callback raised(),我们可以在某component中来重载这个callback。若m_events中有当前obj的索引内容,则触发此中的raised变乱。
  回到m_raise()中,此时ctxt为null,且m_prop_mode为1(default 1),则调用m_propagate():
  1. //uvm_objection.svh
  2. class uvm_objection extends uvm_report_object;
  3.   unction void m_propagate (uvm_object obj,
  4.                              uvm_object source_obj,
  5.                              string description,
  6.                              int count,
  7.                              bit raise,
  8.                              int in_top_thread);
  9.     if (obj != null && obj != m_top) begin
  10.       obj = m_get_parent(obj);
  11.       if(raise)
  12.         m_raise(obj, source_obj, description, count);
  13.       else
  14.         m_drop(obj, source_obj, description, count, in_top_thread);
  15.     end
  16.   endfunction
  17.   function uvm_object m_get_parent(uvm_object obj);
  18.     uvm_component comp;
  19.     uvm_sequence_base seq;
  20.     if ($cast(comp, obj)) begin
  21.       obj = comp.get_parent();
  22.     end
  23.     else if ($cast(seq, obj)) begin
  24.        obj = seq.get_sequencer();
  25.     end
  26.     else
  27.       obj = m_top;
  28.     if (obj == null)
  29.       obj = m_top;
  30.     return obj;
  31.   endfunction
  32. endclass
复制代码
  若当前传入obj不是m_top,则调用m_get_parent()取得其父节点的句柄。这个obj颠末层层参数转达,实际上还是我们在一开始调用raise_objection()时的sequence大概component,若是obj是一个sequence,则m_get_parent()返回该sequence的sequencer,若是一个component节点,则直接返回其父节点。然后在m_propagate()中再次调用m_raise()函数,不外这次obj参数即为其父节点句柄。颠末层层递归调用m_raise(),直到uvm_root节点,此时
 m_source_count[my_seq] = 1;
 m_total_count[env.agt.sqr] = 1;
 m_total_count[env.agt] = 1;
 m_total_count[env] = 1;
 m_total_count[m_top] = 1;
  当我们在某个seq大概component中raise objection数为count时,UVM会将count纪录在以自身为索引的m_source_count[]中。当m_prop_mode默认为1时其会将count以当前节点的全部上级节点为索引纪录在m_total_count[]中,m_prop_mode为0时只会将自身和顶层uvm_root为索引纪录在m_total_count[]中,不会纪录中央节点。
3.3. drop_objection

  同样在某sequence大概component中drop_objection()时,也会在此中调用drop_done的drop_objection()函数:
  1. //uvm_phase.svh
  2. function void uvm_phase::drop_objection (uvm_object obj,
  3.                                                   string description="",
  4.                                                   int count=1);
  5.   if (phase_done != null)
  6.     phase_done.drop_objection(obj,description,count);
  7.   else
  8.     m_report_null_objection(obj, description, count, "drop");
  9. endfunction
  10. //uvm_objection.svh
  11. class uvm_objection extends uvm_report_object;
  12.   virtual function void drop_objection (uvm_object obj=null,
  13.                                         string description="",
  14.                                         int count=1);
  15.     if(obj == null)
  16.       obj = m_top;
  17.     m_drop (obj, obj, description, count, 0);
  18.   endfunction
  19.   // Function- m_drop
  20.   function void m_drop (uvm_object obj,
  21.                         uvm_object source_obj,
  22.                         string description="",
  23.                         int count=1,
  24.                         int in_top_thread=0);
  25.     // Ignore drops if the count is 0
  26.     if (count == 0)
  27.       return;
  28.     if (!m_total_count.exists(obj) || (count > m_total_count[obj])) begin
  29.       if(m_cleared)
  30.         return;
  31.       uvm_report_fatal("OBJTN_ZERO", {"Object "", obj.get_full_name(),
  32.         "" attempted to drop objection '",this.get_name(),"' count below zero"});
  33.       return;
  34.     end
  35.     if (obj == source_obj) begin
  36.       if (!m_source_count.exists(obj) || (count > m_source_count[obj])) begin
  37.         if(m_cleared)
  38.           return;
  39.         uvm_report_fatal("OBJTN_ZERO", {"Object "", obj.get_full_name(),
  40.           "" attempted to drop objection '",this.get_name(),"' count below zero"});
  41.         return;
  42.       end
  43.       m_source_count[obj] -= count;
  44.     end
  45.     m_total_count[obj] -= count;
  46.     if (m_trace_mode)
  47.       m_report(obj,source_obj,description,count,"dropped");
  48.    
  49.     dropped(obj, source_obj, description, count);
  50.   
  51.     // if count != 0, no reason to fork
  52.     if (m_total_count[obj] != 0) begin
  53.       if (!m_prop_mode && obj != m_top)
  54.         m_drop(m_top,source_obj,description, count, in_top_thread);
  55.       else if (obj != m_top) begin
  56.         this.m_propagate(obj, source_obj, description, count, 0, in_top_thread);
  57.       end
  58.     end
  59.     else begin
  60.         uvm_objection_context_object ctxt;
  61.         if (m_context_pool.size())
  62.           ctxt = m_context_pool.pop_front();
  63.         else
  64.           ctxt = new;
  65.         ctxt.obj = obj;
  66.         ctxt.source_obj = source_obj;
  67.         ctxt.description = description;
  68.         ctxt.count = count;
  69.         ctxt.objection = this;
  70.         // Need to be thread-safe, let the background
  71.         // process handle it.
  72.         // Why don't we look at in_top_thread here?  Because
  73.         // a re-raise will kill the drain at object that it's
  74.         // currently occuring at, and we need the leaf-level kills
  75.         // to not cause accidental kills at branch-levels in
  76.         // the propagation.
  77.         // Using the background process just allows us to
  78.         // separate the links of the chain.
  79.         m_scheduled_list.push_back(ctxt);
  80.     end // else: !if(m_total_count[obj] != 0)
  81.   endfunction
  82. endclass
复制代码
  若传入参数obj不是m_top,uvm_objection的drop_objection()会把obj作为前两个参数继承调用m_drop(),若传入的参数count为0,函数直接返回。若m_total_count[]中不存在obj为索引的objection数量纪录,大概当前传入的count数量超出了m_total_count[obj],函数直接返回。bit m_cleared用来纪录当前objection是否已经全部被drop掉,我们会在每次调用raise_objectiion时候把这个bit置为0,若这个bit为1,则函数也直接返回。当前m_drop()的前两个参数obj和source_obj相等,将m_source_count[obj]减去count,同时将m_total_count[obj]也减去count。接下来调用dropped()函数:
  1. //uvm_objection.svh
  2. class uvm_objection extends uvm_report_object;  
  3.   virtual function void dropped (uvm_object obj,
  4.                                  uvm_object source_obj,
  5.                                  string description,
  6.                                  int count);
  7.     uvm_component comp;
  8.     if($cast(comp,obj))   
  9.       comp.dropped(this, source_obj, description, count);
  10.     `uvm_do_callbacks(uvm_objection,uvm_objection_callback,dropped(this,obj,source_obj,description,count))
  11.     if (m_events.exists(obj))
  12.        ->m_events[obj].dropped;
  13.   endfunction
  14. endclass
复制代码
  若obj是一个component类型,则调用component的callback dropped(),我们可以在component中扩展这个callback。别的会触发m_events[obj]的dropped变乱。
  回到m_drop()中来,若减去这次drop的objection数量之后m_total_count[obj]不为0且m_prop_mode为1,则调用m_propagate()并把第五个参数传入0,表示这是一次drop操纵,m_propagate()在之前我们已经见过,这个函数就是从当前的obj开始遍历全部父节点直到uvm_root,根据第五个参数对这些父节点进行raise大概drop objection操纵。
3.4. drain_time

  若当前调用m_drop()减去这次drop的objection数量之后m_total_count[obj]为0,阐明对obj来说,全部的objection都已经被dropped了,当前phase是否可以直接结束进入下一个phase中呢,答案是否定的,UVM会等待执行当前phase的其他节点中的objection被dropped,那若全部节点的全部objection都已经被dropped了,是否可以结束当前phase呢?答案依然是否定的,若某节点中当前phase设置drain_time,则在objection都被dropped之后,该phase会等待drain_time时间之后才会结束。这个drain_time考虑到了验证平台激励和DUT输出可能会存在延时,比如我们在driver的main_phase中输入向DUT灌输激励之后drop_objection,DUT处理这些激励必要一些延时,假如此时直接结束当前phase,monitor就会漏掉末了的一些激励处理之后的结果,所以phase会在全部的objection被dropped之后再延伸drain_time时间才会结束。
  1. //uvm_objection.svh
  2. class uvm_objection extends uvm_report_object;  
  3.   function void set_drain_time (uvm_object obj=null, time drain);
  4.     if (obj==null)
  5.       obj = m_top;
  6.     m_drain_time[obj] = drain;
  7.   endfunction
  8. endclass
复制代码
  通过调用set_drain_time()函数可以设置当前obj的drain_time,将其存入以obj为索引的联合数组中。若传入参数obj为null,则设置m_top的drain_time。
  回到m_drop()中,若当前m_total_count[obj]为0,我们还必要等待当前phase的drain_time时间才能真正结束phase,此时会查抄m_context_pool的内容,若有存储直接弹出最前面的一个,若没有,则创建一个uvm_objection_context_object类型的对象ctxt,并把obj/source_obj/description/count和this指针分别赋值给其成员变量,末了将其放入队列m_scheduled_list[                                   ]                         。                         其                         中                                   m                            c                                  o                         n                         t                         e                         x                                   t                            p                                  o                         o                         l                         [                              ]。此中m_context_pool[                  ]。此中mc​ontextp​ool[]中存放的是用过的uvm_objection_context_object指针,从这里拿跟new()一个没有太大区别。
  1. //uvm_objection.svh
  2. class uvm_objection extends uvm_report_object;
  3.   local static uvm_objection_context_object m_context_pool[$];
  4.   
  5. `ifndef UVM_USE_PROCESS_CONTAINER   
  6.   local process m_drain_proc[uvm_object];
  7. `else
  8.   local process_container_c m_drain_proc[uvm_object];
  9. `endif
  10.   local static uvm_objection_context_object m_scheduled_list[$];
  11.   local uvm_objection_context_object m_scheduled_contexts[uvm_object];
  12.   local uvm_objection_context_object m_forked_list[$];
  13.   local uvm_objection_context_object m_forked_contexts[uvm_object];
  14.   ...
  15. endclass
复制代码
  队列m_scheduled_list[$]存放的是当前在等待drain_time的objection信息,联合数组m_scheduled_contexts[]以uvm_object为索引,存放的是关于uvm_object的在等待drain_time的objection信息,队列m_forked_list[$]存放的是当前已经启动fork线程等待drain_time的objection信息,联合数组m_forked_contexts[]以uvm_object为索引,存放的是关于uvm_object的已经启动fork线程在等待drain_time的objection信息。别的联合数组m_drain_proc[]存放的是以uvm_object为索引的等待drain_time的进程句柄。
  等待phase的drain_time的进程其实不停作为backgroud进程从仿真一开始调用uvm_root::run_test()就启动了,乃至比调用m_run_phases()还要早,在run_test()中调用静态函数m_init_objections()启动该进程,该函数会用fork…join_none调用m_execute_scheduled_forks()。
  1. //uvm_root.svh
  2. task uvm_root::run_test(string test_name="");
  3.   uvm_objection::m_init_objections();
  4. endtask
  5. //uvm_objection.svh
  6. class uvm_objection extends uvm_report_object;
  7.   static function void m_init_objections();
  8.     fork
  9.       uvm_objection::m_execute_scheduled_forks();
  10.     join_none
  11.   endfunction
  12.   // background process; when non
  13.   static task m_execute_scheduled_forks();
  14.     while(1) begin
  15.       wait(m_scheduled_list.size() != 0);
  16.       if(m_scheduled_list.size() != 0) begin
  17.           uvm_objection_context_object c;
  18.           uvm_objection o;
  19.           // Save off the context before the fork
  20.           c = m_scheduled_list.pop_front();
  21.           // A re-raise can use this to figure out props (if any)
  22.           c.objection.m_scheduled_contexts[c.obj] = c;
  23.           // The fork below pulls out from the forked list
  24.           c.objection.m_forked_list.push_back(c);
  25.           // The fork will guard the m_forked_drain call, but
  26.           // a re-raise can kill m_forked_list contexts in the delta
  27.           // before the fork executes.
  28.           fork : guard
  29.               automatic uvm_objection objection = c.objection;
  30.               begin
  31.                   // Check to maike sure re-raise didn't empty the fifo
  32.                   if (objection.m_forked_list.size() > 0) begin
  33.                       uvm_objection_context_object ctxt;
  34.                       ctxt = objection.m_forked_list.pop_front();
  35.                       // Clear it out of scheduled
  36.                       objection.m_scheduled_contexts.delete(ctxt.obj);
  37.                       // Move it in to forked (so re-raise can figure out props)
  38.                       objection.m_forked_contexts[ctxt.obj] = ctxt;
  39.                       // Save off our process handle, so a re-raise can kill it...
  40. `ifndef UVM_USE_PROCESS_CONTAINER                     
  41.                       objection.m_drain_proc[ctxt.obj] = process::self();
  42. `else
  43.                      begin
  44.                         process_container_c c = new(process::self());
  45.                         objection.m_drain_proc[ctxt.obj]=c;
  46.                      end
  47. `endif                     
  48.                       // Execute the forked drain
  49.                       objection.m_forked_drain(ctxt.obj, ctxt.source_obj, ctxt.description, ctxt.count, 1);
  50.                       // Cleanup if we survived (no re-raises)
  51.                       objection.m_drain_proc.delete(ctxt.obj);
  52.                       objection.m_forked_contexts.delete(ctxt.obj);
  53.                       // Clear out the context object (prevent memory leaks)
  54.                       ctxt.clear();
  55.                       // Save the context in the pool for later reuse
  56.                       m_context_pool.push_back(ctxt);
  57.                   end
  58.               end
  59.           join_none : guard
  60.       end
  61.     end
  62.   endtask
  63. endclass
复制代码
  静态task m_execute_scheduled_forks()中使用while无穷循环,一旦队列m_scheduled_list[$]不为空,则从中取出uvm_objection_context_object类型句柄放入m_scheduled_contexts[obj]和队列m_forked_list[$],这些句柄纪录的是当前obj在drop_objection时必要等待drain_time的信息。随纵然用fork…join_none并行线程取出m_forked_list[$]的句柄并清除m_forked_list[$]和m_scheduled_contexts[obj]相关内容,同时启动m_drain_proc[obj]调用m_forked_drain()来执行等待任务,等待该task返回以后清除m_forked_drain[obj]和m_forked_contexts[obj]。
  1. //uvm_objection.svh
  2. class uvm_objection extends uvm_report_object;
  3.   task m_forked_drain (uvm_object obj,
  4.                        uvm_object source_obj,
  5.                        string description="",
  6.                        int count=1,
  7.                        int in_top_thread=0);
  8.       int diff_count;
  9.       if (m_drain_time.exists(obj))
  10.         `uvm_delay(m_drain_time[obj])
  11.       
  12.       if (m_trace_mode)
  13.         m_report(obj,source_obj,description,count,"all_dropped");
  14.       
  15.       all_dropped(obj,source_obj,description, count);
  16.          
  17.           // wait for all_dropped cbs to complete
  18.       wait fork;
  19.       /* NOT NEEDED - Any raise would have killed us!
  20.       if(!m_total_count.exists(obj))
  21.         diff_count = -count;
  22.       else
  23.         diff_count = m_total_count[obj] - count;
  24.       */
  25.       // we are ready to delete the 0-count entries for the current
  26.       // object before propagating up the hierarchy.
  27.       if (m_source_count.exists(obj) && m_source_count[obj] == 0)
  28.         m_source_count.delete(obj);
  29.          
  30.       if (m_total_count.exists(obj) && m_total_count[obj] == 0)
  31.         m_total_count.delete(obj);
  32.       if (!m_prop_mode && obj != m_top)
  33.         m_drop(m_top,source_obj,description, count, 1);
  34.       else if (obj != m_top)
  35.         m_propagate(obj, source_obj, description, count, 0, 1);
  36.   endtask
  37.   virtual task all_dropped (uvm_object obj,
  38.                             uvm_object source_obj,
  39.                             string description,
  40.                             int count);
  41.     uvm_component comp;
  42.     if($cast(comp,obj))   
  43.       comp.all_dropped(this, source_obj, description, count);
  44.     `uvm_do_callbacks(uvm_objection,uvm_objection_callback,all_dropped(this,obj,source_obj,description,count))
  45.     if (m_events.exists(obj))
  46.        ->m_events[obj].all_dropped;
  47.     if (obj == m_top)
  48.       m_top_all_dropped = 1;
  49.   endtask
  50. endclass
复制代码
  在task m_forked_drain()中,若当前obj已经设置了drain_time,会首先等待m_drain_time[obj]时间,然后调用all_dropped()函数,若obj是component类型,该函数中会首先调用all_dropped() callback,我们可以在某component中重载这个callback,其次会触发m_events[obj]的all_dropped变乱,若当前obj是m_top,则把m_top_all_dropped置为1。回到task m_forked_drain()中,接着把m_source_count[]和m_total_count[]中关于obj的部分清除,若m_prop_mode为1则调用m_propagate()继承drop全部父类节点的objection,直到m_top的全部objection被dropped。
  若某obj在等待drain_time期间,又有objection被raised,该如那边理?回到之前的m_raise()中:
  1. //uvm_objection.svh
  2. class uvm_objection extends uvm_report_object;
  3.   function void m_raise (uvm_object obj,
  4.                          uvm_object source_obj,
  5.                          string description="",
  6.                          int count=1);
  7.     int idx;
  8.     uvm_objection_context_object ctxt;
  9.       // Handle any outstanding drains...
  10.     // First go through the scheduled list
  11.     idx = 0;
  12.     while (idx < m_scheduled_list.size()) begin
  13.         if ((m_scheduled_list[idx].obj == obj) &&
  14.             (m_scheduled_list[idx].objection == this)) begin
  15.             // Caught it before the drain was forked
  16.             ctxt = m_scheduled_list[idx];
  17.             m_scheduled_list.delete(idx);
  18.             break;
  19.         end
  20.         idx++;
  21.     end
  22.     // If it's not there, go through the forked list
  23.     if (ctxt == null) begin
  24.         idx = 0;
  25.         while (idx < m_forked_list.size()) begin
  26.             if (m_forked_list[idx].obj == obj) begin
  27.                 // Caught it after the drain was forked,
  28.                 // but before the fork started
  29.                 ctxt = m_forked_list[idx];
  30.                 m_forked_list.delete(idx);
  31.                 m_scheduled_contexts.delete(ctxt.obj);
  32.                 break;
  33.             end
  34.             idx++;
  35.         end
  36.     end
  37.     // If it's not there, go through the forked contexts
  38.     if (ctxt == null) begin
  39.         if (m_forked_contexts.exists(obj)) begin
  40.             // Caught it with the forked drain running
  41.             ctxt = m_forked_contexts[obj];
  42.             m_forked_contexts.delete(obj);
  43.             // Kill the drain
  44. `ifndef UVM_USE_PROCESS_CONTAINER          
  45.             m_drain_proc[obj].kill();
  46.             m_drain_proc.delete(obj);
  47. `else
  48.             m_drain_proc[obj].p.kill();
  49.             m_drain_proc.delete(obj);
  50. `endif
  51.           
  52.         end
  53.     end
  54.     if (ctxt == null) begin
  55.         // If there were no drains, just propagate as usual
  56.         if (!m_prop_mode && obj != m_top)
  57.           m_raise(m_top,source_obj,description,count);
  58.         else if (obj != m_top)
  59.           m_propagate(obj, source_obj, description, count, 1, 0);
  60.     end
  61.     else begin
  62.         // Otherwise we need to determine what exactly happened
  63.         int diff_count;
  64.         // Determine the diff count, if it's positive, then we're
  65.         // looking at a 'raise' total, if it's negative, then
  66.         // we're looking at a 'drop', but not down to 0.  If it's
  67.         // a 0, that means that there is no change in the total.
  68.         diff_count = count - ctxt.count;
  69.         if (diff_count != 0) begin
  70.             // Something changed
  71.             if (diff_count > 0) begin
  72.                 // we're looking at an increase in the total
  73.                 if (!m_prop_mode && obj != m_top)
  74.                   m_raise(m_top, source_obj, description, diff_count);
  75.                 else if (obj != m_top)
  76.                   m_propagate(obj, source_obj, description, diff_count, 1, 0);
  77.             end
  78.             else begin
  79.                 // we're looking at a decrease in the total
  80.                 // The count field is always positive...
  81.                 diff_count = -diff_count;
  82.                 if (!m_prop_mode && obj != m_top)
  83.                   m_drop(m_top, source_obj, description, diff_count);
  84.                 else if (obj != m_top)
  85.                   m_propagate(obj, source_obj, description, diff_count, 0, 0);
  86.             end
  87.         end
  88.         // Cleanup
  89.         ctxt.clear();
  90.         m_context_pool.push_back(ctxt);
  91.     end
  92.         
  93.   endfunction
  94. endclass
复制代码
  首先查抄队列m_scheduled_list[$]中是否有当前obj的纪录信息,若有(阐明此时已经drop全部objection并在等待drain_time)则取出赋值给uvm_objection_context_object类型的ctxt句柄并删除队列中相关纪录,若没有接着查抄队列m_forked_list[$],若有则同样取出赋值给ctxt句柄并删除队列中相关纪录,若没有则查抄数组m_forked_contexts[]是否有相关信息,若有(阐明已经进入等待fork线程)则同样取出赋值给ctxt句柄并删除队列中相关纪录,同时杀掉m_drain_proc[obj]制止等待obj的drain_time进程。当ctxt得到纪录信息,比力当前raise_objection的count和ctxt中的count大小,若raise_objection的count较大,则直接将差值count作为参数调用m_propagate()来raise objection,若raise_objection的count较小,则直接将差值count作为参数调用m_propagate()来drop objection,若相等,则略过。
  转头来看uvm_phase::execute_phase()中在m_state为UVM_PHASE_EXECUTING时,若当前运行的是一个task phase时,一方面会用fork…join_none启动并行线程,在此中启动一个process来执行traverse(),另一方面也会再启动两个并行线程,一个用来检测当前phase的total_objection,另一个会检测timeout相关设置,以判断是否可以退出fork线程进入phase的下一个执行阶段。值得一提的是,这个timeout是界说在uvm_root中的一个time类型变量phase_timeout,目的是为了防止仿真不停block在某个phase中而设置的退出机制。这个default timeout是9200s,我们可以通过调用set_timeout()来重置这个phase_timeout时间:
  1. //uvm_root.svh
  2. function void uvm_root::set_timeout(time timeout, bit overridable=1);
  3.   static bit m_uvm_timeout_overridable = 1;
  4.   if (m_uvm_timeout_overridable == 0) begin
  5.     uvm_report_info("NOTIMOUTOVR",
  6.       $sformatf("The global timeout setting of %0d is not overridable to %0d due to a previous setting.",
  7.          phase_timeout, timeout), UVM_NONE);
  8.     return;
  9.   end
  10.   m_uvm_timeout_overridable = overridable;
  11.   phase_timeout = timeout;
  12. endfunction
复制代码

4. phase的高级应用

4.1. jump()

  UVM提供了在12个run-time phases间进行phase跳转运行的函数jump(),如我们在main_phase中可以调用jump()跳转到reset_phase: phase.jump(uvm_reset_phase.get());
  1. //uvm_phase.svh
  2. function void uvm_phase::end_prematurely() ;
  3.    m_premature_end = 1 ;
  4. endfunction
  5. function void uvm_phase::jump(uvm_phase phase);
  6.    set_jump_phase(phase) ;
  7.    end_prematurely() ;
  8. endfunction
复制代码
  jump()函数会先后调用set_jump_phase()和end_prematurely(),后者很简单就是把变量m_premature_end置为1,这个bit会控制exectute_phase()中当前task phase的运行进入jumping状态。来看set_jump_phase():
  1. //uvm_phase.svh
  2. function void uvm_phase::set_jump_phase(uvm_phase phase) ;
  3.   uvm_phase d;
  4.   if ((m_state <  UVM_PHASE_STARTED) ||
  5.       (m_state >  UVM_PHASE_ENDED) )
  6.   begin
  7.    `uvm_error("JMPPHIDL", { "Attempting to jump from phase "",
  8.       get_name(), "" which is not currently active (current state is ",
  9.       m_state.name(), "). The jump will not happen until the phase becomes ",
  10.       "active."})
  11.   end
  12.   // A jump can be either forward or backwards in the phase graph.
  13.   // If the specified phase (name) is found in the set of predecessors
  14.   // then we are jumping backwards.  If, on the other hand, the phase is in the set
  15.   // of successors then we are jumping forwards.  If neither, then we
  16.   // have an error.
  17.   //
  18.   // If the phase is non-existant and thus we don't know where to jump
  19.   // we have a situation where the only thing to do is to uvm_report_fatal
  20.   // and terminate_phase.  By calling this function the intent was to
  21.   // jump to some other phase. So, continuing in the current phase doesn't
  22.   // make any sense.  And we don't have a valid phase to jump to.  So we're done.
  23.   d = m_find_predecessor(phase,0);
  24.   if (d == null) begin
  25.     d = m_find_successor(phase,0);
  26.     if (d == null) begin
  27.       string msg;
  28.       $sformat(msg,{"phase %s is neither a predecessor or successor of ",
  29.                     "phase %s or is non-existant, so we cannot jump to it.  ",
  30.                     "Phase control flow is now undefined so the simulation ",
  31.                     "must terminate"}, phase.get_name(), get_name());
  32.       `uvm_fatal("PH_BADJUMP", msg);
  33.     end
  34.     else begin
  35.       m_jump_fwd = 1;
  36.       `uvm_info("PH_JUMPF",$sformatf("jumping forward to phase %s", phase.get_name()),
  37.                 UVM_DEBUG);
  38.     end
  39.   end
  40.   else begin
  41.     m_jump_bkwd = 1;
  42.     `uvm_info("PH_JUMPB",$sformatf("jumping backward to phase %s", phase.get_name()),
  43.               UVM_DEBUG);
  44.   end
  45.   
  46.   m_jump_phase = d;
  47. endfunction
复制代码
  若当前phase的m_state还没有走到UVM_PHASE_STARTED大概已经处于UVM_PHASE_ENDED状态,则无法进行phase的跳转,函数直接返回。接着调用m_find_predecessor()在当前phase的全部m_predecessors[]中寻找要跳转的目的phase,若找到则把m_jump_bkwd置为1并把该phase句柄赋值给m_jump_phase;若没有找到,则调用m_find_successor()在该phase的全部m_successors[]中寻找目的phase,若找到则把m_jump_fwd置为1并把该phase句柄赋值给m_jump_phase,若没有找到,则报错。
  回到uvm_phase::execute_phase()中,当前phase的m_state为UVM_PHASE_ENDED之后,即当前task phase已经执行完毕。若m_premature_end为1,则把m_state设置为UVM_PHASE_JUMPING,否则设置为UVM_PHASE_CLEANUP,然后做一些当前phase进程的清理工作。若m_jump_fwd为1,则phase会向前跳转,则把调用clear_successors()把当前phase到目的phase之间的phase从m_successors[]中删去。接下来,若m_jump_fwd大概m_jump_bkwd为1,则将m_jump_phase放入m_phase_hopper邮箱中等待运行,之后吧m_jump_phase赋值为null,把m_jump_fwd和m_jump_bkwd赋值为0。
4.2. 创建新的domain

4.2.1. set_domain()

  UVM有一个default domain叫做m_common_domain,代表8个function phase和run_phase的运行图,另外还包括一个与run_phase并行运行的m_uvm_domain,这个m_uvm_domain代表12个run-time phases的运行图。在m_uvm_domain之外,我们还可以创建新的domain,可以使新domain的12个run-time phases独立运行,而不必与m_uvm_domain中的phase同步。通常我们可以在某component中调用new()函数来创建一个新的domain:
  uvm_domain my_domain = new(“my_domain”);
  set_domain(my_domain);
  set_domain()是界说在uvm_component类中的函数,在此中会调用define_domain()将新domain的句柄传入。若参数hier为default 1,
  1. //uvm_component.svh
  2. function void uvm_component::set_domain(uvm_domain domain, int hier=1);
  3.   // build and store the custom domain
  4.   m_domain = domain;
  5.   define_domain(domain);
  6.   if (hier)
  7.     foreach (m_children[c])
  8.       m_children[c].set_domain(domain);
  9. endfunction
  10. function void uvm_component::define_domain(uvm_domain domain);
  11.   uvm_phase schedule;
  12.   //schedule = domain.find(uvm_domain::get_uvm_schedule());
  13.   schedule = domain.find_by_name("uvm_sched");
  14.   if (schedule == null) begin
  15.     uvm_domain common;
  16.     schedule = new("uvm_sched", UVM_PHASE_SCHEDULE);
  17.     uvm_domain::add_uvm_phases(schedule);
  18.     domain.add(schedule);
  19.     common = uvm_domain::get_common_domain();
  20.     if (common.find(domain,0) == null)
  21.       common.add(domain,.with_phase(uvm_run_phase::get()));
  22.   end
  23. endfunction
复制代码
  此中会首先调用find_by_name()在新domain中寻找是否有名为"uvm_sched"的phase,很明显我们的新domain还没有添加任何phase,所以为null。接下来会创建一个phase_type为UVM_PHASE_SCHEDULE的phase赋值给句柄schedule,并调用add_uvm_phases()将12个run-time phases加入schedule,再将schedule添加进新建的domain中。末了,将新的my_domain加入UVM的default domain m_common_domain中。其实这个过程跟m_uvm_domain的创建过程一样,加入新的my_domain之后,会形成如下图中的运行图(两个domain的run-time phases并不会同步运行):

4.2.2. sync()

  我们可以通过调用sync()来对差别doumain的runtime-phase进行同步,uvm_phase类中有一个队列m_sync[$],此中存储的是必要与当前phase同步的phases。
  1. //uvm_phase.svh
  2. class uvm_phase extends uvm_object;
  3.   local uvm_phase m_sync[$];
  4.   ...
  5. endclass
  6. function void uvm_phase::sync(uvm_domain target,
  7.                               uvm_phase phase=null,
  8.                               uvm_phase with_phase=null);
  9.   if (!this.is_domain()) begin
  10.     `uvm_fatal("PH_BADSYNC","sync() called from a non-domain phase schedule node");
  11.   end
  12.   else if (target == null) begin
  13.     `uvm_fatal("PH_BADSYNC","sync() called with a null target domain");
  14.   end
  15.   else if (!target.is_domain()) begin
  16.     `uvm_fatal("PH_BADSYNC","sync() called with a non-domain phase schedule node as target");
  17.   end
  18.   else if (phase == null && with_phase != null) begin
  19.     `uvm_fatal("PH_BADSYNC","sync() called with null phase and non-null with phase");
  20.   end
  21.   else if (phase == null) begin
  22.     // whole domain sync - traverse this domain schedule from begin to end node and sync each node
  23.     int visited[uvm_phase];
  24.     uvm_phase queue[$];
  25.     queue.push_back(this);
  26.     visited[this] = 1;
  27.     while (queue.size()) begin
  28.       uvm_phase node;
  29.       node = queue.pop_front();
  30.       if (node.m_imp != null) begin
  31.         sync(target, node.m_imp);
  32.       end
  33.       foreach (node.m_successors[succ]) begin
  34.         if (!visited.exists(succ)) begin
  35.           queue.push_back(succ);
  36.           visited[succ] = 1;
  37.         end
  38.       end
  39.     end
  40.   end else begin
  41.     // single phase sync
  42.     // this is a 2-way ('with') sync and we check first in case it is already there
  43.     uvm_phase from_node, to_node;
  44.     int found_to[$], found_from[$];
  45.     if(with_phase == null) with_phase = phase;
  46.     from_node = find(phase);
  47.     to_node = target.find(with_phase);
  48.     if(from_node == null || to_node == null) return;
  49.     found_to = from_node.m_sync.find_index(node) with (node == to_node);
  50.     found_from = to_node.m_sync.find_index(node) with (node == from_node);
  51.     if (found_to.size() == 0) from_node.m_sync.push_back(to_node);
  52.     if (found_from.size() == 0) to_node.m_sync.push_back(from_node);
  53.   end
  54. endfunction
复制代码

  • sync()函数有三个参数,第一个参数传入想要被同步的新domain句柄,第二个参数传入要被同步的phase,第三个参数传入的phase句柄表示要与新domain中的哪个phase进行同步。若我们必要同步m_common_domain中的pre_reset_phase和my_domain中的main_phase,则可以这么调用:
      uvm_domain common_domain = uvm_domain::get_common_domain();
      common_domain.sync(my_domain,uvm_pre_reset_phase::get(),uvm_main_phase::get());
    sync()函数会分别在这两个要被同步的phase的队列m_sync[$]中检索是否已经有了对方的信息,很明显这里没有,那么就把对方的phase句柄分别存入自己的m_sync[$]中。
  • 若调用时第三个参数with_phase为null,如
      common_domain.sync(my_domain,uvm_pre_reset_phase::get());
    则with_phase为pre_reset_phase,两个domain的pre_reset_phase的m_sync[$]中会分别放入一条对方的纪录。
  • 若调用时只提供第一个参数,如
      common_domain.sync(my_domain);
    则会将两个domain的全部12个run-time phase进行sync。
      回到uvm_phase::execute_phase()中,若当前phase的m_state运行到UVM_PHASE_SYNCING,若m_sync中纪录有必要sync的信息,则会不停等待必要sync的这些phase的m_state运行到UVM_PHASE_SYNCING才会继承往下一个状态运行,UVM通过这种方式实现phase间的同步。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

忿忿的泥巴坨

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表