Uvm declare p_sequencer

where should i put this declaration in sequence or sequencer ?
uvm_declare_p_sequencer(SEQUENCER_NAME)

In reply to ayasaad234:

You should never declare or use p_sequencer, as this reduces the reusability of your sequences.

Instead, your sequence should contain handles to all required objects that are set by the component or virtual sequence that creates the sequence.

Additionally, you should never extend uvm_sequencer to add any objects. Your agent should use a typedef of the uvm_sequencer parameterized with the agent’s sequence_item for the sequencer type.

In reply to cgales:

got it, thanks

In reply to cgales:

In reply to ayasaad234:
You should never declare or use p_sequencer, as this reduces the reusability of your sequences.
Instead, your sequence should contain handles to all required objects that are set by the component or virtual sequence that creates the sequence.
Additionally, you should never extend uvm_sequencer to add any objects. Your agent should use a typedef of the uvm_sequencer parameterized with the agent’s sequence_item for the sequencer type.

I am not sure I would agree with any of these recommendations.

#3 - I extend uvm_sequencer all the time instead of the typedef. It makes for very clean agent code, and the sequencer is pretty much simple template code.

#1 - Some people use `uvm_declare_p_sequencer(SEQUENCER_TYPE) (not name) and they put it into a base sequence. The macro ensures that you are running on the correct virtual sequencer by calling the psequencer handle. One common virtual sequence technique is to let the environment store the subsequencer handles in a virtual sequencer so the virtual sequencer acts much like a config object for sequencer handles.

#2 - Perhaps you could explain this a bit better. How do you get handles of sequencers into the sequence? I know Verification Academy shows a set() method in the base test, but I very much dislike this technique since it requires hard-coded paths that can change over time as the verification environment grows or is modified. Am I missing something in your response?

Regards - Cliff Cummings
VP Training Paradigm Works

In reply to cliffc:

I consider creating an agent sequencer as a waste of time which can be better spent developing other parts of your environment. For an agent sequencer, you need to create the file, add the template code, add the include to your package file, and then debug any issues, for no additional benefit. In the space of your include line, you can have a simple typedef which meets all the requirements for your agent. Additionally, I have seen developers add a run_phase() to their sequencer out of habit, and then debug why their environment isn’t working correctly.

I consider creating a virtual sequencer to be a similar waste of time and recommend running a virtual sequence on a null sequencer. In a virtual sequence, you only have handles to the required sequencers based upon the types of sequence items you are generating. Since a test is always environment specific, it knows all the paths of the sequencers, so you can reuse test sequences from sub-environments very easily.

An example of flexibility is a using a test sequence which requires a single Ethernet port sequencer in an environment that has multiple Ethernet agents. The test can now randomly select one of the Ethernet agents to be utilized and assign the sequencer handle to the sequence. If you use a p_sequencer which contains multiple Ethernet sequencer handles, you would have to somehow account for which sequencer handle to use from the p_sequencer.

Assigning sequencer handles to a sequence has the same amount of complexity as adding sequencer handles to a p_sequencer, and can be accomplished with helper functions in your base test and base test sequences. When you are writing tests and test sequences, the overall hierarchy is generally static with minimal changes at that point.

In reply to cgales:

In reply to cliffc:
I consider creating an agent sequencer as a waste of time which can be better spent developing other parts of your environment. For an agent sequencer, you need to create the file, add the template code, add the include to your package file, and then debug any issues, for no additional benefit. In the space of your include line, you can have a simple typedef which meets all the requirements for your agent.

