Inside, implication and distributions

Describe legal values with inside and implication, set how often each one appears with dist, and measure the mix you actually get.

Lesson
5.2
Time
25 min
Runs on
Verilator
Check
5 questions

The test plan says that single-beat writes are part of every regression. After a night of runs, the scoreboard has checked 40,000 bursts and not one write, and no test failed. A new block, constraint reads { beats >= 2; }, was meant for reads only. Together with is_write -> beats == 1, it made every write impossible, so the solver set is_write to 0 every time.

Every burst is a read, and nothing reports it
01234567is_writebeats37258246reads block added

One column per generated burst. Each burst is legal, so no checker complains.

Sets and ranges with inside

x inside {1, 2, 4, 8} is true when x equals one of the listed values. A range [lo:hi] includes both ends, so beats inside {[1:8]} allows 1 and 8. To exclude a set, negate the whole test, as in !(addr inside {[8'hF0:8'hFF]}).

A set can mix single values and ranges, as in beats inside {1, 2, 4, [6:8]}, where 3 and 5 are left out.

Write a range low end first. A range with its ends reversed, such as [8:1], is an empty set, so a constraint that requires it can never hold and every call fails.

inside works the same way in ordinary code, so a checker can reuse the exact set from the constraint: if (!(t.beats inside {[1:8]})) $error(...).

An implication can switch its own condition off

a -> b means "if a, then b". It is satisfied whenever a is false, so it is the same rule as !a || b.

Constraints in different blocks interact exactly like constraints in one block. The solver sees all active blocks of the object as one set of rules, so a block added for one purpose can change the outcome of another.

The solver treats every constraint as a fact about all the random fields together. If b can never be true, the only way left to satisfy a -> b is to make a false, and the solver does exactly that, without a message.

A new rule for every burst

constraint legal { is_write -> beats == 1; }
constraint reads { beats >= 2; }

Writes become impossible, so every call returns a read and succeeds.

The new rule only for reads

constraint legal { is_write -> beats == 1; }
constraint reads { !is_write -> beats >= 2; }

Writes keep one beat, reads get two or more, and both kinds appear.

if (is_write) beats == 1; else beats >= 2; states the same pair of rules and is often easier to read. Whichever form you use, measure the mix: count how often each kind of burst appears. A kind that is rare or missing is the sign of a condition the constraints have quietly switched off.

Implication also works in one direction only. is_write -> beats == 1 says nothing about reads, so reads may have 1 beat as well. When you mean that writes, and only writes, have one beat, write the two-way rule is_write == (beats == 1).

Weights with dist

dist gives values different chances. Each item in the list has a weight, and an item is chosen with probability equal to its weight divided by the total of all the weights.

Weights are relative, so {0 := 4, 1 := 1} and {0 := 40, 1 := 10} mean the same thing. Only the ratio between them matters.

For a range there are two ways to apply a weight. := gives the weight to every value in the range, while :/ gives it to the range as a whole and shares it among the values.

Weight per value

x dist {0 := 4, [1:3] := 1};
// weights 4, 1, 1, 1: total 7

Value 0 is chosen with probability 4/7, and 1, 2 and 3 with 1/7 each.

Weight per range

x dist {0 := 4, [1:3] :/ 1};
// weights 4 and 1: total 5

Value 0 is chosen with probability 4/5, and the range shares 1/5, so 1/15 each.

Value:= expected:= measured:/ expected:/ measured
0229232320318
157552735
257632727
357502720

Counts from 400 calls each, measured by the example on Verilator 5.052. Another simulator or seed gives different counts near the same expected values.

The measured counts wander around the expected ones by up to a dozen. That is normal for 400 samples, so compare shares with a tolerance, never with an exact number.

A weight sets frequency, never legality

A dist also limits the field to the values it lists, just as inside would, and a value with weight 0 is never chosen. It never makes a value legal that another constraint forbids.

Weights only matter among values that are legal. If a weighted value happens to be the only way to satisfy the other rules, it is chosen every time, however small its weight.

A common use is to push stimulus toward the corners. beats dist {1 := 3, [2:7] :/ 2, 8 := 3} makes the shortest and longest bursts frequent and still keeps the middle lengths in play, which is where length-dependent bugs tend to hide.

A dist weights the values of the expression it is attached to. To weight a relationship between fields, attach it to a condition, as lesson 5.7 does with (int'(addr) + 4 * beats == 256) dist {1 := 1, 0 := 3}.

Measuring is cheap: one counter per value and a $display at the end of the test. Keep those counts in the regression log, and a change in the mix shows up as a change in the numbers.

Try it

The two weightings differ only in := and :/, yet 0 appears about 58% of the time in one and 80% in the other. The burst class shows the opening story: 100 legal bursts and 0 writes, with no error.

Change constraint reads to !is_write -> beats >= 2; and predict roughly how many of the 100 bursts become writes.

RUNVerilator · simulator service
Edit the code, then run it
Expected: := counts for 0,1,2,3: 232 55 63 50 :/ counts for 0,1,2,3: 318 35 27 20 writes in 100 bursts: 0

Check yourself

Predict the outputEach line of the output is 1 if more than 70% of the 400 values were 0. Predict the exact output.
class weights;
  rand bit [1:0] a;
  rand bit [1:0] b;
  constraint w {
    a dist {0 := 4, [1:3] := 1};
    b dist {0 := 4, [1:3] :/ 1};
  }
endclass

module tb;
  weights w = new;
  int za, zb;
  initial begin
    repeat (400) begin
      if (!w.randomize()) $fatal(1, "randomize failed");
      za += (w.a == 0);
      zb += (w.b == 0);
    end
    $display("%0d %0d", za > 280, zb > 280);
  end
endmodule
Spot the bugWrites must be single-beat bursts, and reads must have 2 to 8 beats. Each option is the body of the class's rules constraint block. Which option makes writes disappear?
ConceptWith beats dist {1 := 1, [2:5] :/ 1}; and no other rule on beats, how likely is beats == 3?
ConceptA constraint says kind dist {0 := 1, 1 := 1};. In 40 calls you count 24 zeros and 16 ones. What does that show?
ConceptWhich constraint makes a misaligned addr impossible?

Remember

  • inside tests membership in a set; ranges [lo:hi] include both ends.
  • a -> b is satisfied by making a false, so a rule that rules out b silently removes every a.
  • := puts the weight on each value of a range; :/ shares one weight across the whole range.
  • Weights change how often legal values appear; they never make a value legal.
  • Measure the mix you get, and compare shares with a tolerance.

Practice