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

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

Related

The lifetime of systemverilog method in the program block

`define NUM 100
program test;
function automatic int sum(int n);
if(n <= 1)
return n;
else
return n + sum(n-1);
endfunction
initial begin
$display("sum(%0d)=%d",`NUM, sum(`NUM));
end
endprogram
Above is a piece of code that did a recursive exercise of the sum of incremental integers from 1 to NUM. The result is as expected when I ran it in vcs/xcelium/Questa on the EDA playground.
sum(100)= 5050
However, when I tried to make a modify on the lifetime of function int sum(int n):
program test;
function int sum(int n);
if(n <= 1)
return n;
else
return n + sum(n-1);
endfunction
initial begin
$display("sum(%0d)=%d",`NUM, sum(`NUM));
end
endprogram
The results are quite interesting:
VCS : sum(100)= 100
Cadence xcelium: sum(100)= 5050
Questa : sum(100)= 5050
I went back to the IEEE1800-2017 to find out the default lifetime of methods in the program block but I didn't get the explicit description about that. Would someone help me to clarify this?
https://www.edaplayground.com/x/v3gb
The differences you are seeing are not due to the lifetime of the sum method—it's the order of evaluation of function calls within the expression n + sum(n-1), which is indeterminate. If you reverse the expression to sum(n-1) + n, all tools give the correct result for a function with a static lifetime.
BTW, I strongly recommend that you never use program blocks in SystemVerilog. Use modules instead.

Randomize a queue of objects in systemverilog

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.

Creating array of references to objects.

I am looking for way to simplify my code by creating array array with references to logic variables. This will let me to iterate across array. Here is pseudo code that i am envisioning (This is what i would do in C++). I can not put them in array as they part of RTL located in different places.
logic A;
logic B;
task my_algorithm();
ref logic elements[2] = {A, B}; // This wrong, ref cannot be used in this context
foreach(elements[v]) begin
// Do same work on each element
end
endtask : my_algorithm
Thanks,
You can use inout arguments that get copied in on entry to the routine, and then copied out upon exit from the routine. (use a function instead of a task, unless the routine needs to consume time)
function automatic void my_algorithm(inout logic A, B);
logic elements[2] = {A,B};
foreach(elements[v]) begin
//
end
{<<{A,B}} = elements;
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