Randomization With rand and randc
The previous article built transaction objects. This one lets them fill themselves in.
Section 6.4 closed with directed testing: hand-picking inputs that represent the behaviours most likely to fail. That is real, necessary work and nothing here replaces it. But directed testing can only exercise the cases a person thought of. Randomized testing generates a large volume of legal, varied inputs automatically and lets coverage find the corner case nobody thought of.
Declaring randomizable fields
Add rand to a class field and the built-in randomization engine can fill it with a random value on demand:
class alu_transaction;
rand bit [7:0] operand_a, operand_b;
rand bit [2:0] opcode;
bit [7:0] expected_result; // NOT rand: this gets computed, not randomized
endclass
initial begin
alu_transaction t = new();
repeat (5) begin
t.randomize(); // fills operand_a, operand_b, and opcode with fresh random values
$display("a=%h b=%h op=%b", t.operand_a, t.operand_b, t.opcode);
end
end
Each call to randomize() produces a fresh combination of every rand field: five calls, five (very likely) different combinations, generated rather than typed one at a time like every vector in Section 6.4.
rand versus randc
Plain rand can produce the same value twice in a row; it is random with no memory of what it produced before. randc, random cyclic, guarantees every value in the field’s range appears exactly once before any value repeats, cycling through the whole range in a random order.
class opcode_generator;
randc bit [2:0] opcode; // cycles through all 8 possible 3-bit values before repeating ANY
endclass
initial begin
opcode_generator g = new();
repeat (8) begin
g.randomize();
$display("opcode = %b", g.opcode); // guaranteed: all 8 values appear exactly once
end
end
This is the right tool when you want exhaustive coverage of a small, enumerable set: every one of an ALU’s 8 opcodes, every legal state of an FSM, without relying on rand’s luck to eventually hit all of them and without writing eight directed cases. For a field with a huge range, a 32-bit data value say, randc’s guarantee is either impractical (it would visit all 4 billion values before repeating) or simply not the property you want, and plain rand is right.
What randomization finds that directed testing cannot
Take the saturating adder from Section 7.2. A directed suite hand-picks a few overflow cases, both operands near the maximum, both near the minimum, because someone thought to test the boundary. A randomized test run for thousands of iterations over the whole operand range will stumble onto combinations at the overflow boundary a person might never construct, through volume rather than insight, including the pair of operands that exposes an off-by-one in the overflow detection that only shows for one combination out of millions.
Neither approach replaces the other. A real verification plan uses both: directed tests for known, specific, high-risk cases (a corner case that caused a previous silicon bug gets a permanent directed regression test), and randomized tests for the broad coverage that volume can reach. Constrained-random verification, once you add the constraints from the next article, is the dominant methodology for any non-trivial design, and the bulk of a real regression’s volume comes from it.
Randomization still needs a scoreboard
Randomization generates stimulus. It says nothing about whether the response was correct. Every randomized test still needs the self-checking infrastructure from Module 6: a reference model or golden calculation, compared automatically against the DUT’s output, with pass or fail reported per transaction. Randomization without a scoreboard is a lot of activity with nobody watching whether any of it was right.
Practice Exercise
A 4-bit priority encoder (Section 5.3) has 16 possible input combinations. Would rand or randc be the better choice for exhaustively covering every combination with the fewest randomize() calls, and why?
Solution
randc. With 16 combinations, its guarantee that every value appears once before any repeats means exactly 16 calls cover every input, with no calls wasted on a value already tested. Plain rand has no such guarantee: with independent draws, covering all 16 values takes noticeably more than 16 calls on average (the coupon collector’s problem) and has no upper bound at all. It is possible, if unlikely, for rand to take dozens of calls before hitting the last untested value. For a small, enumerable input space, randc is faster and gives an explicit coverage guarantee.
rand fields let an object generate its own random values, producing volume no one would type as directed vectors; randc adds the guarantee that every value in a range appears once before any repeats, which is right for small enumerable sets like opcodes. Randomization complements directed testing rather than replacing it, and it does nothing useful without the scoreboard from Section 6.4 judging the results. In the next article we constrain the randomization so it only generates legal values, not every value the field’s width allows.