Generate unique elements in an array

What would be the constarint to generate unique elements in an array. For ex -
bit [3:0] data [15:0];
I want all the 16 elements of this data aray to be assigned unique values. In other words, this array will contain all values from 0 to 15. Since the order has to be random,

constraint data_values { foreach(data[i]) data[i+1] > data [i] ;}

is not something I want.

See http://go.mentor.com/ready-for-systemverilog-2012 for an easy way to do this in SystemVerilog 2012. Otherwise there’s a link to a solution using the existing syntax.

As Dave mentioned, SV 2012 has this natively in the language. Make sure your tool supports it, if not start demanding it - now it is an approved standard (IEEE SA - IEEE 1800-2012)

In SV 2009 version you will need to use the foreach, perhaps you can do:

constraint data_values { foreach(data[i]) data[i+1] != data [i] ;}

A practical example is available at: Verification Course Blog | VeriLog Courses | CVC

Good Luck
Ajeetha, CVC

In reply to dave_59:

@dave_59 - I am aware of the solution with the existing syntax. But the scenario there was different. At that time, I wanted a unique address value for ‘every’ new sequence item that was generated. So we choose to have a static queue that has one instance and is visible to all sequence items.

Now, I want unique elements in a multi-dimensional array ‘contained within a single’ sequence item. The unique constraint addresses this situation (but the above solution does not). How were you dealing with this situation upto now?

In reply to ssingh:

Looking at your original question, you can simply use the array shuffle method if all you want is a random ordering of an exhaustive set of values in an array;

class A;
bit [3:0] data[16]; // do not declare rand
rand int other_data;
function void pre_randomize;
  data.shuffle();
endfunction
function new;
  for(int i=0;i<data.size();i++) data[i]=i;
endfunction
endclass

Now a call to a_h.randomize() will give you a random shuffling of the array.

I don’t understand your last question. Can you give an example with all the data declarations and show what you would like to see as the set of values that randomize() should generate?

In reply to dave_59:

@dave_59 - If ‘data’ is not declared as rand, will pre_randomize be called? In the above case, we can shift the initialization and shuffle part to post_randomize.


class A;
rand bit [11:0] data_in [99:0];
constraint data_range { foreach(data[i]) data_in[i] inside {[0:500]}; }
endclass

I want to make sure that all the 100 elements in ‘data_in’ are unique 12 bit values.

In reply to ssingh:

‘data’ does not need to be declared as rand. pre_randomize() is called when you call randomize() on that object regardless of it having any rand variables. If the object being randomized contains a class variable referencing another class object, that class variable must be declared rand to call the pre_randomize() method of the other class.

In reply to dave_59:

Oops! Thanks! Please let me know about the solution to the problem.

In reply to ssingh:

I still don’t understand your last question. Can you give an example with all the data declarations and show what you would like to see as the set of values that randomize() should generate?

In reply to dave_59:

Please refer to the class declaration in post #6. I can use unique identifier now. I was just interested in the approach adopted earlier. I don’t want to do something like data_in[i] = i and then shuffle.

Following constraint will generate the unique values in array.

module unique_array;
class data_cl;
  rand bit [7:0] data[];
  constraint data_values { foreach(data[i]) 
                             foreach(data[j])
                               if(i != j) data[i] != data [j] ;} 
endclass

  data_cl cl_ob;

  initial
  begin
     cl_ob = new();
     cl_ob.data = new[20];
     assert(randomize(cl_ob));
     foreach(cl_ob.data[i])
      $display("%d",cl_ob.data[i]);
  end
endmodule

In reply to vishnuprasanth:

@vishnu - thanks

SSingh,
I see my previous solution using old SV-2005/9 syntax doesn’t fully meet your requirement, sorry spoke too fast perhaps. However when you say:

Since the order has to be random,
constraint data_values { foreach(data[i]) data[i+1] > data [i] ;}
is not something I want.

