diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 94e694485..f8559319a 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -3224,6 +3224,34 @@ class RandomizeVisitor final : public VNVisitor { } } randomizep->addStmtsp(implementConstraintsClear(fl, genp)); + + // Restrict enum variables in solver to valid members only + { + AstNodeModule* const genModp = VN_AS(genp->user2p(), NodeModule); + nodep->foreachMember([&](AstClass*, AstVar* memberVarp) { + if (!memberVarp->user3()) return; + AstEnumDType* const enumDtp + = VN_CAST(memberVarp->dtypep()->skipRefToEnump(), EnumDType); + if (!enumDtp) return; + const int width = enumDtp->width(); + const std::string smtName = memberVarp->name(); + std::string constraint = "(__Vbv (or"; + for (AstEnumItem* itemp = enumDtp->itemsp(); itemp; + itemp = VN_AS(itemp->nextp(), EnumItem)) { + const AstConst* const vconstp = VN_AS(itemp->valuep(), Const); + constraint += " (= " + smtName + " (_ bv" + cvtToStr(vconstp->toUInt()) + + " " + cvtToStr(width) + "))"; + } + constraint += "))"; + AstCMethodHard* const callp = new AstCMethodHard{ + fl, new AstVarRef{fl, genModp, genp, VAccess::READWRITE}, + VCMethod::RANDOMIZER_HARD, + new AstCExpr{fl, AstCExpr::Pure{}, "\"" + constraint + "\""}}; + callp->dtypeSetVoid(); + randomizep->addStmtsp(callp->makeStmt()); + }); + } + AstTask* setupAllTaskp = getCreateConstraintSetupFunc(nodep); AstTaskRef* const setupTaskRefp = new AstTaskRef{fl, setupAllTaskp, nullptr}; randomizep->addStmtsp(setupTaskRefp->makeStmt()); diff --git a/test_regress/t/t_randc_enum_constraint.py b/test_regress/t/t_randc_enum_constraint.py new file mode 100755 index 000000000..db1adb3f9 --- /dev/null +++ b/test_regress/t/t_randc_enum_constraint.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: 2026 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_randc_enum_constraint.v b/test_regress/t/t_randc_enum_constraint.v new file mode 100644 index 000000000..5f750931f --- /dev/null +++ b/test_regress/t/t_randc_enum_constraint.v @@ -0,0 +1,69 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 PlanV GmbH +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`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); +// verilog_format: on + +// Test that randc enum variables with user constraints only produce +// valid enum members (not arbitrary bitvector values). + +module t; + + typedef enum bit [2:0] { + RED = 0, + GREEN = 1, + BLUE = 2, + WHITE = 3, + BLACK = 4 + } color_t; + + class ColorClass; + randc color_t color; + constraint c_no_dark { color != BLACK; } + endclass + + // Test with all enum values allowed (no exclusion constraint) + class AllColorsClass; + randc color_t color; + constraint c_range { color <= WHITE; } + endclass + + initial begin + ColorClass c; + AllColorsClass ac; + int color_seen[5]; + + // Test 1: randc enum with exclusion constraint + // Values must be valid enum members (0-4) and not BLACK (4) + c = new; + repeat (40) begin + `checkd(c.randomize(), 1); + // Must be a valid enum member (not 5, 6, 7) + `checkd(c.color <= BLACK, 1); + // Must not be BLACK (excluded by constraint) + `checkd(c.color == BLACK, 0); + end + + // Test 2: randc enum with range constraint - verify all valid values seen + ac = new; + repeat (40) begin + `checkd(ac.randomize(), 1); + `checkd(ac.color <= WHITE, 1); + color_seen[ac.color] = 1; + end + // After 40 iterations (10 full cycles of 4), all values should appear + `checkd(color_seen[0], 1); + `checkd(color_seen[1], 1); + `checkd(color_seen[2], 1); + `checkd(color_seen[3], 1); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule