The inside operator restricts a variable to a set built from values and ranges. The statement x inside {[1:5], 10} means x is one of 1, 2, 3, 4, 5 or 10.
The LRM requires a uniform pick over that set (IEEE 1800 18.5.10), which is the interview model. Real solvers only approximate it, so the bench checks membership and that every legal value appears, never exact shares.
// snippet: the class from the bench below
class pkt;
rand bit [7:0] len;
rand bit [7:0] kind;
// Ranges and discrete values mix freely in one set
constraint len_c { len inside {[1:8], 16, 32, [64:67]}; }
// Negation: kind may be anything EXCEPT 0 and the range 200..255
constraint kind_c { !(kind inside {0, [200:255]}); }
endclassThe len set holds 8 + 1 + 1 + 4 = 14 values. The negated kind set excludes 1 + 56 values, leaving 199 legal values.
Form | Meaning | Size here |
|---|---|---|
| Union of two ranges and two values | 14 |
| Full type range minus the listed set | 199 |
Single value | One member of the union | 1 |
Negation inverts a set, which suits reserved-encoding exclusions. Open ranges with $ track the type bounds when widths change.
// snippet: also in the bench below
class addr_txn;
rand bit [15:0] addr; // unsigned: low dollar = 0, high dollar = 65535
rand int delta; // signed: low dollar = most negative int
constraint hi_region { addr inside {[16'h8000:$]}; } // 0x8000..0xFFFF
constraint neg_only { delta inside {[$:-1]}; } // any negative int
endclassFor unsigned addr, low $ is 0 and high $ is 65535. For signed delta, low $ is the most negative int, so a floor of zero needs an explicit bound.
Bound | Unsigned field | Signed field |
|---|---|---|
Low | Zero | Most negative value of the type |
High | Largest value of the type | Largest value of the type |
| Every value: -1 becomes all-ones unsigned, so this is a no-op and likely a bug | Every negative value |
// snippet: also in the bench below
class cfg_txn;
int legal_ids[$] = '{3, 7, 11, 42}; // non-rand state: a runtime knob
rand int id;
constraint id_c { id inside {legal_ids}; }
endclassThe array form reads the current element values at each randomize call. This is standard (IEEE 1800 11.4.13): unpacked arrays in the set are expanded to their elements.
len inside {[1:8], 16, 32, [64:67]} allows 14 values. A second constraint, len % 2 == 0, is added. How many values are legal now?8: 2, 4, 6, 8, 16, 32, 64 and 66. The two constraints intersect, so only values that pass both remain.
Each added constraint intersects with the rest and never widens the space. An empty intersection makes randomize return 0 instead of picking a near value.
x inside {[1:5], 10}, and what does the interview model say about each value?Six values: 1, 2, 3, 4, 5 and 10. The interview model gives each about 1/6, because the default aims at uniform over the set, not over the two syntax items.
inside set. What does randomize return?It returns 0. Constraints intersect, so an empty intersection has no solution to return.
The three classes above, solved 3,000 times: every value stays in its set, all 14 lengths appear, the array set follows a change to legal_ids, and an inline len % 2 == 0 leaves exactly 8 values.
class pkt;
rand bit [7:0] len;
rand bit [7:0] kind;
// Ranges and discrete values mix freely in one set
constraint len_c { len inside {[1:8], 16, 32, [64:67]}; }
// Negation: kind may be anything EXCEPT 0 and the range 200..255
constraint kind_c { !(kind inside {0, [200:255]}); }
endclass
class addr_txn;
rand bit [15:0] addr; // unsigned: low $ is 0, high $ is 65535
rand int delta; // signed: low $ is the most negative int
constraint hi_region { addr inside {[16'h8000:$]}; }
constraint neg_only { delta inside {[$:-1]}; }
endclass
class cfg_txn;
int legal_ids[$] = '{3, 7, 11, 42}; // non-rand state: a run-time knob
rand int id;
constraint id_c { id inside {legal_ids}; }
endclass
len values seen: 14 of 14; with len % 2 == 0: 8 of 8
PASS: 600 rounds stayed in every set, all 14 lengths appeared, and the intersection left exactly 8One way to write it:
typedef enum bit [1:0] {DATA = 2'd0, CTRL = 2'd1, ERR = 2'd2} kind_e;
class pkt_txn;
rand kind_e kind;
rand bit [1:0] dest;
rand bit [3:0] len;
constraint c_len { len inside {[1:8]}; } // the spec: 1 to 8 payload bytes
constraint c_dest { dest inside {[0:2]}; } // port 3 does not exist yet
constraint c_kind { kind inside {DATA, CTRL, ERR}; }
endclass
module tb_pkt_legal;
pkt_txn p = new();
int dests[int];
initial begin
repeat (1000) begin
if (!p.randomize()) $fatal(1, "FAIL: randomize failed");
if (p.len == 0 || p.len > 8 || p.dest == 3) $fatal(1, "FAIL: illegal packet len %0d dest %0d", p.len, p.dest);
dests[int'(p.dest)]++;
end
$display("dest counts: 0=%0d 1=%0d 2=%0d", dests[0], dests[1], dests[2]);
if (dests.num() != 3) $fatal(1, "FAIL: a legal dest never appeared");
$display("PASS: 1000 packets, every one legal: len 1 to 8, dest 0 to 2");
$finish;
end
endmoduledest counts: 0=373 1=332 2=295
PASS: 1000 packets, every one legal: len 1 to 8, dest 0 to 2Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown. Verilator prints a harmless WIDTHTRUNC warning for if (!obj.randomize()), because randomize() returns an int, and an UNSATCONSTR warning when a solve fails on purpose. Icarus 13 does not support constraints.