One of the most confusing UVM stuff is about m_sequencer and p_sequencer and the difference between the two. In reality, its just a game of polymorphism.
Referring to some forum answer, m_sequencer
is a generic sequencer pointer of type uvm_sequencer_base. It will always exist for a uvm_sequence and is initialized when the sequence is started.
The p_sequencer
is a type specific sequencer pointer, created by registering the sequence to a sequencer using the `uvm_declare_p_sequencer
macros. Being type specific, you will be able to access anything added to the sequencer (i.e. pointers to other sequencers, etc.). p_sequencer
will not exist if the `uvm_declare_p_sequencer
macros isn’t used.
Lets have a look at the UVM sequence and sequencer hierarchy:
As we can see sequence is ultimately extended from uvm_sequence_item
and in turn from uvm_object
. While sequencer has a grandparent of uvm_sequencer_base
and uvm_component
. This is one of the reason why sequencer has phases and sequence does not. But that’s an entire different part of discussion.
What we are interested in is that m_sequencer
is a handle of type uvm_sequencer_base
while p_sequencer
is a handle of type user_defined_sequencer
. The user_defined_sequencer
is a grandchild of uvm_sequencer_base class
.
When we start a sequence, we provide an object handle of our user_defined_sequencer
. Internally, in start method, this child class object is assigned into parent handle called m_sequencer
. So, a static casting occurs such that a parent class handle points to child class object (m_sequencer = user_defined_sequencer_object
).
Now, when referring to sequence, if a p_sequencer
is defined, the macro `uvm_declare_p_sequencer
expands to a function that declares a user_defined_sequencer
handle known as p_sequencer
. This function then casts the m_sequencer
(parent class handle) back to p_sequencer
(child class handle) using dynamic casting ($cast
).
Referring to a simple code as below, the
// uvm_sequencer_base
class base;
int a;
endclass
// user_defined_sequencer
class child extends base;
int b;
endclass
//... some other class
// Just like m_sequencer
base b;
// user created sequencer
child child1;
// Just like p_sequencer
child child2;
// user sequencer object in env/agent
child1 = new;
// internal in start method, m_sequencer = user_sequencer
b = child1;
// internal in declare_p_sequencer macro casting back from m_sequencer to p_sequencer
$cast(child2,b);
//...
A more elaborate example is done at Mimic m_seqr p_seqr link.
Finally, referring to UVM source code, following snippets can give you an intuition.
In uvm_sequence_base:
virtual task start (uvm_sequencer_base sequencer,
uvm_sequence_base parent_sequence = null,
int this_priority = -1,
bit call_pre_post = 1);
// ... some other stuff
set_sequencer(sequencer);
// ... some other stuff...
In uvm_sequence_item:
virtual function void set_sequencer(uvm_sequencer_base sequencer);
m_sequencer = sequencer;
m_set_p_sequencer();
endfunction
virtual function void m_set_p_sequencer();
return;
endfunction
In user sequence:
`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
In test:
user_sequence.start(user_sequencer);
The user_sequencer
is passed to start method which invokes set_sequencer
method of uvm_sequence_item
. This method in turn calls m_set_p_sequencer
(empty by default) method. If a p_sequencer
is declared during macro expansion, then m_set_p_sequencer
sets p_sequencer
handle to user_defined_sequencer
.
Thus, basically it is just a matter of static and dynamic casting between a base class and extended class. Hope this gives a basic idea about m_sequencer
and p_sequencer
help and avoiding confusion between the two.
Excellent article, keep up the good work 🙂
Thanks,
G.Karthik
LikeLike
Nice to hear about that.
Thanks Karthik.
LikeLike
I was just looking at your UVM: m_sequencer, p_sequencer difference | ASIC Verification website and see that your site has the potential to get a lot of visitors. I just want to tell you, In case you don’t already know… There is a website network which already has more than 16 million users, and the majority of the users are looking for topics like yours. By getting your website on this network you have a chance to get your site more visitors than you can imagine. It is free to sign up and you can read more about it here: http://url.laspas.gr/h7 – Now, let me ask you… Do you need your site to be successful to maintain your business? Do you need targeted traffic who are interested in the services and products you offer? Are looking for exposure, to increase sales, and to quickly develop awareness for your website? If your answer is YES, you can achieve these things only if you get your website on the service I am describing. This traffic service advertises you to thousands, while also giving you a chance to test the service before paying anything at all. All the popular blogs are using this service to boost their readership and ad revenue! Why aren’t you? And what is better than traffic? It’s recurring traffic! That’s how running a successful site works… Here’s to your success! Find out more here: http://pixz.nu/lywL
LikeLike
Thanks Rachel for the opportunity. But this blog is all about knowledge sharing. No immediate remuneration is targeted. Regards, Sharvil.
LikeLike
what is the difference in terms of application ? in terms of p_sequencer and m_sequencer ? thats not yet clear to me please help
LikeLike
This is a wide-scope question.
When you want to get some variable or connect some feedback port (from monitor most probably) in sequence, then p_sequencer is to be used. Since p_sequencer is the sequencer of user defined type, one can get the variable (config_db get) or implement some port in it. m_sequencer is a parent class; so one can not connect port or get a variable using m_sequencer handle (since m_sequencer can not access child class variables).
But, the drawback of p_sequencer is that once the p_sequencer is defined, one cannot run the sequence on any other sequencer type.
LikeLike
thanks this is in line with my understanding good work
LikeLike
Nice to hear that. 🙂
LikeLike
on a different note i had this question … tb_top is a static component then how come we do a set_config_object of interface handles in tb_top ?
LikeLike
I quite don’t get it exactly, but here is the best I can try. uvm_top is a singleton of uvm_root declared globally inside uvm_pkg. The interface instances are static entities. When we do a set_config_object/uvm_config_db, we set a “virtual” interface and not the actual interface. We just pass the handle of the actual interface, no new instance is created or passed down the hierarchy.
On the get side, all the components reside inside under the uvm_top instance (somewhere inside m_add_child API). So, setting the object for uvm_top.*, adds the get-able handle of virtual interface to all the components that are registered with factory.
LikeLike
Nice to hear that. 🙂
LikeLike
Amazing lucid explaination! Really appreciate the effort.
LikeLike
Thanks. It was my pleasure.
LikeLike
Hi,
Thanks for the explanation! However, I have a doubt:
As per your explanation, for m_sequencer and p_sequencer to get handle to user_sequencer, user_sequence has to be started at-least once. Correct?
I’ve seen below code at some places :
user_defined_sequnce.start(p_sequencer);
or
user_defined_sequnce.start(m_sequencer);
How does this work?
Thanks
LikeLike
When we call the user_defined_sequence using “m_sequencer” or “p_sequencer” handle, then it must be invoked from some parent sequence and not from the test.
This is mainly used in layered sequence where one sequence invokes other low level sequence. Refer this link where the set_sequencer API sets the m_sequencer to the sequencer provided in the start method. Here, the “m_sequencer” is the sequencer of the low level sequence and “sequencer” is again the “p/m_sequencer” of the high level sequence.
Does it clear your doubt somewhat?
LikeLike
Yes. This explains it clearly. So, we can start a sequence with p/m_sequencer only inside a virtual or hierarchical sequence. Thanks for the clarification. Appreciate your effort!
LikeLike
Correct. Thanks Rishi. I am gratified 🙂
LikeLiked by 1 person
My spouse and I stumbled over here coming from a different website and thought I may as well check things out. I like what I see so now i am following you. Look forward to looking over your web page again.
LikeLike
Thanks. Nice to see that it was helpful. 🙂
LikeLike
Excellent article, but i have a doubt. why p-sequencer came?
LikeLike
Thanks Gaurav. “p_sequencer” is a user-defined type sequencer. When we want to access some variables from user sequencer or there is some feedback mechanism port connected to our sequencer, at that time “p_sequencer” is useful (by using m_sequencer handle, we can’t access variables of extended class — user defined sequencer). Hope this clears the doubt. 🙂
LikeLiked by 1 person
Thanks sharvil, now my doubt is clear.
LikeLike
Well explained.. Will help many..
Thanks Sharvil 😀
LikeLike
Thanks Shrey. Nice to know that it helped. 🙂
LikeLike
Nice explanation, to such a confusing concept.
LikeLike
Thanks Fuwad. Hope it cleared the concept.
LikeLike
Great web site. Lots of useful information here. I’m sending it to several friends ans also sharing in delicious. And obviously, thanks for your effort!
LikeLiked by 1 person
So far the best website, I ever visited.
Kudos to you!!
LikeLiked by 1 person
Thanks Divya. 🙂
LikeLiked by 1 person
Thank you Sharvil. This is a great article and m_sequencer and p_sequencer no longer confusing me. Great work.
LikeLiked by 1 person
Thanks Harish. Nice to see that it is helpful. 🙂
LikeLiked by 1 person
Sharvil, randomly stumbled onto this site & OMG you cleared one of the doubts that has haunted me for a long time ! And I see that you have a talent at explaining things.
What a clear and thorough explanation ! Great job ! Thank you !
I look forward to your explanations to many more such confusing/complex topic !
Also is there a way we could ask you questions ? 🙂
I am adding your blog site to my Favorite list on my browser.
Thanks again !
LikeLike
Thanks.. this means a lot!
You can post the questions on any of the blog post. We can connect over the network via LinkedIn also 🙂
LikeLike
Hi sharvil, that was easy explanation to confusing concept. Thank you so much!
I have a doubt on the concept of packed and unpacked arrays, why basic need of unpacked arrays when we can use packed arrays?
when to use dynamic, associative and queues ??
LikeLike
Thank you so much Sharvil
LikeLike
Wow thanks dude
LikeLike
Hi Sarvil,
Nice explaining. I need some more help on this explanation.
When do really need to modify the sequencer in reality?
And how dose p sequencer compromise the reusability.
If you can expand this blog as part 2 then it would help based on previous comments.
LikeLike
Adding to previous post:
When we really need a p_sequencer in env? Any use case?
Or what can not be done with m_sequencer?
LikeLike
Beautifully explained. Thanks a lot for clearing my concept!
LikeLike
Website is very good. The explanation of concept is very clearance and good to understand.
LikeLike