Constraining Array.sum( ) for One/Two Dimesional Unpacked Array

Hello everyone ,

I am trying to Constraint sum using the following Code :



Class ASUM ;

rand bit [1:0]  Arr[] ;


constraint S1D  { Arr.size()  == 3 ; }


constraint SUMM { SUM_CALC ( Arr ) == 7 ; }


function int SUM_CALC ( input  bit [1:0] Arr [] ) ;

  $display(" Inside Function SUM_CALC with size == %0d ",Arr.size() ); // To See if Function Gets Called b4 OR After Size Constraint !!
    
    foreach(Arr[i])
     begin
      
      SUM_CALC = SUM_CALC + Arr[i] ;
     
     end

endfunction

endclass
.....

if ( a1.randomize() ) // a1 is Class Object 
 begin 
  $display("Success with %p ",a1);
 end
else
  $display("Fails with %p",a1);

end



I get the following Output ::



 Inside Function SUM_CALC with size == 3 
 Fails with '{Arr:'{}} 


(a) I don’t get why the Constraint Fails .

(b) According to my understanding , Function gets Called before any Constraint gets Solved
( apart from randc ) , hence I used a display inside the function to see whether the size() Constraint is Applied before the
Function Call . As observed from output I see size() Constraint gets Solved before Function Call .
How is that possible . AM i missing something ?

(c) I can achieve the Sum Constraint using :: A.sum() with ( int’item ) == 7 ;
Is there any Other way ?

Another Issue is when the Arr is declared 2D ( rand bit [1:0] Arr )



rand bit [1:0]  Arr[][];

constraint S1D { Arr.size()  == 9 ; }

