SV Constraint :: Address must be aligned to the size of the transfer

Hi All,
If AxSize is ‘N’ then the address must be aligned to 2**N
Eg: For AxSize == 2 then address[1:0] == 0 i.e aligned to 4-byte
For AxSize == 3 then address[2:0] == 0 i.e aligned to 8-byte
Thus lower ‘AxSize’ bits are 0’s i.e Address[ (AxSize-1):0] == 0

   //  Declaration of address and axsize random variables

    rand  bit[11:0] axaddr;  // In reality Address Width is 32 bit i.e [31:0]
        
    randc bit[2:0] axsize; 

I was trying different possible solutions

(Solution 1) Intention is to constraint axaddr[ (axsize-1):0 ] to all zeroes

constraint Addr_Aligned1 {      
      
                              // axaddr[ (axsize-1):0 ] == 0; // Error-[IRIPS] Illegal range in part select
                              
                               foreach( axaddr[i] )
                               {
                                 // if( i inside { [0:(axsize-1)] } ) // [Q] What if axsize == 0 ? What will result of ( axsize - 1 )
  be ?
                                 if( i < axsize )
                                 {
                                   axaddr[i] == 0; 
                                 }  
                               }                                                                  
                             }  

[Q1] If I change if - constraint to :: if( i inside { [0:(axsize-1)] } ) and add in-line constraint ::
if( obj.randomize() with { axsize == 0; } ) ,
I observe that the o/p is tool dependent.
When axsize is 0 , what would be the upper bound of [0:(axsize-1)] ? Will it be all ones ?
Is the result dependent on the type ( signed/unsigned ) of “i” ?

(Solution 2) Intention is to constraint axaddr & ( '1 << axsize ) ( which gives result as axaddr[ (axsize - 1) : 0 ] ) as 0

 rand bit[2:0] local_axsize ; // helper variable
    
    function bit[2:0] No_of_Zeroes( bit[2:0] ip );     
      No_of_Zeroes = '1; // All Ones      
      return( No_of_Zeroes << ip );      
    endfunction  
    
    constraint Addr_Aligned2  {       
                                           
                                local_axsize == No_of_Zeroes(axsize);                          
                               
                                ( axaddr & local_axsize ) ; // Need help here. Should there be RHS ?
     
                              }

Result of ( axaddr & local_axsize ) is 12-bit with lower ‘axsize’ no. of bits as 0.
One valid solution is axsize == 0 and axaddr as unconstrained ( including 0x0 )
i.e each address would be aligned to 1-byte
However axsize == 0 and axaddr == 0 won’t be picked via constraint axaddr & local_axsize ( as result is 0 ). The intention is that axsize == 0 and axaddr == 0 should also be picked as a possible solution.

[Q2] How should I use result of local_axsize in order to constraint axaddr accordingly ?

[Q3] Any suggestions for alternative solution are welcome as well

Use a mask

constraint align {
  ( axaddr & ( 1'b1 << axsize ) - 1 ) == '0;
}

You could use modulo operator:

constraint aligned{
axaddr % (2**axsize) == 0;
}

Thank you both Dave and Kranthi.
@dave_59 a question regarding above foreach constraint

foreach( axaddr[i] )
 {
     if( i inside { [0:(axsize-1)] } ) // confusion here                
    {
         axaddr[i] == 0; 
     }  
  }    

[Q] If axsize is chosen as 0 , would there be any constraint on axaddr ?
Since (axsize-1) would be -1 shouldn’t there be a warning/error message ?
( as the range for inside operator must be [low:high] )

If axsize ==0, then there is no constraint on axaddr, which is the correct thing to do.

Yes I do agree but I was curious on how the constraint solver interprets the if-constraint

if( i inside { [0:(axsize-1)] } )

I tried the following

module tb;  
  bit[11:0] axaddr;        
  bit[2:0] axsize ; // Defaults to 0
    
  initial begin
    foreach( axaddr[i] )  
     if( i inside { [0:(axsize-1)] } )      
       $display("i is %0d N (axsize-1) is %0h",i,(axsize-1));             
  end  
endmodule  

I observe “(axsize-1) is ffffffff” i.e result is 32-bit (even though width of axaddr is 12-bit )

LRM 12.7.3 mentions

When loop variables are used in expressions other than as indices to the designated array, they are auto-cast into a type consistent with the type of index. For fixed-size and dynamic arrays, the auto-cast type is int .

Since the iterator argument “i” is int type i.e 32-bit signed type result of (axsize-1) would be FFFF_FFFF.
However shouldn’t this constraint all the bits of axaddr to 0 ? ( as the if condition would be always true )

 rand  bit[11:0] axaddr;  // In reality Address Width is 32 bit i.e [31:0]        
 randc bit[2:0] axsize;

constraint Addr_Aligned1 {      
      
        // Assuming 'axsize' is 0  via in-line constraint                            
           foreach( axaddr[i] )
         {
            if( i inside { [0:(axsize-1)] } ) // Always true as i is inside {[0:FFFF_FFFF] } 
           {
               axaddr[i] == 0; // Shouldn't this constraint all bits to 0's ?
            }  
          }                                                                  
       }

A 2nd possibility I see is that the inside constraint is ignored as range isn’t [low:high] thereby leaving axaddr unconstrained.
However I don’t observe any warning message for the invalid range.

I observe that the o/p is tool depended edalink

Yes, even experienced users like me get caught by the mixing of signed and unsigned types. So for axsize==0 all axaddr bits would be set to 0.

This is why we strongly encourage people to put in minimal,complete reproducible examples to get the best responses.

This made me realize that the range for the inside operator is still [low:high] due to result of (axsize-1) being 32-bit unsigned.
As axsize is bit type i.e unsigned type, (axsize-1) would be 32’hFFFF_FFFF i.e unsigned.
Hence the if-constraint would always be true and axaddr would be constrained to 12’b0.