Randomization examples¶
These examples compare the authored randomization portion of cpptb, Cocotb,
UVM, and pure-SystemVerilog testbenches. The repository’s cpptb and pure-SV
benchmark versions use the same xoshiro256ss-v1 stream, consume random words
in the same order, drive the same DUT transaction, and check the same response
and final checksum.
The Cocotb tabs are runnable authoring equivalents using Python’s random
module. Cocotb 2.0 seeds that module per test from COCOTB_RANDOM_SEED, but its
generator is not the exact xoshiro performance peer. See Cocotb’s official
2.0 release notes and
runner reference
for current seed configuration.
UVM tabs appear on the constrained examples. The constraints themselves are
SystemVerilog; UVM adds a standard sequence-item, sequencer, and driver
lifecycle around them. These compact references follow the explicit
start_item(), randomize(), finish_item() flow described by the
Accellera UVM 1.2 User’s Guide.
They are not another transport or performance result. Verilator 5.050 continues
to describe class support as limited and may warn when a constraint form is
ignored, so the exact repository gate remains the cpptb/pure-SV pair; see the
Verilator language guide
and CONSTRAINTIGN.
Common RNG initialization, clocking, reset, transact(...), and result
reporting are omitted from both tabs. The commands below run the complete
sources, not the excerpts.
Direct mixed stimulus¶
This workload creates a 32-bit payload from a full-width value, a weighted mask, a 65-bit packed value, and a shuffled lane order. It is a good fit for direct generation because the values do not have cross-field legality.
Task<void> random_sequence(Context& context, TestContext& test) {
auto& random = test.random();
constexpr std::array masks{
weighted(0x0000'0000u, 1),
weighted(0x0101'0101u, 2),
weighted(0x1357'9bdfu, 3),
weighted(0xa5a5'5a5au, 4),
};
for (uint32_t iteration = 0; iteration < kIterations; ++iteration) {
uint32_t payload = random.randint<uint32_t>(
0, std::numeric_limits<uint32_t>::max());
payload ^= random.weighted_choice(masks);
const Bits<65> wide = random.randbits<65>();
payload ^= wide.word(0) ^ wide.word(1);
if (wide.word(2) != 0) payload ^= 0x8000'0000u;
std::array<uint32_t, 4> order{0, 1, 2, 3};
random.shuffle(order);
payload ^= order[0] | (order[1] << 4) |
(order[2] << 8) | (order[3] << 12);
co_await transact(context, iteration, payload);
}
}
import random
async def random_sequence(dut):
masks = [0x0000_0000, 0x0101_0101, 0x1357_9BDF, 0xA5A5_5A5A]
for iteration in range(ITERATIONS):
payload = random.getrandbits(32)
payload ^= random.choices(masks, weights=[1, 2, 3, 4], k=1)[0]
wide = random.getrandbits(65)
payload ^= wide & 0xFFFF_FFFF
payload ^= (wide >> 32) & 0xFFFF_FFFF
if (wide >> 64) & 1:
payload ^= 0x8000_0000
order = [0, 1, 2, 3]
random.shuffle(order)
payload ^= order[0] | (order[1] << 4)
payload ^= (order[2] << 8) | (order[3] << 12)
await transact(dut, iteration, payload)
function automatic logic [31:0] random_payload();
logic [31:0] payload;
logic [63:0] wide;
logic [63:0] top;
int unsigned order [0:3] = '{0, 1, 2, 3};
payload = random_next_u64()[31:0];
case (random_below(10))
0: payload ^= 32'h0000_0000;
1, 2: payload ^= 32'h0101_0101;
3, 4, 5: payload ^= 32'h1357_9bdf;
default: payload ^= 32'ha5a5_5a5a;
endcase
wide = random_next_u64();
top = random_next_u64();
payload ^= wide[31:0] ^ wide[63:32];
if (top[0]) payload ^= 32'h8000_0000;
for (int unsigned remaining = 4; remaining > 1; remaining--) begin
int unsigned selected = random_below(remaining);
int unsigned temporary = order[remaining - 1];
order[remaining - 1] = order[selected];
order[selected] = temporary;
end
return payload ^ order[0] ^ (order[1] << 4) ^
(order[2] << 8) ^ (order[3] << 12);
endfunction
task automatic random_sequence();
for (int unsigned iteration = 0; iteration < kIterations; iteration++)
transact(iteration, random_payload(), 1'b0);
endtask
Run the exact pair:
make feature-test FEATURE=random_stimulus
make feature-benchmark FEATURE=random_stimulus
Selection policies and composite fields¶
This transaction combines an inside() set, weighted value/range policy, soft
default, disabled mode, nested object, fixed array, and 65-bit value. The tabs
show why the transaction model becomes more useful as policy and structure
accumulate.
class Header final : public Randomized {
public:
Rand<uint8_t> route{*this, "route"};
explicit Header(Randomized& parent) : Randomized(parent, "header") {
soft_constraint("default route", route == uint8_t{2});
}
};
class ExtendedPacket final : public Randomized {
public:
Rand<uint8_t> opcode{*this, "opcode"};
Rand<uint16_t> length{*this, "length"};
Header header{*this};
RandArray<uint8_t, 2> bytes{*this, "bytes"};
RandBits<65> token{*this, "token"};
ExtendedPacket() {
constraint("selected opcode", inside(opcode, {1, 3, 5}));
distribution(
"packet length mix",
dist(length, weighted(uint16_t{64}, 1),
weighted(range(uint16_t{128}, uint16_t{131}), 3)));
constraint("distinct prefix", bytes[0] != bytes[1]);
constraint("high token bit", token.word(2) == uint32_t{1});
auto legacy = constraint("legacy opcode", opcode == uint8_t{7});
legacy.disable();
}
};
ExtendedPacket packet;
test.randomize(packet);
import random
from dataclasses import dataclass
@dataclass(frozen=True)
class ExtendedPacket:
opcode: int
length: int
route: int
bytes: tuple[int, int]
token: int
def random_extended_packet():
while True:
byte0 = random.randrange(256)
byte1 = random.randrange(256)
if byte0 == byte1:
continue
return ExtendedPacket(
opcode=random.choice([1, 3, 5]),
length=random.choices([64, 128, 129, 130, 131],
weights=[4, 3, 3, 3, 3], k=1)[0],
route=2,
bytes=(byte0, byte1),
token=(1 << 64) | random.getrandbits(64),
)
async def extended_packet_sequence(dut):
for iteration in range(ITERATIONS):
packet = random_extended_packet()
await transact(dut, iteration, encode(packet))
class extended_packet_item extends uvm_sequence_item;
`uvm_object_utils(extended_packet_item)
rand bit [2:0] opcode;
rand bit [15:0] length;
rand bit [7:0] route;
rand bit [7:0] bytes[2];
rand bit [64:0] token;
constraint selected_opcode { opcode inside {1, 3, 5}; }
constraint length_mix {
length dist {16'd64 :/ 1, [16'd128:16'd131] :/ 3};
}
constraint default_route { soft route == 2; }
constraint distinct_prefix { bytes[0] != bytes[1]; }
constraint high_token_bit { token[64] == 1; }
constraint legacy_opcode { opcode == 7; }
function new(string name = "extended_packet_item");
super.new(name);
legacy_opcode.constraint_mode(0);
endfunction
endclass
class extended_packet_sequence extends uvm_sequence #(extended_packet_item);
`uvm_object_utils(extended_packet_sequence)
function new(string name = "extended_packet_sequence");
super.new(name);
endfunction
task body();
extended_packet_item item =
extended_packet_item::type_id::create("item");
start_item(item);
if (!item.randomize() with { route == 7; })
`uvm_fatal("RAND", "extended packet randomization failed")
finish_item(item);
endtask
endclass
function automatic logic [31:0] extended_packet_payload();
logic [7:0] opcode;
logic [15:0] length;
logic [7:0] route;
logic [7:0] byte0;
logic [7:0] byte1;
logic [31:0] token0;
logic [31:0] token1;
logic token2;
forever begin
case (random_below(3))
0: opcode = 1;
1: opcode = 3;
default: opcode = 5;
endcase
if (random_below(4) == 0)
length = 16'd64;
else
length = 16'd128 + random_below(4);
// Bound-one draws preserve the exact field-generation word stream while
// the accepted soft or hard policy fixes the resulting value.
route = 2 + random_below(1);
byte0 = random_below(256);
byte1 = random_below(256);
token0 = random_below(64'h0000_0001_0000_0000);
token1 = random_below(64'h0000_0001_0000_0000);
token2 = 1 + random_below(1);
if (byte0 == byte1) continue;
return ({24'b0, opcode} << 29) ^
({16'b0, length} << 16) ^
({24'b0, route} << 24) ^
({24'b0, byte0} << 8) ^ {24'b0, byte1} ^
token0 ^ token1 ^ ({31'b0, token2} << 31);
end
endfunction
task automatic extended_packet_sequence();
for (int unsigned iteration = 0; iteration < kIterations; iteration++)
transact(iteration, extended_packet_payload(), 1'b0);
endtask
Run the exact pair:
make feature-test FEATURE=constraint_extensions
make feature-benchmark FEATURE=constraint_extensions
The complete implementations are in
benchmarks/authoring_core/testbenches/cpp_dpi/testbench.cpp and
benchmarks/authoring_core/testbenches/systemverilog/authoring_core_sv_tb.sv.
See Performance for measured
ratios and environment qualification.