Randomize a queue of objects in systemverilog - class

I have seen techniques used to randomize queues of integral values, but I am having difficulties with queues of objects. An example code:
class int_queue_class;
rand int x[$];
function void print;
$display($sformatf("Size: %0d", x.size()));
foreach(x[i])
$display($sformatf("x[%0d] = %0d", i, x[i]));
endfunction : print
endclass : int_queue_class
class obj;
rand int value;
endclass : obj
class obj_queue_class;
rand obj x[$];
function void print;
$display($sformatf("Size: %0d", x.size()));
foreach(x[i])
$display($sformatf("x[%0d] = %0d", i, x[i].value));
endfunction : print
endclass : obj_queue_class
program top();
initial
begin
int_queue_class iqc;
obj_queue_class oqc;
iqc = new();
iqc.randomize() with {x.size() == 5; x[2] == 5;};
iqc.print();
oqc = new();
oqc.randomize() with {x.size() == 5; x[2].value == 5;};
oqc.print();
end
endprogram : top
It seems that the same approach cannot be used when it comes to objects. Is there a way to randomize such a queue while keeping the ability to simultaneously constrain the queue size and individual element fields? The ideas that I've come up with that use pre/post randomize methods all loose at least one of those two options.

Randomization doesn't create new objects. When you randomize the size of a queue it will create new entries which contain the default values, which for class types is null.
The only thing you can do is to pre-allocate objects inside your queue, to have some to spare. You won't be able to randomize more objects than you initially created, so this limits the value of the queue's size:
class obj_queue_class;
rand obj x[$];
local const max_size;
constraint legal_size {
x.size() <= max_size;
}
function void pre_randomize();
x = new[max_size];
foreach (x[i])
x[i] = new();
endfunction
function void print();
$display($sformatf("Size: %0d", x.size()));
foreach(x[i])
$display($sformatf("x[%0d] = %0d", i, x[i].value));
endfunction
endclass
I've made max_size a local constant value, but it can be something that you can set from outside, a package parameter, etc.
In pre_randomize() you instantiate max_size objects. Since x.size() is always smaller or equal to max_size, whatever value gets randomized will lead to a queue that contains object instances and no null. The rest of the objects will get thrown away when the queue shrinks.

Related

System Verilog: randomization per instance at initial

I want to simulate a multiple latches with random starting conditions, but I want each instance to have its own initial condition
This is a simplified version of the code. I would like the value to be different in both of the instances, without changing the interface
module random_usage();
integer addr1;
real data;
initial begin
addr1 = $urandom();
data = $urandom();
$display("addr1=%0d, data=%0d",addr1,data);
end
endmodule
module tb();
integer seed = 1;
random_usage a();
random_usage b();
initial
begin
#5;
seed = $get_initial_random_seed();
$display("seed=%0d", seed);
end
endmodule
What I have seen so far:
Instance specific $urandom in system-verilog
solution doesn't work in initial condition, or even when you feed the same clock
https://www.systemverilog.io/randomization
I have modules, so i don't know how to apply the solutions, or even if it will work here
https://www.reddit.com/r/FPGA/comments/jd0dmu/system_verilog_force_randomization_different_per/
seems to be the same question, and there is no straight solution, but the last person gave a VCS flag. I am using VCS, but i have not been able to get the flag to work
The IEEE 1800-2017 SystemVerilog LRM section 18.14.1 Random stability properties says rather naively that each instances gets seeded with the same initialization seed.
Most tools now have a switch changing that behavior by using the hierarchical path name to seed each instance. Some tools have even made that the default. If you want tool independent behavior, you can use this package:
package seed_instance;
int initial_seed = $urandom;
function automatic void srandom(string path);
static int hash[int];
int hash_value = initial_seed;
process p = process::self();
for(int i=0;i<path.len();i++)
hash_value+=path[i]*(i*7);
if (!hash.exists(hash_value))
hash[hash_value] = hash_value;
else
hash[hash_value]+=$urandom; // next seed
p.srandom(hash[hash_value]);
endfunction
endpackage
module random_usage();
integer addr1;
real data;
initial begin
seed_instance::srandom($sformatf("%m"));
addr1 = $urandom();
data = $urandom();
$display("1: addr1=%0d, data=%0d",addr1,data);
end
initial begin
seed_instance::srandom($sformatf("%m"));
addr1 = $urandom();
data = $urandom();
$display("2: addr1=%0d, data=%0d",addr1,data);
end
endmodule
module tb();
integer seed = 1;
random_usage a();
random_usage b();
endmodule

How to make randomize redo until get the number I want?

A basic sequence class for randomize reset below:
class random_reset;
rand int rst_period;
constraint rst_range { rst_period inside {[1:100]}; }
task random_system_reset (
ref reg rst,
ref reg clk);
begin
rst = 1;
repeat (rst_period) #(posedge clk);
rst = 0;
end
endtask
endclass
However, I test need at least 6 clocks. Got anyway to make sure when I call this class, will get the random value bigger than 6?
Assuming you want to keep your original constraint as-is, but in some circumstances, you need to enforce the period to be 6 or more, you can use randomize() with:
random_reset rr = new();
initial rr.randomize() with { rst_period >= 6; };
For this rr object, rst_period will be between 6 and 100.
Refer to IEEE Std 1800-2017, section 18.7 In-line constraints—randomize() with.

