Use `uvm_do macro from uvm_test


I know there’s good advice against using uvm_do macros but it saves so much writing,
1 line instead of a page: create, randomize, start, finish…

I use it in sequences without a problem. However when using it in a testcase you get a typecast error.
This is because `uvm_do_on_with_priority() the base macro has this line of code (UVM v1.2):

   else __seq.start(SEQR, this, PRIORITY, 0);

The 2nd start() argument here is a parent_sequence pointer, this is a problem because
it refers to uvm_test when called from testcase which is not a uvm_sequence type.
Given that this argument is not required, why has it been specified as this. If we used null then we don’t assume the macro is
called from a sequence, would’nt that be better?

In reply to DamianS:

When not using macros, you are supposed to explicitly pass “this” from parent sequences when calling start. This creates a sequence hierarchy which enables features like killing a hierarchy of sequences, child-to-parent-sequence callbacks, and targeting specific sub-sequences for config_db sets or factory overrides. It also is used by some debug tools for transaction, relationship visualization in waveforms.

When starting a sequence from a test, there is no parent sequence so you leave the second field null to denote it is a root sequence running on the sequencer.

w.r.t. to `uvm_do(), I think the point is that it fills in the “this” for you, so you don’t have to remember to do it. If null was the default, then you’d still have to remember to do it.

If you want to use macros, you’re free to create your own macros that work the way you want.

In reply to warnerrs:

Hi thanks for the explanation, I appreciate it.
Still it’s disappointing that it doesn’t work from uvm_test, calling a sequence from a testcase is probably the most common action in verification… Not having an concise way to do that is unfortunate.

I did try rewriting it but encountered additional problems. Seq.create() must be used instead of create_item, this requires knowledge of the sequence type. We also need to know where the macro was called from. Is it possible to access this information without a reflection mechanism?

Here’s an attempt at it but maybe a lost cause. start_item/finish_item will give compile error when used in uvm_test.

  1. 1st branch uses create, randomize, start and the
  2. 2nd branch uses create_item, randomize, start_item, finish_item

`define uvm_do_from_test(SEQ_TYPE, SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS) \
   begin \
   uvm_sequence_base __seq; \
     if (this.get_full_name() == "uvm_test_top") begin \
       SEQ_OR_ITEM = SEQ_TYPE::type_id::create(\"SEQ_OR_ITEM\"); \
       if (!(SEQ_OR_ITEM.randomize() with CONSTRAINTS)) \
          `uvm_fatal(get_type_name(),$sformatf("Sequence randomization failed")) \
       SEQ_OR_ITEM.start(SEQR); \
     end else begin \
       `uvm_create_on(SEQ_OR_ITEM, SEQR) \
       if (!$cast(__seq,SEQ_OR_ITEM)) start_item(SEQ_OR_ITEM, PRIORITY); \
       if (!(SEQ_OR_ITEM.randomize() with CONSTRAINTS)) \
          `uvm_fatal(get_type_name(),$sformatf("Sequence randomization failed")) \
       if (!$cast(__seq,SEQ_OR_ITEM)) finish_item(SEQ_OR_ITEM, PRIORITY); \
     end \

In reply to DamianS:

This is because the test will not generate seq_items. It starts a sequence on a sequencer.
If the sequence is a virtual sequence it starts sequences in the agents. These (local) sequences use `uvm_do or start_item/finish_item.

In reply to chr_sue:

I think I understand that. Do you believe it’s possible to write a macro that takes a sequence or sequence_item and is called from a sequence or somewhere else e.g. uvm_test?

It needs to accept either a SEQ_OR_ITEM

  1. a sequence : calling __seq.create, randomize, __seq.start
  2. sequence_item : calling **__seq.**create_item, **__seq.**start_item, randomize **__seq.**finish_item

In UVM1.2 the selection is done using $cast(__seq, SEQ_OR_ITEM)

It can be called from either

  1. sequence
  2. elsewhere e.g. test case

Here’s another attempt, unfortuately it doesn’t compile due to

Access to protected member ‘create_item’ in class ‘uvm_sequence_base’ is not allowed here.

`define uvm_do_from_test(SEQ_TYPE, SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS) \
   begin \
     uvm_sequence_/base __seq; \
     uvm_object_wrapper w_; \
     w_ = SEQ_OR_ITEM.get_type(); \
     if (!$cast(__seq,SEQ_OR_ITEM)) begin \
         $cast(SEQ_OR_ITEM, __seq.create_item(w_, SEQR, `"SEQ_OR_ITEM`")); \
         SEQ_OR_ITEM.start_item(SEQ_OR_ITEM, PRIORITY); \
     end else SEQ_OR_ITEM = SEQ_TYPE::type_id::create(`"SEQ_OR_ITEM`"); \
     if (!(SEQ_OR_ITEM.randomize() with CONSTRAINTS)) \
         `uvm_fatal(get_type_name(),$sformatf("Sequence randomization failed")) \
     if (!$cast(__seq,SEQ_OR_ITEM)) SEQ_OR_ITEM.finish_item(SEQ_OR_ITEM, PRIORITY); \
     else __seq.start(SEQR, __seq, PRIORITY, 0); \

In reply to DamianS:

I’m not sure if it is a good approach to correlate a sequence/seq_item to a test.
The test is a component which belongs to the testbench and ia created at runtime 0 and exists until the end of the simulation. A sequence/seq_item is a transient object which will be created at any time and disappears after it has been completed. A test does not execute a sequence. It starts only one or more sequences. A sequence will be executed by a sequencer.

In reply to DamianS:

I think the UVM macros confuses the two concepts of sequences and sequence_items. (probably leftover from OVM)

A sequence is a handle to a procedural body of code (hence the body() method). See the functon pointer OOP design pattern). There is no arbitration required for sequences.

A sequence_item is a transaction of data that you want dest to a driver. Arbitration gets manages by a sequencer. You can start sequences from components like the test, and from other sequences.

In reply to dave_59:

Ok thanks for your replies.

So, do you think it’s possible to rewrite the macro accepting only a sequence, removing the SEQ_OR_ITEM complication. I still need to identify if the pointer to parent sequence is null or this, i.e. if it’s being called from a sequence (this) or elsewhere (null).

Or is it simply best to avoid the macros. Test writer must use create/create_item, randomize, start/start_item, finish_item.

The problem with the latter is the code mass/read ability, we have some verification environments with 100K+ lines of code and there’s a lot of boiler plate code required to launch a sequence that’s begging for some kind of wrapper… ?

In reply to DamianS:

One approach is to make a virtual sequence for every test. This will allow you to use the macros inside of the test sequence, and you only need to start one virtual sequence from the test class itself, which is very simple. You can also use a single base uvm_test and use overrides of the test sequence in each test extended from uvm_test.

In reply to cgales:

Thanks cgales, I think this could be a nice workaround.