Jump to content

Issues about config knobs to control sequence constraint


Recommended Posts

Hi experts,

I want to add knobs/config object in virtual sequencer to control sequence constraints. But some errors are not expected, which need your helps. Here is the pieces of codes:

---------------------------------------------------------------------------------

class virtual_sequencer extends uvm_sequencer;
    knobs kb;//assume it configed in an test
endclass

class virtual_sequence extends uvm_sequence;
    `uvm_declare_p_sequencer(virtual_sequencer)

     rand bit[31:0] num;
     constraint cstr { num==p_sequencer.kb.num;}
endclass
---------------------------------------------------------------------------------

The runtime error is that virtual_sequence failed to be randomized because p_sequencer.kb.num is not allocated.Here is the questions:

I guess when virtual_sequence is ran to do a randomization,the p_sequencer is still not assigned and it is null, right?

Any better ways will be appreciated and thanks in advance.

Link to comment
Share on other sites

The sequence/sequencer relationship is established at sequence start (unless the sequencer is explicitly set on the sequence before start), and thus the sequence will be pretty much alone in space at randomize(). One way to deal with this is to write your own sequence start support where the sequencer (and parent sequence) connection is always set up before randomize.

Another way to deal with the config is to have the sequence locate it, instead of pushing config via the sequencer. This way the sequence will be standalone and functional without external config and can be tested/debugged in isolation. In pre_randomize(), try read config from a resource (perhaps produced by another sequence), and/or the command line. If no overrides were found, then fine, have the randomizer produce settings locally. For each override found, turn off rand mode for the corresponding local member and set its value to the external config setting. Also, in post_randomize, check the command line and allow override of "everything" to enable complete control of settings (including illegal values), making it easier inject errors, override or determine constraints, debug etc. It may still be a good idea to establish the sequencer (and parent sequence) relationship before randomize(), in order to have a proper scope when dealing with resources in pre/post/randomize() (an alternative could be unique resource names and global scope, but this will break if you need to duplicate your environment).

Erling

Link to comment
Share on other sites

class myknobs extends uvm_component;

`uvm_component_utils(myknobs)

function new (string name = "myknobs", uvm_component parent = null);

super.new(name,parent);

endfunction

rand int mrange;

constraint mrange_c { mrange inside {[5:10]};}

function void post_randomize();

`uvm_info("MYKNOBS",$sformatf("myknob.mrange=%0d",mrange),UVM_LOW)

endfunction : post_randomize

endclass

sequence:

------------

class otest_seq extends uvm_sequence #(opkt);

myknobs lknobs;

`uvm_object_utils_begin(otest_seq)

`uvm_field_object(lknobs,UVM_PRINT)

`uvm_object_utils_end

...

task body();

`uvm_info(get_type_name(),"sequence RUNNING",UVM_MEDIUM)

void'(uvm_config_db#(myknobs)::get(null,{"uvm_test_top.",get_type_name()},"lknobs",lknobs));

this.print(); //should see handle of "lknobs" printed

test:

-----

myknobs lknobs;

....

virtual function void build_phase(uvm_phase phase);

super.build_phase(phase);

lknobs=myknobs::type_id::create("lknobs",this);

void'(lknobs.randomize());

uvm_config_db#(myknobs)::set(this,"otest_seq","lknobs",lknobs);

uvm_config_db#(int)::set(this,"agnt0","is_active",UVM_ACTIVE);

....

Use +UVM_CONFIG_DB_TRACE to confirm the set/get is working properly...

Once you have the config class in the sequence, you can access it's members as needed.

Jim

Link to comment
Share on other sites

This solution randomize the knobs, not the sequence (ie. it does not have anything rand). Not sure how that is going to help. For example, if the knobs says we're in a certain mode, and the sequence shall be constrained according to the mode, then the knobs must be available at sequence pre/post/randomize(), it is too late to read them from a resource in the sequence body.

Erling

Link to comment
Share on other sites

Use the same technique but place the config file on the sequencer, such that it exists when the sequence is called. This would involve adding the config object to the sequence class (and allocating / randomizing as illustrated previously). The handle to the object can be set in the connect_phase() or end_of_elaboration_phase() using either set/get as illustrated previously, or direct handle assignment from the test. In that situation, the knobs config class would exist when the sequence is created and randomized as illustrated in your original question. Jim

Link to comment
Share on other sites

A better solution which avoids the need to use p_sequencer uses the pre_randomize() method in the sequence:

class otest_seq extends uvm_sequence #(opkt);

rand int delay1;

myknobs lknobs;

function void pre_randomize();

void'(uvm_config_db#(myknobs)::get(get_sequencer(),"","lknobs",lknobs));

this.print(); //option to confirm set/get works as expected

endfunction

constraint d1 {delay1 == lknobs.mrange;} //uses knob in sequence constraint

Link to comment
Share on other sites

This is better, now the sequence reads the config from a resource at the right place. It is just that we're back to the original problem, because get_sequencer() will return null in pre_randomize() unless the sequencer is explicitly set before randomize(), and this (as well as setting the parent sequence if any) can be done in a common base for reuse everywhere you want to start a sequence. With that in place, it shouldn't be necessary to read resources via the sequencer, because the sequence will have a proper scope and can work with resources directly without a helper component.

Erling

Link to comment
Share on other sites

Thanks Erling & cdnmcgrath. Your comments are very helpfull for me.

And to avoid this problem, then I try several ways:

(1) locate the knobs in the sequence or set up the my own p_sequencer before randomization.

I use uvm_top.find("virtual_sequencer") to get the handle of virtual sequencer in pre_randomize(). So the knobs is also got even where ever it is placed.

(2) try a nested sequence,like this:

class virtual_sequence_top extends uvm_sequence;

`uvm_declare_p_sequencer(virtual_sequencer)

