The SystemVerilog constraint solver, by default, tries to give a uniform distribution across the entire space of valid combinations — not necessarily a uniform distribution for each individual variable on its own. solve...before can change that, shifting probability so certain corner cases get chosen far more often than they otherwise would. This article compares an example with and without it.

14 min read | Intermediate Level

What You'll Learn

  • Why the solver aims for a uniform distribution across the whole solution space by default, not independently for each variable
  • How that default behavior can make one variable's value far less likely than you might expect, just because it happens to leave fewer combinations available
  • solve...before, and how it gives a variable an independent, even distribution instead
  • The restrictions on which variables can be ordered this way

Random Distribution Example

A 3-bit random variable b has 8 legal values, and the probability of b landing on any one of them is the same:


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

Output

Output
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.

Repeated values in the output don't mean anything special — a previous randomization has no effect on the current one, so the randomizer is free to pick any of the 8 values regardless of what came before. Every one of the 8 legal values has an equal chance of being chosen.

Without solve...before

Now consider two random variables, where the constraint ensures b gets 0x3 whenever a is 1:


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

Output

Output
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.

a and b are determined together, not one after the other. When a is 0, b can take any of 4 values — 4 valid combinations. When a is 1, b can only be 3 — just 1 valid combination. That's 5 valid (a, b) pairs total, and by default the solver treats all 5 as equally likely:

abProbability
001/5 (20%)
011/5 (20%)
021/5 (20%)
031/5 (20%)
131/5 (20%)

Because only 1 of those 5 equally-likely combinations has a=1, the overall probability of a ending up 1 is just 1/5, or 20% — noticeably less than the 50/50 you might expect from a single-bit variable on its own.

Quick Check

Q: If b were 3 bits wide (8 possible values) instead of 2, with the same a -> b == 7 style constraint, would P(a=1) go up or down compared to this 20% example?

Show Answer

Down — to 1/9, about 11%. There would be 8 valid combinations with a=0 (one per value of b) plus 1 with a=1, for 9 total. A wider b means more combinations share the "pie," shrinking a=1's relative share even further.

Probability of a being 1 without and with solve a before b

With solve...before

SystemVerilog lets you order variables so one is chosen independently of another, using the solve keyword:


class ABC;
  rand  bit			a;
  rand  bit [1:0] 	b;
  
  constraint c_ab { a -> b == 3'h3;

  				  // Tells the solver that "a" has
  				  // to be solved before attempting "b"
  				  // Hence value of "a" determines value
  				  // of "b" here
                  	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

Output

Output
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.

Compare this against the previous output — a=1 now shows up far more often. a is solved first, and whatever it gets determines how b is solved next:

abProbability
001/2 × 1/4 = 1/8 (12.5%)
011/2 × 1/4 = 1/8 (12.5%)
021/2 × 1/4 = 1/8 (12.5%)
031/2 × 1/4 = 1/8 (12.5%)
131/2 (50%)

Because a is now solved on its own, the probability of it landing on either 0 or 1 is a flat 50%. Only once a is settled does the solver work out b — freely among 4 values if a=0 (each getting a quarter of that 50%, or 12.5% overall), or forced straight to 3 if a=1 (keeping the whole 50%, since there's nothing left to choose). P(a=1) has jumped from 20% to 50%, exactly the shift solve...before is meant to produce.

Quick Check

Q: With solve a before b, does the constraint a -> b == 3'h3 still hold in every generated combination?

Show Answer

Yes. solve...before only changes the probability weighting among already-legal combinations — it never makes an illegal combination possible or removes a legal one. Every row in both probability tables still satisfies the original constraint.

Restrictions

A few restrictions apply to solve...before:

  • randc variables aren't allowed, since they're always solved first regardless
  • Variables should be integral values
  • There must be no circular dependency in the ordering, such as solve a before b combined with solve b before a

Where Is This Used?

solve...before is commonly used to boost the probability of a specific, interesting corner case — like an error-injection condition — that would otherwise get drowned out by the sheer number of "normal" combinations in the total solution space. Exactly the a=1 (error case) versus a=0 (normal case) pattern demonstrated in this article.

Common Beginner Mistakes

Mistake #1: Assuming every legal value is independently equally likely by default

As the "Without solve...before" example shows, the solver aims for uniform distribution across the entire joint solution space, not per variable. Why? A variable's value can end up much rarer than expected if it happens to correspond to fewer overall valid combinations than its alternatives.

Mistake #2: Assuming solve...before changes which values are legal

As the second Quick Check shows, it doesn't — the constraint still holds in every case. Why? solve...before only re-weights the probability among combinations that were already valid; it has no effect on legality at all.

Mistake #3: Trying to order a randc variable with solve...before

This isn't allowed. Why? A randc variable is always solved first regardless of any ordering hint, since its cyclic behavior depends on being resolved independently of everything else.

Mistake #4: Creating a circular solve...before ordering

solve a before b combined with solve b before a is illegal. Why? The solver needs a well-defined order to resolve variables in — a circular dependency gives it no valid starting point.

Practice Exercise

Challenge: Compute the Probability Shift

Given rand bit a;, rand bit [2:0] b; (3 bits this time), and constraint c { a -> b == 3'h7; }, compute P(a=1) both without and with solve a before b;.

Hint

Without solve...before, count the total valid (a,b) combinations and treat them as equally likely. With it, a is solved independently first.

Solution

Without solve...before: P(a=1) = 1/9 ≈ 11.1%. There are 8 valid combinations with a=0 (one per value of b) and 1 with a=1, for 9 total, all equally likely.

With solve a before b;: P(a=1) = 50%. Once a is solved on its own with a flat 50/50 split, b is resolved afterward — forced to 7 whenever a=1, so that entire 50% stays intact regardless of how many values b could otherwise take.

Summary

In this tutorial, you learned:

  • By default, the constraint solver aims for a uniform distribution across the entire joint solution space, not independently for each variable
  • This can make a variable's value much less likely than expected if it corresponds to fewer overall valid combinations than its alternatives
  • solve a before b; gives a an independent, even distribution, then resolves b based on whatever a turned out to be
  • solve...before changes probability weighting only — it never changes which combinations are actually legal
  • randc variables can't be ordered with solve...before, and circular orderings are illegal
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5