Verilog has system function $random ,which can be used to generate random input vectors. With this approach, we can generate values which we wouldn't have got, if listed manually. In this topic I would like to discuss what natural things happening behind $random and how we use it in different manners.
$random() system function returns a new 32-bit random number each time it is called. The random number is a signed integer; it can be positive or negative. The following example demonstrates random generation of signed numbers.
We have seen how to generate random numbers. But the numbers range from - (2**32 -1) to 2 **32. Most of the time, the requirement don't need this range. For example, take a memory. The address starts from 0 to some 1k or 1m.Generating a random address which DUT is not supporting is meaningless. In verilog there are no constructs to constraint randomization. Fallowing example demonstrated how to generate random number between 0 to 10.Using % operation, the remainder of any number is always between 0 to 10.
EXAMPLE: module Tb();
integer add_1;
initial begin repeat(5)
begin #1;
add_1 = $random % 10;
end end
OOPS!...... The results are not what is expected. The reason is $random generates negative numbers also. The following example demonstrates proper way of generating a random number between 0 to 10. Concatenation operator returns only bit vector. Bit vectors are unsigned, so the results are correct as we expected. Verilog also has $unsigned systemtask to convert signed numbers to signed number. This can also be used to meet the requirements. The following example shows the usage of concatenation operator and $unsigned.
The above example shows the generation of numbers from 0 to N.Some specification require the range to start from non Zero number. MIN + {$random} % (MAX - MIN ) will generate random numbers between MIN and MAX.
EXAMPLE: module Tb();
integer add;
initial begin repeat(5)
begin #1;
add = 40 + {$random} % (50 - 40) ;
$display("add = %d",add);
end end endmodule
RESULT:
add = 48 add = 47 add = 47 add = 47 add = 47
Now how to generate a random number between two ranges? The number should be between MIN1 and MAX1 or MIN2 and MAX2.The following example show how to generate this specification.
EXAMPLE: module Tb();
integer add;
initial begin repeat(5)
begin #1;
if($random % 2)
add = 40 + {$random} % (50 - 40) ;
else add = 90 + {$random} % (100 - 90) ;
$display("add = %d",add);
end end endmodule
RESULT:
add = 97 add = 47 add = 47 add = 42 add = 49
All the random number generates above generate numbers of 32 vector. Not always the requirements are 32 bit .For example, to generate a 5 bit and 45 bit vector random number, the following method can be used.
Some protocols require a random number which is multiple some number. For example, Ethernet packet is always in multiples of 8bits,and PCIExpress packets are multiples of 4byts .Look at the following example. It generates a random number which is multiple of 3 and 5.
EXAMPLE: module Tb();
integer num_1,num_2,tmp;
initial begin repeat(5)
begin #1;
tmp = {$random} / 3;
num_1 = (tmp) * 3;
tmp = {$random} / 3;
num_2 = (tmp) * 5;
$display("num_1 = %d,num_2 = %d",num_1,num_2);
end end endmodule
All the above example show that the random numbers are integers only. In verilog there is not special construct to generate a random real number. The following method shows the generation of random real number.
EXAMPLE: module Tb();
integer num_1,num_2,num_3;
real r_num;
initial begin repeat(5)
begin #1;
num_1 = $random;
num_2 = $random;
num_3 = $random;
r_num = num_1 + ((10)**(-(num_2)))*(num_3);
$display("r_num = %e",r_num);
end end endmodule
If you want more control over randomizing real numbers in terms of sign, exponential and mantissa, use $bitstoreal() as shown in example below. For positive numbers, use sgn = 0 etc.
initial begin repeat(5)
begin sgn = $random;
exp = $random;
man = $random;
r_num = $bitstoreal({sgn,exp,man});
$display("r_num = %e",r_num);
end end endmodule RESULTS:
Sometimes it is required to generate random numbers without repetition. The random numbers should be unique. For example, to generate 10 random numbers b/w 0 to 9 without repetition, the following logic can be used.
initial begin index=0;
for(i=0;i<10;i=i+1)
begin arr[i] = i;
ind[i] = 1;
end
for(j = 0;j<10 ;j=j+1)
begin got = 0;
while(got == 0)
begin index = { $random() } % 10;
if(ind[index] == 1)
begin ind[index] = 0;
got = 1;
num = arr[index];
end end $write("| num=%2d |",num);
end
Random number system function has a argument called seed. The seed parameter controls the numbers that $random returns such that different seeds generate different random streams. The seed parameter shall be either a reg, an integer, or a time variable. The seed value should be assigned to this variable prior to calling $random. For each system function, the seed parameter is an in-out parameter; that is, a value is passed to the function
and a different value is returned.
EXAMPLE: module Tb();
integer num,seed,i,j;
initial begin for(j = 0;j<4 ;j=j+1)
begin seed = j;
$display(" seed is %d",seed);
for(i = 0;i < 10; i=i+1)
begin num = { $random(seed) } % 10;
$write("| num=%2d |",num);
end $display(" ");
end end endmodule
The $random function has its own implicit variable as seed when the used is not giving explicitly giving seed. The following example shows that seed = 0 and implicit seed are having same sequence. It means that the implicitly taken seed is also 0.
EXAMPLE: module Tb();
integer num,seed,i,j;
initial begin seed = 0;
for(j = 0;j<2 ;j=j+1)
begin if(j ==0)
$display(" seed is %d",seed);
else $display(" No seed is given ");
for(i = 0;i < 10; i=i+1)
begin if( j == 0)
num = { $random(seed) } % 10;
else num = { $random() } % 10;
$write("| num=%2d |",num);
end $display(" ");
end end endmodule
The system functions shall always return the same value given the same seed. This facilitates debugging by making the operation of the system repeatable. The argument for the seed parameter should be an integer variable that is initialized by the user and only updated by the system function. This ensures the desired distribution is achieved.
EXAMPLE: module Tb();
integer num,seed,i,j;
initial begin for(j = 0;j<4 ;j=j+1)
begin seed = 2;
$display(" seed is %d",seed);
for(i = 0;i < 10; i=i+1)
begin num = { $random(seed) } % 10;
$write("| num=%2d |",num);
end $display(" ");
end end endmodule
Seed is inout port. Random number system function returns a random number and also returns a random number to seed inout argument also. The results of the following example demonstrates how the seed value is getting changed.
EXAMPLE: module Tb();
integer num,seed,i,j;
initial begin seed = 0;
for(j = 0;j<10 ;j=j+1)
begin num = { $random(seed) } % 10;
$write("| num=%2d |",num);
$display(" seed is %d ",seed);
end end endmodule
RESULT:
| num= 8 | seed is -1844104698 | num= 7 | seed is 1082744015 | num= 7 | seed is 75814084 | num= 7 | seed is 837833973 | num= 7 | seed is -2034665166 | num= 7 | seed is -958425333 | num= 5 | seed is 851608272 | num= 2 | seed is 154620049 | num= 1 | seed is -2131500770 | num= 9 | seed is -2032678137
From the above results we can make a table of seed values and return values of $random. If a seed is taken from the table, then rest of the sequence has to follow sequence in table.
Table is as falows for initial seed 0;
| num= 8 | seed is -1844104698
| num= 7 | seed is 1082744015
| num= 7 | seed is 75814084
| num= 7 | seed is 837833973
| num= 7 | seed is -2034665166
| num= 7 | seed is -958425333
| num= 5 | seed is 851608272
| num= 2 | seed is 154620049
| num= 1 | seed is -2131500770
| num= 9 | seed is -2032678137
.
.
.
.
.
table goes on........
In the following example, the seed is 837833973, which is the 4 th seed from the above table.
EXAMPLE: module Tb();
integer num,seed,i,j;
initial begin seed = 837833973;
for(j = 0;j<10 ;j=j+1)
begin num = { $random(seed) } % 10;
$write("| num=%2d |",num);
$display(" seed is %d ",seed);
end end endmodule
RESULTS:
| num= 7 | seed is -2034665166 | num= 7 | seed is -958425333 | num= 5 | seed is 851608272 | num= 2 | seed is 154620049 | num= 1 | seed is -2131500770 | num= 9 | seed is -2032678137 | num= 8 | seed is -1155272804 | num= 7 | seed is -1634874387 | num= 9 | seed is -153856566 | num= 2 | seed is -970066749
From the above example we can come to conclusion that $random is not giving a random number. It is randomizing seed and returning corresponding number for that seed.
Total possible seed values are 4294967295. Is it possible for $random to generate all the seeds? . Lets say ,if the seed gets repeated after 10 iterations, then after the 10 iterations, same values are repeated. So $random is circulating inside a chain of 10 numbers.
The following example demonstrates how $random misses many seeds. I tried to display the seeds between 0 to 20 in the chain formed by initial seed of 0. Results show that total possible seeds are 4294967295 , and number of seeds possible in seed chain are 4030768279 , so we are missing some seeds. Look at the seeds between 0 to 20. Seed == 1 is missing.
EXAMPLE:
module Tb();
integer num,seed,j;
reg [0:31] i;
initial begin i = 0;
seed = 1;
while (seed != 0)
begin if(i == 0)
seed = 0;
i = i + 1;
num = $random(seed);
if(seed < 20 && seed > 0)
$display(" seed is %d after values %d ",seed,i);
end $display(" seed is one after this number of random numbers %0d total numbers available are %d",i,{32'hffff_ffff});
end endmodule
RESULTS:
seed is 10 after values 93137101 seed is 17 after values 307298440 seed is 2 after values 410139893 seed is 12 after values 483530075 seed is 19 after values 592243262 seed is 3 after values 720224974 seed is 11 after values 1342230278 seed is 15 after values 2032553666 seed is 7 after values 2266624778 seed is 13 after values 2362534380 seed is 5 after values 2512466932 seed is 9 after values 2575033104 seed is 16 after values 2988686279 seed is 4 after values 3173376451 seed is 6 after values 3483433473 seed is 8 after values 3547878575 seed is 14 after values 3663208793 seed is 18 after values 3930700709 seed is zero after this number of random numbers 4030768279 total numbers available are 4294967295
Now I tried to simulate with seed== 1 . Its interesting to know that some how the sequence is able to enter this chain which is formed with seed==0 and there is no seed value 1 in this chain and my simulation hanged. So aborted the simulation and parter results show that the initial seed = 1 with enter the chain formed by seed 0.
initial begin i = 0;
seed = 0;
while (seed != 1)
begin if(i == 0)
seed = 1;
i = i + 1;
num = $random(seed);
if(seed < 20 && seed > 0)
$display(" seed is %d after values %d ",seed,i);
end $display(" seed is one after this number of random numbers %0d total numbers available are %d",i,{32'hffff_ffff});
end endmodule
RESULTS:
seed is 10 after values 357336117 seed is 17 after values 571497456 seed is 2 after values 674338909 seed is 12 after values 747729091 seed is 19 after values 856442278 seed is 3 after values 984423990 seed is 11 after values 1606429294 seed is 15 after values 2296752682 seed is 7 after values 2530823794 seed is 13 after values 2626733396 seed is 5 after values 2776665948 seed is 9 after values 2839232120 seed is 16 after values 3252885295 seed is 4 after values 3437575467 seed is 6 after values 3747632489 seed is 8 after values 3812077591 seed is 14 after values 3927407809 seed is 18 after values 4194899725 seed is 10 after values 357336117 seed is 17 after values 571497456 seed is 2 after values 674338909 seed is 12 after values 747729091 seed is 19 after values 856442278 seed is 3 after values 984423990
Verilog also has other system functions to generate random numbers. Each of these functions returns a pseudo-random number whose characteristics are described by the function name.
Following are the Verilog random number generator system functions:
All parameters to the system functions are integer values. For the exponential , Poisson , chi-square , t , and erlang functions, the parameters mean, degree of freedom, and k_stage must be greater than 0 .
$dist_uniform(seed, min, max) is similar to min + {$random(seed)}%(max-min+1),the difference is that in $dist_uniform,the distribution is uniform. $dist_uniform returns a number between min and max. In the $dist_uniform function, the start and end parameters are integer inputs that bound the values returned. The start value should be smaller than the end value.
The mean parameter, used by $dist_normal, $dist_exponential, $dist_poisson, and $dist_erlang, is an integer input that causes the average value returned by the function to approach the value specified. The standard deviation parameter used with the $dist_normal function is an integer input that helps determine the shape of the density function. Larger numbers for standard deviation spread the returned values over a wider range.
The degree of freedom parameter used with the $dist_chi_square and $dist_t functions is an integer input that helps determine the shape of the density function. Larger numbers spread the returned values over a wider range.
EXAMPLE: module Tb();
integer num_1,num_2,seed;
initial begin seed = 10;
repeat(5)
begin #1;
num_1 = $dist_uniform(seed,20,25);
num_2 = $dist_uniform(seed,50,55);
$display("num_1 = %d,num_2 = %d",num_1,num_2);
end end endmodule
Look at the results... Its interesting to note that $random and $dist_uniform have same seed sequence flow also.
As I mentioned ,$dist_uniform(seed, min, max) is similar to min + {$random(seed)}%(max-min+1). "similar" means they have some common functionality. $dist_uniform is having uniform distribution, $random for that range, is also uniformly distributed. Fallowing example ,demonstrates that $dist_uniform and $random are uniformly distributed.
As I mentioned ,$dist_uniform(seed, min, max) is similar to min + {$random(seed)}%(max-min+1). "similar" means they have some difference. The difference is that they generate different sequence.
Till now what we have seen is $random has uniform distribution over integer values. It means that distribution should be uniform across all the bits in 32 bit vector also. The following example shows that bits positions 2,3,4,11,12,13 have equal probability of getting 0. For demonstration I showed some indexes only. Try out rest of them and see that results is same for all the bis.
The distribution is uniform for system function $random. Suppose if the requirement is to generate random numbers for more than one variable, and all the variables should have uniform distribution, then use different seeds for each variable. Otherwise distribution is distributed on all the variables as overall. But for lower bits, the distribution is same as shown in example.
Use system time as seed, so the same TB simulated at different times have different random sequences and there is more probability of finding bugs. The following is c code useful in PLI to get system time in to verilog.
#include <stdio.h>
#include <time.h>
char *get_time_string(int mode24);
int get_systime() {
time_t seconds;
seconds = time (NULL);
return seconds;
}
Verilog 1995, every simulator has its own random number generation algorithm. Verilog 2001 , The standard made that every simulator has to follow same algorithm. So the same random number sequence can seen on different simulators for same seed.
Don't expect that the same sequence is generated on all the simulators. They are only following same algorithm. The reason is, race condition. Look at the following example, both the statements num_1 and num_2 are scheduled to execute at same simulation time. The order of execution is not known. Some simulators take num_1 as the first statement to execute and some other num_2 .If the TB is built without any race condition to $random function calls, then the same random sequence can be generated on different simulators.