//----------------------------------------------------------------------
// 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.
//----------------------------------------------------------------------
//------------------------------------------------------------------------------
//
// CLASS: uvm_sequence_base
//
// The uvm_sequence_base class provides the interfaces needed to create streams
// of sequence items and/or other sequences.
//
// A sequence is executed by calling its <start> method, either directly
// or invocation of any of the `uvm_do_* macros.
//
// Executing sequences via <start>:
//
// A sequence's <start> method has a ~parent_sequence~ argument that controls
// whether <pre_do>, <mid_do>, and <post_do> are called *in the parent*
// sequence. It also has a ~call_pre_post~ argument that controls whether its
// <pre_body> and <post_body> methods are called.
// In all cases, its <pre_start> and <post_start> methods are always called.
//
// When <start> is called directly, you can provide the appropriate arguments
// according to your application.
//
// The sequence execution flow looks like this
//
// User code
//
//| sub_seq.randomize(...); // optional
//| sub_seq.start(seqr, parent_seq, priority, call_pre_post)
//|
//
// The following methods are called, in order
//
//|
//| sub_seq.pre_start() (task)
//| sub_seq.pre_body() (task) if call_pre_post==1
//| parent_seq.pre_do(0) (task) if parent_sequence!=null
//| parent_seq.mid_do(this) (func) if parent_sequence!=null
//| sub_seq.body (task) YOUR STIMULUS CODE
//| parent_seq.post_do(this) (func) if parent_sequence!=null
//| sub_seq.post_body() (task) if call_pre_post==1
//| sub_seq.post_start() (task)
//
//
// Executing sub-sequences via `uvm_do macros:
//
// A sequence can also be indirectly started as a child in the <body> of a
// parent sequence. The child sequence's <start> method is called indirectly
// by invoking any of the `uvm_do macros.
// In these cases, <start> is called with
// ~call_pre_post~ set to 0, preventing the started sequence's <pre_body> and
// <post_body> methods from being called. During execution of the
// child sequence, the parent's <pre_do>, <mid_do>, and <post_do> methods
// are called.
//
// The sub-sequence execution flow looks like
//
// User code
//
//|
//| `uvm_do_with_prior(seq_seq, { constraints }, priority)
//|
//
// The following methods are called, in order
//
//|
//| sub_seq.pre_start() (task)
//| parent_seq.pre_do(0) (task)
//| parent_req.mid_do(sub_seq) (func)
//| sub_seq.body() (task)
//| parent_seq.post_do(sub_seq) (func)
//| sub_seq.post_start() (task)
//|
//
// Remember, it is the *parent* sequence's pre|mid|post_do that are called, not
// the sequence being executed.
//
//
// Executing sequence items via <start_item>/<finish_item> or `uvm_do macros:
//
// Items are started in the <body> of a parent sequence via calls to
// <start_item>/<finish_item> or invocations of any of the `uvm_do
// macros. The <pre_do>, <mid_do>, and <post_do> methods of the parent
// sequence will be called as the item is executed.
//
// The sequence-item execution flow looks like
//
// User code
//
//| parent_seq.start_item(item, priority);
//| item.randomize(...) [with {constraints}];
//| parent_seq.finish_item(item);
//|
//| or
//|
//| `uvm_do_with_prior(item, constraints, priority)
//|
//
// The following methods are called, in order
//
//|
//| sequencer.wait_for_grant(prior) (task) \ start_item \
//| parent_seq.pre_do(1) (task) / \
//| `uvm_do* macros
//| parent_seq.mid_do(item) (func) \ /
//| sequencer.send_request(item) (func) \finish_item /
//| sequencer.wait_for_item_done() (task) /
//| parent_seq.post_do(item) (func) /
//
// Attempting to execute a sequence via <start_item>/<finish_item>
// will produce a run-time error.
//------------------------------------------------------------------------------
class [docs]uvm_sequence_base extends uvm_sequence_item;
protected uvm_sequence_state m_sequence_state;
int m_next_transaction_id = 1;
local int m_priority = -1;
uvm_recorder m_tr_recorder;
int m_wait_for_grant_semaphore;
// Each sequencer will assign a sequence id. When a sequence is talking to multiple
// sequencers, each sequence_id is managed separately
protected int m_sqr_seq_ids[int];
protected bit children_array[uvm_sequence_base];
protected uvm_sequence_item response_queue[$];
protected int response_queue_depth = 8;
protected bit response_queue_error_report_disabled;
// Variable: do_not_randomize
//
// If set, prevents the sequence from being randomized before being executed
// by the `uvm_do*() and `uvm_rand_send*() macros,
// or as a default sequence.
//
bit do_not_randomize;
protected process m_sequence_process;
local bit m_use_response_handler;
static string type_name = "uvm_sequence_base";
// bits to detect if is_relevant()/wait_for_relevant() are implemented
local bit is_rel_default;
local bit wait_rel_default;
// Function: new
//
// The constructor for uvm_sequence_base.
//
function [docs]new (string name = "uvm_sequence");
super.new(name);
m_sequence_state = UVM_CREATED;
m_wait_for_grant_semaphore = 0;
m_init_phase_daps(1);
endfunction
// Function: is_item
//
// Returns 1 on items and 0 on sequences. As this object is a sequence,
// ~is_item~ will always return 0.
//
virtual function bit [docs]is_item();
return 0;
endfunction
// Function: get_sequence_state
//
// Returns the sequence state as an enumerated value. Can use to wait on
// the sequence reaching or changing from one or more states.
//
//| wait(get_sequence_state() & (UVM_STOPPED|UVM_FINISHED));
function uvm_sequence_state_enum [docs]get_sequence_state();
return m_sequence_state;
endfunction
// Task: wait_for_sequence_state
//
// Waits until the sequence reaches one of the given ~state~. If the sequence
// is already in one of the state, this method returns immediately.
//
//| wait_for_sequence_state(UVM_STOPPED|UVM_FINISHED);
task [docs]wait_for_sequence_state(int unsigned state_mask);
wait (m_sequence_state & state_mask);
endtask
// Function: get_tr_handle
//
// Returns the integral recording transaction handle for this sequence.
// Can be used to associate sub-sequences and sequence items as
// child transactions when calling <uvm_component::begin_child_tr>.
function integer [docs]get_tr_handle();
if (m_tr_recorder != null)
return m_tr_recorder.get_handle();
else
return 0;
endfunction
//--------------------------
// Group: Sequence Execution
//--------------------------
// Task: start
//
// Executes this sequence, returning when the sequence has completed.
//
// The ~sequencer~ argument specifies the sequencer on which to run this
// sequence. The sequencer must be compatible with the sequence.
//
// If ~parent_sequence~ is ~null~, then this sequence is a root parent,
// otherwise it is a child of ~parent_sequence~. The ~parent_sequence~'s
// pre_do, mid_do, and post_do methods will be called during the execution
// of this sequence.
//
// By default, the ~priority~ of a sequence
// is the priority of its parent sequence.
// If it is a root sequence, its default priority is 100.
// A different priority may be specified by ~this_priority~.
// Higher numbers indicate higher priority.
//
// If ~call_pre_post~ is set to 1 (default), then the <pre_body> and
// <post_body> tasks will be called before and after the sequence
// <body> is called.
virtual task [docs]start (uvm_sequencer_base sequencer,
uvm_sequence_base parent_sequence = null,
int this_priority = -1,
bit call_pre_post = 1);
bit old_automatic_phase_objection;
set_item_context(parent_sequence, sequencer);
if (!(m_sequence_state inside {UVM_CREATED,UVM_STOPPED,UVM_FINISHED})) begin
uvm_report_fatal("SEQ_NOT_DONE",
{"Sequence ", get_full_name(), " already started"},UVM_NONE);
end
if (m_parent_sequence != null) begin
m_parent_sequence.children_array[this] = 1;
end
if (this_priority < -1) begin
uvm_report_fatal("SEQPRI", $sformatf("Sequence %s start has illegal priority: %0d",
get_full_name(),
this_priority), UVM_NONE);
end
if (this_priority < 0) begin
if (parent_sequence == null) this_priority = 100;
else this_priority = parent_sequence.get_priority();
end
// Check that the response queue is empty from earlier runs
clear_response_queue();
m_priority = this_priority;
if (m_sequencer != null) begin
integer handle;
uvm_tr_stream stream;
if (m_parent_sequence == null) begin
stream = m_sequencer.get_tr_stream(get_name(), "Transactions");
handle = m_sequencer.begin_tr(this, get_name());
m_tr_recorder = uvm_recorder::get_recorder_from_handle(handle);
end else begin
stream = m_sequencer.get_tr_stream(get_root_sequence_name(), "Transactions");
handle = m_sequencer.begin_child_tr(this,
(m_parent_sequence.m_tr_recorder == null) ? 0 : m_parent_sequence.m_tr_recorder.get_handle(),
get_root_sequence_name());
m_tr_recorder = uvm_recorder::get_recorder_from_handle(handle);
end
end
// Ensure that the sequence_id is intialized in case this sequence has been stopped previously
set_sequence_id(-1);
// Remove all sqr_seq_ids
m_sqr_seq_ids.delete();
// Register the sequence with the sequencer if defined.
if (m_sequencer != null) begin
void'(m_sequencer.m_register_sequence(this));
end
// Change the state to PRE_START, do this before the fork so that
// the "if (!(m_sequence_state inside {...}" works
m_sequence_state = UVM_PRE_START;
fork
begin
m_sequence_process = process::self();
// absorb delta to ensure PRE_START was seen
#0;
// Raise the objection if enabled
// (This will lock the uvm_get_to_lock_dap)
if (get_automatic_phase_objection()) begin
m_safe_raise_starting_phase("automatic phase objection");
end
pre_start();
if (call_pre_post == 1) begin
m_sequence_state = UVM_PRE_BODY;
#0;
pre_body();
end
if (parent_sequence != null) begin
parent_sequence.pre_do(0); // task
parent_sequence.mid_do(this); // function
end
m_sequence_state = UVM_BODY;
#0;
body();
m_sequence_state = UVM_ENDED;
#0;
if (parent_sequence != null) begin
parent_sequence.post_do(this);
end
if (call_pre_post == 1) begin
m_sequence_state = UVM_POST_BODY;
#0;
post_body();
end
m_sequence_state = UVM_POST_START;
#0;
post_start();
// Drop the objection if enabled
if (get_automatic_phase_objection()) begin
m_safe_drop_starting_phase("automatic phase objection");
end
m_sequence_state = UVM_FINISHED;
#0;
end
join
if (m_sequencer != null) begin
m_sequencer.end_tr(this);
end
// Clean up any sequencer queues after exiting; if we
// were forcibly stoped, this step has already taken place
if (m_sequence_state != UVM_STOPPED) begin
if (m_sequencer != null)
m_sequencer.m_sequence_exiting(this);
end
#0; // allow stopped and finish waiters to resume
if ((m_parent_sequence != null) && (m_parent_sequence.children_array.exists(this))) begin
m_parent_sequence.children_array.delete(this);
end
old_automatic_phase_objection = get_automatic_phase_objection();
m_init_phase_daps(1);
set_automatic_phase_objection(old_automatic_phase_objection);
endtask
// Task: pre_start
//
// This task is a user-definable callback that is called before the
// optional execution of <pre_body>.
// This method should not be called directly by the user.
virtual task [docs]pre_start();
return;
endtask
// Task: pre_body
//
// This task is a user-definable callback that is called before the
// execution of <body> ~only~ when the sequence is started with <start>.
// If <start> is called with ~call_pre_post~ set to 0, ~pre_body~ is not
// called.
// This method should not be called directly by the user.
virtual task [docs]pre_body();
return;
endtask
// Task: pre_do
//
// This task is a user-definable callback task that is called ~on the
// parent sequence~, if any
// sequence has issued a wait_for_grant() call and after the sequencer has
// selected this sequence, and before the item is randomized.
//
// Although pre_do is a task, consuming simulation cycles may result in
// unexpected behavior on the driver.
//
// This method should not be called directly by the user.
virtual task [docs]pre_do(bit is_item);
return;
endtask
// Function: mid_do
//
// This function is a user-definable callback function that is called after
// the sequence item has been randomized, and just before the item is sent
// to the driver. This method should not be called directly by the user.
virtual function void [docs]mid_do(uvm_sequence_item this_item);
return;
endfunction
// Task: body
//
// This is the user-defined task where the main sequence code resides.
// This method should not be called directly by the user.
virtual task [docs]body();
uvm_report_warning("uvm_sequence_base", "Body definition undefined");
return;
endtask
// Function: post_do
//
// This function is a user-definable callback function that is called after
// the driver has indicated that it has completed the item, using either
// this item_done or put methods. This method should not be called directly
// by the user.
virtual function void [docs]post_do(uvm_sequence_item this_item);
return;
endfunction
// Task: post_body
//
// This task is a user-definable callback task that is called after the
// execution of <body> ~only~ when the sequence is started with <start>.
// If <start> is called with ~call_pre_post~ set to 0, ~post_body~ is not
// called.
// This task is a user-definable callback task that is called after the
// execution of the body, unless the sequence is started with call_pre_post=0.
// This method should not be called directly by the user.
virtual task [docs]post_body();
return;
endtask
// Task: post_start
//
// This task is a user-definable callback that is called after the
// optional execution of <post_body>.
// This method should not be called directly by the user.
virtual task [docs]post_start();
return;
endtask
// Group: Run-Time Phasing
//
// Automatic Phase Objection DAP
local uvm_get_to_lock_dap#(bit) m_automatic_phase_objection_dap;
// Starting Phase DAP
local uvm_get_to_lock_dap#(uvm_phase) m_starting_phase_dap;
// Function- m_init_phase_daps
// Either creates or renames DAPS
function void m_init_phase_daps(bit create);
string apo_name = $sformatf("%s.automatic_phase_objection", get_full_name());
string sp_name = $sformatf("%s.starting_phase", get_full_name());
if (create) begin
m_automatic_phase_objection_dap = uvm_get_to_lock_dap#(bit)::type_id::create(apo_name, get_sequencer());
m_starting_phase_dap = uvm_get_to_lock_dap#(uvm_phase)::type_id::create(sp_name, get_sequencer());
end
else begin
m_automatic_phase_objection_dap.set_name(apo_name);
m_starting_phase_dap.set_name(sp_name);
end
endfunction : m_init_phase_daps
`ifndef UVM_NO_DEPRECATED
`define UVM_DEPRECATED_STARTING_PHASE
`endif
`ifdef UVM_DEPRECATED_STARTING_PHASE
// DEPRECATED!! Use get/set_starting_phase accessors instead!
uvm_phase starting_phase;
// Value set via set_starting_phase
uvm_phase m_set_starting_phase;
// Ensures we only warn once per sequence
bit m_warn_deprecated_set;
`endif
// Function: get_starting_phase
// Returns the 'starting phase'.
//
// If non-~null~, the starting phase specifies the phase in which this
// sequence was started. The starting phase is set automatically when
// this sequence is started as the default sequence on a sequencer.
// See <uvm_sequencer_base::start_phase_sequence> for more information.
//
// Internally, the <uvm_sequence_base> uses an <uvm_get_to_lock_dap> to
// protect the starting phase value from being modified
// after the reference has been read. Once the sequence has ended
// its execution (either via natural termination, or being killed),
// then the starting phase value can be modified again.
//
function uvm_phase [docs]get_starting_phase();
`ifdef UVM_DEPRECATED_STARTING_PHASE
begin
bit throw_error;
if (starting_phase != m_set_starting_phase) begin
if (!m_warn_deprecated_set) begin
`uvm_warning("UVM_DEPRECATED", "'starting_phase' is deprecated and not part of the UVM standard. See documentation for uvm_sequence_base::set_starting_phase")
m_warn_deprecated_set = 1;
end
if (m_starting_phase_dap.try_set(starting_phase))
m_set_starting_phase = starting_phase;
else begin
uvm_phase dap_phase = m_starting_phase_dap.get();
`uvm_error("UVM/SEQ/LOCK_DEPR",
{"The deprecated 'starting_phase' variable has been set to '",
(starting_phase == null) ? "<null>" : starting_phase.get_full_name(),
"' after a call to get_starting_phase locked the value to '",
(dap_phase == null) ? "<null>" : dap_phase.get_full_name(),
"'. See documentation for uvm_sequence_base::set_starting_phase."})
end
end
end
`endif
return m_starting_phase_dap.get();
endfunction : get_starting_phase
// Function: set_starting_phase
// Sets the 'starting phase'.
//
// Internally, the <uvm_sequence_base> uses a <uvm_get_to_lock_dap> to
// protect the starting phase value from being modified
// after the reference has been read. Once the sequence has ended
// its execution (either via natural termination, or being killed),
// then the starting phase value can be modified again.
//
function void [docs]set_starting_phase(uvm_phase phase);
`ifdef UVM_DEPRECATED_STARTING_PHASE
begin
if (starting_phase != m_set_starting_phase) begin
if (!m_warn_deprecated_set) begin
`uvm_warning("UVM_DEPRECATED",
{"The deprecated 'starting_phase' variable has been set to '",
starting_phase.get_full_name(),
"' manually. See documentation for uvm_sequence_base::set_starting_phase."})
m_warn_deprecated_set = 1;
end
starting_phase = phase;
m_set_starting_phase = phase;
end
end
`endif
m_starting_phase_dap.set(phase);
endfunction : set_starting_phase
// Function: set_automatic_phase_objection
// Sets the 'automatically object to starting phase' bit.
//
// The most common interaction with the starting phase
// within a sequence is to simply ~raise~ the phase's objection
// prior to executing the sequence, and ~drop~ the objection
// after ending the sequence (either naturally, or
// via a call to <kill>). In order to
// simplify this interaction for the user, the UVM
// provides the ability to perform this functionality
// automatically.
//
// For example:
//| function my_sequence::new(string name="unnamed");
//| super.new(name);
//| set_automatic_phase_objection(1);
//| endfunction : new
//
// From a timeline point of view, the automatic phase objection
// looks like:
//| start() is executed
//| --! Objection is raised !--
//| pre_start() is executed
//| pre_body() is optionally executed
//| body() is executed
//| post_body() is optionally executed
//| post_start() is executed
//| --! Objection is dropped !--
//| start() unblocks
//
// This functionality can also be enabled in sequences
// which were not written with UVM Run-Time Phasing in mind:
//| my_legacy_seq_type seq = new("seq");
//| seq.set_automatic_phase_objection(1);
//| seq.start(my_sequencer);
//
// Internally, the <uvm_sequence_base> uses a <uvm_get_to_lock_dap> to
// protect the ~automatic_phase_objection~ value from being modified
// after the reference has been read. Once the sequence has ended
// its execution (either via natural termination, or being killed),
// then the ~automatic_phase_objection~ value can be modified again.
//
// NEVER set the automatic phase objection bit to 1 if your sequence
// runs with a forever loop inside of the body, as the objection will
// never get dropped!
function void [docs]set_automatic_phase_objection(bit value);
m_automatic_phase_objection_dap.set(value);
endfunction : set_automatic_phase_objection
// Function: get_automatic_phase_objection
// Returns (and locks) the value of the 'automatically object to
// starting phase' bit.
//
// If 1, then the sequence will automatically raise an objection
// to the starting phase (if the starting phase is not ~null~) immediately
// prior to <pre_start> being called. The objection will be dropped
// after <post_start> has executed, or <kill> has been called.
//
function bit [docs]get_automatic_phase_objection();
return m_automatic_phase_objection_dap.get();
endfunction : get_automatic_phase_objection
// m_safe_raise_starting_phase
function void m_safe_raise_starting_phase(string description = "",
int count = 1);
uvm_phase starting_phase = get_starting_phase();
if (starting_phase != null)
starting_phase.raise_objection(this, description, count);
endfunction : m_safe_raise_starting_phase
// m_safe_drop_starting_phase
function void m_safe_drop_starting_phase(string description = "",
int count = 1);
uvm_phase starting_phase = get_starting_phase();
if (starting_phase != null)
starting_phase.drop_objection(this, description, count);
endfunction : m_safe_drop_starting_phase
//------------------------
// Group: Sequence Control
//------------------------
// Function: set_priority
//
// The priority of a sequence may be changed at any point in time. When the
// priority of a sequence is changed, the new priority will be used by the
// sequencer the next time that it arbitrates between sequences.
//
// The default priority value for a sequence is 100. Higher values result
// in higher priorities.
function void [docs]set_priority (int value);
m_priority = value;
endfunction
// Function: get_priority
//
// This function returns the current priority of the sequence.
function int [docs]get_priority();
return m_priority;
endfunction
// Function: is_relevant
//
// The default is_relevant implementation returns 1, indicating that the
// sequence is always relevant.
//
// Users may choose to override with their own virtual function to indicate
// to the sequencer that the sequence is not currently relevant after a
// request has been made.
//
// When the sequencer arbitrates, it will call is_relevant on each requesting,
// unblocked sequence to see if it is relevant. If a 0 is returned, then the
// sequence will not be chosen.
//
// If all requesting sequences are not relevant, then the sequencer will call
// wait_for_relevant on all sequences and re-arbitrate upon its return.
//
// Any sequence that implements is_relevant must also implement
// wait_for_relevant so that the sequencer has a way to wait for a
// sequence to become relevant.
virtual function bit [docs]is_relevant();
is_rel_default = 1;
return 1;
endfunction
// Task: wait_for_relevant
//
// This method is called by the sequencer when all available sequences are
// not relevant. When wait_for_relevant returns the sequencer attempt to
// re-arbitrate.
//
// Returning from this call does not guarantee a sequence is relevant,
// although that would be the ideal. The method provide some delay to
// prevent an infinite loop.
//
// If a sequence defines is_relevant so that it is not always relevant (by
// default, a sequence is always relevant), then the sequence must also supply
// a wait_for_relevant method.
virtual task [docs]wait_for_relevant();
event e;
wait_rel_default = 1;
if (is_rel_default != wait_rel_default)
uvm_report_fatal("RELMSM",
"is_relevant() was implemented without defining wait_for_relevant()", UVM_NONE);
@e; // this is intended to never return
endtask
// Task: lock
//
// Requests a lock on the specified sequencer. If sequencer is ~null~, the lock
// will be requested on the current default sequencer.
//
// A lock request will be arbitrated the same as any other request. A lock is
// granted after all earlier requests are completed and no other locks or
// grabs are blocking this sequence.
//
// The lock call will return when the lock has been granted.
task [docs]lock(uvm_sequencer_base sequencer = null);
if (sequencer == null)
sequencer = m_sequencer;
if (sequencer == null)
uvm_report_fatal("LOCKSEQR", "Null m_sequencer reference", UVM_NONE);
sequencer.lock(this);
endtask
// Task: grab
//
// Requests a lock on the specified sequencer. If no argument is supplied,
// the lock will be requested on the current default sequencer.
//
// A grab request is put in front of the arbitration queue. It will be
// arbitrated before any other requests. A grab is granted when no other grabs
// or locks are blocking this sequence.
//
// The grab call will return when the grab has been granted.
task [docs]grab(uvm_sequencer_base sequencer = null);
if (sequencer == null) begin
if (m_sequencer == null) begin
uvm_report_fatal("GRAB", "Null m_sequencer reference", UVM_NONE);
end
m_sequencer.grab(this);
end
else begin
sequencer.grab(this);
end
endtask
// Function: unlock
//
// Removes any locks or grabs obtained by this sequence on the specified
// sequencer. If sequencer is ~null~, then the unlock will be done on the
// current default sequencer.
function void [docs]unlock(uvm_sequencer_base sequencer = null);
if (sequencer == null) begin
if (m_sequencer == null) begin
uvm_report_fatal("UNLOCK", "Null m_sequencer reference", UVM_NONE);
end
m_sequencer.unlock(this);
end else begin
sequencer.unlock(this);
end
endfunction
// Function: ungrab
//
// Removes any locks or grabs obtained by this sequence on the specified
// sequencer. If sequencer is ~null~, then the unlock will be done on the
// current default sequencer.
function void [docs]ungrab(uvm_sequencer_base sequencer = null);
unlock(sequencer);
endfunction
// Function: is_blocked
//
// Returns a bit indicating whether this sequence is currently prevented from
// running due to another lock or grab. A 1 is returned if the sequence is
// currently blocked. A 0 is returned if no lock or grab prevents this
// sequence from executing. Note that even if a sequence is not blocked, it
// is possible for another sequence to issue a lock or grab before this
// sequence can issue a request.
function bit [docs]is_blocked();
return m_sequencer.is_blocked(this);
endfunction
// Function: has_lock
//
// Returns 1 if this sequence has a lock, 0 otherwise.
//
// Note that even if this sequence has a lock, a child sequence may also have
// a lock, in which case the sequence is still blocked from issuing
// operations on the sequencer.
function bit [docs]has_lock();
return m_sequencer.has_lock(this);
endfunction
// Function: kill
//
// This function will kill the sequence, and cause all current locks and
// requests in the sequence's default sequencer to be removed. The sequence
// state will change to UVM_STOPPED, and the post_body() and post_start() callback
// methods will not be executed.
//
// If a sequence has issued locks, grabs, or requests on sequencers other than
// the default sequencer, then care must be taken to unregister the sequence
// with the other sequencer(s) using the sequencer unregister_sequence()
// method.
function void [docs]kill();
if (m_sequence_process != null) begin
// If we are not connected to a sequencer, then issue
// kill locally.
if (m_sequencer == null) begin
m_kill();
// We need to drop the objection if we raised it...
if (get_automatic_phase_objection()) begin
m_safe_drop_starting_phase("automatic phase objection");
end
return;
end
// If we are attached to a sequencer, then the sequencer
// will clear out queues, and then kill this sequence
m_sequencer.kill_sequence(this);
// We need to drop the objection if we raised it...
if (get_automatic_phase_objection()) begin
m_safe_drop_starting_phase("automatic phase objection");
end
return;
end
endfunction
// Function: do_kill
//
// This function is a user hook that is called whenever a sequence is
// terminated by using either sequence.kill() or sequencer.stop_sequences()
// (which effectively calls sequence.kill()).
virtual function void [docs]do_kill();
return;
endfunction
function void m_kill();
do_kill();
foreach(children_array[i]) begin
i.kill();
end
if (m_sequence_process != null) begin
m_sequence_process.kill;
m_sequence_process = null;
end
m_sequence_state = UVM_STOPPED;
if ((m_parent_sequence != null) && (m_parent_sequence.children_array.exists(this)))
m_parent_sequence.children_array.delete(this);
endfunction
//-------------------------------
// Group: Sequence Item Execution
//-------------------------------
// Function: create_item
//
// Create_item will create and initialize a sequence_item or sequence
// using the factory. The sequence_item or sequence will be initialized
// to communicate with the specified sequencer.
protected function uvm_sequence_item create_item(uvm_object_wrapper type_var,
uvm_sequencer_base l_sequencer, string name);
uvm_coreservice_t cs = uvm_coreservice_t::get();
uvm_factory factory=cs.get_factory();
$cast(create_item, factory.create_object_by_type( type_var, this.get_full_name(), name ));
create_item.set_item_context(this, l_sequencer);
endfunction
// Function: start_item
//
// ~start_item~ and <finish_item> together will initiate operation of
// a sequence item. If the item has not already been
// initialized using create_item, then it will be initialized here to use
// the default sequencer specified by m_sequencer. Randomization
// may be done between start_item and finish_item to ensure late generation
//
virtual task [docs]start_item (uvm_sequence_item item,
int set_priority = -1,
uvm_sequencer_base sequencer=null);
uvm_sequence_base seq;
if(item == null) begin
uvm_report_fatal("NULLITM",
{"attempting to start a null item from sequence '",
get_full_name(), "'"}, UVM_NONE);
return;
end
if($cast(seq, item)) begin
uvm_report_fatal("SEQNOTITM",
{"attempting to start a sequence using start_item() from sequence '",
get_full_name(), "'. Use seq.start() instead."}, UVM_NONE);
return;
end
if (sequencer == null)
sequencer = item.get_sequencer();
if(sequencer == null)
sequencer = get_sequencer();
if(sequencer == null) begin
uvm_report_fatal("SEQ",{"neither the item's sequencer nor dedicated sequencer has been supplied to start item in ",get_full_name()},UVM_NONE);
return;
end
item.set_item_context(this, sequencer);
if (set_priority < 0)
set_priority = get_priority();
sequencer.wait_for_grant(this, set_priority);
if (sequencer.is_auto_item_recording_enabled()) begin
void'(sequencer.begin_child_tr(item,
(m_tr_recorder == null) ? 0 : m_tr_recorder.get_handle(),
item.get_root_sequence_name(), "Transactions"));
end
pre_do(1);
endtask
// Function: finish_item
//
// finish_item, together with start_item together will initiate operation of
// a sequence_item. Finish_item must be called
// after start_item with no delays or delta-cycles. Randomization, or other
// functions may be called between the start_item and finish_item calls.
//
virtual task [docs]finish_item (uvm_sequence_item item,
int set_priority = -1);
uvm_sequencer_base sequencer;
sequencer = item.get_sequencer();
if (sequencer == null) begin
uvm_report_fatal("STRITM", "sequence_item has null sequencer", UVM_NONE);
end
mid_do(item);
sequencer.send_request(this, item);
sequencer.wait_for_item_done(this, -1);
if (sequencer.is_auto_item_recording_enabled()) begin
sequencer.end_tr(item);
end
post_do(item);
endtask
// Task: wait_for_grant
//
// This task issues a request to the current sequencer. If item_priority is
// not specified, then the current sequence priority will be used by the
// arbiter. If a lock_request is made, then the sequencer will issue a lock
// immediately before granting the sequence. (Note that the lock may be
// granted without the sequence being granted if is_relevant is not asserted).
//
// When this method returns, the sequencer has granted the sequence, and the
// sequence must call send_request without inserting any simulation delay
// other than delta cycles. The driver is currently waiting for the next
// item to be sent via the send_request call.
virtual task [docs]wait_for_grant(int item_priority = -1, bit lock_request = 0);
if (m_sequencer == null) begin
uvm_report_fatal("WAITGRANT", "Null m_sequencer reference", UVM_NONE);
end
m_sequencer.wait_for_grant(this, item_priority, lock_request);
endtask
// Function: send_request
//
// The send_request function may only be called after a wait_for_grant call.
// This call will send the request item to the sequencer, which will forward
// it to the driver. If the rerandomize bit is set, the item will be
// randomized before being sent to the driver.
virtual function void [docs]send_request(uvm_sequence_item request, bit rerandomize = 0);
if (m_sequencer == null) begin
uvm_report_fatal("SENDREQ", "Null m_sequencer reference", UVM_NONE);
end
m_sequencer.send_request(this, request, rerandomize);
endfunction
// Task: wait_for_item_done
//
// A sequence may optionally call wait_for_item_done. This task will block
// until the driver calls item_done or put. If no transaction_id parameter
// is specified, then the call will return the next time that the driver calls
// item_done or put. If a specific transaction_id is specified, then the call
// will return when the driver indicates completion of that specific item.
//
// Note that if a specific transaction_id has been specified, and the driver
// has already issued an item_done or put for that transaction, then the call
// will hang, having missed the earlier notification.
virtual task [docs]wait_for_item_done(int transaction_id = -1);
if (m_sequencer == null) begin
uvm_report_fatal("WAITITEMDONE", "Null m_sequencer reference", UVM_NONE);
end
m_sequencer.wait_for_item_done(this, transaction_id);
endtask
// Group: Response API
//--------------------
// Function: use_response_handler
//
// When called with enable set to 1, responses will be sent to the response
// handler. Otherwise, responses must be retrieved using get_response.
//
// By default, responses from the driver are retrieved in the sequence by
// calling get_response.
//
// An alternative method is for the sequencer to call the response_handler
// function with each response.
function void [docs]use_response_handler(bit enable);
m_use_response_handler = enable;
endfunction
// Function: get_use_response_handler
//
// Returns the state of the use_response_handler bit.
function bit [docs]get_use_response_handler();
return m_use_response_handler;
endfunction
// Function: response_handler
//
// When the use_response_handler bit is set to 1, this virtual task is called
// by the sequencer for each response that arrives for this sequence.
virtual function void [docs]response_handler(uvm_sequence_item response);
return;
endfunction
// Function: set_response_queue_error_report_disabled
//
// By default, if the response_queue overflows, an error is reported. The
// response_queue will overflow if more responses are sent to this sequence
// from the driver than get_response calls are made. Setting value to 0
// disables these errors, while setting it to 1 enables them.
function void [docs]set_response_queue_error_report_disabled(bit value);
response_queue_error_report_disabled = value;
endfunction
// Function: get_response_queue_error_report_disabled
//
// When this bit is 0 (default value), error reports are generated when
// the response queue overflows. When this bit is 1, no such error
// reports are generated.
function bit [docs]get_response_queue_error_report_disabled();
return response_queue_error_report_disabled;
endfunction
// Function: set_response_queue_depth
//
// The default maximum depth of the response queue is 8. These method is used
// to examine or change the maximum depth of the response queue.
//
// Setting the response_queue_depth to -1 indicates an arbitrarily deep
// response queue. No checking is done.
function void [docs]set_response_queue_depth(int value);
response_queue_depth = value;
endfunction
// Function: get_response_queue_depth
//
// Returns the current depth setting for the response queue.
function int [docs]get_response_queue_depth();
return response_queue_depth;
endfunction
// Function: clear_response_queue
//
// Empties the response queue for this sequence.
virtual function void [docs]clear_response_queue();
response_queue.delete();
endfunction
virtual function void [docs]put_base_response(input uvm_sequence_item response);
if ((response_queue_depth == -1) ||
(response_queue.size() < response_queue_depth)) begin
response_queue.push_back(response);
return;
end
if (response_queue_error_report_disabled == 0) begin
uvm_report_error(get_full_name(), "Response queue overflow, response was dropped", UVM_NONE);
end
endfunction
// Function- put_response
//
// Internal method.
virtual function void [docs]put_response (uvm_sequence_item response_item);
put_base_response(response_item); // no error-checking
endfunction
// Function- get_base_response
virtual task [docs]get_base_response(output uvm_sequence_item response, input int transaction_id = -1);
int queue_size, i;
if (response_queue.size() == 0)
wait (response_queue.size() != 0);
if (transaction_id == -1) begin
response = response_queue.pop_front();
return;
end
forever begin
queue_size = response_queue.size();
for (i = 0; i < queue_size; i++) begin
if (response_queue[i].get_transaction_id() == transaction_id)
begin
$cast(response,response_queue[i]);
response_queue.delete(i);
return;
end
end
wait (response_queue.size() != queue_size);
end
endtask
//------------------------
// Group- Sequence Library DEPRECATED
//------------------------
`ifndef UVM_NO_DEPRECATED
// Variable- seq_kind
//
// Used as an identifier in constraints for a specific sequence type.
rand int unsigned seq_kind;
// For user random selection. This excludes the exhaustive and
// random sequences.
constraint pick_sequence {
(num_sequences() <= 2) || (seq_kind >= 2);
(seq_kind < num_sequences()) || (seq_kind == 0); }
// Function- num_sequences
//
// Returns the number of sequences in the sequencer's sequence library.
function int [docs]num_sequences();
if (m_sequencer == null)
return 0;
return (m_sequencer.num_sequences());
endfunction
// Function- get_seq_kind
//
// This function returns an int representing the sequence kind that has
// been registerd with the sequencer. The return value may be used with
// the <get_sequence> or <do_sequence_kind> methods.
function int [docs]get_seq_kind(string type_name);
`uvm_warning("UVM_DEPRECATED",$sformatf("%m deprecated."))
if(m_sequencer != null)
return m_sequencer.get_seq_kind(type_name);
else
uvm_report_warning("NULLSQ", $sformatf("%0s sequencer is null.",
get_type_name()), UVM_NONE);
endfunction
// Function- get_sequence
//
// This function returns a reference to a sequence specified by ~req_kind~,
// which can be obtained using the <get_seq_kind> method.
function uvm_sequence_base [docs]get_sequence(int unsigned req_kind);
uvm_sequence_base m_seq;
string m_seq_type;
uvm_coreservice_t cs = uvm_coreservice_t::get();
uvm_factory factory=cs.get_factory();
`uvm_warning("UVM_DEPRECATED",$sformatf("%m deprecated."))
if (req_kind < 0 || req_kind >= m_sequencer.sequences.size()) begin
uvm_report_error("SEQRNG",
$sformatf("Kind arg '%0d' out of range. Need 0-%0d",
req_kind, m_sequencer.sequences.size()-1), UVM_NONE);
end
m_seq_type = m_sequencer.sequences[req_kind];
if (!$cast(m_seq, factory.create_object_by_name(m_seq_type, get_full_name(), m_seq_type))) begin
uvm_report_fatal("FCTSEQ",
$sformatf("Factory cannot produce a sequence of type %0s.",
m_seq_type), UVM_NONE);
end
m_seq.set_use_sequence_info(1);
return m_seq;
endfunction
// Task- do_sequence_kind
//
// This task will start a sequence of kind specified by ~req_kind~,
// which can be obtained using the <get_seq_kind> method.
task [docs]do_sequence_kind(int unsigned req_kind);
string m_seq_type;
uvm_sequence_base m_seq;
uvm_coreservice_t cs = uvm_coreservice_t::get();
uvm_factory factory=cs.get_factory();
`uvm_warning("UVM_DEPRECATED",$sformatf("%m deprecated."))
m_seq_type = m_sequencer.sequences[req_kind];
if (!$cast(m_seq, factory.create_object_by_name(m_seq_type, get_full_name(), m_seq_type))) begin
uvm_report_fatal("FCTSEQ",
$sformatf("Factory cannot produce a sequence of type %0s.", m_seq_type), UVM_NONE);
end
m_seq.set_item_context(this, m_sequencer);
if(!m_seq.randomize()) begin
uvm_report_warning("RNDFLD", "Randomization failed in do_sequence_kind()");
end
m_seq.start(m_sequencer,this,get_priority(),0);
endtask
// Function- get_sequence_by_name
//
// Internal method.
function uvm_sequence_base [docs]get_sequence_by_name(string seq_name);
uvm_sequence_base m_seq;
uvm_coreservice_t cs = uvm_coreservice_t::get();
uvm_factory factory=cs.get_factory();
`uvm_warning("UVM_DEPRECATED",$sformatf("%m deprecated."))
if (!$cast(m_seq, factory.create_object_by_name(seq_name, get_full_name(), seq_name))) begin
uvm_report_fatal("FCTSEQ",
$sformatf("Factory cannot produce a sequence of type %0s.", seq_name), UVM_NONE);
end
m_seq.set_use_sequence_info(1);
return m_seq;
endfunction
// Task- create_and_start_sequence_by_name
//
// Internal method.
task [docs]create_and_start_sequence_by_name(string seq_name);
uvm_sequence_base m_seq;
`uvm_warning("UVM_DEPRECATED",$sformatf("%m deprecated."))
m_seq = get_sequence_by_name(seq_name);
m_seq.start(m_sequencer, this, this.get_priority(), 0);
endtask
`endif // UVM_NO_DEPRECATED
//----------------------
// Misc Internal methods
//----------------------
// m_get_sqr_sequence_id
// ---------------------
function int m_get_sqr_sequence_id(int sequencer_id, bit update_sequence_id);
if (m_sqr_seq_ids.exists(sequencer_id)) begin
if (update_sequence_id == 1) begin
set_sequence_id(m_sqr_seq_ids[sequencer_id]);
end
return m_sqr_seq_ids[sequencer_id];
end
if (update_sequence_id == 1)
set_sequence_id(-1);
return -1;
endfunction
// m_set_sqr_sequence_id
// ---------------------
function void m_set_sqr_sequence_id(int sequencer_id, int sequence_id);
m_sqr_seq_ids[sequencer_id] = sequence_id;
set_sequence_id(sequence_id);
endfunction
endclass