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()开始仿真:
- //uvm_root.svh
- task uvm_root::run_test(string test_name="");
- ...
- process phase_runner_proc;
- ...
- if (test_name != "") begin
- uvm_coreservice_t cs = uvm_coreservice_t::get();
- uvm_factory factory=cs.get_factory();
-
- if(m_children.exists("uvm_test_top")) begin
- uvm_report_fatal("TTINST",
- "An uvm_test_top already exists via a previous call to run_test", UVM_NONE);
- #0; // forces shutdown because $finish is forked
- end
- $cast(uvm_test_top, factory.create_component_by_name(test_name,
- "", "uvm_test_top", null));
- ...
- fork begin
- // spawn the phase runner task
- phase_runner_proc = process::self();
- uvm_phase::m_run_phases();
- end
- join_none
- #0; // let the phase runner start
-
- wait (m_phase_all_done == 1);
-
- // clean up after ourselves
- phase_runner_proc.kill();
- ...
- if (finish_on_completion)
- $finish;
- 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结束本次仿真。
- //uvm_phase.svh
- class uvm_phase extends uvm_object;
- local static mailbox #(uvm_phase) m_phase_hopper = new();
- extern static task m_run_phases();
- ...
- endclass
- task uvm_phase::m_run_phases();
- uvm_root top;
- uvm_coreservice_t cs;
- cs = uvm_coreservice_t::get();
- top = cs.get_root();
- // initiate by starting first phase in common domain
- begin
- uvm_phase ph = uvm_domain::get_common_domain();
- void'(m_phase_hopper.try_put(ph));
- end
- forever begin
- uvm_phase phase;
- m_phase_hopper.get(phase);
- fork
- begin
- phase.execute_phase();
- end
- join_none
- #0; // let the process start running
- end
- 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中。
- static function uvm_domain get_common_domain();
- uvm_domain domain;
- uvm_phase schedule;
- if (m_common_domain != null)
- return m_common_domain;
- domain = new("common");
- domain.add(uvm_build_phase::get());
- domain.add(uvm_connect_phase::get());
- domain.add(uvm_end_of_elaboration_phase::get());
- domain.add(uvm_start_of_simulation_phase::get());
- domain.add(uvm_run_phase::get());
- domain.add(uvm_extract_phase::get());
- domain.add(uvm_check_phase::get());
- domain.add(uvm_report_phase::get());
- domain.add(uvm_final_phase::get());
- m_domains["common"] = domain;
- ...
- m_common_domain = domain;
- domain = get_uvm_domain();
- m_common_domain.add(domain,
- .with_phase(m_common_domain.find(uvm_run_phase::get())));
-
- return m_common_domain;
- 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的运行图,等等。
- //uvm_object_globals.svh
- typedef enum { UVM_PHASE_IMP,
- UVM_PHASE_NODE,
- UVM_PHASE_TERMINAL,
- UVM_PHASE_SCHEDULE,
- UVM_PHASE_DOMAIN,
- UVM_PHASE_GLOBAL
- } uvm_phase_type;
复制代码 别的UVM还界说了phase_state,用来表示某phase当前的差别运行状态,如UVM_PHASE_SYNCING表示差别domain的run-time task phase正在进行同步,UVM_PHASE_EXECUTING表示当前phase正在执行,等等。
- //uvm_object_globals.svh
- typedef enum { UVM_PHASE_UNINITIALIZED = 0,
- UVM_PHASE_DORMANT = 1,
- UVM_PHASE_SCHEDULED = 2,
- UVM_PHASE_SYNCING = 4,
- UVM_PHASE_STARTED = 8,
- UVM_PHASE_EXECUTING = 16,
- UVM_PHASE_READY_TO_END = 32,
- UVM_PHASE_ENDED = 64,
- UVM_PHASE_CLEANUP = 128,
- UVM_PHASE_DONE = 256,
- UVM_PHASE_JUMPING = 512
- } 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;
- //uvm_phase.svh
- class uvm_phase extends uvm_object;
- protected uvm_phase_type m_phase_type;
- protected uvm_phase m_parent; // our 'schedule' node [or points 'up' one level]
- uvm_phase m_imp; // phase imp to call when we execute this node
- local uvm_phase_state m_state;
- ...
- protected bit m_predecessors[uvm_phase];
- protected bit m_successors[uvm_phase];
- protected uvm_phase m_end_node;
- ...
- endclass
- function uvm_phase::new(string name="uvm_phase",
- uvm_phase_type phase_type=UVM_PHASE_SCHEDULE,
- uvm_phase parent=null);
- super.new(name);
- m_phase_type = phase_type;
- // The common domain is the only thing that initializes m_state. All
- // other states are initialized by being 'added' to a schedule.
- if ((name == "common") &&
- (phase_type == UVM_PHASE_DOMAIN))
- m_state = UVM_PHASE_DORMANT;
-
- m_parent = parent;
- ...
- if (parent == null && (phase_type == UVM_PHASE_SCHEDULE ||
- phase_type == UVM_PHASE_DOMAIN )) begin
- //m_parent = this;
- m_end_node = new({name,"_end"}, UVM_PHASE_TERMINAL, this);
- this.m_successors[m_end_node] = 1;
- m_end_node.m_predecessors[this] = 1;
- end
- 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()。
- //uvm_topdown_phase.svh
- virtual class uvm_topdown_phase extends uvm_phase;
- // Function: new
- //
- // Create a new instance of a top-down phase
- //
- function new(string name);
- super.new(name,UVM_PHASE_IMP);
- endfunction
-
- virtual function void traverse(uvm_component comp,
- uvm_phase phase,
- uvm_phase_state state);
- string name;
- uvm_domain phase_domain = phase.get_domain();
- uvm_domain comp_domain = comp.get_domain();
- if (m_phase_trace)
- `uvm_info("PH_TRACE",$sformatf("topdown-phase phase=%s state=%s comp=%s comp.domain=%s phase.domain=%s",
- phase.get_name(), state.name(), comp.get_full_name(),comp_domain.get_name(),phase_domain.get_name()),
- UVM_DEBUG)
- if (phase_domain == uvm_domain::get_common_domain() ||
- phase_domain == comp_domain) begin
- case (state)
- UVM_PHASE_STARTED: begin
- comp.m_current_phase = phase;
- comp.m_apply_verbosity_settings(phase);
- comp.phase_started(phase);
- end
- UVM_PHASE_EXECUTING: begin
- if (!(phase.get_name() == "build" && comp.m_build_done)) begin
- uvm_phase ph = this;
- comp.m_phasing_active++;
- if (comp.m_phase_imps.exists(this))
- ph = comp.m_phase_imps[this];
- ph.execute(comp, phase);
- comp.m_phasing_active--;
- end
- end
- UVM_PHASE_READY_TO_END: begin
- comp.phase_ready_to_end(phase);
- end
- UVM_PHASE_ENDED: begin
- comp.phase_ended(phase);
- comp.m_current_phase = null;
- end
- default:
- `uvm_fatal("PH_BADEXEC","topdown phase traverse internal error")
- endcase
- end
- if(comp.get_first_child(name))
- do
- traverse(comp.get_child(name), phase, state);
- while(comp.get_next_child(name));
- endfunction
-
- // Function: execute
- //
- // Executes the top-down phase ~phase~ for the component ~comp~.
- //
- virtual function void execute(uvm_component comp,
- uvm_phase phase);
- // reseed this process for random stability
- process proc = process::self();
- proc.srandom(uvm_create_random_seed(phase.get_type_name(), comp.get_full_name()));
- comp.m_current_phase = phase;
- exec_func(comp,phase);
- endfunction
- endclass
- //uvm_common_phases.svh
- class uvm_build_phase extends uvm_topdown_phase;
- virtual function void exec_func(uvm_component comp, uvm_phase phase);
- comp.build_phase(phase);
- endfunction
- local static uvm_build_phase m_inst;
- static const string type_name = "uvm_build_phase";
- // Function: get
- // Returns the singleton phase handle
- //
- static function uvm_build_phase get();
- if(m_inst == null)
- m_inst = new();
- return m_inst;
- endfunction
- protected function new(string name="build");
- super.new(name);
- endfunction
- virtual function string get_type_name();
- return type_name;
- endfunction
- 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。
- //uvm_task_phase.svh
- virtual class uvm_task_phase extends uvm_phase
- ...
- virtual function void execute(uvm_component comp,
- uvm_phase phase);
- fork
- begin
- process proc;
- // reseed this process for random stability
- proc = process::self();
- proc.srandom(uvm_create_random_seed(phase.get_type_name(), comp.get_full_name()));
- phase.m_num_procs_not_yet_returned++;
- exec_task(comp,phase);
- phase.m_num_procs_not_yet_returned--;
- end
- join_none
- endfunction
- 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[]。
- //uvm_domain.svh
- class uvm_domain extends uvm_phase;
- static local uvm_domain m_common_domain;
- static local uvm_domain m_domains[string];
- ...
- function new(string name);
- super.new(name,UVM_PHASE_DOMAIN);
- if (m_domains.exists(name))
- `uvm_error("UNIQDOMNAM", $sformatf("Domain created with non-unique name '%s'", name))
- m_domains[name] = this;
- endfunction
- ...
- endclass
复制代码 2.1.4. get_uvm_domain()
这个函数就是将12个run-time phases的运行顺序排好然后返回一个uvm_domain类型的运行图句柄m_uvm_domain。
- //uvm_domain.svh
- class uvm_domain extends uvm_phase;
- static local uvm_domain m_uvm_domain; // run-time
- static local uvm_phase m_uvm_schedule;
- ...
- static function void add_uvm_phases(uvm_phase schedule);
- schedule.add(uvm_pre_reset_phase::get());
- schedule.add(uvm_reset_phase::get());
- schedule.add(uvm_post_reset_phase::get());
- schedule.add(uvm_pre_configure_phase::get());
- schedule.add(uvm_configure_phase::get());
- schedule.add(uvm_post_configure_phase::get());
- schedule.add(uvm_pre_main_phase::get());
- schedule.add(uvm_main_phase::get());
- schedule.add(uvm_post_main_phase::get());
- schedule.add(uvm_pre_shutdown_phase::get());
- schedule.add(uvm_shutdown_phase::get());
- schedule.add(uvm_post_shutdown_phase::get());
- endfunction
- // Function: get_uvm_domain
- //
- // Get a handle to the singleton ~uvm~ domain
- //
- static function uvm_domain get_uvm_domain();
-
- if (m_uvm_domain == null) begin
- m_uvm_domain = new("uvm");
- m_uvm_schedule = new("uvm_sched", UVM_PHASE_SCHEDULE);
- add_uvm_phases(m_uvm_schedule);
- m_uvm_domain.add(m_uvm_schedule);
- end
- return m_uvm_domain;
- endfunction
- 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的部分:
- function void uvm_phase::add(uvm_phase phase,
- uvm_phase with_phase=null,
- uvm_phase after_phase=null,
- uvm_phase before_phase=null);
- uvm_phase new_node, begin_node, end_node, tmp_node;
- uvm_phase_state_change state_chg;
- if (phase == null)
- `uvm_fatal("PH/NULL", "add: phase argument is null")
- if (with_phase != null && with_phase.get_phase_type() == UVM_PHASE_IMP) begin
- string nm = with_phase.get_name();
- with_phase = find(with_phase);
- if (with_phase == null)
- `uvm_fatal("PH_BAD_ADD",
- {"cannot find with_phase '",nm,"' within node '",get_name(),"'"})
- end
- if (before_phase != null && before_phase.get_phase_type() == UVM_PHASE_IMP) begin
- string nm = before_phase.get_name();
- before_phase = find(before_phase);
- if (before_phase == null)
- `uvm_fatal("PH_BAD_ADD",
- {"cannot find before_phase '",nm,"' within node '",get_name(),"'"})
- end
- if (after_phase != null && after_phase.get_phase_type() == UVM_PHASE_IMP) begin
- string nm = after_phase.get_name();
- after_phase = find(after_phase);
- if (after_phase == null)
- `uvm_fatal("PH_BAD_ADD",
- {"cannot find after_phase '",nm,"' within node '",get_name(),"'"})
- end
- if (with_phase != null && (after_phase != null || before_phase != null))
- `uvm_fatal("PH_BAD_ADD",
- "cannot specify both 'with' and 'before/after' phase relationships")
- if (before_phase == this || after_phase == m_end_node || with_phase == m_end_node)
- `uvm_fatal("PH_BAD_ADD",
- "cannot add before begin node, after end node, or with end nodes")
- // If we are inserting a new "leaf node"
- if (phase.get_phase_type() == UVM_PHASE_IMP) begin
- uvm_task_phase tp;
- new_node = new(phase.get_name(),UVM_PHASE_NODE,this);
- new_node.m_imp = phase;
- begin_node = new_node;
- end_node = new_node;
- // The phase_done objection is only required
- // for task-based nodes
- if ($cast(tp, phase)) begin
- if (new_node.get_name() == "run") begin
- new_node.phase_done = uvm_test_done_objection::get();
- end
- else begin
- new_node.phase_done = uvm_objection::type_id::create({phase.get_name(), "_objection"});
- end
- end
- end
- // We are inserting an existing schedule
- else begin
- begin_node = phase;
- end_node = phase.m_end_node;
- phase.m_parent = this;
- end
- // If 'with_phase' is us, then insert node in parallel
- /*
- if (with_phase == this) begin
- after_phase = this;
- before_phase = m_end_node;
- end
- */
- // If no before/after/with specified, insert at end of this schedule
- if (with_phase == null && after_phase == null && before_phase == null) begin
- before_phase = m_end_node;
- end
- // INSERT IN PARALLEL WITH 'WITH' PHASE
- if (with_phase != null) begin
- begin_node.m_predecessors = with_phase.m_predecessors;
- end_node.m_successors = with_phase.m_successors;
- foreach (with_phase.m_predecessors[pred])
- pred.m_successors[begin_node] = 1;
- foreach (with_phase.m_successors[succ])
- succ.m_predecessors[end_node] = 1;
- end
-
-
- // INSERT BEFORE PHASE
- else if (before_phase != null && after_phase == null) begin
- begin_node.m_predecessors = before_phase.m_predecessors;
- end_node.m_successors[before_phase] = 1;
- foreach (before_phase.m_predecessors[pred]) begin
- pred.m_successors.delete(before_phase);
- pred.m_successors[begin_node] = 1;
- end
- before_phase.m_predecessors.delete();
- before_phase.m_predecessors[end_node] = 1;
- end
-
- // INSERT AFTER PHASE
- else if (before_phase == null && after_phase != null) begin
- end_node.m_successors = after_phase.m_successors;
- begin_node.m_predecessors[after_phase] = 1;
- foreach (after_phase.m_successors[succ]) begin
- succ.m_predecessors.delete(after_phase);
- succ.m_predecessors[end_node] = 1;
- end
- after_phase.m_successors.delete();
- after_phase.m_successors[begin_node] = 1;
- end
-
- // IN BETWEEN 'BEFORE' and 'AFTER' PHASES
- else if (before_phase != null && after_phase != null) begin
- if (!after_phase.is_before(before_phase)) begin
- `uvm_fatal("PH_ADD_PHASE",{"Phase '",before_phase.get_name(),
- "' is not before phase '",after_phase.get_name(),"'"})
- end
- // before and after? add 1 pred and 1 succ
- begin_node.m_predecessors[after_phase] = 1;
- end_node.m_successors[before_phase] = 1;
- after_phase.m_successors[begin_node] = 1;
- before_phase.m_predecessors[end_node] = 1;
- if (after_phase.m_successors.exists(before_phase)) begin
- after_phase.m_successors.delete(before_phase);
- before_phase.m_predecessors.delete(after_phase);
- end
- end // if (before_phase != null && after_phase != null)
- // Transition nodes to DORMANT state
- if (new_node == null)
- tmp_node = phase;
- else
- tmp_node = new_node;
- state_chg = uvm_phase_state_change::type_id::create(tmp_node.get_name());
- state_chg.m_phase = tmp_node;
- state_chg.m_jump_to = null;
- state_chg.m_prev_state = tmp_node.m_state;
- tmp_node.m_state = UVM_PHASE_DORMANT;
- `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(tmp_node, state_chg))
- 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代码如下:
- //uvm_phase.svh
- task uvm_phase::execute_phase();
- uvm_task_phase task_phase;
- uvm_root top;
- uvm_phase_state_change state_chg;
- uvm_coreservice_t cs;
- cs = uvm_coreservice_t::get();
- top = cs.get_root();
- // If we got here by jumping forward, we must wait for
- // all its predecessor nodes to be marked DONE.
- // (the next conditional speeds this up)
- // Also, this helps us fast-forward through terminal (end) nodes
- foreach (m_predecessors[pred])
- wait (pred.m_state == UVM_PHASE_DONE);
- // If DONE (by, say, a forward jump), return immed
- if (m_state == UVM_PHASE_DONE)
- return;
- ...
- // If we're a schedule or domain, then "fake" execution
- if (m_phase_type != UVM_PHASE_NODE) begin
- state_chg.m_prev_state = m_state;
- m_state = UVM_PHASE_STARTED;
- `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
- #0;
- state_chg.m_prev_state = m_state;
- m_state = UVM_PHASE_EXECUTING;
- `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
- #0;
- end
- else begin // PHASE NODE
- //---------
- // STARTED:
- //---------
- state_chg.m_prev_state = m_state;
- m_state = UVM_PHASE_STARTED;
- `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
- m_imp.traverse(top,this,UVM_PHASE_STARTED);
- m_ready_to_end_count = 0 ; // reset the ready_to_end count when phase starts
- #0; // LET ANY WAITERS WAKE UP
- //if (m_imp.get_phase_type() != UVM_PHASE_TASK) begin
- if (!$cast(task_phase,m_imp)) begin
- //-----------
- // EXECUTING: (function phases)
- //-----------
- state_chg.m_prev_state = m_state;
- m_state = UVM_PHASE_EXECUTING;
- `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
- #0; // LET ANY WAITERS WAKE UP
- m_imp.traverse(top,this,UVM_PHASE_EXECUTING);
- end
- else begin
- m_executing_phases[this] = 1;
- state_chg.m_prev_state = m_state;
- m_state = UVM_PHASE_EXECUTING;
- `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
- fork : master_phase_process
- begin
-
- m_phase_proc = process::self();
-
- //-----------
- // EXECUTING: (task phases)
- //-----------
- task_phase.traverse(top,this,UVM_PHASE_EXECUTING);
-
- wait(0); // stay alive for later kill
-
- end
- join_none
-
- uvm_wait_for_nba_region(); //Give sequences, etc. a chance to object
-
- // Now wait for one of three criterion for end-of-phase.
- fork
- begin // guard
-
- fork
- // JUMP
- begin
- wait (m_premature_end);
- `UVM_PH_TRACE("PH/TRC/EXE/JUMP","PHASE EXIT ON JUMP REQUEST",this,UVM_DEBUG)
- end
-
- // WAIT_FOR_ALL_DROPPED
- begin
- bit do_ready_to_end ; // bit used for ready_to_end iterations
- // OVM semantic: don't end until objection raised or stop request
- if (phase_done.get_objection_total(top) ||
- m_use_ovm_run_semantic && m_imp.get_name() == "run") begin
- if (!phase_done.m_top_all_dropped)
- phase_done.wait_for(UVM_ALL_DROPPED, top);
- `UVM_PH_TRACE("PH/TRC/EXE/ALLDROP","PHASE EXIT ALL_DROPPED",this,UVM_DEBUG)
- end
- else begin
- if (m_phase_trace) `UVM_PH_TRACE("PH/TRC/SKIP","No objections raised, skipping phase",this,UVM_LOW)
- end
-
- wait_for_self_and_siblings_to_drop() ;
- do_ready_to_end = 1;
-
- //--------------
- // READY_TO_END:
- //--------------
-
- while (do_ready_to_end) begin
- uvm_wait_for_nba_region(); // Let all siblings see no objections before traverse might raise another
- `UVM_PH_TRACE("PH_READY_TO_END","PHASE READY TO END",this,UVM_DEBUG)
- m_ready_to_end_count++;
- if (m_phase_trace)
- `UVM_PH_TRACE("PH_READY_TO_END_CB","CALLING READY_TO_END CB",this,UVM_HIGH)
- state_chg.m_prev_state = m_state;
- m_state = UVM_PHASE_READY_TO_END;
- `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
- if (m_imp != null)
- m_imp.traverse(top,this,UVM_PHASE_READY_TO_END);
-
- uvm_wait_for_nba_region(); // Give traverse targets a chance to object
- wait_for_self_and_siblings_to_drop();
- 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
- end
- end
-
- // TIMEOUT
- begin
- if (this.get_name() == "run") begin
- if (top.phase_timeout == 0)
- wait(top.phase_timeout != 0);
- if (m_phase_trace)
- `UVM_PH_TRACE("PH/TRC/TO_WAIT", $sformatf("STARTING PHASE TIMEOUT WATCHDOG (timeout == %t)", top.phase_timeout), this, UVM_HIGH)
- `uvm_delay(top.phase_timeout)
- if ($time == `UVM_DEFAULT_TIMEOUT) begin
- if (m_phase_trace)
- `UVM_PH_TRACE("PH/TRC/TIMEOUT", "PHASE TIMEOUT WATCHDOG EXPIRED", this, UVM_LOW)
- foreach (m_executing_phases[p]) begin
- if ((p.phase_done != null) && (p.phase_done.get_objection_total() > 0)) begin
- if (m_phase_trace)
- `UVM_PH_TRACE("PH/TRC/TIMEOUT/OBJCTN",
- $sformatf("Phase '%s' has outstanding objections:\n%s", p.get_full_name(), p.phase_done.convert2string()),
- this,
- UVM_LOW)
- end
- end
-
- `uvm_fatal("PH_TIMEOUT",
- $sformatf("Default timeout of %0t hit, indicating a probable testbench issue",
- `UVM_DEFAULT_TIMEOUT))
- end
- else begin
- if (m_phase_trace)
- `UVM_PH_TRACE("PH/TRC/TIMEOUT", "PHASE TIMEOUT WATCHDOG EXPIRED", this, UVM_LOW)
- foreach (m_executing_phases[p]) begin
- if ((p.phase_done != null) && (p.phase_done.get_objection_total() > 0)) begin
- if (m_phase_trace)
- `UVM_PH_TRACE("PH/TRC/TIMEOUT/OBJCTN",
- $sformatf("Phase '%s' has outstanding objections:\n%s", p.get_full_name(), p.phase_done.convert2string()),
- this,
- UVM_LOW)
- end
- end
-
- `uvm_fatal("PH_TIMEOUT",
- $sformatf("Explicit timeout of %0t hit, indicating a probable testbench issue",
- top.phase_timeout))
- end
- if (m_phase_trace)
- `UVM_PH_TRACE("PH/TRC/EXE/3","PHASE EXIT TIMEOUT",this,UVM_DEBUG)
- end // if (this.get_name() == "run")
- else begin
- wait (0); // never unblock for non-run phase
- end
- end // if (m_phase_trace)
-
- join_any
- disable fork;
-
- end
-
- join // guard
- end
- end
- m_executing_phases.delete(this);
- //---------
- // JUMPING:
- //---------
- // If jump_to() was called then we need to kill all the successor
- // phases which may still be running and then initiate the new
- // phase. The return is necessary so we don't start new successor
- // phases. If we are doing a forward jump then we want to set the
- // state of this phase's successors to UVM_PHASE_DONE. This
- // will let us pretend that all the phases between here and there
- // were executed and completed. Thus any dependencies will be
- // satisfied preventing deadlocks.
- // GSA TBD insert new jump support
- if (m_phase_type == UVM_PHASE_NODE) begin
- if(m_premature_end) begin
- if(m_jump_phase != null) begin
- state_chg.m_jump_to = m_jump_phase;
-
- `uvm_info("PH_JUMP",
- $sformatf("phase %s (schedule %s, domain %s) is jumping to phase %s",
- get_name(), get_schedule_name(), get_domain_name(), m_jump_phase.get_name()),
- UVM_MEDIUM);
- end
- else begin
- `uvm_info("PH_JUMP",
- $sformatf("phase %s (schedule %s, domain %s) is ending prematurely",
- get_name(), get_schedule_name(), get_domain_name()),
- UVM_MEDIUM);
- end
-
-
- #0; // LET ANY WAITERS ON READY_TO_END TO WAKE UP
- if (m_phase_trace)
- `UVM_PH_TRACE("PH_END","ENDING PHASE PREMATURELY",this,UVM_HIGH)
- end
- else begin
- // WAIT FOR PREDECESSORS: // WAIT FOR PREDECESSORS:
- // function phases only
- if (task_phase == null)
- m_wait_for_pred();
- end
-
- //-------
- // ENDED:
- //-------
- // execute 'phase_ended' callbacks
- if (m_phase_trace)
- `UVM_PH_TRACE("PH_END","ENDING PHASE",this,UVM_HIGH)
- state_chg.m_prev_state = m_state;
- m_state = UVM_PHASE_ENDED;
- `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
- if (m_imp != null)
- m_imp.traverse(top,this,UVM_PHASE_ENDED);
- #0; // LET ANY WAITERS WAKE UP
-
-
- //---------
- // CLEANUP:
- //---------
- // kill this phase's threads
- state_chg.m_prev_state = m_state;
- if(m_premature_end) m_state = UVM_PHASE_JUMPING;
- else m_state = UVM_PHASE_CLEANUP ;
- `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
- if (m_phase_proc != null) begin
- m_phase_proc.kill();
- m_phase_proc = null;
- end
- #0; // LET ANY WAITERS WAKE UP
- if (phase_done != null)
- phase_done.clear();
- end
- //------
- // DONE:
- //------
- m_premature_end = 0 ;
- if(m_jump_fwd || m_jump_bkwd) begin
- if(m_jump_fwd) begin
- clear_successors(UVM_PHASE_DONE,m_jump_phase);
- end
- m_jump_phase.clear_successors();
- uvmkit_gettimeofday(phase_finish_time);
- void'(uvmkit_timeval_subtract(phase_diff_time, phase_finish_time, phase_start_time));
- `uvm_info("TIMED_PHASE",
- $sformatf("'%s' phase took a total of %f seconds",
- this.get_full_name(),
- uvmkit_timeval_to_real(phase_diff_time)),
- UVM_LOW)
- end
- else begin
- if (m_phase_trace)
- `UVM_PH_TRACE("PH/TRC/DONE","Completed phase",this,UVM_LOW)
- state_chg.m_prev_state = m_state;
- m_state = UVM_PHASE_DONE;
- `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
- m_phase_proc = null;
- uvmkit_gettimeofday(phase_finish_time);
- void'(uvmkit_timeval_subtract(phase_diff_time, phase_finish_time, phase_start_time));
- `uvm_info("TIMED_PHASE",
- $sformatf("'%s' phase took a total of %f seconds",
- this.get_full_name(),
- uvmkit_timeval_to_real(phase_diff_time)),
- UVM_LOW)
- #0; // LET ANY WAITERS WAKE UP
- end
- #0; // LET ANY WAITERS WAKE UP
- if (phase_done != null)
- phase_done.clear();
- //-----------
- // SCHEDULED:
- //-----------
- if(m_jump_fwd || m_jump_bkwd) begin
- void'(m_phase_hopper.try_put(m_jump_phase));
- m_jump_phase = null;
- m_jump_fwd = 0;
- m_jump_bkwd = 0;
- uvmkit_gettimeofday(phase_finish_time);
- void'(uvmkit_timeval_subtract(phase_diff_time, phase_finish_time, phase_start_time));
- `uvm_info("TIMED_PHASE",
- $sformatf("'%s' phase took a total of %f seconds",
- this.get_full_name(),
- uvmkit_timeval_to_real(phase_diff_time)),
- UVM_LOW)
- end
- // If more successors, schedule them to run now
- else if (m_successors.size() == 0) begin
- top.m_phase_all_done=1;
- end
- else begin
- // execute all the successors
- foreach (m_successors[succ]) begin
- if(succ.m_state < UVM_PHASE_SCHEDULED) begin
- state_chg.m_prev_state = succ.m_state;
- state_chg.m_phase = succ;
- succ.m_state = UVM_PHASE_SCHEDULED;
- `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(succ, state_chg))
- #0; // LET ANY WAITERS WAKE UP
- void'(m_phase_hopper.try_put(succ));
- if (m_phase_trace)
- `UVM_PH_TRACE("PH/TRC/SCHEDULED",{"Scheduled from phase ",get_full_name()},succ,UVM_LOW)
- end
- end
- end
- 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。
- //uvm_objection.svh
- class uvm_objection_events;
- int waiters;
- event raised;
- event dropped;
- event all_dropped;
- endclass
- class uvm_objection extends uvm_report_object;
- `uvm_register_cb(uvm_objection, uvm_objection_callback)
- protected int m_source_count[uvm_object];
- protected int m_total_count [uvm_object];
- protected time m_drain_time [uvm_object];
- protected uvm_objection_events m_events [uvm_object];
- /*protected*/ bit m_top_all_dropped;
- protected uvm_root m_top;
-
- static uvm_objection m_objections[$];
- ...
- function new(string name="");
- super.new(name);
- ...
- m_objections.push_back(this);
- endfunction
- ...
- 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。
- //uvm_phase.svh
- class uvm_phase extends uvm_object;
- uvm_objection phase_done;
- ...
- endclass
- function void uvm_phase::add(uvm_phase phase,
- uvm_phase with_phase=null,
- uvm_phase after_phase=null,
- uvm_phase before_phase=null);
- if (phase.get_phase_type() == UVM_PHASE_IMP) begin
- uvm_task_phase tp;
- ...
- // The phase_done objection is only required
- // for task-based nodes
- if ($cast(tp, phase)) begin
- if (new_node.get_name() == "run") begin
- new_node.phase_done = uvm_test_done_objection::get();
- end
- else begin
- new_node.phase_done = uvm_objection::type_id::create({phase.get_name(), "_objection"});
- end
- end
- end
- ...
- endfunction
- function void uvm_phase::raise_objection (uvm_object obj,
- string description="",
- int count=1);
- if (phase_done != null)
- phase_done.raise_objection(obj,description,count);
- else
- m_report_null_objection(obj, description, count, "raise");
- 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()函数。
- //uvm_objection.svh
- class uvm_objection extends uvm_report_object;
- virtual function void raise_objection (uvm_object obj=null,
- string description="",
- int count=1);
- if(obj == null)
- obj = m_top;
- m_cleared = 0;
- m_top_all_dropped = 0;
- m_raise (obj, obj, description, count);
- endfunction
- function void m_raise (uvm_object obj,
- uvm_object source_obj,
- string description="",
- int count=1);
- int idx;
- uvm_objection_context_object ctxt;
- // Ignore raise if count is 0
- if (count == 0)
- return;
- if (m_total_count.exists(obj))
- m_total_count[obj] += count;
- else
- m_total_count[obj] = count;
- if (source_obj==obj) begin
- if (m_source_count.exists(obj))
- m_source_count[obj] += count;
- else
- m_source_count[obj] = count;
- end
-
- if (m_trace_mode)
- m_report(obj,source_obj,description,count,"raised");
- raised(obj, source_obj, description, count);
- ...
- if (ctxt == null) begin
- // If there were no drains, just propagate as usual
- if (!m_prop_mode && obj != m_top)
- m_raise(m_top,source_obj,description,count);
- else if (obj != m_top)
- m_propagate(obj, source_obj, description, count, 1, 0);
- end
- else begin
- // Otherwise we need to determine what exactly happened
- int diff_count;
- // Determine the diff count, if it's positive, then we're
- // looking at a 'raise' total, if it's negative, then
- // we're looking at a 'drop', but not down to 0. If it's
- // a 0, that means that there is no change in the total.
- diff_count = count - ctxt.count;
- if (diff_count != 0) begin
- // Something changed
- if (diff_count > 0) begin
- // we're looking at an increase in the total
- if (!m_prop_mode && obj != m_top)
- m_raise(m_top, source_obj, description, diff_count);
- else if (obj != m_top)
- m_propagate(obj, source_obj, description, diff_count, 1, 0);
- end
- else begin
- // we're looking at a decrease in the total
- // The count field is always positive...
- diff_count = -diff_count;
- if (!m_prop_mode && obj != m_top)
- m_drop(m_top, source_obj, description, diff_count);
- else if (obj != m_top)
- m_propagate(obj, source_obj, description, diff_count, 0, 0);
- end
- end
- // Cleanup
- ctxt.clear();
- m_context_pool.push_back(ctxt);
- end
-
- endfunction
- endclass
复制代码 在此中界说了一个uvm_objection_context_object类型的变量ctxt,在uvm_objection_context_object类中界说了uvm_object类型的变量obj和source_obj,字符串description以及数量count,其实就是纪录下我们每次raise_objection大概drop_objection的参数信息。
- //uvm_objection.svh
- class uvm_objection_context_object;
- uvm_object obj;
- uvm_object source_obj;
- string description;
- int count;
- uvm_objection objection;
- // Clears the values stored within the object,
- // preventing memory leaks from reused objects
- function void clear();
- obj = null;
- source_obj = null;
- description = "";
- count = 0;
- objection = null;
- endfunction : clear
- 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():
- //uvm_objection.svh
- class uvm_objection extends uvm_report_object;
- virtual function void raised (uvm_object obj,
- uvm_object source_obj,
- string description,
- int count);
- uvm_component comp;
- if ($cast(comp,obj))
- comp.raised(this, source_obj, description, count);
- `uvm_do_callbacks(uvm_objection,uvm_objection_callback,raised(this,obj,source_obj,description,count))
- if (m_events.exists(obj))
- ->m_events[obj].raised;
- endfunction
- endclass
复制代码 若传入的obj参数是component类型,会调用component的callback raised(),我们可以在某component中来重载这个callback。若m_events中有当前obj的索引内容,则触发此中的raised变乱。
回到m_raise()中,此时ctxt为null,且m_prop_mode为1(default 1),则调用m_propagate():
- //uvm_objection.svh
- class uvm_objection extends uvm_report_object;
- unction void m_propagate (uvm_object obj,
- uvm_object source_obj,
- string description,
- int count,
- bit raise,
- int in_top_thread);
- if (obj != null && obj != m_top) begin
- obj = m_get_parent(obj);
- if(raise)
- m_raise(obj, source_obj, description, count);
- else
- m_drop(obj, source_obj, description, count, in_top_thread);
- end
- endfunction
- function uvm_object m_get_parent(uvm_object obj);
- uvm_component comp;
- uvm_sequence_base seq;
- if ($cast(comp, obj)) begin
- obj = comp.get_parent();
- end
- else if ($cast(seq, obj)) begin
- obj = seq.get_sequencer();
- end
- else
- obj = m_top;
- if (obj == null)
- obj = m_top;
- return obj;
- endfunction
- 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()函数:
- //uvm_phase.svh
- function void uvm_phase::drop_objection (uvm_object obj,
- string description="",
- int count=1);
- if (phase_done != null)
- phase_done.drop_objection(obj,description,count);
- else
- m_report_null_objection(obj, description, count, "drop");
- endfunction
- //uvm_objection.svh
- class uvm_objection extends uvm_report_object;
- virtual function void drop_objection (uvm_object obj=null,
- string description="",
- int count=1);
- if(obj == null)
- obj = m_top;
- m_drop (obj, obj, description, count, 0);
- endfunction
- // Function- m_drop
- function void m_drop (uvm_object obj,
- uvm_object source_obj,
- string description="",
- int count=1,
- int in_top_thread=0);
- // Ignore drops if the count is 0
- if (count == 0)
- return;
- if (!m_total_count.exists(obj) || (count > m_total_count[obj])) begin
- if(m_cleared)
- return;
- uvm_report_fatal("OBJTN_ZERO", {"Object "", obj.get_full_name(),
- "" attempted to drop objection '",this.get_name(),"' count below zero"});
- return;
- end
- if (obj == source_obj) begin
- if (!m_source_count.exists(obj) || (count > m_source_count[obj])) begin
- if(m_cleared)
- return;
- uvm_report_fatal("OBJTN_ZERO", {"Object "", obj.get_full_name(),
- "" attempted to drop objection '",this.get_name(),"' count below zero"});
- return;
- end
- m_source_count[obj] -= count;
- end
- m_total_count[obj] -= count;
- if (m_trace_mode)
- m_report(obj,source_obj,description,count,"dropped");
-
- dropped(obj, source_obj, description, count);
-
- // if count != 0, no reason to fork
- if (m_total_count[obj] != 0) begin
- if (!m_prop_mode && obj != m_top)
- m_drop(m_top,source_obj,description, count, in_top_thread);
- else if (obj != m_top) begin
- this.m_propagate(obj, source_obj, description, count, 0, in_top_thread);
- end
- end
- else begin
- uvm_objection_context_object ctxt;
- if (m_context_pool.size())
- ctxt = m_context_pool.pop_front();
- else
- ctxt = new;
- ctxt.obj = obj;
- ctxt.source_obj = source_obj;
- ctxt.description = description;
- ctxt.count = count;
- ctxt.objection = this;
- // Need to be thread-safe, let the background
- // process handle it.
- // Why don't we look at in_top_thread here? Because
- // a re-raise will kill the drain at object that it's
- // currently occuring at, and we need the leaf-level kills
- // to not cause accidental kills at branch-levels in
- // the propagation.
- // Using the background process just allows us to
- // separate the links of the chain.
- m_scheduled_list.push_back(ctxt);
- end // else: !if(m_total_count[obj] != 0)
- endfunction
- 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()函数:
- //uvm_objection.svh
- class uvm_objection extends uvm_report_object;
- virtual function void dropped (uvm_object obj,
- uvm_object source_obj,
- string description,
- int count);
- uvm_component comp;
- if($cast(comp,obj))
- comp.dropped(this, source_obj, description, count);
- `uvm_do_callbacks(uvm_objection,uvm_objection_callback,dropped(this,obj,source_obj,description,count))
- if (m_events.exists(obj))
- ->m_events[obj].dropped;
- endfunction
- 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时间才会结束。
- //uvm_objection.svh
- class uvm_objection extends uvm_report_object;
- function void set_drain_time (uvm_object obj=null, time drain);
- if (obj==null)
- obj = m_top;
- m_drain_time[obj] = drain;
- endfunction
- 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[ ]。此中mcontextpool[]中存放的是用过的uvm_objection_context_object指针,从这里拿跟new()一个没有太大区别。
- //uvm_objection.svh
- class uvm_objection extends uvm_report_object;
- local static uvm_objection_context_object m_context_pool[$];
-
- `ifndef UVM_USE_PROCESS_CONTAINER
- local process m_drain_proc[uvm_object];
- `else
- local process_container_c m_drain_proc[uvm_object];
- `endif
- local static uvm_objection_context_object m_scheduled_list[$];
- local uvm_objection_context_object m_scheduled_contexts[uvm_object];
- local uvm_objection_context_object m_forked_list[$];
- local uvm_objection_context_object m_forked_contexts[uvm_object];
- ...
- 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()。
- //uvm_root.svh
- task uvm_root::run_test(string test_name="");
- uvm_objection::m_init_objections();
- endtask
- //uvm_objection.svh
- class uvm_objection extends uvm_report_object;
- static function void m_init_objections();
- fork
- uvm_objection::m_execute_scheduled_forks();
- join_none
- endfunction
- // background process; when non
- static task m_execute_scheduled_forks();
- while(1) begin
- wait(m_scheduled_list.size() != 0);
- if(m_scheduled_list.size() != 0) begin
- uvm_objection_context_object c;
- uvm_objection o;
- // Save off the context before the fork
- c = m_scheduled_list.pop_front();
- // A re-raise can use this to figure out props (if any)
- c.objection.m_scheduled_contexts[c.obj] = c;
- // The fork below pulls out from the forked list
- c.objection.m_forked_list.push_back(c);
- // The fork will guard the m_forked_drain call, but
- // a re-raise can kill m_forked_list contexts in the delta
- // before the fork executes.
- fork : guard
- automatic uvm_objection objection = c.objection;
- begin
- // Check to maike sure re-raise didn't empty the fifo
- if (objection.m_forked_list.size() > 0) begin
- uvm_objection_context_object ctxt;
- ctxt = objection.m_forked_list.pop_front();
- // Clear it out of scheduled
- objection.m_scheduled_contexts.delete(ctxt.obj);
- // Move it in to forked (so re-raise can figure out props)
- objection.m_forked_contexts[ctxt.obj] = ctxt;
- // Save off our process handle, so a re-raise can kill it...
- `ifndef UVM_USE_PROCESS_CONTAINER
- objection.m_drain_proc[ctxt.obj] = process::self();
- `else
- begin
- process_container_c c = new(process::self());
- objection.m_drain_proc[ctxt.obj]=c;
- end
- `endif
- // Execute the forked drain
- objection.m_forked_drain(ctxt.obj, ctxt.source_obj, ctxt.description, ctxt.count, 1);
- // Cleanup if we survived (no re-raises)
- objection.m_drain_proc.delete(ctxt.obj);
- objection.m_forked_contexts.delete(ctxt.obj);
- // Clear out the context object (prevent memory leaks)
- ctxt.clear();
- // Save the context in the pool for later reuse
- m_context_pool.push_back(ctxt);
- end
- end
- join_none : guard
- end
- end
- endtask
- 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]。
- //uvm_objection.svh
- class uvm_objection extends uvm_report_object;
- task m_forked_drain (uvm_object obj,
- uvm_object source_obj,
- string description="",
- int count=1,
- int in_top_thread=0);
- int diff_count;
- if (m_drain_time.exists(obj))
- `uvm_delay(m_drain_time[obj])
-
- if (m_trace_mode)
- m_report(obj,source_obj,description,count,"all_dropped");
-
- all_dropped(obj,source_obj,description, count);
-
- // wait for all_dropped cbs to complete
- wait fork;
- /* NOT NEEDED - Any raise would have killed us!
- if(!m_total_count.exists(obj))
- diff_count = -count;
- else
- diff_count = m_total_count[obj] - count;
- */
- // we are ready to delete the 0-count entries for the current
- // object before propagating up the hierarchy.
- if (m_source_count.exists(obj) && m_source_count[obj] == 0)
- m_source_count.delete(obj);
-
- if (m_total_count.exists(obj) && m_total_count[obj] == 0)
- m_total_count.delete(obj);
- if (!m_prop_mode && obj != m_top)
- m_drop(m_top,source_obj,description, count, 1);
- else if (obj != m_top)
- m_propagate(obj, source_obj, description, count, 0, 1);
- endtask
- virtual task all_dropped (uvm_object obj,
- uvm_object source_obj,
- string description,
- int count);
- uvm_component comp;
- if($cast(comp,obj))
- comp.all_dropped(this, source_obj, description, count);
- `uvm_do_callbacks(uvm_objection,uvm_objection_callback,all_dropped(this,obj,source_obj,description,count))
- if (m_events.exists(obj))
- ->m_events[obj].all_dropped;
- if (obj == m_top)
- m_top_all_dropped = 1;
- endtask
- 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()中:
- //uvm_objection.svh
- class uvm_objection extends uvm_report_object;
- function void m_raise (uvm_object obj,
- uvm_object source_obj,
- string description="",
- int count=1);
- int idx;
- uvm_objection_context_object ctxt;
- // Handle any outstanding drains...
- // First go through the scheduled list
- idx = 0;
- while (idx < m_scheduled_list.size()) begin
- if ((m_scheduled_list[idx].obj == obj) &&
- (m_scheduled_list[idx].objection == this)) begin
- // Caught it before the drain was forked
- ctxt = m_scheduled_list[idx];
- m_scheduled_list.delete(idx);
- break;
- end
- idx++;
- end
- // If it's not there, go through the forked list
- if (ctxt == null) begin
- idx = 0;
- while (idx < m_forked_list.size()) begin
- if (m_forked_list[idx].obj == obj) begin
- // Caught it after the drain was forked,
- // but before the fork started
- ctxt = m_forked_list[idx];
- m_forked_list.delete(idx);
- m_scheduled_contexts.delete(ctxt.obj);
- break;
- end
- idx++;
- end
- end
- // If it's not there, go through the forked contexts
- if (ctxt == null) begin
- if (m_forked_contexts.exists(obj)) begin
- // Caught it with the forked drain running
- ctxt = m_forked_contexts[obj];
- m_forked_contexts.delete(obj);
- // Kill the drain
- `ifndef UVM_USE_PROCESS_CONTAINER
- m_drain_proc[obj].kill();
- m_drain_proc.delete(obj);
- `else
- m_drain_proc[obj].p.kill();
- m_drain_proc.delete(obj);
- `endif
-
- end
- end
- if (ctxt == null) begin
- // If there were no drains, just propagate as usual
- if (!m_prop_mode && obj != m_top)
- m_raise(m_top,source_obj,description,count);
- else if (obj != m_top)
- m_propagate(obj, source_obj, description, count, 1, 0);
- end
- else begin
- // Otherwise we need to determine what exactly happened
- int diff_count;
- // Determine the diff count, if it's positive, then we're
- // looking at a 'raise' total, if it's negative, then
- // we're looking at a 'drop', but not down to 0. If it's
- // a 0, that means that there is no change in the total.
- diff_count = count - ctxt.count;
- if (diff_count != 0) begin
- // Something changed
- if (diff_count > 0) begin
- // we're looking at an increase in the total
- if (!m_prop_mode && obj != m_top)
- m_raise(m_top, source_obj, description, diff_count);
- else if (obj != m_top)
- m_propagate(obj, source_obj, description, diff_count, 1, 0);
- end
- else begin
- // we're looking at a decrease in the total
- // The count field is always positive...
- diff_count = -diff_count;
- if (!m_prop_mode && obj != m_top)
- m_drop(m_top, source_obj, description, diff_count);
- else if (obj != m_top)
- m_propagate(obj, source_obj, description, diff_count, 0, 0);
- end
- end
- // Cleanup
- ctxt.clear();
- m_context_pool.push_back(ctxt);
- end
-
- endfunction
- 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时间:
- //uvm_root.svh
- function void uvm_root::set_timeout(time timeout, bit overridable=1);
- static bit m_uvm_timeout_overridable = 1;
- if (m_uvm_timeout_overridable == 0) begin
- uvm_report_info("NOTIMOUTOVR",
- $sformatf("The global timeout setting of %0d is not overridable to %0d due to a previous setting.",
- phase_timeout, timeout), UVM_NONE);
- return;
- end
- m_uvm_timeout_overridable = overridable;
- phase_timeout = timeout;
- endfunction
复制代码
4. phase的高级应用
4.1. jump()
UVM提供了在12个run-time phases间进行phase跳转运行的函数jump(),如我们在main_phase中可以调用jump()跳转到reset_phase: phase.jump(uvm_reset_phase.get());
- //uvm_phase.svh
- function void uvm_phase::end_prematurely() ;
- m_premature_end = 1 ;
- endfunction
- function void uvm_phase::jump(uvm_phase phase);
- set_jump_phase(phase) ;
- end_prematurely() ;
- endfunction
复制代码 jump()函数会先后调用set_jump_phase()和end_prematurely(),后者很简单就是把变量m_premature_end置为1,这个bit会控制exectute_phase()中当前task phase的运行进入jumping状态。来看set_jump_phase():
- //uvm_phase.svh
- function void uvm_phase::set_jump_phase(uvm_phase phase) ;
- uvm_phase d;
- if ((m_state < UVM_PHASE_STARTED) ||
- (m_state > UVM_PHASE_ENDED) )
- begin
- `uvm_error("JMPPHIDL", { "Attempting to jump from phase "",
- get_name(), "" which is not currently active (current state is ",
- m_state.name(), "). The jump will not happen until the phase becomes ",
- "active."})
- end
- // A jump can be either forward or backwards in the phase graph.
- // If the specified phase (name) is found in the set of predecessors
- // then we are jumping backwards. If, on the other hand, the phase is in the set
- // of successors then we are jumping forwards. If neither, then we
- // have an error.
- //
- // If the phase is non-existant and thus we don't know where to jump
- // we have a situation where the only thing to do is to uvm_report_fatal
- // and terminate_phase. By calling this function the intent was to
- // jump to some other phase. So, continuing in the current phase doesn't
- // make any sense. And we don't have a valid phase to jump to. So we're done.
- d = m_find_predecessor(phase,0);
- if (d == null) begin
- d = m_find_successor(phase,0);
- if (d == null) begin
- string msg;
- $sformat(msg,{"phase %s is neither a predecessor or successor of ",
- "phase %s or is non-existant, so we cannot jump to it. ",
- "Phase control flow is now undefined so the simulation ",
- "must terminate"}, phase.get_name(), get_name());
- `uvm_fatal("PH_BADJUMP", msg);
- end
- else begin
- m_jump_fwd = 1;
- `uvm_info("PH_JUMPF",$sformatf("jumping forward to phase %s", phase.get_name()),
- UVM_DEBUG);
- end
- end
- else begin
- m_jump_bkwd = 1;
- `uvm_info("PH_JUMPB",$sformatf("jumping backward to phase %s", phase.get_name()),
- UVM_DEBUG);
- end
-
- m_jump_phase = d;
- 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,
- //uvm_component.svh
- function void uvm_component::set_domain(uvm_domain domain, int hier=1);
- // build and store the custom domain
- m_domain = domain;
- define_domain(domain);
- if (hier)
- foreach (m_children[c])
- m_children[c].set_domain(domain);
- endfunction
- function void uvm_component::define_domain(uvm_domain domain);
- uvm_phase schedule;
- //schedule = domain.find(uvm_domain::get_uvm_schedule());
- schedule = domain.find_by_name("uvm_sched");
- if (schedule == null) begin
- uvm_domain common;
- schedule = new("uvm_sched", UVM_PHASE_SCHEDULE);
- uvm_domain::add_uvm_phases(schedule);
- domain.add(schedule);
- common = uvm_domain::get_common_domain();
- if (common.find(domain,0) == null)
- common.add(domain,.with_phase(uvm_run_phase::get()));
- end
- 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。
- //uvm_phase.svh
- class uvm_phase extends uvm_object;
- local uvm_phase m_sync[$];
- ...
- endclass
- function void uvm_phase::sync(uvm_domain target,
- uvm_phase phase=null,
- uvm_phase with_phase=null);
- if (!this.is_domain()) begin
- `uvm_fatal("PH_BADSYNC","sync() called from a non-domain phase schedule node");
- end
- else if (target == null) begin
- `uvm_fatal("PH_BADSYNC","sync() called with a null target domain");
- end
- else if (!target.is_domain()) begin
- `uvm_fatal("PH_BADSYNC","sync() called with a non-domain phase schedule node as target");
- end
- else if (phase == null && with_phase != null) begin
- `uvm_fatal("PH_BADSYNC","sync() called with null phase and non-null with phase");
- end
- else if (phase == null) begin
- // whole domain sync - traverse this domain schedule from begin to end node and sync each node
- int visited[uvm_phase];
- uvm_phase queue[$];
- queue.push_back(this);
- visited[this] = 1;
- while (queue.size()) begin
- uvm_phase node;
- node = queue.pop_front();
- if (node.m_imp != null) begin
- sync(target, node.m_imp);
- end
- foreach (node.m_successors[succ]) begin
- if (!visited.exists(succ)) begin
- queue.push_back(succ);
- visited[succ] = 1;
- end
- end
- end
- end else begin
- // single phase sync
- // this is a 2-way ('with') sync and we check first in case it is already there
- uvm_phase from_node, to_node;
- int found_to[$], found_from[$];
- if(with_phase == null) with_phase = phase;
- from_node = find(phase);
- to_node = target.find(with_phase);
- if(from_node == null || to_node == null) return;
- found_to = from_node.m_sync.find_index(node) with (node == to_node);
- found_from = to_node.m_sync.find_index(node) with (node == from_node);
- if (found_to.size() == 0) from_node.m_sync.push_back(to_node);
- if (found_from.size() == 0) to_node.m_sync.push_back(from_node);
- end
- 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企服之家,中国第一个企服评测及商务社交产业平台。 |