Cast p_sequencer m_sequencer

Hello Verification Academy Team,

In my understanding, we can use p_sequencer as a handle to the sequencer and through that we can access other sequencers or environment hierarchy in the sequence where uvm_declare_p_sequencer macro has been used.

Below is my understanding and relevant information. You may please correct if required.

define uvm_declare_p_sequencer(SEQUENCER) \ SEQUENCER p_sequencer;\ virtual function void m_set_p_sequencer();\ super.m_set_p_sequencer(); \ if( !$cast(p_sequencer, m_sequencer)) \ uvm_fatal(“DCLPSQ”,
$sformatf(“%m %s Error casting p_sequencer, please verify that this sequence/sequence item is intended to execute on this type of sequencer”, get_full_name()))
endfunction

p_sequencer type is specified in the argument(SEQUENCER) of uvm_declare_p_sequencer macro. When we call uvm_declare_p_sequencer from sequence, it calls super.m_set_p_sequencer function, which internally will call m_set_sequencer of uvm_base_sequencer.
When we call start task of sequence, it internally calls for set_item_context, set_item_context calls set_sequencer, set_sequencer assigns handle of sequencer (which we have passed in start task of sequence) to m_sequencer using static cast (***This is first casting or down casting)
virtual function void set_sequencer(uvm_sequencer_base sequencer);
m_sequencer = sequencer;
m_set_p_sequencer();
endfunction

set_sequencer also calls m_set_p_sequencer(declaring `uvm_declare_p_sequencer macro should override the m_set_p_sequencer hence m_set_p_sequencer defined in macro should be executed) which will cast m_sequencer back into p_sequencer using dynamic $cast. (***This is reverse casting or up casting)


MY QUESTIONs:

  1. Why do we need this reverse/up casting? what is intended and achieved with this step?
  2. Can we pass virtual sequencer as an argument to uvm_declare_p_sequencer inside both virtual sequence and non-virtual sequence?
  3. In case when we are using virtual sequence, and passed virtual sequencer as an argument to uvm_declare_p_sequencer inside it , when we call this virtual sequence from test , we pass null in the start task of the virtual sequence. In this case what will happen? m_sequencer = null and then , m_sequencer = p_sequencer and p_sequencer = m_sequencer , is that correct understanding? OR m_sequencer = p_sequencer and then m_sequencer = null and p_sequencer = m_sequencer , is that correct understanding? I am getting lost here!

In reply to W I Master:

You should never use p_sequencer. If you need to access the config_db, you can use the m_sequencer handle. In the case of a virtual sequence, you should have the sequencer handles as part of the virtual sequence and assign the handles when the virtual sequence is created.

Thank you for the guidelines. If possible, please answer each of my original questions too. It will help me to understand more about it. Thank you again.

In reply to W I Master:

  1. m_sequencer is a uvm_sequencer. p_sequencer is of a user-defined class extended from uvm_sequencer. In order to provide access to the additional things added to the customized sequencer, the m_sequencer handle needs to be cast to the p_sequencer handle.

  2. You can for a virtual sequence, but a non-virtual sequence expects to be run on a sequencer typed for the specific sequence item.

  3. When you pass a null sequencer to a virtual sequence, a sequencer is still created. The cast to the p_sequencer handle will fail since it is not of the same type as the p_sequencer.