diff --git a/Changes b/Changes index 706bc0d76..acf36fcbd 100644 --- a/Changes +++ b/Changes @@ -20,7 +20,7 @@ Verilator 5.031 devel * Add error on `wait` with missing `.triggered` (#4457). * Add error when improperly storing to parameter (#5147). [Gökçe Aydos] * Add coverage point hierarchy to coverage reports (#5575) (#5576). [Andrew Nolte] -* Add error on `solve before` of `randc` variable. +* Add error on `solve before` or soft constraints of `randc` variable. * Fix can't locate scope error in interface task delayed assignment (#5462) (#5568). [Zhou Shen] * Fix BLKANDNBLK for for VARXREFs (#5569). [Todd Strader] * Fix VPI error instead of fatal for vpi_get_value() on large signals (#5571). [Todd Strader] diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 49442e9e7..68fdbcd31 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2763,14 +2763,16 @@ public: class AstConstraintExpr final : public AstNodeStmt { // Constraint expression // @astgen op1 := exprp : AstNodeExpr - bool m_isSoft = false; // Soft constraint expression bool m_isDisableSoft = false; // Disable soft constraint expression + bool m_isSoft = false; // Soft constraint expression public: AstConstraintExpr(FileLine* fl, AstNodeExpr* exprp) : ASTGEN_SUPER_ConstraintExpr(fl) { this->exprp(exprp); } ASTGEN_MEMBERS_AstConstraintExpr; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool same(const AstNode* /*samep*/) const override { return true; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 00ca3aff5..5a2ba837f 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -376,6 +376,16 @@ void AstConstraint::dumpJson(std::ostream& str) const { dumpJsonBoolFunc(str, isStatic); dumpJsonGen(str); } +void AstConstraintExpr::dump(std::ostream& str) const { + this->AstNode::dump(str); + if (isDisableSoft()) str << " [DISSOFT]"; + if (isSoft()) str << " [SOFT]"; +} +void AstConstraintExpr::dumpJson(std::ostream& str) const { + dumpJsonBoolFunc(str, isDisableSoft); + dumpJsonBoolFunc(str, isSoft); + dumpJsonGen(str); +} AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) { bool success = false; if (literal[0] == '"') { diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 4558423ff..9b059d512 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -131,7 +131,9 @@ class RandomizeMarkVisitor final : public VNVisitor { BaseToDerivedMap m_baseToDerivedMap; // Mapping from base classes to classes that extend them AstClass* m_classp = nullptr; // Current class - AstNode* m_constraintExprp = nullptr; // Current constraint expression + AstConstraintBefore* m_constraintBeforep = nullptr; // Current before constraint + AstConstraintExpr* m_constraintExprp = nullptr; // Current constraint expression + AstNode* m_constraintExprGenp = nullptr; // Current constraint or constraint if expression AstNodeModule* m_modp; // Current module AstNodeStmt* m_stmtp = nullptr; // Current statement std::set m_staticRefs; // References to static variables under `with` clauses @@ -402,50 +404,57 @@ class RandomizeMarkVisitor final : public VNVisitor { } } void visit(AstConstraintBefore* nodep) override { - nodep->foreach([&](AstVarRef* const refp) { - if (refp->varp() && refp->varp()->isRandC()) { - nodep->v3error( - "Randc variables not allowed in 'solve before' (IEEE 1800-2023 18.5.9)"); - } - }); + VL_RESTORER(m_constraintBeforep); + m_constraintBeforep = nodep; iterateChildrenConst(nodep); } void visit(AstConstraintExpr* nodep) override { VL_RESTORER(m_constraintExprp); m_constraintExprp = nodep; + VL_RESTORER(m_constraintExprGenp); + m_constraintExprGenp = nodep; iterateChildrenConst(nodep); } void visit(AstConstraintIf* nodep) override { { - VL_RESTORER(m_constraintExprp); - m_constraintExprp = nodep; + VL_RESTORER(m_constraintExprGenp); + m_constraintExprGenp = nodep; iterateConst(nodep->condp()); } iterateAndNextConstNull(nodep->thensp()); iterateAndNextConstNull(nodep->elsesp()); } void visit(AstNodeVarRef* nodep) override { - if (!m_constraintExprp) return; + if (nodep->varp()->isRandC()) { + if (m_constraintExprp && m_constraintExprp->isSoft()) { + nodep->v3error( + "Randc variables not allowed in 'constraint soft' (IEEE 1800-2023 18.5.13.1)"); + } else if (m_constraintBeforep) { + nodep->v3error( + "Randc variables not allowed in 'solve before' (IEEE 1800-2023 18.5.9)"); + } + } + if (!m_constraintExprGenp) return; if (nodep->varp()->lifetime().isStatic()) m_staticRefs.emplace(nodep); if (!nodep->varp()->rand().isRandomizable()) return; - for (AstNode* backp = nodep; backp != m_constraintExprp && !backp->user1(); + for (AstNode* backp = nodep; backp != m_constraintExprGenp && !backp->user1(); backp = backp->backp()) backp->user1(true); } void visit(AstMemberSel* nodep) override { - if (!m_constraintExprp) return; + if (!m_constraintExprGenp) return; if (VN_IS(nodep->fromp(), LambdaArgRef)) { if (!nodep->varp()->rand().isRandomizable()) return; - for (AstNode* backp = nodep; backp != m_constraintExprp && !backp->user1(); + for (AstNode* backp = nodep; backp != m_constraintExprGenp && !backp->user1(); backp = backp->backp()) backp->user1(true); } } void visit(AstArraySel* nodep) override { - if (!m_constraintExprp) return; - for (AstNode* backp = nodep; backp != m_constraintExprp && !backp->user1(); + if (!m_constraintExprGenp) return; + for (AstNode* backp = nodep; backp != m_constraintExprGenp && !backp->user1(); backp = backp->backp()) backp->user1(true); iterateChildrenConst(nodep); diff --git a/test_regress/t/t_randomize_before_randc_bad.out b/test_regress/t/t_randomize_before_randc_bad.out index 5392c2b5f..9480b7624 100644 --- a/test_regress/t/t_randomize_before_randc_bad.out +++ b/test_regress/t/t_randomize_before_randc_bad.out @@ -1,5 +1,5 @@ -%Error: t/t_randomize_before_randc_bad.v:11:29: Randc variables not allowed in 'solve before' (IEEE 1800-2023 18.5.9) +%Error: t/t_randomize_before_randc_bad.v:11:45: Randc variables not allowed in 'solve before' (IEEE 1800-2023 18.5.9) : ... note: In instance 't' 11 | constraint raint2_bad { solve b1 before b2; } - | ^~~~~ + | ^~ %Error: Exiting due to diff --git a/test_regress/t/t_randomize_soft_randc_bad.out b/test_regress/t/t_randomize_soft_randc_bad.out new file mode 100644 index 000000000..a1646e725 --- /dev/null +++ b/test_regress/t/t_randomize_soft_randc_bad.out @@ -0,0 +1,5 @@ +%Error: t/t_randomize_soft_randc_bad.v:10:28: Randc variables not allowed in 'constraint soft' (IEEE 1800-2023 18.5.13.1) + : ... note: In instance 't' + 10 | constraint c_bad { soft rc > 4; } + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_randomize_soft_randc_bad.py b/test_regress/t/t_randomize_soft_randc_bad.py new file mode 100755 index 000000000..30c3d4f77 --- /dev/null +++ b/test_regress/t/t_randomize_soft_randc_bad.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('linter') + +test.lint(fails=test.vlt_all, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_randomize_soft_randc_bad.v b/test_regress/t/t_randomize_soft_randc_bad.v new file mode 100644 index 000000000..f10a49f34 --- /dev/null +++ b/test_regress/t/t_randomize_soft_randc_bad.v @@ -0,0 +1,14 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Cls1; + randc int rc; + + constraint c_bad { soft rc > 4; } // Bad, no soft on randc +endclass + +module t (/*AUTOARG*/); +endmodule