Randomizing a UVM_SEQUENCE using a config object from the sequencer upon which it will be run

I have a uvm_sequence subclass with random constraints which need to access some of the runtime behavior knobs present in the config object for the sequencer which will run it:


class my_seq extends uvm_sequence#(my_tr);
my_config cfg;
rand bit foo;
constraint c {
 if( cfg.always_use_foo ) foo == 1;
}
/* ... body(), etc ... */
endclass

My problem is that the sequence needs to be randomized before I send it to the sequencer but it doesn’t know what sequencer it will run on when I randomize it. Normally I would get the config object from the uvm_config_db at the beginning of the sequence’s run phase, but in this case I want to base my random constraint on information on the config object.


my_seq p = my_seq::type_id::create();
assert( p.randomize() ); // <-- can't randomize here because p.cfg hasn't been set, and p doesn't know what sequencer will run it
p.start( some_sequencer );

I know I can just set p.cfg before I randomize it, but I’m looking for some way for this to happen automatically, similar to when I get the config object in body() it can automatically retrieve it from the sequencer on which the sequence is running.

Unfortunately sequences don’t seem to have any kind of pre-run setup which configures the sequencer the way sequence_items do:


start_item(tr);
assert( tr.randomize() ); // <-- tr knows what its sequencer is already because of start_item()
finish_item(tr);

In reply to ndesch:

You can use the uvm_config_db() in the pre_randomize() function of your sequence. Use m_sequencer as the context for the get().


function void pre_randomize();
  if (cfg == null) begin
    if (!uvm_config_db#(my_config)::get(m_sequencer, "", "cfg", cfg)) begin
      `uvm_error("NOCFG", "No config object available");
    end
  end
endfunction

In reply to cgales:

I don’t see how this option works, as m_sequencer would be null; the sequence hasn’t been started on a sequencer yet.

Off the cuff, perhaps you could use set_sequencer() before calling randomize, if, by then you know which sequencer it will use.

not as ideal, but you could use the config db as you mentioned (or p_sequencer) at the beginning of body, and have the sequence randomize itself; slightly less flexible.

In reply to bmorris:

Yes, m_sequencer is null during pre_randomize unless I first call set_sequencer. I think this may be the best solution:


my_seq p = my_seq::type_id::create();
p.set_sequencer( some_sequencer );
assert( p.randomize() );
p.start( some_sequencer );

And then pre-randomize looks pretty much as cgales posted. Thanks, everyone.

I can just add an error message to my sequence to remind me if I forget to use it this way.

In reply to ndesch:

Also use if instead of assert for your randomize; asserts are generally for verifying design constructs, not testbench. also they can be disabled

In reply to ndesch:

Sorry about that. I missed that the sequence didn’t have the sequencer set.

If you have to set something directly in the sequence, my recommendation would be to set the cfg directly (if possible), instead of the sequencer. This would save you the overhead of using the config_db().

What I use in this case is the config in the p_sequencer:


class some_sequence extends uvm_sequence;
  `uvm_declare_p_sequencer(some_sequencer)

  constraint some_constraint {
    if (p_sequencer != null)
      some_field == p_sequencer.cfg.some_field;
  }
endclass

When you start the sequence, just make sure that it gets created on your specific sequencer:


some_sequence seq;
`uvm_create_on(seq, some_seqr)

seq.randomize();
`uvm_send(seq)

Sequences don’t have to be started on the sequencer to be associated with it.

In reply to Tudor Timi:

It is highly recommended to NOT use p_sequencers. The main reason is that it requires you to create a specific sequencer type, which normally isn’t needed. It also requires that a specific sequencer of that type exist in your environment, which limits re-usability.

By not using p_sequencer, your sequence can run on any sequencer that is type-compatible and is therefor inherently more re-usable.

In reply to cgales:

It’s possible to split it: define the sequence without knowledge of p_sequencer and then implement the code to do the auto-setting separately:


class some_sequence extends uvm_sequence;
  my_config cfg;

  // ...
  // keep it reusable

  `uvm_object_utils(some_sequence)
endclass

// more sequences
// ...

In a certain testbench you can define a mixin that handles setting the cfg based on the sequencer:


class set_cfg_mixin #(type T) extends T;
  `uvm_declare_p_sequencer

  function void pre_randomize();
    if (cfg == null)
      if (p_sequencer != null)
        cfg = p_sequencer.cfg;
      else
        `uvm_fatal("...")
  endfunction

  `uvm_object_param_utils(set_cfg_mixin #(T))
endclass


some_sequence seq = set_cfg_mixin #(some_sequence)::type_id::create(...);

Factory overrides should also work. Depending on how many sequences you have and how much of a concern reusability is for you, this is something you could consider.