| // |
| //---------------------------------------------------------------------- |
| // Copyright 2007-2011 Mentor Graphics Corporation |
| // Copyright 2007-2011 Cadence Design Systems, Inc. |
| // Copyright 2010-2011 Synopsys, Inc. |
| // All Rights Reserved Worldwide |
| // |
| // Licensed under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in |
| // compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in |
| // writing, software distributed under the License is |
| // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
| // CONDITIONS OF ANY KIND, either express or implied. See |
| // the License for the specific language governing |
| // permissions and limitations under the License. |
| //---------------------------------------------------------------------- |
| |
| `ifndef UVM_OBJECTION_SVH |
| `define UVM_OBJECTION_SVH |
| |
| typedef class uvm_objection_context_object; |
| typedef class uvm_objection; |
| typedef class uvm_sequence_base; |
| typedef class uvm_objection_callback; |
| typedef uvm_callbacks #(uvm_objection,uvm_objection_callback) uvm_objection_cbs_t; |
| typedef class uvm_cmdline_processor; |
| typedef class uvm_callbacks_objection; |
| |
| class uvm_objection_events; |
| int waiters; |
| event raised; |
| event dropped; |
| event all_dropped; |
| endclass |
| |
| //------------------------------------------------------------------------------ |
| // Title: Objection Mechanism |
| //------------------------------------------------------------------------------ |
| // The following classes define the objection mechanism and end-of-test |
| // functionality, which is based on <uvm_objection>. |
| //------------------------------------------------------------------------------ |
| |
| //------------------------------------------------------------------------------ |
| // |
| // Class: uvm_objection |
| // |
| //------------------------------------------------------------------------------ |
| // Objections provide a facility for coordinating status information between |
| // two or more participating components, objects, and even module-based IP. |
| // |
| // Tracing of objection activity can be turned on to follow the activity of |
| // the objection mechanism. It may be turned on for a specific objection |
| // instance with <uvm_objection::trace_mode>, or it can be set for all |
| // objections from the command line using the option +UVM_OBJECTION_TRACE. |
| //------------------------------------------------------------------------------ |
| |
| class uvm_objection extends uvm_report_object; |
| |
| protected bit m_trace_mode; |
| 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 = uvm_root::get(); |
| |
| static uvm_objection m_objections[$]; |
| |
| //// Drain Logic |
| |
| // The context pool holds used context objects, so that |
| // they're not constantly being recreated. The maximum |
| // number of contexts in the pool is equal to the maximum |
| // number of simultaneous drains you could have occuring, |
| // both pre and post forks. |
| // |
| // There's the potential for a programmability within the |
| // library to dictate the largest this pool should be allowed |
| // to grow, but that seems like overkill for the time being. |
| local static uvm_objection_context_object m_context_pool[$]; |
| |
| // These are the active drain processes, which have been |
| // forked off by the background process. A raise can |
| // use this array to kill a drain. |
| `ifndef UVM_USE_PROCESS_CONTAINER |
| local process m_drain_proc[uvm_object]; |
| `else |
| local process_container_c m_drain_proc[uvm_object]; |
| `endif |
| |
| // These are the contexts which have been scheduled for |
| // retrieval by the background process, but which the |
| // background process hasn't seen yet. |
| local static uvm_objection_context_object m_scheduled_list[$]; |
| |
| // Once a context is seen by the background process, it is |
| // removed from the scheduled list, and placed in the forked |
| // list. At the same time, it is placed in the scheduled |
| // contexts array. A re-raise can use the scheduled contexts |
| // array to detect (and cancel) the drain. |
| local uvm_objection_context_object m_scheduled_contexts[uvm_object]; |
| local uvm_objection_context_object m_forked_list[$]; |
| |
| // Once the forked drain has actually started (this occurs |
| // ~1 delta AFTER the background process schedules it), the |
| // context is removed from the above array and list, and placed |
| // in the forked_contexts list. |
| local uvm_objection_context_object m_forked_contexts[uvm_object]; |
| |
| /*protected*/ bit m_hier_mode = 1; |
| |
| uvm_root top = uvm_root::get(); |
| |
| |
| protected bit m_cleared; /* for checking obj count<0 */ |
| |
| |
| // Function: clear |
| // |
| // Immediately clears the objection state. All counts are cleared and the |
| // any processes waiting on a call to wait_for(UVM_ALL_DROPPED, uvm_top) |
| // are released. |
| // |
| // The caller, if a uvm_object-based object, should pass its 'this' handle |
| // to the ~obj~ argument to document who cleared the objection. |
| // Any drain_times set by the user are not effected. |
| // |
| virtual function void clear(uvm_object obj=null); |
| string name; |
| uvm_objection_context_object ctxt; |
| int idx; |
| |
| if (obj==null) |
| obj=m_top; |
| name = obj.get_full_name(); |
| if (name == "") |
| name = "uvm_top"; |
| else |
| name = obj.get_full_name(); |
| if (!m_top_all_dropped && get_objection_total(m_top)) |
| uvm_report_warning("OBJTN_CLEAR",{"Object '",name, |
| "' cleared objection counts for ",get_name()}); |
| //Should there be a warning if there are outstanding objections? |
| m_source_count.delete(); |
| m_total_count.delete(); |
| |
| // Remove any scheduled drains from the static queue |
| idx = 0; |
| while (idx < m_scheduled_list.size()) begin |
| if (m_scheduled_list[idx].objection == this) begin |
| m_scheduled_list[idx].clear(); |
| m_context_pool.push_back(m_scheduled_list[idx]); |
| m_scheduled_list.delete(idx); |
| end |
| else begin |
| idx++; |
| end |
| end |
| |
| // Scheduled contexts and m_forked_lists have duplicate |
| // entries... clear out one, free the other. |
| m_scheduled_contexts.delete(); |
| while (m_forked_list.size()) begin |
| m_forked_list[0].clear(); |
| m_context_pool.push_back(m_forked_list[0]); |
| void'(m_forked_list.pop_front()); |
| end |
| |
| // running drains have a context and a process |
| foreach (m_forked_contexts[o]) begin |
| `ifndef UVM_USE_PROCESS_CONTAINER |
| m_drain_proc[o].kill(); |
| m_drain_proc.delete(o); |
| `else |
| m_drain_proc[o].p.kill(); |
| m_drain_proc.delete(o); |
| `endif |
| |
| m_forked_contexts[o].clear(); |
| m_context_pool.push_back(m_forked_contexts[o]); |
| m_forked_contexts.delete(o); |
| end |
| |
| m_top_all_dropped = 0; |
| m_cleared = 1; |
| if (m_events.exists(m_top)) |
| ->m_events[m_top].all_dropped; |
| |
| endfunction |
| |
| |
| // Function: new |
| // |
| // Creates a new objection instance. Accesses the command line |
| // argument +UVM_OBJECTION_TRACE to turn tracing on for |
| // all objection objects. |
| |
| function new(string name=""); |
| uvm_cmdline_processor clp; |
| string trace_args[$]; |
| super.new(name); |
| set_report_verbosity_level(m_top.get_report_verbosity_level()); |
| |
| // Get the command line trace mode setting |
| clp = uvm_cmdline_processor::get_inst(); |
| if(clp.get_arg_matches("+UVM_OBJECTION_TRACE", trace_args)) begin |
| m_trace_mode=1; |
| end |
| m_objections.push_back(this); |
| endfunction |
| |
| |
| // Function: trace_mode |
| // |
| // Set or get the trace mode for the objection object. If no |
| // argument is specified (or an argument other than 0 or 1) |
| // the current trace mode is unaffected. A trace_mode of |
| // 0 turns tracing off. A trace mode of 1 turns tracing on. |
| // The return value is the mode prior to being reset. |
| |
| function bit trace_mode (int mode=-1); |
| trace_mode = m_trace_mode; |
| if(mode == 0) m_trace_mode = 0; |
| else if(mode == 1) m_trace_mode = 1; |
| endfunction |
| |
| // Function- m_report |
| // |
| // Internal method for reporting count updates |
| |
| function void m_report(uvm_object obj, uvm_object source_obj, string description, int count, string action); |
| string desc; |
| int _count = m_source_count.exists(obj) ? m_source_count[obj] : 0; |
| int _total = m_total_count.exists(obj) ? m_total_count[obj] : 0; |
| if (!uvm_report_enabled(UVM_NONE,UVM_INFO,"OBJTN_TRC") || !m_trace_mode) return; |
| |
| //desc = description == "" ? "" : {" ", description, "" }; |
| if (source_obj == obj) |
| |
| uvm_report_info("OBJTN_TRC", |
| $sformatf("Object %0s %0s %0d objection(s)%s: count=%0d total=%0d", |
| obj.get_full_name()==""?"uvm_top":obj.get_full_name(), action, |
| count, description != ""? {" (",description,")"}:"", _count, _total), UVM_NONE); |
| else begin |
| int cpath = 0, last_dot=0; |
| string sname = source_obj.get_full_name(), nm = obj.get_full_name(); |
| int max = sname.len() > nm.len() ? nm.len() : sname.len(); |
| |
| // For readability, only print the part of the source obj hierarchy underneath |
| // the current object. |
| while((sname[cpath] == nm[cpath]) && (cpath < max)) begin |
| if(sname[cpath] == ".") last_dot = cpath; |
| cpath++; |
| end |
| |
| if(last_dot) sname = sname.substr(last_dot+1, sname.len()); |
| uvm_report_info("OBJTN_TRC", |
| $sformatf("Object %0s %0s %0d objection(s) %0s its total (%s from source object %s%s): count=%0d total=%0d", |
| obj.get_full_name()==""?"uvm_top":obj.get_full_name(), action=="raised"?"added":"subtracted", |
| count, action=="raised"?"to":"from", action, sname, |
| description != ""?{", ",description}:"", _count, _total), UVM_NONE); |
| end |
| endfunction |
| |
| |
| // Function- m_get_parent |
| // |
| // Internal method for getting the parent of the given ~object~. |
| // The ultimate parent is uvm_top, UVM's implicit top-level component. |
| |
| 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 |
| |
| |
| // Function- m_propagate |
| // |
| // Propagate the objection to the objects parent. If the object is a |
| // component, the parent is just the hierarchical parent. If the object is |
| // a sequence, the parent is the parent sequence if one exists, or |
| // it is the attached sequencer if there is no parent sequence. |
| // |
| // obj : the uvm_object on which the objection is being raised or lowered |
| // source_obj : the root object on which the end user raised/lowered the |
| // objection (as opposed to an anscestor of the end user object)a |
| // count : the number of objections associated with the action. |
| // raise : indicator of whether the objection is being raised or lowered. A |
| // 1 indicates the objection is being raised. |
| |
| function 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 |
| |
| |
| // Group: Objection Control |
| |
| // Function: m_set_hier_mode |
| // |
| // Hierarchical mode only needs to be set for intermediate components, not |
| // for uvm_root or a leaf component. |
| |
| function void m_set_hier_mode (uvm_object obj); |
| uvm_component c; |
| if((m_hier_mode == 1) || (obj == m_top)) begin |
| // Don't set if already set or the object is uvm_top. |
| return; |
| end |
| if($cast(c,obj)) begin |
| // Don't set if object is a leaf. |
| if(c.get_num_children() == 0) begin |
| return; |
| end |
| end |
| else begin |
| // Don't set if object is a non-component. |
| return; |
| end |
| |
| // restore counts on non-source nodes |
| m_total_count.delete(); |
| foreach (m_source_count[obj]) begin |
| uvm_object theobj = obj; |
| int count = m_source_count[obj]; |
| do begin |
| if (m_total_count.exists(theobj)) |
| m_total_count[theobj] += count; |
| else |
| m_total_count[theobj] = count; |
| theobj = m_get_parent(theobj); |
| end |
| while (theobj != null); |
| end |
| |
| m_hier_mode = 1; |
| endfunction |
| |
| |
| // Function: raise_objection |
| // |
| // Raises the number of objections for the source ~object~ by ~count~, which |
| // defaults to 1. The ~object~ is usually the ~this~ handle of the caller. |
| // If ~object~ is not specified or null, the implicit top-level component, |
| // <uvm_root>, is chosen. |
| // |
| // Rasing an objection causes the following. |
| // |
| // - The source and total objection counts for ~object~ are increased by |
| // ~count~. ~description~ is a string that marks a specific objection |
| // and is used in tracing/debug. |
| // |
| // - The objection's <raised> virtual method is called, which calls the |
| // <uvm_component::raised> method for all of the components up the |
| // hierarchy. |
| // |
| |
| 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- m_raise |
| |
| function void m_raise (uvm_object obj, |
| uvm_object source_obj, |
| string description="", |
| int count=1); |
| int idx; |
| uvm_objection_context_object ctxt; |
| |
| 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); |
| |
| // 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_hier_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_hier_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_hier_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 |
| |
| |
| // Function: drop_objection |
| // |
| // Drops the number of objections for the source ~object~ by ~count~, which |
| // defaults to 1. The ~object~ is usually the ~this~ handle of the caller. |
| // If ~object~ is not specified or null, the implicit top-level component, |
| // <uvm_root>, is chosen. |
| // |
| // Dropping an objection causes the following. |
| // |
| // - The source and total objection counts for ~object~ are decreased by |
| // ~count~. It is an error to drop the objection count for ~object~ below |
| // zero. |
| // |
| // - The objection's <dropped> virtual method is called, which calls the |
| // <uvm_component::dropped> method for all of the components up the |
| // hierarchy. |
| // |
| // - If the total objection count has not reached zero for ~object~, then |
| // the drop is propagated up the object hierarchy as with |
| // <raise_objection>. Then, each object in the hierarchy will have updated |
| // their ~source~ counts--objections that they originated--and ~total~ |
| // counts--the total number of objections by them and all their |
| // descendants. |
| // |
| // If the total objection count reaches zero, propagation up the hierarchy |
| // is deferred until a configurable drain-time has passed and the |
| // <uvm_component::all_dropped> callback for the current hierarchy level |
| // has returned. The following process occurs for each instance up |
| // the hierarchy from the source caller: |
| // |
| // A process is forked in a non-blocking fashion, allowing the ~drop~ |
| // call to return. The forked process then does the following: |
| // |
| // - If a drain time was set for the given ~object~, the process waits for |
| // that amount of time. |
| // |
| // - The objection's <all_dropped> virtual method is called, which calls the |
| // <uvm_component::all_dropped> method (if ~object~ is a component). |
| // |
| // - The process then waits for the ~all_dropped~ callback to complete. |
| // |
| // - After the drain time has elapsed and all_dropped callback has |
| // completed, propagation of the dropped objection to the parent proceeds |
| // as described in <raise_objection>, except as described below. |
| // |
| // If a new objection for this ~object~ or any of its descendents is raised |
| // during the drain time or during execution of the all_dropped callback at |
| // any point, the hierarchical chain described above is terminated and the |
| // dropped callback does not go up the hierarchy. The raised objection will |
| // propagate up the hierarchy, but the number of raised propagated up is |
| // reduced by the number of drops that were pending waiting for the |
| // all_dropped/drain time completion. Thus, if exactly one objection |
| // caused the count to go to zero, and during the drain exactly one new |
| // objection comes in, no raises or drops are propagted up the hierarchy, |
| // |
| // As an optimization, if the ~object~ has no set drain-time and no |
| // registered callbacks, the forked process can be skipped and propagation |
| // proceeds immediately to the parent as described. |
| |
| 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); |
| |
| 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_hier_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 |
| // seperate the links of the chain. |
| m_scheduled_list.push_back(ctxt); |
| |
| end // else: !if(m_total_count[obj] != 0) |
| |
| endfunction |
| |
| |
| |
| // m_execute_scheduled_forks |
| // ------------------------- |
| |
| // 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 |
| |
| |
| // m_forked_drain |
| // ------------- |
| |
| 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_hier_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 |
| |
| |
| // m_init_objections |
| // ----------------- |
| |
| // Forks off the single background process |
| static function void m_init_objections(); |
| fork |
| uvm_objection::m_execute_scheduled_forks(); |
| join_none |
| endfunction |
| |
| // Function: set_drain_time |
| // |
| // Sets the drain time on the given ~object~ to ~drain~. |
| // |
| // The drain time is the amount of time to wait once all objections have |
| // been dropped before calling the all_dropped callback and propagating |
| // the objection to the parent. |
| // |
| // If a new objection for this ~object~ or any of its descendents is raised |
| // during the drain time or during execution of the all_dropped callbacks, |
| // the drain_time/all_dropped execution is terminated. |
| |
| // AE: set_drain_time(drain,obj=null)? |
| function void set_drain_time (uvm_object obj=null, time drain); |
| if (obj==null) |
| obj = m_top; |
| m_drain_time[obj] = drain; |
| m_set_hier_mode(obj); |
| endfunction |
| |
| |
| //---------------------- |
| // Group: Callback Hooks |
| //---------------------- |
| |
| // Function: raised |
| // |
| // Objection callback that is called when a <raise_objection> has reached ~obj~. |
| // The default implementation calls <uvm_component::raised>. |
| |
| 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); |
| if (m_events.exists(obj)) |
| ->m_events[obj].raised; |
| endfunction |
| |
| |
| // Function: dropped |
| // |
| // Objection callback that is called when a <drop_objection> has reached ~obj~. |
| // The default implementation calls <uvm_component::dropped>. |
| |
| 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); |
| if (m_events.exists(obj)) |
| ->m_events[obj].dropped; |
| endfunction |
| |
| |
| // Function: all_dropped |
| // |
| // Objection callback that is called when a <drop_objection> has reached ~obj~, |
| // and the total count for ~obj~ goes to zero. This callback is executed |
| // after the drain time associated with ~obj~. The default implementation |
| // calls <uvm_component::all_dropped>. |
| |
| 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); |
| if (m_events.exists(obj)) |
| ->m_events[obj].all_dropped; |
| if (obj == m_top) |
| m_top_all_dropped = 1; |
| endtask |
| |
| |
| //------------------------ |
| // Group: Objection Status |
| //------------------------ |
| |
| // Function: get_objectors |
| // |
| // Returns the current list of objecting objects (objects that |
| // raised an objection but have not dropped it). |
| |
| function void get_objectors(ref uvm_object list[$]); |
| list.delete(); |
| foreach (m_source_count[obj]) list.push_back(obj); |
| endfunction |
| |
| |
| // Task: wait_for |
| // |
| // Waits for the raised, dropped, or all_dropped ~event~ to occur in |
| // the given ~obj~. The task returns after all corresponding callbacks |
| // for that event have been executed. |
| // |
| task wait_for(uvm_objection_event objt_event, uvm_object obj=null); |
| |
| if (obj==null) |
| obj = m_top; |
| |
| if (!m_events.exists(obj)) begin |
| m_events[obj] = new; |
| end |
| |
| m_events[obj].waiters++; |
| case (objt_event) |
| UVM_RAISED: @(m_events[obj].raised); |
| UVM_DROPPED: @(m_events[obj].dropped); |
| UVM_ALL_DROPPED: @(m_events[obj].all_dropped); |
| endcase |
| |
| m_events[obj].waiters--; |
| |
| if (m_events[obj].waiters == 0) |
| m_events.delete(obj); |
| |
| endtask |
| |
| |
| task wait_for_total_count(uvm_object obj=null, int count=0); |
| if (obj==null) |
| obj = m_top; |
| |
| if(!m_total_count.exists(obj) && count == 0) |
| return; |
| if (count == 0) |
| wait (!m_total_count.exists(obj) && count == 0); |
| else |
| wait (m_total_count.exists(obj) && m_total_count[obj] == count); |
| endtask |
| |
| |
| // Function: get_objection_count |
| // |
| // Returns the current number of objections raised by the given ~object~. |
| |
| function int get_objection_count (uvm_object obj=null); |
| if (obj==null) |
| obj = m_top; |
| |
| if (!m_source_count.exists(obj)) |
| return 0; |
| return m_source_count[obj]; |
| endfunction |
| |
| |
| // Function: get_objection_total |
| // |
| // Returns the current number of objections raised by the given ~object~ |
| // and all descendants. |
| |
| function int get_objection_total (uvm_object obj=null); |
| uvm_component c; |
| string ch; |
| |
| if (obj==null) |
| obj = m_top; |
| |
| if (!m_total_count.exists(obj)) |
| return 0; |
| if (m_hier_mode) |
| return m_total_count[obj]; |
| else begin |
| if ($cast(c,obj)) begin |
| if (!m_source_count.exists(obj)) |
| get_objection_total = 0; |
| else |
| get_objection_total = m_source_count[obj]; |
| if (c.get_first_child(ch)) |
| do |
| get_objection_total += get_objection_total(c.get_child(ch)); |
| while (c.get_next_child(ch)); |
| end |
| else begin |
| return m_total_count[obj]; |
| end |
| end |
| endfunction |
| |
| |
| // Function: get_drain_time |
| // |
| // Returns the current drain time set for the given ~object~ (default: 0 ns). |
| |
| function time get_drain_time (uvm_object obj=null); |
| if (obj==null) |
| obj = m_top; |
| |
| if (!m_drain_time.exists(obj)) |
| return 0; |
| return m_drain_time[obj]; |
| endfunction |
| |
| |
| // m_display_objections |
| |
| protected function string m_display_objections(uvm_object obj=null, bit show_header=1); |
| |
| static string blank=" "; |
| |
| string s; |
| int total; |
| uvm_object list[string]; |
| uvm_object curr_obj; |
| int depth; |
| string name; |
| string this_obj_name; |
| string curr_obj_name; |
| |
| foreach (m_total_count[o]) begin |
| uvm_object theobj = o; |
| if ( m_total_count[o] > 0) |
| list[theobj.get_full_name()] = theobj; |
| end |
| |
| if (obj==null) |
| obj = m_top; |
| |
| total = get_objection_total(obj); |
| |
| s = $sformatf("The total objection count is %0d\n",total); |
| |
| if (total == 0) |
| return s; |
| |
| s = {s,"---------------------------------------------------------\n"}; |
| s = {s,"Source Total \n"}; |
| s = {s,"Count Count Object\n"}; |
| s = {s,"---------------------------------------------------------\n"}; |
| |
| |
| this_obj_name = obj.get_full_name(); |
| curr_obj_name = this_obj_name; |
| |
| do begin |
| |
| curr_obj = list[curr_obj_name]; |
| |
| // determine depth |
| depth=0; |
| foreach (curr_obj_name[i]) |
| if (curr_obj_name[i] == ".") |
| depth++; |
| |
| // determine leaf name |
| name = curr_obj_name; |
| for (int i=curr_obj_name.len()-1;i >= 0; i--) |
| if (curr_obj_name[i] == ".") begin |
| name = curr_obj_name.substr(i+1,curr_obj_name.len()-1); |
| break; |
| end |
| if (curr_obj_name == "") |
| name = "uvm_top"; |
| else |
| depth++; |
| |
| // print it |
| s = {s, $sformatf("%-6d %-6d %s%s\n", |
| m_source_count.exists(curr_obj) ? m_source_count[curr_obj] : 0, |
| m_total_count.exists(curr_obj) ? m_total_count[curr_obj] : 0, |
| blank.substr(0,2*depth), name)}; |
| |
| end while (list.next(curr_obj_name) && |
| curr_obj_name.substr(0,this_obj_name.len()-1) == this_obj_name); |
| |
| s = {s,"---------------------------------------------------------\n"}; |
| |
| return s; |
| |
| endfunction |
| |
| |
| function string convert2string(); |
| return m_display_objections(m_top,1); |
| endfunction |
| |
| |
| // Function: display_objections |
| // |
| // Displays objection information about the given ~object~. If ~object~ is |
| // not specified or ~null~, the implicit top-level component, <uvm_root>, is |
| // chosen. The ~show_header~ argument allows control of whether a header is |
| // output. |
| |
| function void display_objections(uvm_object obj=null, bit show_header=1); |
| $display(m_display_objections(obj,show_header)); |
| endfunction |
| |
| |
| // Below is all of the basic data stuff that is needed for an uvm_object |
| // for factory registration, printing, comparing, etc. |
| |
| typedef uvm_object_registry#(uvm_objection,"uvm_objection") type_id; |
| static function type_id get_type(); |
| return type_id::get(); |
| endfunction |
| |
| function uvm_object create (string name=""); |
| uvm_objection tmp = new(name); |
| return tmp; |
| endfunction |
| |
| virtual function string get_type_name (); |
| return "uvm_objection"; |
| endfunction |
| |
| function void do_copy (uvm_object rhs); |
| uvm_objection _rhs; |
| $cast(_rhs, rhs); |
| m_source_count = _rhs.m_source_count; |
| m_total_count = _rhs.m_total_count; |
| m_drain_time = _rhs.m_drain_time; |
| m_hier_mode = _rhs.m_hier_mode; |
| endfunction |
| |
| endclass |
| |
| |
| `ifdef UVM_USE_CALLBACKS_OBJECTION_FOR_TEST_DONE |
| typedef uvm_callbacks_objection m_uvm_test_done_objection_base; |
| `else |
| typedef uvm_objection m_uvm_test_done_objection_base; |
| `endif |
| |
| |
| // TODO: change to plusarg |
| `define UVM_DEFAULT_TIMEOUT 9200s |
| |
| typedef class uvm_cmdline_processor; |
| |
| |
| |
| //------------------------------------------------------------------------------ |
| // |
| // Class- uvm_test_done_objection DEPRECATED |
| // |
| // Provides built-in end-of-test coordination |
| //------------------------------------------------------------------------------ |
| |
| class uvm_test_done_objection extends m_uvm_test_done_objection_base; |
| |
| protected static uvm_test_done_objection m_inst; |
| protected bit m_forced; |
| |
| // For communicating all objections dropped and end of phasing |
| local bit m_executing_stop_processes; |
| local int m_n_stop_threads; |
| |
| |
| // Function- new DEPRECATED |
| // |
| // Creates the singleton test_done objection. Users must not to call |
| // this method directly. |
| |
| function new(string name="uvm_test_done"); |
| super.new(name); |
| endfunction |
| |
| |
| // Function- qualify DEPRECATED |
| // |
| // Checks that the given ~object~ is derived from either <uvm_component> or |
| // <uvm_sequence_base>. |
| |
| virtual function void qualify(uvm_object obj=null, |
| bit is_raise, |
| string description); |
| uvm_component c; |
| uvm_sequence_base s; |
| string nm = is_raise ? "raise_objection" : "drop_objection"; |
| string desc = description == "" ? "" : {" (\"", description, "\")"}; |
| if(! ($cast(c,obj) || $cast(s,obj))) begin |
| uvm_report_error("TEST_DONE_NOHIER", {"A non-hierarchical object, '", |
| obj.get_full_name(), "' (", obj.get_type_name(),") was used in a call ", |
| "to uvm_test_done.", nm,"(). For this objection, a sequence ", |
| "or component is required.", desc }); |
| end |
| endfunction |
| |
| |
| `ifndef UVM_NO_DEPRECATED |
| // m_do_stop_all |
| // ------------- |
| |
| task m_do_stop_all(uvm_component comp); |
| |
| string name; |
| |
| // we use an external traversal to ensure all forks are |
| // made from a single threaad. |
| if (comp.get_first_child(name)) |
| do begin |
| m_do_stop_all(comp.get_child(name)); |
| end |
| while (comp.get_next_child(name)); |
| |
| if (comp.enable_stop_interrupt) begin |
| m_n_stop_threads++; |
| fork begin |
| comp.stop_phase(run_ph); |
| m_n_stop_threads--; |
| end |
| join_none |
| end |
| endtask |
| |
| |
| // Function- stop_request DEPRECATED |
| // |
| // Calling this function triggers the process of shutting down the currently |
| // running task-based phase. This process involves calling all components' |
| // stop tasks for those components whose enable_stop_interrupt bit is set. |
| // Once all stop tasks return, or once the optional global_stop_timeout |
| // expires, all components' kill method is called, effectively ending the |
| // current phase. The uvm_top will then begin execution of the next phase, |
| // if any. |
| |
| function void stop_request(); |
| `uvm_info_context("STOP_REQ", |
| "Stop-request called. Waiting for all-dropped on uvm_test_done", |
| UVM_FULL,m_top); |
| fork |
| m_stop_request(); |
| join_none |
| endfunction |
| |
| task m_stop_request(); |
| raise_objection(m_top,"stop_request called; raising test_done objection"); |
| uvm_wait_for_nba_region(); |
| drop_objection(m_top,"stop_request called; dropping test_done objection"); |
| endtask |
| |
| |
| // Variable- stop_timeout DEPRECATED |
| // |
| // These set watchdog timers for task-based phases and stop tasks. You can not |
| // disable the timeouts. When set to 0, a timeout of the maximum time possible |
| // is applied. A timeout at this value usually indicates a problem with your |
| // testbench. You should lower the timeout to prevent "never-ending" |
| // simulations. |
| |
| time stop_timeout = 0; |
| |
| |
| // Task- all_dropped DEPRECATED |
| // |
| // This callback is called when the given ~object's~ objection count reaches |
| // zero; if the ~object~ is the implicit top-level, <uvm_root> then it means |
| // there are no more objections raised for the ~uvm_test_done~ objection. |
| // Thus, after calling <uvm_objection::all_dropped>, this method will call |
| // <global_stop_request> to stop the current task-based phase (e.g. run). |
| |
| virtual task all_dropped (uvm_object obj, |
| uvm_object source_obj, |
| string description, |
| int count); |
| if (obj != m_top) begin |
| super.all_dropped(obj,source_obj,description,count); |
| return; |
| end |
| |
| m_top.all_dropped(this, source_obj, description, count); |
| |
| // All stop tasks are forked from a single thread within a 'guard' process |
| // so 'disable fork' can be used. |
| |
| if(m_cleared == 0) begin |
| `uvm_info_context("TEST_DONE", |
| "All end-of-test objections have been dropped. Calling stop tasks", |
| UVM_FULL,m_top); |
| fork begin // guard |
| fork |
| begin |
| m_executing_stop_processes = 1; |
| m_do_stop_all(m_top); |
| wait (m_n_stop_threads == 0); |
| m_executing_stop_processes = 0; |
| end |
| begin |
| if (stop_timeout == 0) |
| wait(stop_timeout != 0); |
| `uvm_delay(stop_timeout) |
| `uvm_error("STOP_TIMEOUT", |
| {$sformatf("Stop-task timeout of %0t expired. ", stop_timeout), |
| "'run' phase ready to proceed to extract phase"}) |
| end |
| join_any |
| disable fork; |
| end |
| join // guard |
| |
| `uvm_info_context("TEST_DONE", {"'run' phase is ready ", |
| "to proceed to the 'extract' phase"}, UVM_LOW,m_top) |
| |
| end |
| |
| if (m_events.exists(obj)) |
| ->m_events[obj].all_dropped; |
| m_top_all_dropped = 1; |
| |
| endtask |
| |
| |
| // Function- raise_objection DEPRECATED |
| // |
| // Calls <uvm_objection::raise_objection> after calling <qualify>. |
| // If the ~object~ is not provided or is ~null~, then the implicit top-level |
| // component, ~uvm_top~, is chosen. |
| |
| virtual function void raise_objection (uvm_object obj=null, |
| string description="", |
| int count=1); |
| if(obj==null) |
| obj=m_top; |
| else |
| qualify(obj, 1, description); |
| |
| if (m_executing_stop_processes) begin |
| string desc = description == "" ? "" : {"(\"", description, "\") "}; |
| `uvm_warning("ILLRAISE", {"The uvm_test_done objection was ", |
| "raised ", desc, "during processing of a stop_request, i.e. stop ", |
| "task execution. The objection is ignored by the stop process"}) |
| return; |
| end |
| |
| super.raise_objection(obj,description,count); |
| |
| endfunction |
| |
| |
| // Function- drop_objection DEPRECATED |
| // |
| // Calls <uvm_objection::drop_objection> after calling <qualify>. |
| // If the ~object~ is not provided or is ~null~, then the implicit top-level |
| // component, ~uvm_top~, is chosen. |
| |
| virtual function void drop_objection (uvm_object obj=null, |
| string description="", |
| int count=1); |
| if(obj==null) |
| obj=m_top; |
| else |
| qualify(obj, 0, description); |
| super.drop_objection(obj,description,count); |
| endfunction |
| |
| |
| // Task- force_stop DEPRECATED |
| // |
| // Forces the propagation of the all_dropped() callback, even if there are still |
| // outstanding objections. The net effect of this action is to forcibly end |
| // the current phase. |
| |
| virtual task force_stop(uvm_object obj=null); |
| uvm_report_warning("FORCE_STOP",{"Object '", |
| (obj!=null?obj.get_name():"<unknown>"),"' called force_stop"}); |
| m_cleared = 1; |
| all_dropped(m_top,obj,"force_stop() called",1); |
| clear(obj); |
| endtask |
| `endif |
| |
| |
| // Below are basic data operations needed for all uvm_objects |
| // for factory registration, printing, comparing, etc. |
| |
| typedef uvm_object_registry#(uvm_test_done_objection,"uvm_test_done") type_id; |
| static function type_id get_type(); |
| return type_id::get(); |
| endfunction |
| |
| function uvm_object create (string name=""); |
| uvm_test_done_objection tmp = new(name); |
| return tmp; |
| endfunction |
| |
| virtual function string get_type_name (); |
| return "uvm_test_done"; |
| endfunction |
| |
| static function uvm_test_done_objection get(); |
| if(m_inst == null) |
| m_inst = uvm_test_done_objection::type_id::create("run"); |
| return m_inst; |
| endfunction |
| |
| endclass |
| |
| |
| |
| // Have a pool of context objects to use |
| 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 |
| |
| |
| //------------------------------------------------------------------------------ |
| // |
| // Class: uvm_callbacks_objection |
| // |
| //------------------------------------------------------------------------------ |
| // The uvm_callbacks_objection is a specialized <uvm_objection> which contains |
| // callbacks for the raised and dropped events. Callbacks happend for the three |
| // standard callback activities, <raised>, <dropped>, and <all_dropped>. |
| // |
| // The <uvm_heartbeat> mechanism use objections of this type for creating |
| // heartbeat conditions. Whenever the objection is raised or dropped, the component |
| // which did the raise/drop is considered to be alive. |
| // |
| |
| |
| class uvm_callbacks_objection extends uvm_objection; |
| `uvm_register_cb(uvm_callbacks_objection, uvm_objection_callback) |
| function new(string name=""); |
| super.new(name); |
| endfunction |
| |
| // Function: raised |
| // |
| // Executes the <uvm_objection_callback::raised> method in the user callback |
| // class whenever this objection is raised at the object ~obj~. |
| |
| virtual function void raised (uvm_object obj, uvm_object source_obj, |
| string description, int count); |
| `uvm_do_callbacks(uvm_callbacks_objection,uvm_objection_callback,raised(this,obj,source_obj,description,count)) |
| endfunction |
| |
| // Function: dropped |
| // |
| // Executes the <uvm_objection_callback::dropped> method in the user callback |
| // class whenever this objection is dropped at the object ~obj~. |
| |
| virtual function void dropped (uvm_object obj, uvm_object source_obj, |
| string description, int count); |
| `uvm_do_callbacks(uvm_callbacks_objection,uvm_objection_callback,dropped(this,obj,source_obj,description,count)) |
| endfunction |
| |
| // Function: all_dropped |
| // |
| // Executes the <uvm_objection_callback::all_dropped> task in the user callback |
| // class whenever the objection count for this objection in reference to ~obj~ |
| // goes to zero. |
| |
| virtual task all_dropped (uvm_object obj, uvm_object source_obj, |
| string description, int count); |
| `uvm_do_callbacks(uvm_callbacks_objection,uvm_objection_callback,all_dropped(this,obj,source_obj,description,count)) |
| endtask |
| endclass |
| |
| |
| //------------------------------------------------------------------------------ |
| // |
| // Class: uvm_objection_callback |
| // |
| //------------------------------------------------------------------------------ |
| // The uvm_objection is the callback type that defines the callback |
| // implementations for an objection callback. A user uses the callback |
| // type uvm_objection_cbs_t to add callbacks to specific objections. |
| // |
| // For example: |
| // |
| //| class my_objection_cb extends uvm_objection_callback; |
| //| function new(string name); |
| //| super.new(name); |
| //| endfunction |
| //| |
| //| virtual function void raised (uvm_objection objection, uvm_object obj, |
| //| uvm_object source_obj, string description, int count); |
| //| $display("%0t: Objection %s: Raised for %s", $time, objection.get_name(), |
| //| obj.get_full_name()); |
| //| endfunction |
| //| endclass |
| //| ... |
| //| initial begin |
| //| my_objection_cb cb = new("cb"); |
| //| uvm_objection_cbs_t::add(null, cb); //typewide callback |
| //| end |
| |
| |
| class uvm_objection_callback extends uvm_callback; |
| function new(string name); |
| super.new(name); |
| endfunction |
| |
| // Function: raised |
| // |
| // Objection raised callback function. Called by <uvm_callbacks_objection::raised>. |
| |
| virtual function void raised (uvm_objection objection, uvm_object obj, |
| uvm_object source_obj, string description, int count); |
| endfunction |
| |
| // Function: dropped |
| // |
| // Objection dropped callback function. Called by <uvm_callbacks_objection::dropped>. |
| |
| virtual function void dropped (uvm_objection objection, uvm_object obj, |
| uvm_object source_obj, string description, int count); |
| endfunction |
| |
| // Function: all_dropped |
| // |
| // Objection all_dropped callback function. Called by <uvm_callbacks_objection::all_dropped>. |
| |
| virtual task all_dropped (uvm_objection objection, uvm_object obj, |
| uvm_object source_obj, string description, int count); |
| endtask |
| |
| endclass |
| |
| |
| `endif |
| |