Randomization Methods in SystemVerilog

SystemVerilog provides special methods that are called before and after randomization, control over randomization success, and ways to ensure reproducible results. Let's explore these powerful features.

pre_randomize() and post_randomize()

These are callback methods automatically called by the randomize() function. They let you perform setup before randomization and cleanup/calculation after.

pre_randomize and post_randomize
class Packet;
    rand bit [31:0] data[$];
    rand int        num_words;
    bit [31:0]      checksum;  // Not randomized, calculated
    
    constraint valid_size {
        num_words inside {[1:16]};
        data.size() == num_words;
    }
    
    // Called BEFORE randomization
    function void pre_randomize();
        $display("Starting randomization...");
        // Could set up any pre-conditions here
    endfunction
    
    // Called AFTER randomization (if successful)
    function void post_randomize();
        // Calculate checksum from randomized data
        checksum = 0;
        foreach(data[i]) begin
            checksum ^= data[i];
        end
        $display("Randomized %0d words, checksum=0x%h", 
                 num_words, checksum);
    endfunction
endclass

Packet p = new();
p.randomize();  // Calls pre_randomize, then randomize, then post_randomize

Key Points

pre_randomize() is called before solving constraints. post_randomize() is called only if randomization succeeds. Both are called automatically - you just need to define them.

Common Use Cases

1. Calculating CRC/Checksum

Checksum Calculation
function void post_randomize();
    crc = calculate_crc(header, payload);  // Based on random data
endfunction

2. Setting Dependent Fields

Field Dependencies
function void post_randomize();
    // Set packet type string based on random type code
    case(type_code)
        0: type_name = "READ";
        1: type_name = "WRITE";
        2: type_name = "IDLE";
    endcase
endfunction

3. Adjusting Array Sizes

Dynamic Sizing
function void pre_randomize();
    // Set array size before randomization based on config
    if (config.burst_mode) begin
        data = new[config.burst_length];
    end else begin
        data = new[1];
    end
endfunction

Don't Randomize in post_randomize!

Avoid calling randomize() inside post_randomize() - it will cause infinite recursion!

rand_mode() - Enable/Disable Random Variables

rand_mode() lets you turn randomization on or off for specific variables. Disabled variables keep their current values during randomization.

Using rand_mode
class Transaction;
    rand bit [31:0] addr;
    rand bit [31:0] data;
    rand bit        write;
endclass

Transaction t = new();

// Set a fixed address, randomize the rest
t.addr = 32'h1000;
t.addr.rand_mode(0);  // Disable addr randomization
t.randomize();        // addr stays 0x1000, data and write are random

// Re-enable
t.addr.rand_mode(1);
t.randomize();        // Now addr is also random

// Check if variable is randomizable
if (t.addr.rand_mode())
    $display("addr is randomizable");

rand_mode vs constraint_mode

rand_mode() controls whether a variable is randomized. constraint_mode() controls whether a constraint is active. They're different!

srandom() - Seeding for Reproducibility

Sometimes you need to reproduce a specific random sequence - especially when debugging a failure. srandom() lets you set the random seed for an object.

Using srandom
class Packet;
    rand bit [7:0] data;
endclass

Packet p = new();

// Set a known seed for reproducible results
p.srandom(12345);

// These randomizations will always produce the same sequence
p.randomize();  // Same result every time with seed 12345
$display("data = %h", p.data);

p.randomize();  // Next value in the sequence
$display("data = %h", p.data);

// Reset the seed to get the same sequence again
p.srandom(12345);
p.randomize();  // Same as first randomize
$display("data = %h", p.data);

Debugging with Seeds

When a test fails, log the seed used. Later, you can replay the exact same random sequence by setting the same seed.

randomize() Return Value

randomize() returns 1 on success, 0 on failure. Always check the return value when constraints might fail!

Checking Randomization Success
class Packet;
    rand int size;
    
    constraint small_size {
        size inside {[1:10]};
    }
endclass

Packet p = new();

// Basic check
if (!p.randomize()) begin
    $error("Randomization failed!");
end

// With inline constraint that might fail
if (!p.randomize() with {size > 100;}) begin
    // This WILL fail because size must be 1-10 AND > 100
    $error("Cannot satisfy constraints!");
end

// Using assert for immediate failure
assert(p.randomize()) else $fatal("Randomization failed!");

randomize(null) - Check Constraints

Calling randomize(null) checks if current values satisfy constraints without actually changing anything.

Constraint Checking
class Transaction;
    rand bit [31:0] addr;
    
    constraint aligned {
        addr[1:0] == 0;  // Word aligned
    }
endclass

Transaction t = new();

// Set a value manually
t.addr = 32'h1003;  // Not aligned!

// Check if it satisfies constraints (without changing)
if (t.randomize(null)) begin
    $display("Address is valid");
end else begin
    $display("Address violates constraints!");
end

Quick Summary

  • pre_randomize() - Called before randomization
  • post_randomize() - Called after successful randomization
  • rand_mode(0/1) - Disable/enable variable randomization
  • constraint_mode(0/1) - Disable/enable constraints
  • srandom(seed) - Set random seed for reproducibility
  • randomize() returns 0 on failure - always check!
  • randomize(null) - Check constraints without changing values

What's Next?

Continue learning about SystemVerilog: