Generate unique elements in an array

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.

In reply to dave_59:

Hi Dave, i have a new question that is related but it is a little different. I haven’t found any solution in the forum so maybe you can help.
What i want is to generate an array with unique ranges. This is typically useful for addressing ranges that are not overlapping. What is the best and efficient way to do that?
In a paper of the DVCon from Mentor i have seen: http://proceedings.dvcon-europe.org/2014/presentations_and_papers/T5_1_paper.pdf

There is the following code:



rand bit [31:0] start_addr;
 rand bit [5:0] length;
 bit [31:0] start_end_addr_hash [bit[31:0]];
 constraint c { //Generate Non-Overlapping address ranges
    if (start_end_addr_hash.num()) {
       foreach (start_end_addr_hash *) {
             if (i >= length ) {
                !(start_addr inside {[i-length+1 : start_end_addr_hash [i]]});
             } else {
                 !(start_addr inside {[0 : start_end_addr_hash [i]]});
             }
       }
    }
  length == 6'h10;
 }
 ...
 start_end_addr_hash [start_addr] = start_addr + length - 1;


Is that the best way? Besides, it is not clear for me where i have to put the code.


 ...
 start_end_addr_hash [start_addr] = start_addr + length - 1;

Is it in post_randomization?

Another option is using only random variables. The advantage is that the user can define the start and length by himself for each non overlapping part ([i]notice that it uses a dynamic array and not only one value for start_address and lengt*h). I think the previous example doesn’t allow you to do that because is all coded in the hash iteratively.
Besides, if you do constraint “length[j]==1” then this code generates a vector with unique values in the start_address. That means, this somehow supersedes the codes above at cost of efficiency and memory.


rand bit [31:0] start_address[]; //notice that it is a dynamic array
rand bit [5:0] length[]; //dynamic array, to specify many different ranges

constraint c { 
start_address.size()==length.size();
if (start_address.size()) {
  foreach (start_address[j])  {//j is the chosen start address  
           // end_address [j] == start_address[j] + length[j] - 1;
    foreach (start_address[i])  {//i is the chosen start address 
       if (i!=j) {
            
            ///Constraint start_address (first byte of the buffer)
         if (start_address [j]  >=  length[i] ) {
           !(start_address[i] inside {[start_address [j] - length[i]+1 : start_address [j]+length[j]-1  ]}) ;
         } else {
           !(start_address[i] inside {[0 : start_address [j]+length[j]-1 ]});
         }
       } //end if i!=j
        
     } //end foreach i
   } //end foreach j
 }  //end if

} //end contraint c

What do you think about the implementations?

Thanks in advance

In reply to Jonathan_Alvarez:

In the paper’s example, the constraint about the length (i.e. length==6’h10) is just for demonstration, you can remove that to get the variable length you want. In reality length limitation constraints do exist. And yes you can add the following line in any procedural block after a successful randomization attempt (post_randomize is a good place).


  start_end_addr_hash [start_addr] = start_addr + length - 1; 

Back to the debate about performance, the introduced example (second one) shall generate all address ranges upfront. Although I am fan of late-randomization (randomize at the right time to take the DUT and testbench states into consideration), this is not just the problem I see with the second code. The second code has a nested foreach loops, this is a solver performance overkill, especially if size of the arrays are large enough. An interesting exercise is to measure the time the solver will solve the second example with array sizes of 100 and above.
The first code (code in the paper) kind of escape from this problem by only randomizing a single attempt at a time, then saving the start and end addresses in a hash table acting as a back-log/guiding reference for the next randomization attempt. Hence only one foreach is used in constraint, which still possess a performance bottleneck, however much faster than the second solution.

That said, if I were to write the code I would neither use the first nor the second solutions to solve the simple problem of non-overlapping address ranges. I would just remove the constraint completely and embrace the constraint with a checking loop like so.


class c;
  rand bit [31:0] start_addr;
  rand bit [5:0] length;
  bit [31:0] start_end_addr_hash [bit[31:0]];
