SystemVerilog provides the support to use foreach loop inside a constraint so that arrays can be constrained.

The foreach construct iterates over the elements of an array and its argument is an identifier that represents a single entity in the array.

Click here to refresh loops in SystemVerilog !

Example

The code shown below declares a static array called array with size 5. This array can hold 5 elements where each element can be accessed using an index from 0 to 4.

The constraint uses foreach loop to iterate over all the elements and assign the value of each element to that of its index.


class ABC;
  rand bit[3:0] array [5];
  
  // This constraint will iterate through each of the 5 elements
  // in an array and set each element to the value of its
  // particular index
  constraint c_array { foreach (array[i]) {
    					array[i] == i;
  						}
                     }
endclass

module tb;
  
  initial begin
    ABC abc = new;
    abc.randomize();
    $display ("array = %p", abc.array);
  end
endmodule

The output given below shows that each element of the array was assigned to a value equal to that of its index.

 Simulation Log
ncsim> run
array = '{'h0, 'h1, 'h2, 'h3, 'h4}
ncsim: *W,RNQUIE: Simulation is complete.

Dynamic Arrays/Queues

Dynamic arrays and queues do not have a size at the time of declaration and hence the foreach loop cannot be directly used. Therefore, the array's size has to be either assigned directly or constrained as part of the set of constraints.

Example


class ABC;
  rand bit[3:0] darray []; 		// Dynamic array -> size unknown
  rand bit[3:0] queue [$]; 		// Queue -> size unknown
  
  // Assign size for the queue if not already known
  constraint c_qsize  { queue.size() == 5; }
  
  // Constrain each element of both the arrays
  constraint c_array  { foreach (darray[i])    					  
    					  darray[i] == i;    					
                        foreach (queue[i]) 
                          queue[i] == i + 1;
                      } 
                      
    // Size of an array can be assigned using a constraint like
    // we have done for the queue, but let's assign the size before
    // calling randomization
    function new ();
		darray = new[5]; 	// Assign size of dynamic array
	endfunction
endclass

module tb;
  
  initial begin
    ABC abc = new;
    abc.randomize();
    $display ("array = %p
queue = %p", abc.darray, abc.queue);
  end
endmodule
 Simulation Log
ncsim> run
array = '{'h0, 'h1, 'h2, 'h3, 'h4}
queue = '{'h1, 'h2, 'h3, 'h4, 'h5}
ncsim: *W,RNQUIE: Simulation is complete.

Multidimensional Arrays

SystemVerilog constraints are powerful enough to be applied on multidimensional arrays as well. In the following example we have a multidimensional static array with a packed structure.

multidimensional array constraint

Here we attempt to assign the pattern 0xF0F0F to each element of the multidimensional array.


class ABC;
  rand bit[4:0][3:0] md_array [2][5]; 	// Multidimansional Arrays
  
  constraint c_md_array { 
    foreach (md_array[i]) {
    	foreach (md_array[i][j]) {
          foreach (md_array[i][j][k]) {
            if (k %2 == 0) 
              md_array[i][j][k] == 'hF;
            else
              md_array[i][j][k] == 0;
        }
      }
    }
  }
  
  
endclass

module tb;
  
  initial begin
    ABC abc = new;
    abc.randomize();
    $display ("md_array = %p", abc.md_array);
  end
endmodule
 Simulation Log
ncsim> run
md_array = '{'{'hf0f0f, 'hf0f0f, 'hf0f0f, 'hf0f0f, 'hf0f0f}, '{'hf0f0f, 'hf0f0f, 'hf0f0f, 'hf0f0f, 'hf0f0f}}
ncsim: *W,RNQUIE: Simulation is complete.

Multidimensional Dynamic Arrays

Constraining a multi-dimensional dynamic array is a little more tricky and may not be supported by all simulators. In the example shown below, the size of X or Y elements of the 2D array md_array is not known.


class ABC;
  rand bit[3:0] md_array [][]; 	// Multidimansional Arrays with unknown size
  
  constraint c_md_array { 
     // First assign the size of the first dimension of md_array
     md_array.size() == 2; 
    
     // Then for each sub-array in the first dimension do the following:
     foreach (md_array[i]) {
                           
        // Randomize size of the sub-array to a value within the range
        md_array[i].size() inside {[1:5]};
                           
        // Iterate over the second dimension 
        foreach (md_array[i][j]) {
                             
           // Assign constraints for values to the second dimension
           md_array[i][j] inside {[1:10]};
         }
      }
   }

endclass

module tb;
  
  initial begin
    ABC abc = new;
    abc.randomize();
    $display ("md_array = %p", abc.md_array);
  end
endmodule
 Simulation Log
ncsim> run
md_array = '{'{'h9, 'h6, 'h7, 'h9, 'h1}, '{'h5, 'h9, 'h4, 'h2}}
ncsim: *W,RNQUIE: Simulation is complete.

Array Reduction Iterative Constraint

This is another pretty useful construct and technique supported in SystemVerilog.

Array reduction methods can produce a single value from an unpacked array of integral values. This can be used within a constraint to allow the expression to be considered during randomization.

For example, consider that an array of N elements have to be randomized such that the sum of all elements equal some value. An array reduction operator can be used with the with clause such that it iterates over each element of the array and include it in the constraint solver.


class ABC;
  rand bit[3:0] array [5];
  
  // Intrepreted as int'(array[0]) + int'(array[1]) + .. + int'(array[4]) == 20;
  constraint c_sum { array.sum() with (int'(item)) == 20; }
  
endclass

module tb;
  initial begin
    ABC abc = new;
    abc.randomize();
    $display ("array = %p", abc.array);
  end
endmodule
 Simulation Log
ncsim> run
array = '{'h4, 'h2, 'h2, 'h4, 'h8}
ncsim: *W,RNQUIE: Simulation is complete.