diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 4d3d26c4c..98b7ca537 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -164,6 +164,100 @@ static AstNodeDType* arrayElementDTypep(AstNodeDType* dtypep) { return dtypep; } +// Whether dtypep is, or contains anywhere through unpacked array/struct +// nesting, a real value. A packed struct can't hold one (IEEE disallows +// real as a packed-struct member), so it's never worth descending into. +static bool dtypeContainsReal(AstNodeDType* dtypep) { + dtypep = dtypep->skipRefp(); + if (AstNodeDType* const subp = dtypep->subDTypep()) return dtypeContainsReal(subp); + if (const AstNodeUOrStructDType* const structp = VN_CAST(dtypep, NodeUOrStructDType)) { + if (structp->packed()) return false; + for (AstMemberDType* memberp = structp->membersp(); memberp; + memberp = VN_AS(memberp->nextp(), MemberDType)) { + if (dtypeContainsReal(memberp->subDTypep())) return true; + } + return false; + } + const AstBasicDType* const basicp = VN_CAST(dtypep, BasicDType); + return basicp && basicp->isDouble(); +} + +// Check a rand/randc variable's type against IEEE 1800-2023 18.4's +// allowed list. Purely diagnostic: codegen sites that would otherwise +// mishandle one of these types guard themselves on V3Error::errorCount() +// rather than relying on anything checked here. +static void checkRandTypeEligibility(AstNode* contextp, AstNodeDType* dtypep, bool isRandc) { + dtypep = dtypep->skipRefp(); + if (AstNodeDType* const subp = dtypep->subDTypep()) { + checkRandTypeEligibility(contextp, subp, isRandc); // Containers inherit rand/randc kind + return; + } + if (VN_IS(dtypep, ClassRefDType)) { + // rand on a class handle is legal (recursive randomization). + // Only randc is disallowed. + if (isRandc) { + contextp->v3error("'randc' on an object handle (IEEE 1800-2023 18.4: object " + "handles shall not be declared randc)"); + } + return; + } + if (const AstIfaceRefDType* const ifacep = VN_CAST(dtypep, IfaceRefDType)) { + // A class property can only hold a *virtual* interface handle -- + // a plain interface can't be a class member at all -- so a + // rand-qualified var landing here is always the virtual kind. + UASSERT_OBJ(ifacep->isVirtual(), dtypep, "Non-virtual interface as class property type"); + // Not in 18.4's enumerated random-variable domain, same reasoning + // as chandle/string/event; also generates C++ that does not + // compile if left unchecked. + contextp->v3error("'rand'/'randc' on a virtual interface handle (not " + "in IEEE 1800-2023 18.4's random-variable type domain)"); + return; + } + if (const AstNodeUOrStructDType* const structp = VN_CAST(dtypep, NodeUOrStructDType)) { + if (structp->packed()) return; // Integral by construction; members checked separately + if (VN_IS(structp, UnionDType)) { + contextp->v3error("'rand'/'randc' on an unpacked union (IEEE " + "1800-2023 18.4: unpacked unions shall not be declared as rand " + "or randc)"); + return; + } + for (AstMemberDType* memberp = structp->membersp(); memberp; + memberp = VN_AS(memberp->nextp(), MemberDType)) { + // Each unpacked-struct member independently declares its own rand/randc. + if (memberp->rand().isRandomizable()) { + checkRandTypeEligibility(memberp, memberp->subDTypep(), memberp->rand().isRandC()); + } + } + return; + } + const AstBasicDType* const basicp = VN_CAST(dtypep, BasicDType); + // V3Width has already resolved every dtype by this pass, and every + // other leaf kind (class handle, virtual interface, struct/union) was + // already dispatched above, so a rand-qualified var's dtype can only + // be a BasicDType here. + UASSERT_OBJ(basicp, dtypep, "Unexpected non-basic leaf dtype for rand var"); + if (basicp->isDouble()) { + // rand on a real variable is legal; only randc is disallowed. + if (isRandc) { + contextp->v3error("'randc' on a real variable (IEEE 1800-2023 18.4: " + "real variables shall not be declared randc)"); + } + return; + } + switch (basicp->keyword()) { + case VBasicDTypeKwd::STRING: + case VBasicDTypeKwd::CHANDLE: + case VBasicDTypeKwd::EVENT: { + const char* const articlep = basicp->keyword() == VBasicDTypeKwd::EVENT ? "an" : "a"; + contextp->v3error("'rand'/'randc' on " << articlep << " " << basicp->keyword().ascii() + << " variable (not in IEEE 1800-2023 18.4's " + "random-variable type domain)"); + break; + } + default: break; + } +} + //###################################################################### // Visitor that marks classes needing a randomize() method @@ -739,6 +833,9 @@ class RandomizeMarkVisitor final : public VNVisitor { } void visit(AstVar* nodep) override { nodep->user2p(m_modp); + if (nodep->rand().isRandomizable()) { + checkRandTypeEligibility(nodep, nodep->dtypep(), nodep->rand().isRandC()); + } iterateChildrenConst(nodep); } void visit(AstWith* nodep) override { @@ -1739,6 +1836,9 @@ class ConstraintExprVisitor final : public VNVisitor { AstMemberSel* memberselp = nullptr; bool structSelOrCMeth = false; + // Set below only when narrowing to one struct field/array element; + // staying null means "check varp's own type" (see its use further down). + AstNodeDType* selectedDtypep = nullptr; std::string smtName; if (m_nestedAccess) { m_nestedAccess->addVarNamePart( @@ -1762,6 +1862,7 @@ class ConstraintExprVisitor final : public VNVisitor { if (VN_IS(topNodep, StructSel) || VN_IS(topNodep, CMethodHard)) { structSelOrCMeth = true; + selectedDtypep = VN_AS(topNodep, NodeExpr)->dtypep(); } memberselp = VN_CAST(topNodep, MemberSel); @@ -1776,6 +1877,14 @@ class ConstraintExprVisitor final : public VNVisitor { } if (memberselp) varp = memberselp->varp(); + // The SMT translation assumes bit-vector operands and does not + // type-check; embedding a real value produces a malformed width. + // Check the selected field/element's type, not the whole + // struct/array's -- a real sibling field elsewhere is irrelevant. + if (dtypeContainsReal(structSelOrCMeth ? selectedDtypep : varp->dtypep())) { + nodep->v3warn(E_UNSUPPORTED, "Unsupported: real value in this constraint expression"); + return; + } AstNodeModule* const classOrPackagep = nodep->classOrPackagep(); const RandomizeMode randMode = {.asUQuad = varp->user1()}; if (!randMode.usesMode && editFormat(nodep)) return; @@ -2082,6 +2191,19 @@ class ConstraintExprVisitor final : public VNVisitor { VL_DO_DANGLING(nodep->deleteTree(), nodep); iterate(resultp); } + void visit(AstIsUnbounded* nodep) override { + // Only true for the literal '$' token, never a real expression, so + // this always folds to false. It has no runtime implementation of + // its own, so it must be substituted now rather than left for later. + nodep->v3warn(CONSTRAINTIGN, + "Unsupported: $isunbounded() in a constraint, treating as constant"); + AstConst* const zerop = new AstConst{nodep->fileline(), AstConst::BitFalse{}}; + nodep->replaceWith(zerop); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + // So the constant is rendered as an SMT literal like every other + // folded constant here, instead of being left behind unconverted. + iterate(zerop); + } void handlePow(AstNodeBiop* nodep) { if (AstConst* const exponentp = VN_CAST(nodep->rhsp(), Const)) { FileLine* const fl = nodep->fileline(); @@ -4345,8 +4467,12 @@ class RandomizeVisitor final : public VNVisitor { items = static_cast(enumDtp->itemCount()); } else if (AstBasicDType* const basicp = varp->dtypep()->skipRefp()->basicp()) { if (basicp->width() > 32) { - varp->v3error("Maximum implemented width for randc is 32 bits, " - << varp->prettyNameQ() << " is " << basicp->width() << " bits"); + // Real/chandle/string/event are rejected by type upstream already; + // an over-width integral type isn't, so still needs its own message. + if (!V3Error::errorCount()) { + varp->v3error("Maximum implemented width for randc is 32 bits, " + << varp->prettyNameQ() << " is " << basicp->width() << " bits"); + } varp->rand(VRandAttr::RAND); return nullptr; } @@ -4357,7 +4483,12 @@ class RandomizeVisitor final : public VNVisitor { " (IEEE 1800-2023 18.4)"); return nullptr; } else { - varp->v3fatalSrc("Unexpected randc variable dtype"); + // Only remaining possibility: an object handle, virtual interface, + // or unpacked union declared randc -- checkRandTypeEligibility() + // already rejected each of those with its own error, earlier in + // this same pass, before newRandcVarsp() ever runs on this var. + UASSERT_OBJ(V3Error::errorCount(), varp, "Unexpected randc variable dtype"); + return nullptr; } AstCDType* const newdtp = findVlRandCDType(varp->fileline(), items); AstVar* const newp @@ -4442,11 +4573,9 @@ class RandomizeVisitor final : public VNVisitor { } return stmtsp; } else if (const auto* const unionDtp = VN_CAST(memberDtp, UnionDType)) { - if (!unionDtp->packed()) { - unionDtp->v3error("Unpacked unions shall not be declared as rand or randc." - " (IEEE 1800-2023 18.4)"); - return nullptr; - } + // Illegal for rand/randc, rejected by type upstream for every such + // case; nothing valid to build a randomize statement for here. + if (!unionDtp->packed()) return nullptr; AstMemberDType* const firstMemberp = unionDtp->membersp(); return newRandStmtsp(fl, exprp, nullptr, outputVarp, offset, firstMemberp); } else if (const AstClassRefDType* const classRefDtp = VN_CAST(memberDtp, ClassRefDType)) { diff --git a/test_regress/t/t_constraint_isunbounded.py b/test_regress/t/t_constraint_isunbounded.py new file mode 100755 index 000000000..db1adb3f9 --- /dev/null +++ b/test_regress/t/t_constraint_isunbounded.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_constraint_isunbounded.v b/test_regress/t/t_constraint_isunbounded.v new file mode 100644 index 000000000..86223ccf7 --- /dev/null +++ b/test_regress/t/t_constraint_isunbounded.v @@ -0,0 +1,41 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Aditya Shevade +// 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 + +// $isunbounded(x) on a plain variable always folds to false, so !$isunbounded(x) +// is an always-true constraint that shouldn't affect x's range at all. +class C; + rand int x; + constraint c { + // verilator lint_off CONSTRAINTIGN + !$isunbounded(x); + // verilator lint_on CONSTRAINTIGN + x inside {[1 : 10]}; + } +endclass + +module t; + initial begin + C obj; + int ok; + obj = new; + repeat (10) begin + ok = obj.randomize(); + `checkd(ok, 1); + if (obj.x < 1 || obj.x > 10) begin + $write("%%Error: x out of range: %0d\n", obj.x); + `stop; + end + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_constraint_isunbounded_unsup.out b/test_regress/t/t_constraint_isunbounded_unsup.out new file mode 100644 index 000000000..3ee779610 --- /dev/null +++ b/test_regress/t/t_constraint_isunbounded_unsup.out @@ -0,0 +1,5 @@ +%Warning-CONSTRAINTIGN: t/t_constraint_isunbounded_unsup.v:13:6: Unsupported: $isunbounded() in a constraint, treating as constant + 13 | !$isunbounded(x); + | ^~~~~~~~~~~~ + ... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest + ... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message. diff --git a/test_regress/t/t_constraint_isunbounded_unsup.py b/test_regress/t/t_constraint_isunbounded_unsup.py new file mode 100755 index 000000000..235c3ecec --- /dev/null +++ b/test_regress/t/t_constraint_isunbounded_unsup.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(verilator_flags2=['-Wno-fatal'], expect_filename=test.golden_filename) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_isunbounded_unsup.v b/test_regress/t/t_constraint_isunbounded_unsup.v new file mode 100644 index 000000000..1db75aaad --- /dev/null +++ b/test_regress/t/t_constraint_isunbounded_unsup.v @@ -0,0 +1,35 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Aditya Shevade +// SPDX-License-Identifier: CC0-1.0 + +// $isunbounded() on a plain variable always folds to false; this triggers +// a suppressible CONSTRAINTIGN warning (see t_constraint_isunbounded.v for +// the suppressed case), but still compiles and runs correctly. +class C; + rand int x; + constraint c { + !$isunbounded(x); + x inside {[1 : 10]}; + } +endclass + +module t; + initial begin + C obj; + int ok; + obj = new; + repeat (10) begin + ok = obj.randomize(); + if (ok != 1) $stop; + if (obj.x < 1 || obj.x > 10) begin + $write("%%Error: x out of range: %0d\n", obj.x); + $stop; + end + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_constraint_real_array_eq_unsup.out b/test_regress/t/t_constraint_real_array_eq_unsup.out new file mode 100644 index 000000000..99c654eaf --- /dev/null +++ b/test_regress/t/t_constraint_real_array_eq_unsup.out @@ -0,0 +1,14 @@ +%Error-UNSUPPORTED: t/t_constraint_real_array_eq_unsup.v:12:18: Unsupported: real value in this constraint expression + 12 | constraint c { frame == target; } + | ^~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_constraint_real_array_eq_unsup.v:12:27: Unsupported: real value in this constraint expression + 12 | constraint c { frame == target; } + | ^~~~~~ +%Error-UNSUPPORTED: t/t_constraint_real_array_eq_unsup.v:22:18: Unsupported: real value in this constraint expression + 22 | constraint c { frame != target; } + | ^~~~~ +%Error-UNSUPPORTED: t/t_constraint_real_array_eq_unsup.v:22:27: Unsupported: real value in this constraint expression + 22 | constraint c { frame != target; } + | ^~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_constraint_real_array_eq_unsup.py b/test_regress/t/t_constraint_real_array_eq_unsup.py new file mode 100755 index 000000000..18ef27714 --- /dev/null +++ b/test_regress/t/t_constraint_real_array_eq_unsup.py @@ -0,0 +1,16 @@ +#!/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('vlt') + +test.lint(fails=test.vlt_all, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_constraint_real_array_eq_unsup.v b/test_regress/t/t_constraint_real_array_eq_unsup.v new file mode 100644 index 000000000..786e3c686 --- /dev/null +++ b/test_regress/t/t_constraint_real_array_eq_unsup.v @@ -0,0 +1,38 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Aditya Shevade +// SPDX-License-Identifier: CC0-1.0 + +// Array real == has no type check of its own. The scalar case in +// t_constraint_unsup.v does, and diagnoses cleanly instead of crashing. +class C1; + rand real frame[2]; + real target[2]; + constraint c { frame == target; } + function new(); + target[0] = 1.5; + target[1] = 2.5; + endfunction +endclass + +class C2; + rand real frame[2]; + real target[2]; + constraint c { frame != target; } + function new(); + target[0] = 1.5; + target[1] = 2.5; + endfunction +endclass + +module t; + initial begin + C1 obj1; + C2 obj2; + obj1 = new; + obj2 = new; + if (obj1.randomize() == 0) $stop; + if (obj2.randomize() == 0) $stop; + end +endmodule diff --git a/test_regress/t/t_constraint_real_struct_eq_unsup.out b/test_regress/t/t_constraint_real_struct_eq_unsup.out new file mode 100644 index 000000000..063c3b603 --- /dev/null +++ b/test_regress/t/t_constraint_real_struct_eq_unsup.out @@ -0,0 +1,8 @@ +%Error-UNSUPPORTED: t/t_constraint_real_struct_eq_unsup.v:20:18: Unsupported: real value in this constraint expression + 20 | constraint c { s == target; } + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_constraint_real_struct_eq_unsup.v:20:23: Unsupported: real value in this constraint expression + 20 | constraint c { s == target; } + | ^~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_constraint_real_struct_eq_unsup.py b/test_regress/t/t_constraint_real_struct_eq_unsup.py new file mode 100755 index 000000000..18ef27714 --- /dev/null +++ b/test_regress/t/t_constraint_real_struct_eq_unsup.py @@ -0,0 +1,16 @@ +#!/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('vlt') + +test.lint(fails=test.vlt_all, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_constraint_real_struct_eq_unsup.v b/test_regress/t/t_constraint_real_struct_eq_unsup.v new file mode 100644 index 000000000..85dfb7da0 --- /dev/null +++ b/test_regress/t/t_constraint_real_struct_eq_unsup.v @@ -0,0 +1,32 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Aditya Shevade +// SPDX-License-Identifier: CC0-1.0 + +// Whole-struct == has no type check of its own, the same gap +// t_constraint_real_array_eq_unsup.v closes for whole-array ==: without +// this, `s == target` where the struct has a real field compiles clean +// and silently returns 0 for the real field instead of the constrained +// value, since the real-value check only looked at the struct variable's +// own dtype, never at whether a member nested inside it was real. +typedef struct { + real r; +} coord_t; + +class C; + rand coord_t s; + coord_t target; + constraint c { s == target; } + function new(); + target.r = 1.5; + endfunction +endclass + +module t; + initial begin + C obj; + obj = new; + if (obj.randomize() == 0) $stop; + end +endmodule diff --git a/test_regress/t/t_constraint_real_struct_field.py b/test_regress/t/t_constraint_real_struct_field.py new file mode 100755 index 000000000..db1adb3f9 --- /dev/null +++ b/test_regress/t/t_constraint_real_struct_field.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_constraint_real_struct_field.v b/test_regress/t/t_constraint_real_struct_field.v new file mode 100644 index 000000000..34705dffe --- /dev/null +++ b/test_regress/t/t_constraint_real_struct_field.v @@ -0,0 +1,31 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Aditya Shevade +// SPDX-License-Identifier: CC0-1.0 + +// A non-real struct field constrained through a StructSel should work +// normally even though a sibling field in the same struct is real -- the +// real-value check must look at the selected field's own type, not the +// whole struct's type (see t_constraint_real_struct_eq_unsup.v for the +// whole-struct case, which is legitimately unsupported). +typedef struct { + int a; + real b; +} pair_t; + +class C; + rand pair_t s; + constraint c1 { s.a == 5; } +endclass + +module t; + initial begin + C obj; + obj = new; + if (obj.randomize() == 0) $stop; + if (obj.s.a != 5) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_rand_type_domain_bad.out b/test_regress/t/t_randomize_rand_type_domain_bad.out new file mode 100644 index 000000000..3723e6d89 --- /dev/null +++ b/test_regress/t/t_randomize_rand_type_domain_bad.out @@ -0,0 +1,47 @@ +%Warning-SHORTREAL: t/t_randomize_rand_type_domain_bad.v:31:9: Unsupported: shortreal being promoted to real (suggest use real instead) + 31 | randc shortreal shortreal_randc; + | ^~~~~~~~~ + ... For warning description see https://verilator.org/warn/SHORTREAL?v=latest + ... Use "/* verilator lint_off SHORTREAL */" and lint_on around source to disable this message. +%Error: t/t_randomize_rand_type_domain_bad.v:26:26: 'rand'/'randc' on an unpacked union (IEEE 1800-2023 18.4: unpacked unions shall not be declared as rand or randc) + : ... note: In instance 't' + 26 | randc unpacked_union_t union_randc; + | ^~~~~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_randomize_rand_type_domain_bad.v:28:14: 'randc' on an object handle (IEEE 1800-2023 18.4: object handles shall not be declared randc) + : ... note: In instance 't' + 28 | randc Item handle_randc; + | ^~~~~~~~~~~~ +%Error: t/t_randomize_rand_type_domain_bad.v:30:14: 'randc' on a real variable (IEEE 1800-2023 18.4: real variables shall not be declared randc) + : ... note: In instance 't' + 30 | randc real real_randc; + | ^~~~~~~~~~ +%Error: t/t_randomize_rand_type_domain_bad.v:31:19: 'randc' on a real variable (IEEE 1800-2023 18.4: real variables shall not be declared randc) + : ... note: In instance 't' + 31 | randc shortreal shortreal_randc; + | ^~~~~~~~~~~~~~~ +%Error: t/t_randomize_rand_type_domain_bad.v:32:18: 'randc' on a real variable (IEEE 1800-2023 18.4: real variables shall not be declared randc) + : ... note: In instance 't' + 32 | randc realtime realtime_randc; + | ^~~~~~~~~~~~~~ +%Error: t/t_randomize_rand_type_domain_bad.v:34:16: 'rand'/'randc' on a chandle variable (not in IEEE 1800-2023 18.4's random-variable type domain) + : ... note: In instance 't' + 34 | rand chandle chandle_rand; + | ^~~~~~~~~~~~ +%Error: t/t_randomize_rand_type_domain_bad.v:35:15: 'rand'/'randc' on a string variable (not in IEEE 1800-2023 18.4's random-variable type domain) + : ... note: In instance 't' + 35 | rand string string_rand; + | ^~~~~~~~~~~ +%Error: t/t_randomize_rand_type_domain_bad.v:36:14: 'rand'/'randc' on an event variable (not in IEEE 1800-2023 18.4's random-variable type domain) + : ... note: In instance 't' + 36 | rand event event_rand; + | ^~~~~~~~~~ +%Error: t/t_randomize_rand_type_domain_bad.v:38:20: 'rand'/'randc' on a virtual interface handle (not in IEEE 1800-2023 18.4's random-variable type domain) + : ... note: In instance 't' + 38 | rand virtual Bus vif_rand; + | ^~~~~~~~ +%Error: t/t_randomize_rand_type_domain_bad.v:40:20: 'rand'/'randc' on a chandle variable (not in IEEE 1800-2023 18.4's random-variable type domain) + : ... note: In instance 't' + 40 | rand chandle_q_t chandle_queue_rand; + | ^~~~~~~~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_randomize_rand_type_domain_bad.py b/test_regress/t/t_randomize_rand_type_domain_bad.py new file mode 100755 index 000000000..18ef27714 --- /dev/null +++ b/test_regress/t/t_randomize_rand_type_domain_bad.py @@ -0,0 +1,16 @@ +#!/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('vlt') + +test.lint(fails=test.vlt_all, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_randomize_rand_type_domain_bad.v b/test_regress/t/t_randomize_rand_type_domain_bad.v new file mode 100644 index 000000000..c944213c3 --- /dev/null +++ b/test_regress/t/t_randomize_rand_type_domain_bad.v @@ -0,0 +1,49 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Aditya Shevade +// SPDX-License-Identifier: CC0-1.0 + +// IEEE 1800-2023 18.4 type-eligibility rules for rand/randc. Each +// member below is illegal for a different reason. + +class Item; + rand int val; +endclass + +interface Bus; +endinterface + +typedef chandle chandle_q_t[$]; + +typedef union { + int a; + bit [31:0] b; +} unpacked_union_t; + +class C; + // Unpacked unions shall not be declared as rand or randc. + randc unpacked_union_t union_randc; + // Object handles shall not be declared randc. + randc Item handle_randc; + // Real (and shortreal, which promotes to real) shall not be randc. + randc real real_randc; + randc shortreal shortreal_randc; + randc realtime realtime_randc; + // Not in the rand type domain at all. + rand chandle chandle_rand; + rand string string_rand; + rand event event_rand; + // Not a basic type, checked separately. + rand virtual Bus vif_rand; + // Typedef'd queue: confirms the check unwraps both layers. + rand chandle_q_t chandle_queue_rand; +endclass + +module t; + initial begin + C obj; + obj = new; + if (obj.randomize() == 0) $stop; + end +endmodule diff --git a/test_regress/t/t_randomize_union_bad.out b/test_regress/t/t_randomize_union_bad.out index 9d932c13d..8d3f68db4 100644 --- a/test_regress/t/t_randomize_union_bad.out +++ b/test_regress/t/t_randomize_union_bad.out @@ -1,5 +1,6 @@ -%Error: t/t_randomize_union_bad.v:7:9: Unpacked unions shall not be declared as rand or randc. (IEEE 1800-2023 18.4) - 7 | typedef union { - | ^~~~~ +%Error: t/t_randomize_union_bad.v:13:22: 'rand'/'randc' on an unpacked union (IEEE 1800-2023 18.4: unpacked unions shall not be declared as rand or randc) + : ... note: In instance 't_randomize_union_bad' + 13 | rand UnpackedUnion union_instance; + | ^~~~~~~~~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to