rand vs randc showed the cycle on a plain field: every value once before any repeat. Real fields carry constraints and sit beside rand fields, and that is where the cycle gets interesting.
Constraints still apply to a randc field. The cycle is a permutation of the values that are legal, not of the whole type: a 4-bit field limited to 2 to 6 deals cycles of five.
The standard also says the cycle restarts when the constraints on the field change. A test that switches a range mid-cycle should expect a fresh permutation, not the rest of the old one.
Each randc field owns its own cycle. Two 2-bit randc fields each visit 0 to 3 every four calls, but nothing coordinates them, so sixteen calls do not cover all sixteen pairs. Verilator covered 12.
Concatenate the pair into one 4-bit randc field and slice it. One cycle of sixteen calls then covers every pair exactly once.
// snippet
randc bit [1:0] a, b; // 16 calls: some pairs repeat, some never come
randc bit [3:0] ab; // 16 calls: all 16 pairs, once each
// a = ab[3:2]; b = ab[1:0];The standard solves randc fields first and alone, then solves the rand fields with the randc values fixed. A rule linking the two can only bend the rand side, so when the dealt value leaves no legal partner, the call fails.
// snippet
randc bit [3:0] slot; // standard: dealt first, then fixed
rand bit [3:0] data;
constraint c { data > slot; } // slot == 15 leaves no 4-bit data
// Standard: once per 16 calls the cycle deals 15 and randomize() returns 0.About 4: the cycle deals 15 once per 16 calls. Verilator solved slot together with data, never dealt 15, and failed none. Its cycle had quietly shrunk to 15 values.
Neither behaviour is what you want. Keep a randc field free of rules that tie it to rand fields, or limit it directly with slot inside {[0:14]} so every tool cycles the same set.
A cycle needs memory for every value, so the standard lets tools cap randc width; the cap must be at least 8 bits. Beyond it, and whenever you need weights, keep a history queue and exclude it with inside. The window can slide, and the field stays an ordinary rand field.
Need | Choice |
|---|---|
Every value of one narrow field |
|
Every pair of two fields | One wider |
Wide values, or weights, with no recent repeats |
|
Weighted values |
|
The bench runs all four cases: four cycles of the constrained field, the pair coverage, the randc and rand trap, and a history window of 8 over 200 calls.
class sub_cycle;
randc bit [3:0] v;
constraint c { v inside {[2:6]}; } // the cycle covers the 5 legal values
endclass
class two_cyclers;
randc bit [1:0] a, b; // two independent cycles
endclass
class one_cycler;
randc bit [3:0] ab; // one cycle over all 16 pairs
endclass
class slot_trap;
randc bit [3:0] slot;
rand bit [3:0] data;
constraint c { data > slot; } // no data value works when slot == 15
endclass
class no_repeat8; // history window in place of randc
rand bit [3:0] v;
bit [3:0] used[$];
constraint c_new { !(v inside {used}); }
function void post_randomize();
used.push_back(v);
if (used.size() > 8) void'(used.pop_front());
endfunction
endclass
randc in 2..6: 6 5 3 4 2 | 5 4 2 6 3 | 6 4 5 3 2 | 3 4 6 2 5 |
two 2-bit randc, 16 calls: 12 of 16 pairs
one 4-bit randc, 16 calls: 16 of 16 pairs
slot_trap, 64 calls: 0 failed, slot == 15 dealt 0 times
history window of 8: 200 calls, no repeat inside the window
PASS: constrained randc cycles over its legal values, one wide randc covers every pair, and the history window holdsdist illegal on a randc field?Weights contradict once-per-cycle visiting. The cycle already fixes each value's long-run share at exactly once per cycle, so there is nothing left to weight.
Run 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.
Two 2-bit randc fields covered 12 of 16 pairs in 16 calls; one 4-bit field covered 16. With data > slot, Verilator never dealt slot == 15 and never failed, where the standard deals it once per cycle and fails.