After going through the LRM “random stability” section one can easily has this big picture: every thread and object(class instance) has its own local RNG. During some operations like executing $urandom in a thread or calling randomize on an object, the current state of RNG will be sampled and used for that specific operation and RNG will switch to its next state after the operation is completed. This understanding should be enough in most cases to write a constraint random test framework.
My question is that, the LRM is unclear on the RNG update method. We don’t know on what the simulator depends to update the RNG. This causes some confusing in the following code:
…
var_a = $urandom_range( value_1, value_2 );
program register with value of var_a;
…
obj = new;
obj.randomize();
send out the obj to uvm driver
…
We run this code snippet twice. All remains the same except the value of value_1, value_2. And we need to have the same result of obj.randomize.
Here we can have two possibilities:
- Simulator implementation just update the RNG based on the current RNG state only, the obj randomization would generate the same result sine the obj RNG is the same.
- Simulator implementation references some information of $urandom_range randomization to generate next state of the RNG, the obj would have different value.
Other than using save/restore randstate around the $urandom_range call, is there any other good idea regarding maintaining the random stability?
In reply to robert.liu:
After going through the LRM “random stability” section one can easily has this big picture: every thread and object(class instance) has its own local RNG. During some operations like executing $urandom in a thread or calling randomize on an object, the current state of RNG will be sampled and used for that specific operation and RNG will switch to its next state after the operation is completed. This understanding should be enough in most cases to write a constraint random test framework.
My question is that, the LRM is unclear on the RNG update method. We don’t know on what the simulator depends to update the RNG. This causes some confusing in the following code:
…
var_a = $urandom_range( value_1, value_2 );
program register with value of var_a;
…
obj = new;
obj.randomize();
send out the obj to uvm driver
…
We run this code snippet twice. All remains the same except the value of value_1, value_2. And we need to have the same result of obj.randomize.
Here we can have two possibilities:
- Simulator implementation just update the RNG based on the current RNG state only, the obj randomization would generate the same result sine the obj RNG is the same.
- Simulator implementation references some information of $urandom_range randomization to generate next state of the RNG, the obj would have different value.
Other than using save/restore randstate around the $urandom_range call, is there any other good idea regarding maintaining the random stability?
What about using std::randomize(var_a) with {var_a inside {[var_1:var_2}} I don’t remember exactly but there’s a paper about random stability suggesting using this instead of $random and $urandom_range
HTH,
-R
In reply to robert.liu:
The LRM describes random stability in terms of a seeds for threads and objects. It lists the operations that advance the random number generator (RNG) to the next state. The 1800-2017 LRM recently clarifies all the operations that advance the RNG to the next state. The seed drives the value chosen by the constraint solver, not the other way around. So 1) is the correct behavior.
In reply to rgarcia07:
$urandom and std::randomize both use calling thread’s RNG. Would it make any essential difference between them?
-Robert
In reply to dave_59:
In reply to robert.liu:
The seed drives the value chosen by the constraint solver, not the other way around. So 1) is the correct behavior.
I wish it isn’t too late to add this exact example and statement to the LRM so we are clear about the behavior of the code we wrote.
I went through the LRM multiple times for any RNG advance information. These are only related sentences:
Thread stability : When a new dynamic thread is created, its RNG is seeded with the next random value from its parent thread.
Object stability : When an object is created using new, its RNG is seeded with the next random value from the thread that creates the object.
In reply to robert.liu:
I don’t understand why anyone needs to know how the RNG advances, just that it changes value in a stable fashion. It could advance serially like 1, 2, 3, etc. for all that matters to you. The LRM specifically left that to the implementation of the tool. That is why there is no guarantee of random numbers series between one tool and the next (even no guarentee within different versions of the same tool)
The LRM now clearly specifies thread stability of your example such that as long as the RNG is the same value before executing $urandom_range, and you do not insert any other RNG advancing operations before the call to new(), the result of obj.randomize remains the same. If you were to insert any other RNG advancing operation, like another call to $urandom_range, it would get the RNG of what would previously gone to the constructor, and the constructor now gets the next RNG.
In reply to dave_59:
I have a feeling that tool implementation might use multiple RNG values to finish a randomization operation like array.shuffle or std::randomize. So if the size of the array is different, the process randstate at obj = new; could be different.
So I just need to figure out some of details about these operation and put necessary randstate save/restore around them, just like what has been done in UVM BCL.
In reply to robert.liu:
In reply to dave_59:
I have a feeling that tool implementation might use multiple RNG values to finish a randomization operation like array.shuffle or std::randomize. So if the size of the array is different, the process randstate at obj = new; could be different.
Not correct. Each operation advances the RNG to the next state. One goal of random stability is allowing you to make certain changes without disturbing the behavior of other things. It would be incredibly unstable if one call to randomize() or shuffle() affected the next call to randomize() or shuffle().
You should do some experiments to convince yourself this is the case.
In reply to dave_59:
Thanks Dave. I really learnt a lot from your replies. Just now I designed some small tests on Questa and get confirmed about what your said. Here I also have found an interesting thing, about “shuffle”.
function automatic thread_0 ();
int xxx;
int yyy[$] = '{1, 2, 3, 4};
process p = process::self();
$display("YYY:",,,, p.get_randstate());
$display("do $urandom");
xxx = $urandom;
$display("YYY:",,,, p.get_randstate());
$display("do shuffle");
yyy.shuffle; $display(yyy);
$display("YYY:",,,, p.get_randstate());
$display("do shuffle");
yyy.shuffle; $display(yyy);
$display("YYY:",,,, p.get_randstate());
$display("do $urandom");
xxx = $urandom;
$display("YYY:",,,, p.get_randstate());
endfunction: thread_0
After observing the display output, I surprisingly saw that the yyy.shuffle never advanced thread RNG at all. This went beyond my imagination in another direction. However, this still kept random stable. :-)
In reply to robert.liu:
As I said above, the LRM was recently modified listing shuffle() as an operation that advances the RNG.
In reply to dave_59:
Dave, forgive me, I still doesn’t get that thread RNG “use” and “advance” information from there. I knew that change long ago. I thought it was a text editorial fix though. Some text was missing but we still had that intention. In 1800-2017 we will clearly and explicitly see that array.shuffle, randcase/randsequence, scope randomization will be random stable. Does this imply that thread RNG will be used and advanced once? Maybe it’s good to put it inside LRM directly.