In this article, we will explore common examples of how to write constraints inside a SystemVerilog constraint block. Constraints are essential in SystemVerilog for controlling the behavior and properties of randomized variables.

Simple Constraint Expressions

SystemVerilog constraints allow you to define specific ranges for random variables. It’s important to note that a single relational operator such as <, <=, >, or >= should be used in each constraint expression. Using multiple relational operators in a single expression is not allowed.

Example of Simple Constraints:

class MyClass;
  rand bit [7:0] min, typ, max;

  // Valid expression
  constraint my_range {
    0 < min;
    typ < max;
    typ > min;
    max < 128;
  }

  // Invalid expression (not allowed)
  constraint my_error { 0 < min < typ < max < 128; }

  // Fixing min to 16
  constraint my_min { min == 16; }

  // Constraining max to be >= 64
  constraint my_max { max >= 64; }
endclass

Explanation:

  • The my_range constraint ensures that min, typ, and max meet specific conditions.
  • In my_min, we fix the min value to 16, while other variables are randomized within valid ranges.
  • The my_max constraint sets max to a value greater than or equal to 64.

Handling Complex Expressions:

You can also perform calculations within constraints, as shown below, where the min value is set based on a temperature conversion formula from Celsius to Fahrenheit.

constraint my_min { min == temp.low * 9/5 + 32; }

In this example, temp.low is a variable representing temperature in Celsius, and min stores the converted value in Fahrenheit.

Advanced Example with Fixed and Random Variables

Let’s consider an example where some variables are fixed while others are randomized. Here, fixed will always have a value of 5, while min, typ, and max are randomized but meet the constraints.

class myClass;
  rand bit [3:0] min, typ, max;
  rand bit [3:0] fixed;

  constraint my_range {
    3 < min;
    typ < max;
    typ > min;
    max < 14;
  }

  constraint c_fixed { fixed == 5; }

  function string display ();
    return $sformatf ("min=%0d typ=%0d max=%0d fixed=%d", min, typ, max, fixed);
  endfunction
endclass

module tb;
  initial begin
    for (int i = 0; i < 10; i++) begin
      myClass cls = new ();
      cls.randomize();
      $display ("itr=%0d %s", i, cls.display());
    end
  end
endmodule

Simulation Output:

# KERNEL: itr=0 min=7 typ=9 max=12 fixed= 5
# KERNEL: itr=1 min=4 typ=9 max=12 fixed= 5
# KERNEL: itr=2 min=7 typ=10 max=13 fixed= 5
# KERNEL: itr=3 min=4 typ=6 max=11 fixed= 5
# KERNEL: itr=4 min=8 typ=12 max=13 fixed= 5
...
# KERNEL: Simulation has finished.

As seen from the output, fixed remains constant at 5 while the values for min, typ, and max are randomized based on the constraints.

Using the inside Operator

The inside operator can help define a range for a variable. Instead of using multiple relational operators, this operator allows you to specify a lower and upper limit more easily.

Example of Using inside:

constraint my_range { typ > 32; typ < 256; }
constraint new_range { typ inside {[32:256]}; }

In the above example, both constraints are equivalent. new_range defines that typ must be within the range of 32 to 256.

Specifying Specific Values Using inside:

constraint spec_range { typ inside {32, 64, 128}; }

This constraint specifies that typ can only be one of the values 32, 64, or 128.

Inverted inside Operator

You can also use the inverted inside operator to ensure that a variable does not fall within a specific range.

rand bit [2:0] typ;
constraint inv_range { ! (typ inside {[3:6]}); }

In this example, typ will never have values between 3 and 6. Random values for typ will be chosen outside of this range.

Simulation Output:

# KERNEL: itr=0 typ=7
# KERNEL: itr=1 typ=0
# KERNEL: itr=2 typ=7
# KERNEL: itr=3 typ=0
...
# KERNEL: Simulation has finished.

As expected, the values for typ avoid the range 3-6.

Weighted Distribution with dist Operator

The dist operator allows you to create a weighted distribution for a variable. You can assign different weights to values, making some values more likely to be chosen than others.

Using := Operator:

rand bit [2:0] typ;
constraint dist1 {
  typ dist { 0:=20, [1:5]:=50, 6:=40, 7:=10; }
}

In dist1, the weight of 0 is 20, 6 is 40, and 7 is 10, while the values between 1 and 5 share a weight of 50. The probability of choosing each value depends on its weight.

Example Code for Distribution:

class myClass;
  rand bit [2:0] typ;
  constraint dist1 {
    typ dist { 0:=20, [1:5]:=50, 6:=40, 7:=10; }
  }
endclass

module tb;
  initial begin
    for (int i = 0; i < 10; i++) begin
      myClass cls = new ();
      cls.randomize();
      $display ("itr=%0d typ=%0d", i, cls.typ);
    end
  end
endmodule

Simulation Output:

# KERNEL: itr=0 typ=5
# KERNEL: itr=1 typ=1
# KERNEL: itr=2 typ=6
# KERNEL: itr=3 typ=3
...
# KERNEL: Simulation has finished.

As seen in the output, values between 1 and 5 are chosen more often due to their higher weight.

Using :/ Operator:

The :/ operator divides the total weight evenly among the specified values.

rand bit [2:0] typ;
constraint dist2 {
  typ dist { 0:/20, [1:5]:/50, 6:/10, 7:/20; }
}

In dist2, the weight of 0 is 20, 6 is 10, and 7 is 20, while the values between 1 and 5 share a weight of 50.

Example Code for :/ Distribution:

class myClass;
  rand bit [2:0] typ;
  constraint dist2 {
    typ dist { 0:/20, [1:5]:/50, 6:/40, 7:/10; }
  }
endclass

module tb;
  initial begin
    for (int i = 0; i < 10; i++) begin
      myClass cls = new ();
      cls.randomize();
      $display ("itr=%0d typ=%0d", i, cls.typ);
    end
  end
endmodule

Simulation Output:

# KERNEL: itr=0 typ=5
# KERNEL: itr=1 typ=6
# KERNEL: itr=2 typ=4
# KERNEL: itr=3 typ=6
...
# KERNEL: Simulation has finished.

In this case, 6 is more frequent because it has the highest weight.

Bidirectional Constraints

SystemVerilog applies constraints in parallel rather than in sequence. This means all constraints are active at the same time, which can lead to complex interactions between constraints.

Example:

class myClass;
  rand bit [3:0] val;
  constraint c1 { val > 3; val < 12; }
  constraint c2 { val >= 10; }
endclass

module tb;
  initial begin
    for (int i = 0; i < 10; i++) begin
      myClass cls = new ();
      cls.randomize();
      $display ("itr=%0d typ=%0d", i, cls.val);
    end
  end
endmodule

Simulation Output:

# KERNEL: itr=0 typ=11
# KERNEL: itr=1 typ=11
# KERNEL: itr=2 typ=11
# KERNEL: itr=3 typ=10
...
# KERNEL: Simulation has finished.

The constraints c1 and c2 limit val to either 10 or 11.

Scroll to Top