In SystemVerilog, the constraint solver usually generates random values with equal probability, ensuring that each valid solution has the same chance of being selected. However, you can control the order in which variables are solved using the solve before construct. This can make certain corner cases more likely to be selected compared to others. Let’s explore how the solve before feature works by comparing an example with and without this construct.
Random Distribution Example
Consider a scenario where a 3-bit random variable b
can take 8 possible values (from 0 to 7). Without any special constraints, each value has an equal chance of being selected. Here’s the example code for this:
Example Code:
class ABC;
rand bit [2:0] b;
endclass
module tb;
initial begin
ABC abc = new;
for (int i = 0; i < 10; i++) begin
abc.randomize();
$display("b=%0d", abc.b);
end
end
endmodule
In the simulation output below, you will see that even though some values repeat, this doesn’t mean that the randomization had no effect. It simply shows that previous randomizations don’t affect the current iteration, and each value can be chosen freely.
Simulation Log:
ncsim> run
b=7
b=7
b=2
b=1
b=6
b=4
b=2
b=4
b=0
b=1
ncsim: *W,RNQUIE: Simulation is complete.
Here, the probability of b
being any of the 8 legal values is the same for all possible values.
Without solve-before
Let’s now consider an example with two random variables a
and b
. The constraint in this case ensures that b
should always be 3’h3
whenever a
is 1
. We’ll look at the behavior of randomization here without using solve-before
.
Example Code:
class ABC;
rand bit a;
rand bit [1:0] b;
constraint c_ab { a -> b == 3'h3; }
endclass
module tb;
initial begin
ABC abc = new;
for (int i = 0; i < 8; i++) begin
abc.randomize();
$display("a=%0d b=%0d", abc.a, abc.b);
end
end
endmodule
Simulation Log:
ncsim> run
a=0 b=0
a=0 b=1
a=0 b=0
a=0 b=1
a=0 b=2
a=1 b=3
a=0 b=3
a=0 b=3
ncsim: *W,RNQUIE: Simulation is complete.
- When
a
is0
,b
can take any value from 0 to 3, meaning there are 4 possible combinations. - When
a
is1
,b
can only be3’h3
, so there’s only 1 valid combination.
The total number of combinations is 5, and if the constraint solver has to assign equal probability, the chance of any combination is 1/5.
Probability Table Without solve-before
:
a | b | Probability |
---|---|---|
0 | 0 | 1/(1 + 22) |
0 | 1 | 1/(1 + 22) |
0 | 2 | 1/(1 + 22) |
0 | 3 | 1/(1 + 22) |
1 | 3 | 1/(1 + 22) |
With solve-before
Now let’s introduce the solve-before construct. This allows you to solve one variable before another, changing the way random values are chosen. In this case, we ensure that a
is solved first, and b
depends on the value of a
.
Example Code with solve-before
:
class ABC;
rand bit a;
rand bit [1:0] b;
constraint c_ab {
a -> b == 3'h3; // If a is 1, b will be 3'h3
solve a before b; // Solve 'a' before 'b'
}
endclass
module tb;
initial begin
ABC abc = new;
for (int i = 0; i < 8; i++) begin
abc.randomize();
$display("a=%0d b=%0d", abc.a, abc.b);
end
end
endmodule
Simulation Log with solve-before
:
ncsim> run
a=1 b=3
a=1 b=3
a=0 b=1
a=0 b=0
a=0 b=0
a=0 b=1
a=1 b=3
a=0 b=2
ncsim: *W,RNQUIE: Simulation is complete.
Here, we see that a is solved first, and based on its value, b is then solved. This affects the probability of each combination. When a
is 0
, b
can be any of 4 values. When a
is 1
, b
must be 3’h3
.
Probability Table with solve-before
:
a | b | Probability |
---|---|---|
0 | 0 | 1/2 * 1/22 |
0 | 1 | 1/2 * 1/22 |
0 | 2 | 1/2 * 1/22 |
0 | 3 | 1/2 * 1/22 |
1 | 3 | 1/2 |
The probability of b
being 3
is now 50% when a
is 1
, while it’s distributed among the other values when a
is 0
.
Restrictions of solve-before
There are some restrictions when using solve-before:
- randc variables cannot be used because they are always solved first.
- The variables must be integral values.
- Circular dependencies (e.g., solving
a
beforeb
andb
beforea
) are not allowed.
Conclusion
Using solve-before in SystemVerilog allows you to control the order in which variables are solved during randomization, affecting the distribution of possible values. This is helpful when you need to enforce certain dependencies between variables while still maintaining a degree of randomization. Keep in mind the restrictions and use this construct carefully to avoid creating circular dependencies.