One of the main advantages to have a dedicated sequencer is clearly in the event of reactive agents, where the sequencer will need to add separate TLM ports to get responses from the Monitor (see: https://www.verilab.com/files/mastering_reactive_slaves.pdf)

I consider creating a virtual sequencer to be a similar waste of time and recommend running a virtual sequence on a null sequencer. In a virtual sequence, you only have handles to the required sequencers based upon the types of sequence items you are generating. Since a test is always environment specific, it knows all the paths of the sequencers, so you can reuse test sequences from sub-environments very easily.

While I agree that a virtual sequencer doesn’t “add” much, I still consider it a useful component to have since it can provide some facility to check for agent sequencers or sub virtual sequencers. Imagine the following:


class vsequencer extends uvm_sequencer;
 `uvm_component_utils(vsequencer)
 tb_ahb_sequencer ahb_sqr;
 tb_eth_sequencer eth_sqr;

 function new(string name, uvm_component parent);
   super.new(name, parent);
 endfunction
 function void end_of_elaboration_phase(uvm_phase phase);
   super.end_of_elaboration_phase(phase);
   if (!uvm_config_db#(tb_ahb_sequencer)::get(this, "", "ahb_sqr", ahb_sqr))
     `uvm_fatal("VSQR/CFG/NOAHB", "No ahb_sqr specified for this instance");

   if (!uvm_config_db#(tb_eth_sequencer)::get(this, "", "eth_sqr", eth_sqr))
     `uvm_fatal("VSQR/CFG/NOETH", "No eth_sqr specified for this instance");

   if (ahb_sqr == null)
      `uvm_fatal(get_full_name(), "ahb_sqr sub-sequencer null pointer: this test case will fail, check config or virtual sequence")

   if (eth_sqr == null)
      `uvm_fatal(get_full_name(), "eth_sqr sub-sequencer null pointer: this test case will fail, check config or virtual sequence")

 endfunction
endclass 

EDIT: courtesy of Clifford E. Cummings and Janick Bergeron (DVCON16)
As you can see I’m encapsulating knowledge in the virtual sequencer and have it work for me. The tester needs to make sure the virtual sequence is run on the appropriate virtual_sequencer (you could actually leverage the p_sequencer with the `uvm_declare_p_sequencer macro that performs this check for you).

An example of flexibility is a using a test sequence which requires a single Ethernet port sequencer in an environment that has multiple Ethernet agents. The test can now randomly select one of the Ethernet agents to be utilized and assign the sequencer handle to the sequence. If you use a p_sequencer which contains multiple Ethernet sequencer handles, you would have to somehow account for which sequencer handle to use from the p_sequencer.

I could achieve that by doing the following:


class ethernet_vseq extends ethernet_base_vseq;

  // random sequencer to chose from
  ethernet_sequencer eth_sqr;

  function void post_randomize ();
    for (int i=0, i < NUM_ETH_AGENTS; i++)
      randcase
	0 : eth_sqr = p_sequencer.eth_sqr0;
        1 : eth_sqr = p_sequencer.eth_sqr1;
	2 : eth_sqr = p_sequencer.eth_sqr2;
      endcase
    end
	
    tast body();
      eth_seq.start(eth_sqr);
    endtask	
  endfunction : post_randomize
endclass: ethernet_vseq

I know can be sure that using the above virtual sequence on the correct virtual sequencer I am guaranteed to have it running.

Assigning sequencer handles to a sequence has the same amount of complexity as adding sequencer handles to a p_sequencer, and can be accomplished with helper functions in your base test and base test sequences. When you are writing tests and test sequences, the overall hierarchy is generally static with minimal changes at that point.

[/quote]

You’ve consistently claimed that using virtual sequencers will hamper reusability, but I failed to find an example for that. Can you please provide clarity on why you think using a virtual sequencer will hamper reusability?

In reply to abasili:

A virtual sequencer is not really required to run virtual sequences. You can use a null object to run a virtual sequence on. This is because a virtual sequencer is never generating seq_items.
And virtual sequences are not intended for reuse only agent specific sequences are.

In reply to chr_sue:

In reply to abasili:
A virtual sequencer is not really required to run virtual sequences. You can use a null object to run a virtual sequence on.

I understand it’s ont required, just like UVM is not required, but my argument is that you have a great deal of opportunities available if you have a virtual sequencer. For one, as demonstrated in my previous post, you can issue a uvm_fatal before you get to start your test. You would need to check for null sequencers anyhow (see coding guidelines here: Virtual Sequencers | UVM Cookbook), but imagine your virtual sequence is just one of many and it is started way after the beginning of the test, it’s going to be a nasty surprise to realize you forgot to configure your bench properly after hours of simulation time.

And virtual sequences are not intended for reuse only agent specific sequences are.

Why not? Virtual sequences can be aware of the environment through their virtual sequencer handle and without the need to pull in the configuration from the config_db at runtime (which might have performance implications), so if well designed they can surely be aware that your environment has 1 eth interface instead of multiple (in the example user cgales brought up).

Here’s a simple example on how you could make your virtual sequence aware of the env:


class ethernet_vseq extends ethernet_base_vseq;

  // in this case you virtual sequencer doesn't need to check for all sequencers to be
  // built and it's up to the virtual sequence to decide what to do.
  task body ();
    if (p_sequencer.eth_sqr0 != null)
       eth_seq.start(p_sequencer.eth_sqr0);
    if (p_sequencer.eth_sqr1 != null)
       eth_seq.start(p_sequencer.eth_sqr1);
    if (p_sequencer.eth_sqr2 != null)
       eth_seq.start(p_sequencer.eth_sqr2);
  endtask : body
endclass: ethernet_vseq

So I do not necessarily get your point on virtual sequences not being “intended for reuse”.

In reply to abasili:

Typically agents and their sequences are reused because they are implementing functional interface capabilities.
A virtual sequence orchestrates agent sequences.
In another project we find the same interfaces of an old project anc maybe additional functional interfaces. A virtuaöl sequence of the old project does not consider the agent sequences of new interfaces, i.e. we have to implement new virtual sequences.

In reply to chr_sue:

In reply to abasili:
In another project we find the same interfaces of an old project anc maybe additional functional interfaces. A virtuaöl sequence of the old project does not consider the agent sequences of new interfaces, i.e. we have to implement new virtual sequences.

It is clear that not everything is going to be reusable, but even in your example one may argue that if the additional interface can be exercised independently from the other interfaces, than you could still reuse the old virtual sequence as a sub-sequence of a higher level virtual sequence that is including the additional sequence for the new interface.

It seems to me that your basic argument is that virtual sequences are not reusable, while my initial question to user cgales was more trying to get a rationale on why we should not have a virtual sequencer.

So far I haven’t seen any evidence that usage of virtual sequencers will hurt virtual sequences reusability, on the contrary, it separates concerns, performs sanity checks on the existence of the needed handles and also provides a straightforward way to provide configuration access to virtual sequences.

I’d also say that in case you want to reuse a virtual sequence in a sub-env (e.g. vertical reuse from IP to SOC), having a virtual sequencer will not need the test to assign sequencers handles of low level components (which would require knowledge of the sub-env), but could simply integrate the available virtual sequence in an soc vseq and an additional virtual sequencer that can simply integrate the IP level virtual sequencer. This is yet another situation where you would certainly prefer to delegate knowledge of the sub-env to the associated virtual sequencer, rather than having to know all the low level details of your sub-env.

On one hand, using a virtual sequencer to just hold config information (like sequencer handles) seems like buying a car and only using the glove box.

On the other hand, passing a virtual sequencer handle through the start() method avoids having to pass configuration information (like sequencer handles) through another approach, such as uvm_config_db or a user-defined init_start() method.

In reply to chrisspear:

Another benefit of having a virtual sequencer is that you can leverage the uvm runtime phases and have default sequences without the need to clutter your test with unnecessary logic.

Any device will require some clock/reset logic to be started, possibly a register configuration and some shutdown procedure to check for status. I get that you could easily get by without the virtual sequencer and could simply have your test define all those phases and start the virtual sequences in your base class, I just think the extra coding is not worth the effort. I’d rather have this code:


class my_test extends uvm_test;
// ...
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    // ...
    uvm_config_db #(uvm_object_wrapper)::set(this,"env.vsqr.configure_phase", "default_sequence", myseq::get_type());
  endtask : build_phase
endclass : my_test 

Other than having the following:


class my_test extends uvm_test;
// ...
  virtual function void configure_phase(uvm_phase phase);
    super.configure_phase(phase);
    // ...
    my_sequence myseq;
    my_sequence::type_id::create("myseq", this);
    if (!myseq.randomize())
      `uvm_error("", "Randomize failed")
    myseq.start(env.vsqr);
  endtask : configure_phase
endclass : my_test 

By the way, contrary to what has been said in this forum as an argument against using the default_sequence, a default_sequence is randomized by the sequencer through the start_phase_sequence method, which does much more than just starting the sequence (like type checking, rand failure, deprecation warnings, etc.).

Having a virtual sequencer to get access to the configuration allows the user to configure the default_sequence as desired, without the need to embed that logic in the run_phase()

I believe we should use whatever is most suitable for the job and understand what the UVM library offers us already, without reinventing the wheel multiple times.