constraint S2D {       foreach(Arr[i])
                      {
		         Arr[i].size() == 2 ;	  
		      }
	         }

  constraint sum_2d  {
                        if ( Arr.size() > 0 ) // Refer (d) below   
    		          Arr.sum(item) with ( item.sum() with ( int'(item) ) ) == 27 ;
                      
  		     }


   

(d) In the Constraint Guard above How Can I Check that Both Dimensions 1 as well as 2 of Arr are greater than 0 ?
According to LRM :: $size(Arr,1) && $size(Arr,2) won’t work since the Dimensions are Dynamic .

Arr.sum(item) with ( item.sum() with ( int'(item) ) ) Unfortunately doesn't work in declarative Context . I observe that it works fine in procedural Context which is why I tried the Following :: 


  constraint sum_2d  {
                        if ( Arr.size() > 0 ) // Would be evaluated Only Once 
    		          FUNCTN_SUM(Arr) == 27 ;
                      
  		     }
  
  typedef int unsigned intu ;
  
  function intu  FUNCTN_SUM (input bit [1:0]  Arr[][] ) ;
  
  $display(" Inside FUNCTN_SUM ");
  
  FUNCTN_SUM = Arr.sum(item) with ( item.sum() with ( int'(item) ) ) ;
  
  endfunction



I get the following Output ::



 Inside FUNCTN_SUM 
 Fails with '{A:'{}} // Similar Issue as (a) Above 


Thanks in advance ,
FS

In reply to Have_A_Doubt:

Constraints involving inputs to function get solved before calling the function. See this post. The built-in iterative sum() method gets unrolled into an equation and does not behave like a function call.

I don’t understand the next part of your question. Why do you need to check for non-zero sizes if you already have constraints to keep them non-zero? Also, please explain what you meant by “Unfortunately doesn’t work in declarative Context”

In reply to dave_59:

Hi Dave ,

I used the check for Non-Zero Size to make sure that sum() Constraint gets solved after the 2 Size Constraints .
I missed out on the fact that SV implicitly gives us the feature such that Size() Constraints get Implicitly Solved before sum() Constraint ( Reduction Operation ) . So the Check for size() > 0 isn’t really needed .

I noticed that Arr.sum(item) with ( item.sum() with ( int’(item) ) ) works in a Procedural Code like Module level function and initial blocks whereas none of the 3 Major Vendors have support for it ( Tried it on the Latest version for all 3 Simulators ) when using it in a Constraint ( which is Declarative rather than procedural , Hence the { } ) .
So I tried using it in a function called within a Constraint ( FUNCTN_SUM above ) but it still gives a Failure . Need your suggestions regarding it

For 1 Dimensional Unpacked Arrays ::

constraint SUMM { A.sum() with ( int'item) ) == 7 ; }
 

works fine but I run into trouble when i try the same for 2D Unpacked Arrays .

So I asked whether there’s an alternative for
A.sum() with ( int’(item ) so that it can be Extended and used for 2D Unpacked Arrays as well

In reply to Have_A_Doubt:

If you mean the 3 main simulators on EDAplayground, those versions are fairly old. It works fine for me on Questa.

The alternative is converting your 2D array to a 1D array by calculating the index yourself.

In reply to dave_59:

Hi Dave ,

I use the Simulators at my workplace and not from EDA Playground .
Which version of Questa did you try it on ?

I didn’t quite get your reply " The alternative is converting your 2D array to a 1D array by calculating the index yourself. "

My interpretation was as follows ::

Use a Queue which has each element of A ( 2D Unapcked Array ) and then constraint the sum of Elements in Queue .

The queue ( q ) below would be ::

q = '{ A[0][0] , A[0][1] , A[1][0] , A[1][1] , A[2][0] , A[2][1] }
so I tried to Implement the logic for it inside a Function .


 
rand bit [1:0]  A[][];

rand bit [1:0] q[];

constraint SIZE_CONSTRAINT_1D { A.size()  == 3 ; }


constraint S2_2d {       foreach(A[i])
                      {
		         A[i].size() == 2 ;	  
		      }
	         }

constraint Q_SIZE { q.size() == 6 ; } // 3 X 2 from above 2 Constraints . 


constraint Value_constraint_2d {   foreach(A[i,j])
                                  {
			             A[i][j] inside { [0:1] } ;
				  }
			     }


constraint Q_VAL {  
                        foreach( q[Index] ) 
                     {
                         
                           q[Index] == FUNC(A,Index) ;
	                  
		     }

		 }    

constraint Q_SUM { q.sum() with ( int'(item) ) == 10 ; }

function int FUNC ( input bit [1:0]  A[][] , const ref int Ind );
static int Count ; // Retains its value for Different Calls
int Iter ; // Counts the no. of Iterations of foreach(A[i,j]) 
      
	   foreach(A[i,j])
	   begin
            
	    if ( Iter == Count )
	     begin   
                Iter ++ ; // Can be Commented/Skipped

	     if ( Ind == Count )
             begin
                
		Count++;
                return A[i][j];
	        
	     end
         
	   end
	  
	  else
	   
	   begin

	        Iter ++ ;

	   end
	  
	  end

endfunction



[a] This Unfortunately doesn’t work . Could you please guide me as to what goes wrong in the above Code ? .
I believe the above logic could be expanded to 3D Unpacked Arrays and so on .

[b] Other confusion is regarding [d] above in the Original Question

Why does it Fail ? 


 rand bit [1:0]  Arr[][];
 
 constraint S1D { Arr.size()  == 9 ; }
 
 constraint S2D {       foreach(Arr[i])
                      {
		         Arr[i].size() == 2 ;	  
		      }
	         }

 constraint sum_2d  {
                        
    		          FUNCTN_SUM(Arr) == 27 ;
 
  		     }
 
  typedef int unsigned intu ;
 
  function intu  FUNCTN_SUM (input bit [1:0]  Arr[][] ) ;
 
  $display(" Inside FUNCTN_SUM ");
 
  FUNCTN_SUM = Arr.sum(item) with ( item.sum() with ( int'(item) ) ) ;
 
  endfunction

LRM 18.5.12 Says :: " Function calls in active constraints are executed an unspecified number of times ( at least once ) "



So shouldn’t the function FUNCTN_SUM be called till the Constraint sum_2D is True ?

Thanks in advance .

In reply to Have_A_Doubt:

your requirement is simple.
try to simplify step by step, no need to solve everything in constraint body.


class trial;
    rand bit [1:0] tmp[];
    rand int first_dim;
    rand int second_dim;
    rand int total;
    bit [1:0] arr[][];      // final output
    constraint c {
        first_dim == 9;     // your design
        second_dim == 2;    // your design
        total == 27;        // your design
        tmp.size() == first_dim * second_dim;
        tmp.sum() with (int'(item)) == total;
    }
    function void post_randomize(); 
        arr = new[first_dim];
        foreach (arr[i]) arr[i] = new[second_dim]; 
        for (int i = 0; i < first_dim; i++) begin
            for (int j = 0; j < second_dim; j++) begin
                arr[i][j] = tmp[i*second_dim+j];
            end
        end
        tmp.delete();
    endfunction
endclass

In reply to javatea:

Hi ,

Thanks for your reply . I do have a solution using post_randomize() such as yours .

But achieving the same using Function Call in Constraints is of Interest to me

In reply to Have_A_Doubt:

then read LRM 18.5.12 Function in constraints, to understand how a function work in constraint block first and also the restrictions mentioned there, then review your code again.
long story short, the function in constraint block doesn’t like a function under begin-end block.
if you really wanna dig more, try to understand SystemVerilog random solver mechanism. then your problem is solved.

In reply to javatea:

Hi ,

If you would have read my question properly u would know that I did go through the LRM 18.5.12 ( Have even mentioned a quote above ) . But unfortunately I still am missing out on something.

If you know where do I go wrong , I would have preferred that over your 3 lines ( 2 to be precise since the 2nd line doesn’t make sense ) that I already know .

In reply to Have_A_Doubt:

for your 1st code:
from LRM 18.5.12

  • Functions that appear in constraint expressions should be automatic (or preserve no state information) and have no side effects.

// your function has a static variable which tries to store some state information, it’s why I mentioned it doesn’t like a function under begin-end block.

this is your first mistake, in your code_1

for your 2nd code:


function intu  FUNCTN_SUM (input bit [1:0]  Arr[][] ) ;
  $display(" Inside FUNCTN_SUM ");
  FUNCTN_SUM = Arr.sum(item) with ( item.sum() with ( int'(item) ) ) ;
endfunction

This code is wrong, try to find out by yourself.
I can see 2 mistakes. one relates to .sum(), the other one relates to LRM 18.5.12, ha, read LRM carefully. and you will learn more.