Why not use shuffle in post_randomize? Point is - you are asking the solver to go and do all the hardwork by writing some fancy expression to simply do:

  1. Fill all values from 0…15
  2. Shuffle the order

IMHO - this is bad for performance and an unnecessary overkill of available horse power. Sure Vishu’s code looks nice, but hopefully you agree it is not so intuitive. We generally recommend KISS principle (KISS principle - Wikipedia). Consider the below code - this would achieve what you intended in a simpler manner - relatively speaking. But - you do have a fancy code choice, thanks to Vishu!

constraint cst_ascend { foreach(data[i]) 
                               i > 0 -> 
                    (data[i] > data [i-1]) ;} 


  function void post_randomize;
    this.data.shuffle;
    $display("%p",this);
  endfunction : post_randomize

The above code scales nicely for 1 - to - SIZE-1 array size. Since it uses one foreach loop in constraints, tools will have it easy and is easier to read/maintain/explain to others.

Nice topic, BTW.

HTH
Ajeetha, CVC

In reply to ajeetha:

@ajeetha - Please refer to post #6. Now I don’t want to fill the values from 0 to 15. My range of values is now much bigger. I agree with you though.

In reply to ssingh:

Hi Dave,

Reading the above link for I tried to generate unique random numbers, but the following code doesn’t seem to work, dont know why?

package env_constants;
  localparam PDW_INDEX_BITS = 6;
  localparam DCD_INDEX_BITS = 3;
endpackage: env_constants


module dcd2pdw;

  import env_constants::*;

  class A;
     rand bit[PDW_INDEX_BITS-1:0] dcd2pdw[bit[DCD_INDEX_BITS-1:0]];
     constraint uniq {unique {dcd2pdw};}
  endclass:A

 initial begin
   A a;

   a = new();

   assert(a.randomize()) else begin
      $display("Randomization failure...");
   end

   $display("size of array: %d",a.dcd2pdw.size());
   foreach(a.dcd2pdw[i]) begin
     $display("PDW: %0d, at dcd_id: %0d",a.dcd2pdw[i],i);
   end 
 end

endmodule: dcd2pdw

Size of array is getting printed as: zero. What is the problem here ?

In reply to mseyunni:

You have declared dcd2pdw as an associative array whose index type is bit[DCD_INDEX_BITS-1:0]. You cannot randomize the size of an associative array. Did you mean to declare a fixed sized array?

rand bit[PDW_INDEX_BITS-1:0] dcd2pdw[DCD_INDEX_BITS-1:0];

In reply to dave_59:

Ok I have declared it as an associative array.
I actually need 2**DCD_INDEX_BITS as my array depth. But if I say [DCD_INDEX_BITS-1:0], the array depth would be 2 items only.

I also need to generate something like:

DCD PDW
0 3, 4, 5, 6, 8, 9, 49, 50
1 51, 63, 54, 45, 0, 1, 33, 11

So for each DCD I neeed to generate 8-pdw values, such that the PDW values for a DCD should not be repeated for any other DCD.

In reply to mseyunni:

rand bit[PDW_INDEX_BITS-1:0] dcd2pdw[2**DCD_INDEX_BITS];

In reply to dave_59:

Hi Dave,

Thank you. How can I generate 8-PDW values for each DCD such that no PDW value is repeated between two DCD’s ? (explained above).

Thanks in advance,
Madhu

In reply to mseyunni:

class A;
     bit [PDW_INDEX_BITS-1:0] used_dcd2pdw[$][2**DCD_INDEX_BITS];
     rand bit[PDW_INDEX_BITS-1:0] dcd2pdw[2**DCD_INDEX_BITS];
     constraint uniq {unique {dcd2pdw,used_dcd2pdw};}
     function void post_randomize;
	used_dcd2pdw.push_back(dcd2pdw);
     endfunction
   
endclass:A

The uniq constraint will eventually fail after so many calls to randomize(). I’ll leave it to you to figure out what to do when it does.