Seeding random number generator

Guest
Sorry if this has been covered already. I am developing a testbench
for a design and I want the behavioral models for external devices to
use random parameters within the constraints of the data sheets. I
figured out how to start up a sim with a seed variable in Modelsim,
but now I'm confused about how to use that seed. Say I have two
processes, each controlling one aspect of an ADC. Does each process
have its own random number stream or do they share some global random
number stream? In other words, does each process access a global seed
or do they each maintain their own seeds. I was thinking that a global
seed may be dangerous because it may change in a non-deterministic
fashion. Here is an example with local seeds. The first process
controls the ADC convert to busy timing, the second process controls
the prop delay through a mux. Each takes the initial value for the
seed from the generic for the testbench and then keeps a local copy of
the seed. Is this the right way to do this?

procedure rand_time(
variable seed1, seed2 : inout positive;
min, max : in integer;
result : out time
) is
variable rand : real;
begin
uniform(seed1, seed2, rand);
result := (integer(real(min) + (rand * (real(max)-real(min)) ) ))* 1
ps;
end procedure;

-- ADC busy timing
convert : process
variable s1 : integer;
variable s2 : integer;
variable t6 : time;
variable tconv : time;
variable init : std_logic;
begin
-- Init seeds from generic if not already initialized
if(init /= '1')then
s1 := gSEED;
s2 := s1/2 + 50;
init := '1';
end if;
busy_n <= '1';

wait until falling_edge(convst_n);
if(cs_n = '0')and(shtdn_n = '1')then
rand_time(s1, s2, MIN_T6, MAX_T6, t6);
rand_time(s1, s2, MIN_TCONV, MAX_TCONV, tconv);
busy_n <= '0' after t6;
wait for (tconv - t6);
busy_n <= '1';
dtemp <= din;
end if;
end process convert;

-- mux timing
mux : process (en_n, mux_sel)
variable s1: integer;
variable s2: integer;
variable p : time;
variable init : std_logic;
begin
-- Init seeds from generic if not already initialized
if(init /= '1')then
s1 := gSEED;
s2 := s1/2 + 50;
init := '1';
end if;

rand_time(s1, s2, MIN_DELAY, MAX_DELAY, p);

if(en_n = '1') then
dout <= (others => 'Z') after p;

else
dout(15 downto 12) <= mux_sel after p;
dout(11 downto 0) <= (others => '0') after p;
end if;
end process mux;
 
On Thu, 7 May 2009 09:08:03 -0700 (PDT), hepmehepme@comcast.net wrote:

Sorry if this has been covered already. I am developing a testbench
for a design and I want the behavioral models for external devices to
use random parameters within the constraints of the data sheets. I
figured out how to start up a sim with a seed variable in Modelsim,
but now I'm confused about how to use that seed. Say I have two
processes, each controlling one aspect of an ADC. Does each process
have its own random number stream or do they share some global random
number stream? In other words, does each process access a global seed
or do they each maintain their own seeds. I was thinking that a global
seed may be dangerous because it may change in a non-deterministic
fashion. Here is an example with local seeds. The first process
controls the ADC convert to busy timing, the second process controls
the prop delay through a mux. Each takes the initial value for the
seed from the generic for the testbench and then keeps a local copy of
the seed. Is this the right way to do this?
Yes.

The seeds must be variables, and variables are local to a process,
so each process's randomization has its own seeds and doesn't
interfere with the others. This gives you "random stability";
changing the code and behaviour of one process will not affect
the random number stream you get in the other.

You can get the same effect within a single process, by
having additional seed variables - the randomization state
is entirely held in two variables, and if you want two independent
streams of random numbers within a process you simply create four
variables and use two of them in one bunch of randomize calls,
and two in the other.

procedure rand_time(
variable seed1, seed2 : inout positive;
min, max : in integer;
result : out time
)
Nice; this procedure can go in a package so that it can be
re-used across various processes and even various different
testbenches. However, it's pretty tedious to be forced to
supply the two seeds every time you call the procedure.
I like to create a "partially applied" procedure within
each process that uses it:

process
variable s1, s2: positive; -- seeds for this process
procedure rand_time (
min, max: in integer;
result: out time) is
begin
-- invoke the general-purpose package procedure
-- with the correct seed variables
rand_time(s1, s2, min, max, result);
end;
....... other declarations etc ....
begin
.....
rand_time(3, 10, t); -- no need to specify seeds!
.....

You can even smarten up the syntax to look like a
function call:

impure function f_rand_time
(min, max: in integer)
return time
is
variable t: time;
begin
rand_time(s1, s2, min, max, t);
return t;
end;

so that you can then do

t := rand_time(3, 10);

By the way, stupid question: why does rand_time have
INTEGER minimum and maximum parameters? Wouldn't it
make more sense for them to be of type TIME?

HTH
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
There is no Global seed. The seeds are stored in the seed variables
(s1 and s2 in each process) you have. If you monitored them you'd
notice that they change every time you call the uniform function, and
hence why they are of mode "inout".

So in effect there is no "random number stream" as you put it - just a
formula that gives you a value based on s1 and s2, and s1 and s2 are
changed after each call.

This is useful because it allows repeatability of random streams. You
can check output by seeding the expected output sequence with the same
seeds you initialised the input sequence with.
 
procedure rand_time(
variable seed1, seed2 : inout positive;
min, max : in integer;
result : out time
) is
variable rand : real;
begin
uniform(seed1, seed2, rand);
result := (integer(real(min) + (rand * (real(max)-real(min)) ) ))* 1
ps;
end procedure;

Another point: this function doesnt have the correct probability for
min and max occuring.
If n is the probability for any value occuring, the values of Min and
Max themselves have a probability of n/2. This is because the integer
conversion function rounds to nearest rather than truncate which means
min and max only have a 0-0.5 range each, rather than 0-1.0.

eg:

Min = 0, Max = 3.

result Actual output
before rounding
0.0-0.5 0
0.5-1.5 1
1.5-2.5 2
2.5-3.0 3

Therefore 1 and 2 are each twice as likely to occur than 0 and 3.


I have a very similar procedure for integers, and found the solution
to the problem thus (thanks to who posted the random testing package
the other week :) ):

procedure rand_int( variable seed1, seed2 : inout positive; min, max :
in integer; result : out integer) is
variable rand : real;
variable val_range : real;
begin
assert (max >= min) report "Rand_int: Range Error" severity Failure;

uniform(seed1, seed2, rand);
val_range := real(Max - Min + 1);
result := integer( trunc(rand * val_range )) + min;
end procedure;

This increases the
 
You can simplify this greatly by using the packages that I developed.
They layer on top of procedure uniform.

Packages and usage notes are available at:
http://www.synthworks.com/downloads/index.htm

The presentation focuses on randomizing integers, however,
time values can be generated by multiplying by 1 ns.
To inspire you, your process would be:

Compile packages - directions are in the download

Use SynthWorks.RandomPkg.all ; -- reference package

process
variable RV : RandomPType ; -- declare randomization variable
begin
-- Initialize Seed -- done once
RV.SetSeed( (7, 1) ) ; -- optional if you only are doing one thread
of randomization
...
p := 1 ns * RV.RandInt(MIN_DELAY, MAX_DELAY) ;


Cheers,
Jim
SynthWorks VHDL Training

P.S. We teach randomization plus self-checking, transaction-based
testing,
and verification data structures (linked-lists, scoreboards,
memories),
in our VHDL Testbenches and Verification classes.
See: http://www.synthworks.com/vhdl_testbench_verification.htm
 

Welcome to EDABoard.com

Sponsor

Back
Top