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);
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
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.
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().
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.
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.