Let’s say we have a class defined as:
class basePacket;
rand bit [15:0] frame;
endclass;
Such class could be part of a VIP library, thus can’t be modified. The basePacket could contain much more random variables which depend on frame variable (through constraints). It means, frame must be calculated during randomize() call, not in post_randomize().
We would like to extend this class to our specific application in order to make it more user-friendly. Let’s say that in our application frame contains address on MSBs and data on LSBs. Intentionally, we don’t want to define new variables like address and data in the derived class in order to avoid coherency problem between frame, address and data (ie. a UVM driver/monitor from the VIP uses only the base class and is not aware of fields from the derived class to be updated). Thus, the preferred method would be to use function calls, like
class myPacket extends basedPacket;
function bit[7:0] address();
return frame[15:8];
endfunction
function bit[7:0] data();
return frame[7:0];
endfunciton
endclass
It allows to avoid coherency issue, but has a problem with randomization: the randomize() call in the sequence below will fail ( 0==3 and 0==5 can’t be satisfied).
task body();
myPacket packet = new();
if ( ! packet.randomize() with {
data() == 3;
address() == 5;} )
`uvm_fatal("RANDOMIZE_FAILURE", "mySequence::body()~myPacket")
endtask
It’s because, according to standard, chapter “18.5.12 Functions in constraints”:
Functions shall be called before constraints are solved, and their return values shall be treated as state variables.
Is there are any universal methodology for this kind of problems?