How do I constrain data within hierarchy at test level?

Hi, I’m new to UVM and have a constraint/general test bench architecture question. I’m wondering how can I override my sequence_item/transaction constraints (that are buried within my hierarchy)? I’m architecting this test bench such that the default constraints buried in the hierarchy have the “good” configuration (such that the DUT receives “good/expected” data). I want to have the atypical/variations on the “good/default” case occur in each test via overriding various constraints. I’m having trouble figuring out how to access these constraints at the test level. Below is the pseudocode of my hierarchy. In this example, how could I constrain “value” to something other than [0:200] at the test level?

class my_data extends uvm_object;
rand bit [15:0] value;
uvm_object_utils_begin(my_data) uvm_field_int(value, UVM_ALL_ON)
`uvm_object_utils_end
constraint value_c {value inside {[0:200]}; }
endclass

class my_transaction extends uvm_sequence_item;
rand my_data data_obj;
uvm_object_utils_begin(my_transaction) uvm_field_object(data_obj, UVM_ALL_ON)
`uvm_object_utils_end
endclass

class my_sequence extends uvm_sequence#(my_transaction);
task body();
my_transaction my_tr;
repeat(10) begin
my_tr = my_transaction::type_id::create(“my_tr”);
start_item(my_tr);
assert(my_tr.randomize());
finish_item(my_tr);
end
endtask: body
endclass

class my_base_test extends uvm_test;
task run_phase(uvm_phase phase);
my_sequence my_seq;

    phase.raise_objection(.obj(this));
        my_seq = my_sequence::type_id::create("my_seq", this);
        assert(my_seq.randomize());
        my_seq.start(my_env.my_agnt.my_seqr);
    phase.drop_objection(.obj(this));
endtask: run_phase

endclass: my_base_test

In reply to Dragewood:

Instead of trying to manage the constraints explicitly, it would be easier to override data from the factory.

Incidentally, I think you have one too many layers of hierarchy. I see no benefit in declaring data as a separate object rather than just putting value directly into the sequence item:


class my_transaction extends uvm_sequence_item;
rand bit [15:0] value;
`uvm_object_utils_begin(my_transaction)
`uvm_field_object(data_obj, UVM_ALL_ON)
`uvm_object_utils_end
endclass

And I’ll once again admonish you not to use the field macros.

In reply to tfitz:

It is a bad approach to implement constraints in the seq_item definition. You should contrain data memebres of the seq_item in the sequence.
If you are using the constraints in the sequence item this constraint might conflict with constraints coming from the sequence.

In reply to chr_sue:

Suppose you want to run one test with value < 100 and another with value > 100. You could have two sequence_items and use the factory to override the sequence_item type and then have one sequence to generate the items. This would let you have a base test that starts the sequence and an extended test that just overrides the sequence_item type and uses super.run to execute the same sequence.

Or you could have two sequences, each of which randomizes the sequence_item using a ‘with’ clause. Then you could have two tests, each of which runs one sequence. If you make one sequence an extension of the other (ie overriding the body() method), then you could still have a base test and an extended test to override the sequence type.

Personally, I’m not a big fan of randomize-with when I can use the factory override. The override is particularly useful when you have multiple fields and multiple constraints that you may want to override.

1 Like

In reply to Dragewood:

As, I understand from your query you wanted to good/default constraints as it is and try to use some variation in constraints when it needed.

You can also achieve your objective by below way.


rand bit [15:0] value;

constraint value_c {
  // Declare string type variable inside top_cfg
  // Overwrite by command line using $value$plusargs.
  if(top_cfg.constraints == "DEFAULT") {
     value inside {[0:200]};
  } else {
     // What ever you want.
  }
}

Thanks,
Harsh

In reply to tfitz:

Thanks for the suggestions. I went with the “my_tr.randomize() with { [list all the constraints] }” approach, such that I can extend a sequence/test pair each time I add a test. This worked. One drawback of this approach is, in the future if I add several tests, and then need to add/modify a constraint in the base sequence, I’ll need to make that same change in every extended sequence.

In reply to Dragewood:

In reply to tfitz:
One drawback of this approach is, in the future if I add several tests, and then need to add/modify a constraint in the base sequence, I’ll need to make that same change in every extended sequence.

That’s a pretty big drawback and is the main reason I recommended against using randomize-with. Since you have to manage test/sequence pairs anyway, you could just as easily manage test/item pairs, where the test uses the factory to choose the properly-constrained sequence item type. Then, if you have to modify a constraint in the base item type, it will automatically get picked up by all the others and you don’t have to make the same edit in a whole bunch of sequences. That’s kind of the whole point of object-oriented programming, by the way.

Good luck,
-Tom

In reply to harsh pandya:

In reply to Dragewood:
As, I understand from your query you wanted to good/default constraints as it is and try to use some variation in constraints when it needed.
You can also achieve your objective by below way.


rand bit [15:0] value;
constraint value_c {
// Declare string type variable inside top_cfg
// Overwrite by command line using $value$plusargs.
if(top_cfg.constraints == "DEFAULT") {
value inside {[0:200]};
} else {
// What ever you want.
}
}

Thanks,
Harsh

Where/how does top_cfg get set? I assume this constraint is inside the sequence_item?

In reply to sjohnson-aril:

No, constraints written inside sequence.
Because, generally dut configuration register/field is not part of vip.
So,related field and constraints written inside sequence.

And here top_cfg set from top_base_test and get inside sequence.



//build phase of top_base_test
uvm_config_db#(top_configuration)::set(null, "*", "top_cfg", top_cfg);

//sequence extends from uvm_sequence
function new(string name= "test_seq");
  uvm_config_db#(top_configuration)::get(null, "", "top_cfg", top_cfg);
endfunction : new


Thanks,
Harsh