Way to have a function like urandom_range(); which will return unique values?

I want to have a urandom_range(); which will not repeat a value once its picked in a simulation ? If it has exhausted its supply of 'available' numbers, then perhaps it can repeat .
Is there any keyword in systemverilog which will help quickly to get around this ?
Not a SV expert here so an example would really help! Thanks
randc does exactly this. (cyclic randomization)
class A;
randc bit[7:0] m;
endclass
Each time you call randomize() on the same object, it will not repeat value for m until all possible values have been given.
Simulators have limits on how large the cyclic value can be, but the standard requires a minimum of 8-bits. If you have a larger value, then you can use the inside operator.
class A;
rand bit[23:0] r;
bit [23:0] list[$];
constraint c { !(r inside {list}); }
function void post_randomize();
list.push_back(r);
endfunction
endclass
If you really expect to cycle through the list, it might be simpler to build the list first, and then shuffle through the list.
bit [7:0] list[20];
for(int i=0;i<20;i++) list[i] = i+10; // range 10-29
list.shuffle();
// cycle through list[0] ... list[29]
list.shuffle();
// cycle through list[0] ... list[29]
You can declare a variable with randc identifier. This is called 'cyclical random' and will ensure exactly what you are requiring.
Note: This requires a license that supports randomization and random variables. Most commercial simulators do provide this but at a higher cost. If you are constrained by this and need to only use the system calls - $urandom or $urandom_range, I would implement something like a queue that tracks all the values returned.
function automatic void find_unique_num();
int c;
int vals[$];
bit found;
do begin
c = $urandom_range(10, 1);
foreach(vals[i])
if (c == vals[i]) found = 1;
end
while (!found);
vals.push_back(c);
return c
endfunction

What are the common and good usage of pre_randomize() and post_randomize() in systemverilog?