endclass

...
c c1 = new;
... 
forever begin
  duplicate = 0;
  assert(c1.randomize());
  foreach (c1.start_end_addr_hash [i])
    if ((c1.start_addr <= c1.start_end_addr_hash [i]) && ((c1.start_addr + c1.length -1) >=i )) begin
      duplicate = 1;
      break;
    end
  if (!duplicate) break;
end
c1.start_end_addr_hash [c1.start_addr] = c1.start_addr + c1.length - 1;


The code above shall free the solver from complicated constraints allowing it to focus on the distribution. If we assumed an ideal solver distribution (e.g. BDD solver will be efficient to solve the simplified problem above), the third solution will be much faster than both solutions above (especially the second one).

Same stands true for the original question of this post; how to generate an array of random unique elements. Well you can also use procedural code and minimal solver work if possible, like so:


class dummy_c;
  randc bit [31:0] val;
endclass

class trans;
  rand bit [31:0] addrs [];

  function void post_randomize();
    dummy_c dc = new;
    foreach (addrs[i]) begin
      assert (dc.randomize);
      addrs[i] = dc.val;
    end
  endfunction
endclass

Thanks,
Ahmed Yehia

In reply to ayehia:

Thanks for your detailed answer. I agree with you. For long vectors your last solution has much better performance. Thanks for the insight in the constraint solver performance.

I have also noticed that if we would have been talking about a transaction item class, then the user can also create random variables and a flag (e.g. fullrandom_or_definedbyuser) so that he can assign their user-values in the post randomization phase. In that way, the user can decide if he want to find a new solution (using your last code) or test a previous solution defined (hardcoded) in the explicit part of the item randomization.

For example:


class c;
  rand bit [31:0] start_addr[];
  rand bit [5:0] length[];//size of 
  rand bit user_define_all_rand_members=0;
  bit [31:0] start_end_addr_hash [bit[31:0]];
  constraint c {
     length.size()==start_addr.size();
     soft user_define_all_rand_members==0; 
  } //end contraint c
endclass
 
...
c c1 = new;
... 
repeat(number_of_blocks2generate) begin
  forever begin
     duplicate = 0;
     assert(c1.randomize( ) with {
	           start_addr.size()==2;
	           user_define_all_rand_members==1;
	           start_addr = { 32'hFFFF, 32'h0000 };
	     } );//try to add a block of 2 ranges, you can define more
     //Start check of the random value
     foreach (c1.start_end_addr_hash [i]) begin
        foreach (c1.start_addr [j]) begin
            if ((c1.start_addr[j] <= c1.start_end_addr_hash [i]) && ((c1.start_addr[j] + c1.length[j] -1) >=i )) begin
                 duplicate = 1;
	         if (user_define_all_rand_members==1) begin
                    $fatal (0, "Randomization defined by user failed")   
                 end
                 break;
            end
            //ADD A NEW RANGE-ELEMENT START->END ADDRESS TO THE ASSOCIATIVE ARRAY
            if (!duplicate) begin
               c1.start_end_addr_hash [c1.start_addr[j]] = c1.start_addr[j] + c1.length[j] - 1;
            end
        end
        if (!duplicate) break;
    end//end for associative array
  end//end forever begin
end//end repeat number of blocks to add to the associative array

With this version, the user can still defines/direct the randomization with several ranges in one shot (using the with clause). These “with” constraints are checked by the forever loop without using the constraint solver, improving the performance.

In reply to dave_59:

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];

Hi Dave, does this mean that an associative array cannot be randomized? If it can be, can we have constraints on elements of the associative array? What happens when a rand object containing an associative array is randomized?

In reply to itsmyturn:

You can randomize the existing elements of an associative array, just like any other array using the foreach constraint. You cannot add or delete elements, or change the index values of an associative array.

In reply to dave_59:

Ok. Thanks Dave.

In reply to dave_59:

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.

Hi dave,

I tried to use that but a compile error says that the other array is not rand.
This is my code:


