SystemVerilog prohibits a class declared as virtual to be directly instantiated and is called an abstract class.

Syntax

  
  
virtual class <class_name>
	// class definition
endclass

  

However, this class can be extended to form other sub-classes which can then be instantiated. This is useful to enforce testcase developers to always extend a base class to form another class for their needs. So base classes are usually declared asvirtual although it is not mandatory.

Normal Class Example

  
  
class BaseClass;
	int data;
	
	function new();
		data = 32'hc0de_c0de;
	endfunction
endclass

module tb;
	BaseClass base;
	initial begin
		base = new();
		$display ("data=0x%0h", base.data);
	end
endmodule

  

Just like static variables in a class, constraints can be declared as static. A static constraint is shared across all the class instances.

Constraints are affected by the static keyword only if they are turned on and off using constraint_mode() method. When a non-static constraint is turned off using this method, the constraint is turned off in that particular instance of the class which calls the method. But, when a static constraint is turned off and on using this method, the constraint is turned off and on in all the instances of the class.

A constraint block can be declared as static by including the static keyword in its definition.

Syntax

  
  
class [class_name];
	...
	
	static constraint [constraint_name] [definition]
endclass

  

Next, we'll compare non-static constraints with static constraints and see how they differ.

The SystemVerilog constraint solver by default tries to give a uniform distribution of random values. Hence the probability of any legal value of being a solution to a given constraint is the same.

But the use of solve - before can change the distribution of probability such that certain corner cases can be forced to be chosen more often than others. We'll see the effect of solve - before by comparing an example with and without the use of this construct.

Random Distribution Example

For example, consider the example below where a 3-bit random variable b can have 8 legal values ( 23 combinations). The probability of b getting a value 0 is the same as that of all other possible values.

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.

SystemVerilog gives us two constructs to declare conditional relations - implication and if else.

The following code snippet shows both styles

  
  
// Implication operator "->" tells that len should be 
// greater than 10 when mode is equal to 2
constraint c_mode {  mode == 2 -> len > 10; }

// Same thing can be achieved with "if-else" construct
constraint c_mode { if (mode == 2) 
						len > 10; 
				  }

  

Note that mode need not be 2 for all values of len greater than 10. However, the constraint says that len should be greater than 10 if mode is 2.

Example

  
  
class ABC;
  rand bit [2:0] mode;
  rand bit [3:0] len;
  
  constraint c_mode { mode == 2 -> len > 10; }
endclass

module tb;
  initial begin
    ABC abc = new;
    for(int i = 0; i < 10; i++) begin
      abc.randomize();
      $display ("mode=%0d len=%0d", abc.mode, abc.len);
    end
  end
endmodule

  

The simulation results show that mode need not have a value of 2 when len is greater than 10.

Simulation Log
ncsim> run
mode=1 len=11
mode=6 len=3
mode=3 len=9
mode=7 len=11
mode=3 len=15
mode=2 len=12
mode=3 len=6
mode=2 len=12
mode=4 len=9
mode=7 len=13
ncsim: *W,RNQUIE: Simulation is complete.

Implication Operator

An implication operator -> can be used in a constraint expression to show conditional relationship between two variables.

If the expression on the LHS of -> operator is true, then the constraint expression on the RHS will be satisfied. If the LHS is not true, then RHS constraint expression is not considered.

Example

  
  
class ABC;
  rand bit [3:0] mode;
  rand bit 		 mod_en;
  
  // If 5 <= mode <= 11, mod_en should be 1 constraint c_mode {	mode inside {[4'h5:4'hB]} -> mod_en == 1; }
    			
endclass

module tb;
  initial begin
    ABC abc = new;
    for (int i = 0; i < 10; i++) begin
    	abc.randomize();
      $display ("mode=0x%0h mod_en=0x%0h", abc.mode, abc.mod_en);
    end
  end
  
endmodule	

  

Note that mod_en is 1 whenever the LHS expression for mode is inside 4'h5 and 4'hB. However, mod_en can be randomized to any value if the LHS evaluates to false.

Simulation Log
ncsim> run
mode=0xf mod_en=0x1
mode=0x9 mod_en=0x1
mode=0x3 mod_en=0x1
mode=0xe mod_en=0x1
mode=0x1 mod_en=0x1
mode=0x0 mod_en=0x0
mode=0x1 mod_en=0x0
mode=0xe mod_en=0x0
mode=0x5 mod_en=0x1
mode=0x0 mod_en=0x0
ncsim: *W,RNQUIE: Simulation is complete.

if-else Constraint

The if-else constraint provides an option to specify the else part of a conditional expression. If the conditional expression is true, then all of the constraints specified in the the first constraint set shall be satisfied. Otherwise, all of the constraints in the optional else part will be satisfied.

Nested if-else blocks are allowed and multiple constraint statements require them to be enclosed in curly braces { }. This is similar to the begin-end used in a procedural block like initial and always. However, constraints are classified as declarative code and hence require curly braces instead.

Example

In the code shown below, the first if block checks whether mode is inside 5 and 11. If this condition is true, then mod_en should be constrained to 1 and if it is false, then the else part is executed. There is another if-else block within the else part which checks if mode is 1 and tries to satisfy the constraints mentioned in the appropriate parts.

  
  
class ABC;
  rand bit [3:0] mode;
  rand bit 		 mod_en;
  
  constraint c_mode {	
    					// If 5 <= mode <= 11, then constrain mod_en to 1
    					// This part only has 1 statement and hence do not
    					// require curly braces {}
    					if (mode inside {[4'h5:4'hB]}) 
  							mod_en == 1;
    
    					// If the above condition is false, then do the following
   						else {
                          // If mode is constrained to be 1, then mod_en should be 1
                          if ( mode == 4'h1) {
      							mod_en == 1;
                            // If mode is any other value than 1 and not within
                            // 5:11, then mod_en should be constrained to 0
    						} else {
      							mod_en == 0;
    						}
  						}
                    }
    			
endclass

module tb;
  initial begin
    ABC abc = new;
    for (int i = 0; i < 10; i++) begin
    	abc.randomize();
      $display ("mode=0x%0h mod_en=0x%0h", abc.mode, abc.mod_en);
    end
  end
  
endmodule

  
Simulation Log
ncsim> run
mode=0xb mod_en=0x1
mode=0x1 mod_en=0x1
mode=0x6 mod_en=0x1
mode=0x7 mod_en=0x1
mode=0x2 mod_en=0x0
mode=0x2 mod_en=0x0
mode=0x2 mod_en=0x0
mode=0x9 mod_en=0x1
mode=0x7 mod_en=0x1
mode=0x8 mod_en=0x1
ncsim: *W,RNQUIE: Simulation is complete.