task body();

virtual_sequence vseq0;

virtual_sequence vseq1;

`uvm_do_with(vseq0,{vseq0.num==p_sequencer.knobs.num;}) //constraint solved after p_sequencer is set up

`uvm_do_with(vseq1,{vseq1.num==p_sequencer.knobs.num;})

endtask

endclass

Thanks a lot

Edited by IChip
Link to comment
Share on other sites

Why is it you want to have the config settings on the sequencer? What problem does this solve?

Erling

I want to share the config bewteen 2 different type of sequences. How they were executed is based on the config and there may have lots of such sequences.

Thanks

Link to comment
Share on other sites

If num in your sample code is meant to be configurable, why not get hold of it explicitly at point of usage? This can be reduced to a one-liner with support from a common base class for sequences. The sub-sequence could go like this for example:

class MySubSeq ...;

rand int num;

function void pre_randomize();

if (IsConfigInt("N", num)) num.rand_mode(0);

endfunction

endclass

The inherited IsConfigInt() could first check the command line for +N=value, and if not found, try read an int resource named "N" in scope of the sequence. If a value was found in this way, randomization of num is turned off, otherwise num is randomized locally as usual.

The top level virtual sequence could be responsible for coming up with a value for num, for example:

class MyTopSeq ...;

rand int num;

function void pre_randomize();

if (IsUserInt("N", num)) num.rand_mode(0);

endfunction

function void post_randomize();

SetConfigInt("N", num);

endfunction

endclass

The inherited IsUserInt() checks if there a command line value for num, and SetConfigInt() writes the actual num to a resource named "N" in scope of the sequence.

The root component in the environment where the sequences shall run, could be responsible for creating the num resource with default value and scope to be matched by the sequences. This could happen in build or end_of_elaboration, so there will be a self-documenting list of system-wide settings at one place in the environment.

I know this isn't rocket science exactly, but it is simple, explicit, and debuggable, and you can still choose to randomize sequences with inline constraints when this makes sense.

Erling

Link to comment
Share on other sites

If num in your sample code is meant to be configurable, why not get hold of it explicitly at point of usage? This can be reduced to a one-liner with support from a common base class for sequences. The sub-sequence could go like this for example:

class MySubSeq ...;

rand int num;

function void pre_randomize();

if (IsConfigInt("N", num)) num.rand_mode(0);

endfunction

endclass

The inherited IsConfigInt() could first check the command line for +N=value, and if not found, try read an int resource named "N" in scope of the sequence. If a value was found in this way, randomization of num is turned off, otherwise num is randomized locally as usual.

The top level virtual sequence could be responsible for coming up with a value for num, for example:

class MyTopSeq ...;

rand int num;

function void pre_randomize();

if (IsUserInt("N", num)) num.rand_mode(0);

endfunction

function void post_randomize();

SetConfigInt("N", num);

endfunction

endclass

The inherited IsUserInt() checks if there a command line value for num, and SetConfigInt() writes the actual num to a resource named "N" in scope of the sequence.

The root component in the environment where the sequences shall run, could be responsible for creating the num resource with default value and scope to be matched by the sequences. This could happen in build or end_of_elaboration, so there will be a self-documenting list of system-wide settings at one place in the environment.

I know this isn't rocket science exactly, but it is simple, explicit, and debuggable, and you can still choose to randomize sequences with inline constraints when this makes sense.

Erling

Thanks Erling.

In my knobs class, there are lots of config variables used for different sequences.And the knobs need to be visiable in all sequences.

Link to comment
Share on other sites

In my knobs class, there are lots of config variables used for different sequences.And the knobs need to be visiable in all sequences.

Yes, I'd hoped there were more than a lonely num :)

I have many settings too, but they need not be visible in all sequences. If all knobs need to be visible everywhere, I'd suspect there is something unfortunate going on that isn't related to knobs.

Also, if sequences indeed need access to many external settings, then it is always possible to aggregate and get hold of them in one operation. If you're concerned about performance, I'd recommend to fix the problem at one place, instead of distributing caches with direct access.

Erling

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...