program try_unique_array();

class unique_array;
  rand bit [3:0] arr[16];
       bit [3:0] excludes[4] = {0,5,4,3};

  constraint array_elements_should_be_unique {
    unique {arr, excludes};
  }
endclass : unique_array

unique_array ua;

initial begin
  ua = new();

  assert(ua.randomize());

  foreach(ua.arr[i]) begin
    $display("ua.arr[%0d] = %0d", i, ua.arr[i]);
  end
end

endprogram : try_unique_array

Regards,
Reuben

In reply to Reuben:
I don’t get a compiler error when I run this in Questa. I do get an assertion error when I run this because the randomization fails. That is because you are excluding 4 values which leaves only 12 possible values of a 4-bit number that could be unique. So you need to change your array declaration to arr[12].

This is about specifying the hard coded values in unique constrains as shown in below.

class nibbleset;
rand bit [3:0] nibbles[10];
const rand bit [3:0] excludes = {0,15};
rand bit[3:0] excluded;
constraint uniq {unique {nibbles, excludes,7};}
//constraint uniq {unique {nibbles, excludes,excluded};}
constraint exclusion { excluded==1; }
endclass

We are seeing the error as shown in below for the above code snippet(uncommitted line ).

Error-[CSTR-NET] Non-equivalent type
customer_test.sv, 18

If I replace the value 7 with an identifier(excluded) the test case is passing.

The following blog was mentioning about the hard coded values in the unique constraints is allowed .

Is this valid to mention the hardcoded values in the unique constraint list ( constraint uniq {unique {nibbles, excludes,7};}

In reply to kvssrohit:

My guess is your compiler sees “7” as 32-bit integer and hence saying “non-equivalent” - try 4’d7

Srini
www.verifworks.com

In reply to Srini @ CVCblr.com:

Hello Everyone,

In reference with discussion going on unique constructs i have few points as a query.

I have compiled & executed this below simple code to check the sv-2012 unique construct.However it is executing successfully with VCS 2014.10 but for cadence incisive 15.20 it is reporting an error like below::
ncvlog: *E,MSTBER (design.sv,5|38): Expecting a random variable (declared with the ‘rand’ modifier).

Is it having possibility like we have to pass some switch to enable 2012 constructs?
Also the concept of unique is related only with randomizing unique array elements or it has some other use cases as well apart from array randomizing?

 class dummy;
   rand bit [3:0] mode[5];
   bit[3:0] exludes[]={1,2,3,4,5,6};
   constraint uniq{unique {mode,exludes};} //excluding elements 
  endclass : dummy

module test_unique;
  
  initial begin
  dummy clkgen = new();
  clkgen.randomize();
  for(int i=0;i<5;i++) 
    begin
    $display("clkgen.mode = %0d\n",clkgen.mode[i]);
    end   
   end

endmodule

Regards,
Geet

In reply to Geet:

It seems Incisive has a problem with the unique constraint or it is caused by a weakness in your code. You should clean-up your code in test_unique:
(1) make the declaration of clkgen outside of the inital block.
(2) construct clkgen in the initial.

It works fine with Questa.
unique is not a simple keyword. It defines the so-called ‘Uniqueness constraints’.
See chapter 18.5.5 in the SV Standard 2012.

In reply to chr_sue:

Thanks for reply,

I had referred the 18.5.5 chapter in SV and by overriding ‘exclusion’ constraint we can achieve the requirement


  class dummy;
    rand bit [3:0] mode[5];
    rand bit [3:0]excluded[5];
    constraint uniq{unique {mode,excluded};}
    constraint exclusion { excluded[0] == 12; excluded[1] == 13;excluded[2] == 14;excluded[3] == 15;}
  endclass : dummy

I had overridden the constraint for the values which i desired to exclude & it is working fine with cadences as well.

It was necessary to call this constraint to exlude the numbers,it was not dependant on the declaration on the construct clkgen.

Thank you for your pointer to LRM it has the solution.