diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index a42397298..9a0c5e0ce 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -3739,6 +3739,14 @@ class RandomizeVisitor final : public VNVisitor { void lowerDistConstraints(AstTask* taskp, AstNode* constrItemsp) { for (AstNode *nextip, *itemp = constrItemsp; itemp; itemp = nextip) { nextip = itemp->nextp(); + + // Recursively handle ConstraintIf nodes (dist can be inside if/else) + if (AstConstraintIf* const cifp = VN_CAST(itemp, ConstraintIf)) { + if (cifp->thensp()) lowerDistConstraints(taskp, cifp->thensp()); + if (cifp->elsesp()) lowerDistConstraints(taskp, cifp->elsesp()); + continue; + } + AstConstraintExpr* const constrExprp = VN_CAST(itemp, ConstraintExpr); if (!constrExprp) continue; AstDist* const distp = VN_CAST(constrExprp->exprp(), Dist); diff --git a/test_regress/t/t_randomize_dist_conditional.py b/test_regress/t/t_randomize_dist_conditional.py new file mode 100755 index 000000000..8862c2c31 --- /dev/null +++ b/test_regress/t/t_randomize_dist_conditional.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2025 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_dist_conditional.v b/test_regress/t/t_randomize_dist_conditional.v new file mode 100644 index 000000000..2a9773970 --- /dev/null +++ b/test_regress/t/t_randomize_dist_conditional.v @@ -0,0 +1,104 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// Test constraint with conditional dist operator + +class DistScalar; + rand bit [7:0] x; + rand bit mode; // 0: favor zeros (weight 3:1), 1: favor max (weight 1:3) + constraint c { + if (mode) { + x dist { 8'd0 := 1, 8'd255 := 3 }; // Favor 255 + } else { + x dist { 8'd0 := 3, 8'd255 := 1 }; // Favor 0 + } + } + + function void display(int trial); + $display("Trial %0d: mode=%b, x=%0d (0x%02x)", trial, mode, x, x); + endfunction +endclass + +module t; + DistScalar obj; + int mode1_zeros = 0; // Count of zeros when mode=1 (favor 255) + int mode1_maxes = 0; // Count of 255s when mode=1 (favor 255) + int mode0_zeros = 0; // Count of zeros when mode=0 (favor 0) + int mode0_maxes = 0; // Count of 255s when mode=0 (favor 0) + int mode1_trials = 0; // Total trials with mode=1 + int mode0_trials = 0; // Total trials with mode=0 + + // Verify distribution ratio matches expected range + function void check_distribution(int trials, int match_count, string match_name, + int expected_pct, string mode_name); + int actual_pct; + int lower_bound; + int upper_bound; + + if (trials <= 0) return; + + actual_pct = (match_count * 100) / trials; + $display(" %s %s ratio: %0d/%0d = %0d%% (expected ~%0d%%)", + mode_name, match_name, match_count, trials, actual_pct, expected_pct); + + // Allow +/-15% deviation from expected ratio + lower_bound = expected_pct - 15; + upper_bound = expected_pct + 15; + + if (actual_pct >= lower_bound && actual_pct <= upper_bound) begin + $display("Distribution OK"); + end else begin + $display("WARNING: Distribution appears off (expected %0d+/-15%%)", expected_pct); + $stop; + end + endfunction + + initial begin + int p; + obj = new; + + // Run multiple trials to verify distribution + for (int i = 0; i < 1000; i++) begin + p = obj.randomize(); + `checkd(p, 1); + + if (i < 10) obj.display(i); + + // Track distribution based on mode value + if (obj.mode) begin + mode1_trials++; + if (obj.x == 8'd0) mode1_zeros++; + else if (obj.x == 8'd255) mode1_maxes++; + end else begin + mode0_trials++; + if (obj.x == 8'd0) mode0_zeros++; + else if (obj.x == 8'd255) mode0_maxes++; + end + end + + $display(""); + $display("Statistics after 1000 trials:"); + $display(" Mode 1 (favor 255): trials=%0d, zeros=%0d, maxes=%0d", + mode1_trials, mode1_zeros, mode1_maxes); + $display(" Mode 0 (favor 0): trials=%0d, zeros=%0d, maxes=%0d", + mode0_trials, mode0_zeros, mode0_maxes); + + $display(""); + $display("Distribution Verification:"); + // Mode 1: x dist { 0 := 1, 255 := 3 } means 25% for 0, 75% for 255 + check_distribution(mode1_trials, mode1_maxes, "maxes", 75, "Mode 1"); + + // Mode 0: x dist { 0 := 3, 255 := 1 } means 75% for 0, 25% for 255 + check_distribution(mode0_trials, mode0_zeros, "zeros", 75, "Mode 0"); + + $display(""); + $display("PASSED: Conditional dist constraint test"); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule