# 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](https://docs.cocotb.org/en/stable/release_notes.html) and [runner reference](https://docs.cocotb.org/en/stable/library_reference.html#cocotb_tools.runner.Runner.test) 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](https://www.accellera.org/images/downloads/standards/uvm/uvm_users_guide_1.2.pdf). 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](https://verilator.org/guide/latest/languages.html) and [`CONSTRAINTIGN`](https://verilator.org/guide/latest/warnings.html#cmdoption-arg-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.
cpptb (C++ DPI)
```cpp Task 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( 0, std::numeric_limits::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 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); } } ```
Cocotb
```python 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) ```
Pure SystemVerilog
```systemverilog 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: ```sh make feature-test FEATURE=random_stimulus make feature-benchmark FEATURE=random_stimulus ``` ## Related packet fields The packet has range, alignment, modulo, and cross-field rules. cpptb keeps those rules with the transaction. The exact pure-SV peer performs the same candidate generation and rejection explicitly because that is the portable Verilator implementation being measured.
cpptb (C++ DPI)
```cpp class Packet final : public Randomized { public: Rand opcode{*this, "opcode"}; Rand length{*this, "length"}; Rand address{*this, "address"}; Rand tag{*this, "tag"}; Packet() { constraint("supported opcode", opcode <= uint8_t{6}); constraint("packet length", length >= uint16_t{64} && length <= uint16_t{1500}); constraint("address window", address >= uint16_t{0x1000} && address <= uint16_t{0x1fff}); constraint("word-sized packet", length % uint16_t{4} == 0); constraint("aligned address", address % uint16_t{4} == 0); constraint("short control packet", opcode != uint8_t{6} || length <= uint16_t{256}); } uint32_t payload() const { return (uint32_t{opcode.get()} << 29) ^ (uint32_t{length.get()} << 16) ^ (uint32_t{address.get()} << 1) ^ tag.get(); } }; Task packet_sequence(Context& context, TestContext& test) { Packet packet; for (uint32_t iteration = 0; iteration < kIterations; ++iteration) { test.randomize(packet); co_await transact(context, iteration, packet.payload()); } } ```
Cocotb
```python import random from dataclasses import dataclass @dataclass(frozen=True) class Packet: opcode: int length: int address: int tag: int def payload(self): return ((self.opcode << 29) ^ (self.length << 16) ^ (self.address << 1) ^ self.tag) & 0xFFFF_FFFF def random_packet(): while True: packet = Packet( opcode=random.randrange(7), length=random.randrange(64, 1501), address=random.randrange(0x1000, 0x2000), tag=random.randrange(256), ) if packet.length % 4 != 0 or packet.address % 4 != 0: continue if packet.opcode == 6 and packet.length > 256: continue return packet async def packet_sequence(dut): for iteration in range(ITERATIONS): await transact(dut, iteration, random_packet().payload()) ```
UVM
```systemverilog class packet_item extends uvm_sequence_item; `uvm_object_utils(packet_item) rand bit [2:0] opcode; rand bit [15:0] length; rand bit [15:0] address; rand bit [7:0] tag; constraint legal { opcode <= 6; length inside {[64:1500]}; address inside {[16'h1000:16'h1fff]}; length % 4 == 0; address % 4 == 0; opcode == 6 -> length <= 256; } function new(string name = "packet_item"); super.new(name); endfunction endclass class packet_sequence extends uvm_sequence #(packet_item); `uvm_object_utils(packet_sequence) function new(string name = "packet_sequence"); super.new(name); endfunction task body(); repeat (ITERATIONS) begin packet_item item = packet_item::type_id::create("item"); start_item(item); if (!item.randomize()) `uvm_fatal("RAND", "packet_item randomization failed") finish_item(item); // the driver translates fields to DUT pins end endtask endclass ```
Pure SystemVerilog
```systemverilog function automatic logic [31:0] constrained_packet_payload(); logic [7:0] opcode; logic [15:0] length; logic [15:0] address; logic [7:0] tag; forever begin opcode = random_below(7); length = 16'd64 + random_below(1437); address = 16'h1000 + random_below(4096); tag = random_below(256); if ((length % 4) != 0) continue; if ((address % 4) != 0) continue; if ((opcode == 6) && (length > 256)) continue; return ({24'b0, opcode} << 29) ^ ({16'b0, length} << 16) ^ ({16'b0, address} << 1) ^ {24'b0, tag}; end endfunction task automatic packet_sequence(); for (int unsigned iteration = 0; iteration < kIterations; iteration++) transact(iteration, constrained_packet_payload(), 1'b0); endtask ``` Run the exact pair: ```sh make feature-test FEATURE=constrained_packet make feature-benchmark FEATURE=constrained_packet ``` ## 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.
cpptb (C++ DPI)
```cpp class Header final : public Randomized { public: Rand 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 opcode{*this, "opcode"}; Rand length{*this, "length"}; Header header{*this}; RandArray 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); ```
Cocotb
```python 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)) ```
UVM
```systemverilog 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 ```
Pure SystemVerilog
```systemverilog 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: ```sh 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](../performance.md#deterministic-random-stimulus) for measured ratios and environment qualification. ## Related pages - [Random value generation](value-generation.md) documents the direct `test.random()` API used in the mixed-stimulus example. - [Constrained transactions](constrained-transactions.md) explains the `Randomized` class, fields, and constraints in the packet example. - [Policies and composite fields](policies-and-composition.md) covers the membership, distribution, soft-default, handle, nested, array, and packed features in the extension example. - [Solvers and diagnostics](solvers-and-diagnostics.md) describes the backends that solve these models and their failure diagnostics. - [Randomization library reference](../library/randomization.md) lists the exact signatures used across every tab.