How can I change/add constraint or constraint_mode in pre_randomize()?
I know I can overwrite results in post_randomize and I can call rand_mode on and off in pre_randomize, but I am looking for some additional functionality especially related to constraints.
pre_randomize is generally used to set some pre-conditions before object randomization. Here one can print the results of previous randomization, set some variables on which the constraints are dependent etc.
As you mentioned, pre_randomize can be used to set rand_mode(0) for any variable. It can be used to manipulate constraints as well.
post_randomize is used to manipulate some variables like ECC check, print randomization result, manipulate some non-random fields based on existing randomization etc.
One another usage of post_randomize is to generate 'x' or 'z' in randomization process. The randomization by default generates 0 and 1 known values only. But one can use the existing randomized variable to generate x/z values also.
Here is a dummy example of what we can do in pre_randomize and post_randomize functions. Here, depending on non_rand_var, we can enable/disable the constraint mode and set the rand mode of any variable. In the post_randomize function, one can overwrite 'my_x' variable byt 'x' or 'z'.
class A;
int non_rand_var;
rand int rand_var;
rand int rand_var2;
rand logic my_x;
constraint c1{non_rand_var==1 -> rand_var=='h5;}
function new(int non_rand_var);
this.non_rand_var = non_rand_var; // set non random variable
endfunction
function void pre_randomize();
if(non_rand_var==5) begin // set randomization mode of rand_var2
rand_var2.rand_mode(0);
c1.constraint_mode(0); // disable constraint
end
$display("In pre randomize, non_rand_var=0x%0x rand_var=0x%0x",non_rand_var, rand_var);
endfunction
function void post_randomize();
// my_x = $urandom_range(0,1) ? 0 : 'x;
my_x = (non_rand_var==1) ? 0 : 'x; // Manipulate my_x to generate 'x' values
$display("In post randomize, rand_var=0x%0x",rand_var);
endfunction
endclass
module top();
A a=new(1);
initial begin
a.randomize();
$display("Initial block:\na.my_x = 0x%0x\na.rand_var=0x%0x\na.non_rand_var=0x%0x\na.rand_var2=0x%0x",a.my_x,a.rand_var,a.non_rand_var,a.rand_var2);
end
endmodule
pre_randomize & post_randomize function can have several uses based on the application.
Here is the list of few usage of those functions.
Both functions can be overridden, and hence it can be possible to modify the randomization behavior with extended class
Turn on/off few random variables
Turn on/off few constraints
Assignments to other nonrandom variable, on which the randomization is dependent
Changing the weight of random variables, based on certain conditions
Typical usage of pre_randomization function is to generate an array of unique values.
class helper;
randc bit [7:0] a;
endclass
class original;
bit [7:0] unique[64];
function void pre_randomize();
helper h = new();
foreach (unique[i])
begin
void'(h.randomize());
unique[i] = h.a;
end
endfunction
endclass

Constraining an entire object in SystemVerilog

I'm trying to constrain an entire object (not just the fields of an object) based on some other object. Here is a stripped down version of my production code:
I have the following class:
class some_class;
bit[7:0] some_field;
bit[3:0] some_other_field;
// this function would do some complex procedural
// operations on the fields of the object
function void do_some_op();
bit[3:0] tmp = some_field[3:0];
some_field[3:0] = some_other_field;
some_other_field = some_field[7:4];
some_field[7:4] = tmp;
endfunction
function some_class some_function(bit some_param);
some_function = new this;
$display("foo"); // this print here to see that method is executed
if (some_param)
some_function.do_some_op();
endfunction
function void print();
$display("some_field = %x", some_field);
$display("some_other_field = %x", some_other_field);
endfunction
endclass // some_class
This class contains some integral fields. It also has a method that does some complex procedural on the fields of that class. In the example I've simplified it. I also have another class that returns a new object on which the operation has been performed.
I have another class that operates with some_class instances. As per Dave's input I have made it create the objects first (as randomize() does not create objects).
class some_shuffler;
rand bit params[];
rand some_class objects[];
constraint size_c {
params.size() == objects.size() - 1;
params.size() <= 10;
};
constraint shuffle_c {
// not allowed by standard
// foreach (params[i])
// objects[i+1].some_field == objects[i].some_function(params[i]);
foreach (params[i])
objects[i+1].some_field ==
objects[i].some_function(params[i]).some_field &&
objects[i+1].some_other_field ==
objects[i].some_function(params[i]).some_other_field;
};
function new();
objects = new[10]; // create more objects than needed
foreach (objects[i])
objects[i] = new();
// initialize first object
objects[0].some_field = 8'hA5;
endfunction // new
function void post_randomize();
foreach (objects[i]) begin
$display("objects[%0d]:", i);
objects[i].print();
$display("");
end
endfunction
endclass
This class has two arrays, one of operations performed and one of the intermediate states. There is an initial object. On this one, some_function is performed and it results in the next object.
This is how I wanted to test it:
module top;
import some_pkg::*;
initial begin
static some_shuffler shuffler = new();
bit rand_ok;
rand_ok = shuffler.randomize() with {
params.size() == 1;
};
assert (rand_ok);
end
endmodule
When trying to constrain the objects directly I immediately get a constraint violation. The simulator seems to try to make the 2 handles equal. This is anyway forbidden by the standard and I'm not doing it anymore (though a compile failure would have been nice). I've unraveled the constraints as suggested by Dave and Greg (I think doing some_function().some_field is non-standard, but it compiles in Questa).
Even now, the foo print does not appear on the command line (some_function() is not getting executed). What I see is that objects[1] contains the initial value (all 0s for both fields).
I can't just generate the list of params and then procedurally randomize the objects for each iteration, because I want to be able to constrain the last object to have a certain value - basically giving the constraint solver the start and the end points and let it figure out the way to get there.
Object vs. object constraints are not allowed in SystemVerilog because they are not integral types. See IEEE Std 1800-2012 § 18.3:
Constraints can be any SystemVerilog expression with variables and constants of integral type (e.g., bit, reg, logic, integer, enum, packed struct).
You can constrain the integral components of class object if the component is a rand (ex obj[1].value == obj[0].value+1;).
Functions are allowed in constraints, but there limitation. See IEEE Std 1800-2012 § 18.5.12 Functions in constraints for full details. Limitations include:
Functions cannot contain output or ref arguments
Functions should be automatic and leave no side effects
The functions arguments have an implicit priority (ex x<=F(y) infers solve y before x)
Circular dependencies will result in an error
Update:
Looks like the only thing truly being randomized is params. The values of some_field and some_other_fieldare calculations. So it makes more sense to move the loop for shuffling into thepost_randomize` function.
constraint size_c {
params.size() == objects.size() - 1;
params.size() <= 10;
};
function void postrand_shuffle();
foreach (params[i])
objects[i+1] = objects[i].some_function(params[i]);
endfunction
function void post_randomize();
postrand_shuffle();
// ... your other post_rand code...
endfunction
SystemVerilog's random constraint solver will work when there is at least one solution. However when the solution space is small and difficult to determine or a long chain, simulator performance drops. For these scenarios it is better move the one-to-one sequential calculations into post_randomize.
A couple of problems with your example code.
Objects must be constructed first before calling randomize(). If
you know the exact size before calling randomize (like in your
example), just new[n] the dynamic arrays constructing each object
element first, and remove the size constraints. If the size will be
random, you need an upper limit constraint on the size. construct the max
number of objects before calling randomize(), and after randomizing the array, the unused objects will be eliminated.
Constraint expressions must be integral. You can do objects[i+1].some_field == objects[i].some_field but the solver cannot manipulate class handles.
The return values of functions are treated as state variables. Move these to post_randomize