Constraint Randomization question

200 int in the array, random array to have only 5 different numbers in the range [0:50], make sure every number appears 40 times and no same number in any 4 consecutive slots.

Below is the code I have come up with and I am still working on the solution.

class packet;
  rand int unsigned array[];
  //create the size of the dynamic array to hold 5 random integers 
  array = new[5]; 
  //array can have 5 random numbers in the range [0:50]
  constraint c1 {foreach(array[i]) array[i] inside {[0:50};}
  //constraint such that each element should appear exactly 40 times in the array with 200 elements
  constraint c2 {foreach(array[i]) array[i] dist {:= 40};}
  //constraint such that no same number should appear in any 4 consecutive slots
  constraint c3 {i inside {[0:197]};}
  constraint c4 {foreach(array[i]) array[i] != array[i+1];
                                   array[i] != array[i+2]; 
                                   array[i] != array[i+3];}
endclass
    
    module top;
      initial begin
      //handle and constructor for the class  
      packet pkt;
      pkt = new();
      
        repeat(200)begin 
       pkt.randomize();
       $display ("array size is %d", pkt.array.size);
       foreach (s1.array[i]) $display("square of array[i] = %d,%d",i,s1.array[i]);
      end
    endmodule

It would be great if anyone could help me out figure out how to make the above code work.
Thanks in advance for any additional solutions too.

In reply to sk7799:

Your approach to learning Constrained Randomization reminds me of the Infinite Monkey Protocol. If you keep throwing arbitrary constraints at the problem, you’ll eventually get the correct result! It looks like you cut and pasted code snippets from different examples and tried putting them together into a single randomization.

You wanted to create an array of 200 integers, yet nowhere do you create an array of 200 integers. You cannot use a distribution constraint if you need an exact count. You need to use the sum() reduction method to count the number of occurances.

This is the brute force way of creating the constraints, but you probably won’t be able to run this with the tools on EDAPlayground as it creates way too many interconnected constraints.

class packet;
  parameter int N = 15;
  rand byte unsigned array[N];
  rand byte unsigned list[5];
  //array can have 5 random numbers in the range [0:50]
  constraint c1 {foreach(list[i]) list[i] inside {[0:50]}; unique {list}; }
  //constraint such that each element should appear exactly 40 times in the array with 200 elements
 constraint c2 {
   foreach(list[i]) array.sum() with (int'(item==list[i])) == N/5; }
 
  //constraint such that no same number should appear in any 4 consecutive slots
 constraint c4 {foreach(array[i]) i< N-3 -> 
  {array[i] == array[i+1] ->
   array[i] == array[i+2] ->
   array[i] != array[i+3];
 }}
endclass
module top;
  initial begin
    //handle and constructor for the class  
    packet pkt;
    pkt = new();
    repeat(200) begin
      assert(pkt.randomize());
      foreach (pkt.array[i]) $display("array[%0d] = %d",i,pkt.array[i]);
      $display;
    end
  end
endmodule

You might have to break it up into two separate randomizations:

class packet;
  parameter int N = 200;
  rand byte unsigned array[N];
  byte unsigned list[5];
  function void pre_randomize();
  //array can have 5 random numbers in the range [0:50]
    assert(std::randomize(list) with {
      foreach(list[i]) list[i] inside {[0:50]}; unique {list}; });
  endfunction
  //constraint such that each element should appear exactly 40 times in the array with 200 elements
 constraint c2 {
   foreach(list[i]) array.sum() with (int'(item==list[i])) == N/5; }
 
  //constraint such that no same number should appear in any 4 consecutive slots
  constraint c4 {foreach(array[i]) i< N-3 -> 
  {array[i] == array[i+1] ->
   array[i] == array[i+2] ->
   array[i] != array[i+3];
 }}
endclass
 
module top;
  initial begin
    //handle and constructor for the class  
    packet pkt;
    pkt = new();
    repeat(2) begin
      assert(pkt.randomize());
      foreach (pkt.array[i]) $display("array[%0d] = %d",i,pkt.array[i]);
      $display;
    end
  end
endmodule

In reply to dave_59:

Hi Dave,

Thank you for the response.

In your above solution, you said to use the sum() reduction method to count the number of occurrences. How will sun() count the number of occurrences? Isn’t sum mean to add all the elements inside the array? And since I don’t know what elements would be in what indices, how should I understand the sum() reduction method to count the number of occurrences?

Could you please explain the below constraint:
constraint c2 {
foreach(list[i]) array.sum() with (int’(item==list[i])) == N/5; }

Also in method 1, can you please let me know why do we need the parameter N to be to 15?

Thanks in advance.

Hi Dave,

The way I tried approaching the problem is to break it into multiple sub-problems.
I did not copy-paste the snippets but translated the problem into comments and then substituted each comment with a constraint. Since I am in the learning stage, I did refer to multiple websites for syntax and working style of each constraint and kept working on the code in eda playground: Edit code - EDA Playground

Please let me know if there’s a better approach to learn randomization.
I am currently following multiple websites: verificationguide.com/chipverify.com/siemens videos by chirs spear and system Verilog lrm.

Thanks,
-s

In reply to sk7799:

The sum() array reduction method sums up all the elements or sums up whatever is inside the with() clause. You can think of
array.sum()
the same as
array.sum with (item)
, and
item
gets replaced with each element. You should look at this.

Your last $display statement references s1, which does not exist your example, and tries to print “Square of array” which has nothing to do with anything in this discussion, so I assumed you had pasted from some other example. You should at least get your code compiling and executing. Then focus on getting the correct results.

N=15 was just the larges value I get get a result with using the tools on EDAPlayground.

In reply to dave_59:

Thank you Dave. Will take a look at your reference.

In reply to dave_59:

Hi Dave,

Why should the above method 2 code be divided into 2 parts of randomization and why the repeat needs to be 2. Please let me know.

Thanks

In reply to sk7799:

Sometimes the constraint solving problem becomes too complicated to be solved in a reasonable amount of time. This example has an array of 200 elements each needing to be at least 6 bits. So that is 1200 bits all with many interwoven constraints between them. Breaking the constraints into two separate sets reduces the complexity.

The repeat(2) is insignificant, it was just for debugging. I should have changed it back to 200 as in your original example.

In reply to dave_59:

In reply to sk7799:
Sometimes the constraint solving problem becomes too complicated to be solved in a reasonable amount of time. This example has an array of 200 elements each needing to be at least 6 bits. So that is 1200 bits all with many interwoven constraints between them. Breaking the constraints into two separate sets reduces the complexity.
The repeat(2) is insignificant, it was just for debugging. I should have changed it back to 200 as in your original example.

Hi Dave,
we have an issue with solver, at integration of chain of blocks sequences as sub sequence one of another (dinamic tree of sub sequences with constraitns between), relying on this sentence in LRM: (IEEE 1800-2017 SV LRM section 18.5.9 Global constraints)

“… A class may declare an object
member (i.e. class instance) as “rand”. When the top-level object is randomized, the lower level objects are also randomized. All rand variables and constraints in the top- and lower-level objects are solved simultaneously…”

is solver work similar between this example (LRM’s example) and using array with foreach?


class A; 
    rand bit [7:0] v;
endclass

class B extends A;
    rand A left;
    rand A right;
    //...CHAINS_SPLIT
    constraint heapcond {left.v <= v; right.v > v;}
endclass

Class C extends A;
    rand A[];
    constraints heapcond { 
        A.size() == CHAINS_SPLIT;
        foreach(A[i]){
            (A.size() > i+1) -> A[i].v <= A[i+1].v;
        }
    }
endclass

In other words, is there a difference between the solver’s work in B vs C?
CHAINS_SPLIT is a project and structure dependent parameter, the easy way is to implement with an array but if another way reduces the solver’s effort, it can make the simulation faster and thrift

just to correct, in our situation the C looks more like this:


Class C;
    rand bit [7:0] v;
    rand C[string];
    constraints heapcond { 
        foreach(C[i]){
            (C.size() > i+1) -> C[i].v <= C[i+1].v;
            C[i].v > v;
        }
    }
endclass

we build the tree manually with factory according to chain tree architecture and call randomize for root

thanks!