How to write constraint for generating a continuous mask


class A;
  rand bit [7:0]mask;
  rand bit [7:0]b;
  function void display();
    $display("Mask is %8b::b=%8b",mask,b);
  endfunction
  //mask_validation function is to be used in the constraint block.
  //This fucntion returns "1" if input mask value is having continuous 1s.
  //Otherwise returns zero.
  //example: 1111,1110,1100,1000,0111,0110,0100,0011,0010,0001,0000 should return "1". 
  //Other values will return "0".

  //Explanation: Here it will keep on checking the each bit, If "1" is appeared,
  //then it will set the first_one_occured = 1. After a one if zero will be hit,
  //then it sets zero_after_one = 1. If after this again a one come then it will return "0".
  //As mask value is non continuous. (e.g. 1010.1001,1101 etc)
  function bit mask_validation(bit [7:0]mask);
    bit first_one_occured = 0;
    bit zero_after_one = 0;
    foreach(mask[i]) begin
      if (mask[i]) begin
        if(!first_one_occured)
          first_one_occured = 1;
        if(zero_after_one) begin
          return 0;
        end
      end
      else begin
        if(first_one_occured)
          zero_after_one=1;
      end
    end//foreach
    return 1;
  endfunction
  constraint nonzero_b{b != 8'b00000000;}
  constraint nonzero_mask{mask!=8'b00000000;}
  constraint valid_mask{mask_validation(mask) == 1;}
endclass
module test;
  A a;
  initial begin
    a=new();
    repeat (10) begin
      void'(a.randomize());
      a.display();
    end
  end
endmodule

Please point me where I am doing wrong. It shows constraint failure.
Please correct me.
If there is any simpler logic to generate a continuous mask, then please share.

In reply to Tapas:

I rewrote your function, but I still got the same results of No solutions exist

Like you, I fail to understand why.
I tried other approaches, but my algorithms are wrong.
Perhaps this will help you into a different solution.
Also, perhaps someone else can explain why the failure.


class A;
  rand bit [7:0]mask;
  rand bit [7:0]b;
  function void display();
    $display("Mask is %8b::b=%8b",mask,b);
  endfunction
  //mask_validation function is to be used in the constraint block.
  //This fucntion returns "1" if input mask value is having continuous 1s.
  //Otherwise returns zero.
  //example: 1111,1110,1100,1000,0111,0110,0100,0011,0010,0001,0000 should return "1". 
  //Other values will return "0".
 
  //Explanation: Here it will keep on checking the each bit, If "1" is appeared,
  //then it will set the a_one_occurred = 1. After a one if zero will be hit,
  //then it sets zero_after_one = 1. If after this again a one come then it will return "0".
  //As mask value is non continuous. (e.g. 1010.1001,1101 etc)
  function  bit mask_validation(bit [7:0]mask);
    bit a_one_occurred = 0;
    bit zero_after_one = 0;
    bit fail=0; 
    foreach(mask[i]) begin // Mask is 01110010::b=00011010 
        case({a_one_occurred, zero_after_one})
            2'b00: if(mask[i])  a_one_occurred=1'b1;            
            //2'b01: if(mask[i])  a_one_occurred=1'b1;   
            2'b10: if(mask[i]==1'b0) zero_after_one=1'b1; 
            2'b11: if(mask[i]) fail=1'b1; // mask[i]==1'b0; // must be 0
        endcase 
    end
    return fail;
  endfunction
  constraint nonzero_b{b != 8'b00000000;}
  constraint nonzero_mask{mask!=8'b00000000;}
  constraint valid_maskF{mask_validation(mask) == 0;}
  /* constraint valid_mask{{mask[7:4]!=4'b1001}; //2 zeros and 1 
                        {mask[6:3]!=4'b1001};
                        {mask[5:2]!=4'b1001};
                        {mask[4:1]!=4'b1001};
                        {mask[3:0]!=4'b1001};  
                        {mask[7:3]!=5'b10001}; //3 zeros and 1 
                        {mask[6:2]!=5'b10001};
                        {mask[5:1]!=5'b10001};
                        {mask[4:0]!=5'b10001};
                        {mask[7:1]!=6'b100001}; //4 zeros and 1 
                        {mask[6:0]!=6'b100001};  } */
   /*constraint new_mask{ {mask[7]==1 -> mask[6:4]!= 3'b001 && 
                                       mask[5:3]!= 3'b001 && 
                                       mask[4:2]!= 3'b001 &&
                                       mask[3:1]!= 3'b001 && 
                                       mask[2:0]!= 3'b001};  

                        {mask[6]==1 -> mask[5:3]!= 3'b001 &&
                                       mask[4:2]!= 3'b001 &&
                                       mask[3:1]!= 3'b001 && 
                                       mask[2:0]!= 3'b001};  

                        {mask[5]==1 -> mask[4:2]!= 3'b001 && 
                                       mask[3:1]!= 3'b001 &&
                                       mask[2:0]!= 3'b001};  

                        {mask[4]==1 -> mask[3:1]!= 3'b001 && 
                                       mask[2:0]!= 3'b001};  }*/                        
  
endclass

module test;
  A a;
  initial begin
    a=new();
    repeat (10) begin
      void'(a.randomize());
      a.display();
    end
  end
endmodule

In reply to ben@SystemVerilog.us:

Another cut at the use of the function with more debug features.
Still an issue.


 function  bit mask_validation(bit [7:0]mask);
    bit a_one_occurred = 0;
    bit zero_after_one = 0;
    bit fail=0; 
    $display("Mask in call before loop  %8b, fail= %d",mask, fail);
    foreach(mask[i]) begin // Mask is 01110010::b=00011010 
        $display("i= %d", i);
        case({a_one_occurred, zero_after_one})
            2'b00: if(mask[i])  a_one_occurred=1'b1;            
            //2'b01: if(mask[i])  a_one_occurred=1'b1;   
            2'b10: if(mask[i]==1'b0) zero_after_one=1'b1; 
            2'b11: if(mask[i]) fail=1'b1; // mask[i]==1'b0; // must be 0
        endcase 
    end
    $display("Mask in call %8b, fail= %d",mask, fail);
    $display("a_one_occurred %d, zero_after_one %d", a_one_occurred, zero_after_one);
    return fail;
  endfunction
  constraint nonzero_b{b != 8'b00000000;}
  constraint nonzero_mask{mask!=8'b00000000;}
  constraint valid_maskF{mask_validation(mask) == 1;}
  // In above constraint we need a ==0, but that causes "No solutions exist"
  // The ==1 shows the displays for 1 randomize cal 
/* # Mask in call before loop  11001111, fail= 0
# i=           7
# i=           6
# i=           5
# i=           4
# i=           3
# i=           2
# i=           1
# i=           0
# Mask in call 11001111, fail= 1
# a_one_occurred 1, zero_after_one 1
# Mask is 11001111::b=11010000  */ 


In reply to Tapas:
Calling a user defined function in a constraint breaks the solution space in half. The inputs to the function get randomized to a solution first, then the function gets called, and the output of the function becomes a non-random state variable.In your example. if the output of mask_validation returns 0, it won’t go back and try another set of inputs until the output returns 1. the constraint will just fail.

What you need to do is express your constraints entirely as a set of equations.

class A;
  rand bit [7:0] mask;
  rand int unsigned N; // Number of ones
  rand int unsigned S; // shifted position
  constraint valid_mask {
    N inside {[1:8]};
    S <= 8-N;
    (9'b1 << N)-9'd1 << S == mask;
  }
endclass
module top;
  A a= new;
  initial repeat(100) begin
    assert(a.randomize());
    $display("mask %b N %0d S %0d",a.mask,a.N, a.S);
  end
endmodule

In reply to dave_59:
That was a clever solution Dave!
On inputs to the function get randomized to a solution first, then the function gets called:
Isn’t that similar to a post-randomize with the exception that if the result of the function is zero you fail the randomize? What are the benefits of the function call in a constraint? To fine-tune it after the randomization of the variable?
Ben

In reply to dave_59:
Changing bit size 9 to 8 have impact? I didn’t get any issues. Do you have any specific region to use bit size equals to 9 not 8 in following constraint.
(9’b1 << N)-9’d1 << S == mask;

In reply to ben@SystemVerilog.us:

You can use a function in a constraint as long as the random variable dependencies flow through correctly. For examlpe in the orginal example , you could write instead

constraint valid_mask{mask_validation(mask) -> b < 10; }

and that will always find a solution.

In reply to voraravi:
Yes, you’re correct the bit-width doesn’t matter in this case. I was worried that when N==8 the 'b1 would get truncated. But it turns out (9’b1_00000000 - 9’b0_00000001) and (8’b00000000 - 8’b00000001) both give me 'b11111111

In reply to dave_59:

Yes, got the point. Thank you!

In reply to dave_59:

Thank you Dave. A tricky solution.

In reply to dave_59:
Thanks for your earlier input.

constraint valid_mask{mask_validation(mask) -> b < 10; }

But for this constraint, I am not able to get the proper output. Will you please explain a bit, how it is going to work? Actually I am not able to figure out the idea behind this.

In reply to Tapas:

You have to run more than 10 randomization’s to see it working. The mask becomes an unconstrained random variable, and there’s less than a 15% chance (37/256) that the mask has a continuous set of ones. See function in constraint - EDA Playground.

In reply to dave_59:

Hi Dave, can you please explain what was your thought process when you start working on this problem?(which are the points that decided that streaming operator will be used?)

In reply to juhi_p:

There is no mention of streaming operators anywhere in this thread.

In reply to dave_59:

Oh, sorry not streaming operator. just want to know what was thought process to come to right solution?

In reply to juhi_p:

(1 << N) - 1

This is a common way of creating a variable sized bit mask

In reply to Tapas:


class seq_item;
  rand bit[7:0] mask;
  rand bit[2:0] q[$];
    
  constraint cont_mask_c { q.size() inside {[1:$size(mask)]}; //generate a queue of random size but less than size of mask
                           q[$] inside {[0:$size(mask)-1]};
                           
                          foreach(q[i]) {if(q.size() > 1) if(i<= q.size()-2) q[i+1] == q[i] + 1;} //fill random queue with contiguous numbers
                            foreach(mask[i]) {if (i inside {q} ) mask[i] == 1; else mask[i] == 0;} //only in those contiguos positions generate 1, else 0
                         }
endclass: seq_item


This generates continuous masks, you can test it here…

In reply to dave_59:

Hi Dave!

I believe this solution should also work!

  


    class check;
    
    rand bit[31:0] a;
    rand int num_ones;
    
    constraint num_ones_c {num_ones inside {[1:31]};}
    
    
    constraint a_c {  if(a[31]==0) 
                       ($countones(a ^ a<<1)-1)==1;
                      else 
                        ($countones(a ^ a<<1))==1;
                    
                      $countones(a) == num_ones; }
    
    
  endclass