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 thatmin
,typ
, andmax
meet specific conditions. - In
my_min
, we fix themin
value to 16, while other variables are randomized within valid ranges. - The
my_max
constraint setsmax
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.