Jump to content
Sign in to follow this  
cliffc

UVM's m_sequencer, p_sequencer, get_sequencer() and `uvm_declare_p_sequencer

Recommended Posts

Cliff's Pre-Read(!) Summary:

Use: get_sequencer() method or the less-descriptive m_sequencer handle.

Avoid: p_sequencer and `uvm_declare_p_sequencer macro.

Feedback: Curious about what other engineers think of these guidelines.

Also reference the thread:

http://www.uvmworld.org/forums/showthread.php?427-m_sequencer-vs-p_sequencer

class uvm_sequence_item extends uvm_transaction

Has a protected m_sequencer handle

(uses public set_sequencer and get_sequencer methods to access - see below)

methods:

set_sequencer: sets m_sequencer handle.

(also calls m_set_p_sequencer(empty), which should set p_sequencer if `uvm_declare_p_sequencer was called).

`uvm_declare_p_sequencer macro is defined in (uvm/src/macros directory)

get_sequencer: returns m_sequencer handle.

m_set_p_sequencer: An empty method in this class. It is fully defined by the `uvm_declare_p_sequencer macro (details below), if used, else the p_sequencer variable does not get set.

class uvm_sequence_base extends uvm_sequence_item

Note: inherits m_sequencer handle and set_sequencer() and get_sequencer() methods.

methods:

start (<on this> sequencer-hierarchical-path):

calls set_sequencer() to set m_sequencer to the sequencer handle that was passed as an argument to the start method

calls m_set_p_sequencer() that casts m_sequencer to p_sequencer (if the `uvm_declare_p_sequencer macro was called in the sequence)

class uvm_sequence extends uvm_sequence_base

methods:

Important methods were inherited from uvm_sequence_base class.

Includes the start(<on specified> sequencer-hierarchical-path) method.

From the uvm_sequence_defines.svh macros file (uvm/src/macros directory)

`uvm_declare_p_sequencer seems to be a macro that is useful if the user has added some special variables to the tb_sequencer (which I have never seen done). Setting the sequencer in a sequence using the `uvm_declare_p_sequencer macro seems like a bad idea. It ties the sequence to a particular sequencer. Better to let the sequence inherit the sequencer when the sequence is invoked with the start() method (sets the m_sequencer handle and enables use of the get_sequencer() method). p_sequencer seems to have very limited unique utility.

Macro definition:

`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", <error message>) \

endfunction

The only reasonable example of using the p_sequencer handle from the documentation (?? - I have never needed to do this - hence, no need for p_sequencer). In the example, there is a need to access a local variable declared in a particular sequencer (who sets local variables in the sequencer??)

class mysequence extends uvm_sequence#(mydata);

`uvm_object_utils(mysequence)

`uvm_declare_p_sequencer(some_seqr_type)

task body;

//Access some variable in the user's custom sequencer

if(p_sequencer.some_variable) begin

...

end

endtask

endclass

Regards - Cliff Cummings

Verilog & SystemVerilog Guru

Share this post


Link to post
Share on other sites

The only reasonable example of using the p_sequencer handle from the documentation (?? - I have never needed to do this - hence, no need for p_sequencer). In the example, there is a need to access a local variable declared in a particular sequencer (who sets local variables in the sequencer??)

I do. All the time. Here's one situation:

Say you have a knobs class that contains all of the randomize-able fields that your agent needs access to. It may even contain a reference to the reg_block corresponding to your device. From the test, you can push a reference to this knobs class down to all of the components that need it with a uvm_config_db::set call. The driver can point to it, the scoreboard could point to it, the sequencer could point to it. And, with the p_sequencer variable set, so could your sequences.

Here's another situation: Your monitor sees transactions coming out that represent MSI interrupt messages. Being a good little monitor, it sends them out of an analysis port. The sequencer is one component that is connected to this port, and puts them in a TLM FIFO. Your interrupt handler is a persistant sequence (because it must push sequence items representing reads and writes) and fetches each interrupt that occurs out of the sequencer's FIFO, via the p_sequencer variable.

True, you could always just call get_sequencer(), and then cast it to the type of the sequencer it's attached to so that it can references that components members. But then, that's what the uvm_declare_p_sequencer macro does for you.

Do you want to do this all the time? Probably not.

Some sequences need access to their sequencers. But those are only ever going to run on one agent (like an MSI interrupt handler running on a PCIE agent).

Other sequences you want to be portable, so that you can drive high-level transactions in via different agents that are capable of accepting them.

Share this post


Link to post
Share on other sites

The driver can point to it, the scoreboard could point to it, the sequencer could point to it. And, with the p_sequencer variable set, so could your sequences. ...

It seems to me this would be reinventing global variables. I believe it is better to encapsulate related properties in components and sequences and strive to make them standalone and decoupled. If you need additional behavior later on, it may be better to extend instead of if-else-ing the original class on global knobs.

Being a good little monitor, it sends them out of an analysis port. The sequencer is one component that is connected to this port ...

Yes, but there are other solutions to this problem. For example, the good little monitor could handle interrupts as events, and transfer any additional info, if necessary, through the event. A sequence could fetch the event of interest from a pool setup by the environment. Things would be decoupled with no sequencer in between.

Erling

Share this post


Link to post
Share on other sites

Cliff, I'm going to have to take my black marker to your new UVM Guidelines fridge magnet too??

Avoid: p_sequencer and `uvm_declare_p_sequencer macro

Nope. Use them to get sequencer-context variables. You want to avoid repeatedly getting variables from the config/resource DB if they are not going to change or be identical across different sequences. A prime example is for a virtual sequencer/virtual sequences. The virtual sequencer has variables referencing the sub-sequencers. The virtual sequences then use them through a p_sequencer reference to coordinate the execution of sub-sequences on sub-sequencers.

Edited by janick

Share this post


Link to post
Share on other sites

It seems to me this would be reinventing global variables.

Not really, because the objects are created in the test and can only be distributed via the config_db.

I believe it is better to encapsulate related properties in components and sequences and strive to make them standalone and decoupled. If you need additional behavior later on, it may be better to extend instead of if-else-ing the original class on global knobs.

You'll find that this method is perfectly suitable for that. One such example might be if your DUT has 6 different PCIE MACs, each of which having its own associated reg_block, then putting references to the reg_block that a sequencer is driving to in the sequencer allows its sequences to be used on any of agents without needing to know which one they're running on.

Yes, but there are other solutions to this problem. For example, the good little monitor could handle interrupts as events, and transfer any additional info, if necessary, through the event. A sequence could fetch the event of interest from a pool setup by the environment. Things would be decoupled with no sequencer in between.

Unfortunately in this framework, all problems have multiple solutions. I merely contested that there is no reason to put any variables in a sequencer that a sequence might wish to reference. Janick's example of the virtual sequencer is a third such example.

Share this post


Link to post
Share on other sites

The virtual sequencer has variables referencing the sub-sequencers.

Not necessarily. Starting sequences could be delegated, without reference to sequencers. What often has to be dealt with, though, is the little things in between sequences, necessary to orchestrate operations, but it is not obvious to me that the sequencer should be involved in this (either). Another option could be to implement virtual sequences by means of the environment, and leave the sequencers an implementation detail. For example, if a piece of information is needed, and all the sequence has to talk to is the sequencer, then it is necessary for the environment to cache the information on the sequencer, and for the sequence to get hold of it via a dynamic downcast, i.e the p_sequencer solution. But if the sequence is implemented by means of the environment, on the other hand, it can go directly to the source, without casting or caching anything, and this would also work nicely in cases where the information isn't cacheable. What do you think, are there obvious problems with this solution? Other ideas?

Erling

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×