diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 98f93c0fc..5f5792216 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -236,14 +236,27 @@ class RandomizeMarkVisitor final : public VNVisitor { // Process a single constraint during nested constraint cloning void processNestedConstraint(AstConstraint* const constrp, AstVarRef* rootVarRefp, const std::vector& newPath) { - AstConstraint* const cloneConstrp = constrp->cloneTree(false); - std::string pathPrefix = rootVarRefp->name(); for (AstVar* pathMemberVarp : newPath) { pathPrefix += GLOBAL_CONSTRAINT_SEPARATOR + pathMemberVarp->name(); } - cloneConstrp->name(pathPrefix + GLOBAL_CONSTRAINT_SEPARATOR + cloneConstrp->name()); + const std::string newName = pathPrefix + GLOBAL_CONSTRAINT_SEPARATOR + constrp->name(); + + for (const AstConstraint* existingConstrp : m_clonedConstraints) { + if (existingConstrp->name() == newName) { + // Multiple paths lead to same constraint - unsupported pattern + std::string fullPath = rootVarRefp->name(); + for (AstVar* pathVar : newPath) { fullPath += "." + pathVar->name(); } + constrp->v3warn(E_UNSUPPORTED, "Unsupported: One variable '" + << fullPath + << "' cannot have multiple global constraints"); + return; + } + } + + AstConstraint* const cloneConstrp = constrp->cloneTree(false); + cloneConstrp->name(newName); cloneConstrp->foreach([&](AstVarRef* varRefp) { AstNodeExpr* const chainp = buildMemberSelChain(rootVarRefp, newPath); AstMemberSel* const finalSelp diff --git a/test_regress/t/t_constraint_global_nested_unsup.out b/test_regress/t/t_constraint_global_nested_unsup.out new file mode 100644 index 000000000..645226c83 --- /dev/null +++ b/test_regress/t/t_constraint_global_nested_unsup.out @@ -0,0 +1,6 @@ +%Error-UNSUPPORTED: t/t_constraint_global_nested_unsup.v:9:14: Unsupported: Variable 'm_mid.m_inner' cannot have multiple global constraints + : ... note: In instance 't' + 9 | constraint c_inner { m_val inside {[1:10]}; } + | ^~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_constraint_global_nested_unsup.py b/test_regress/t/t_constraint_global_nested_unsup.py new file mode 100755 index 000000000..6585af685 --- /dev/null +++ b/test_regress/t/t_constraint_global_nested_unsup.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('vlt') + +test.lint(fails=test.vlt_all, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_constraint_global_nested_unsup.v b/test_regress/t/t_constraint_global_nested_unsup.v new file mode 100755 index 000000000..903571156 --- /dev/null +++ b/test_regress/t/t_constraint_global_nested_unsup.v @@ -0,0 +1,51 @@ +// DESCRIPTION: Verilator: Test for unsupported multiple global constraints +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + + +class Inner; + rand int m_val; + constraint c_inner { m_val inside {[1:10]}; } + function new(); m_val = 0; endfunction +endclass + +class Mid; + rand Inner m_inner; + rand int m_x; + // Mid has global constraint on m_inner.m_val + constraint c_mid_global { + m_x > m_inner.m_val; + m_x inside {[5:15]}; + } + function new(); + m_inner = new(); + m_x = 0; + endfunction +endclass + +class Top; + rand Mid m_mid; + rand int m_y; + // Top also has global constraint on m_mid.m_inner.m_val + constraint c_top_global { + m_y < m_mid.m_inner.m_val; + m_y inside {[1:5]}; + } + function new(); + m_mid = new(); + m_y = 0; + endfunction +endclass + +module t; + Top top; + /* verilator lint_off WIDTHTRUNC */ + initial begin + top = new(); + if (!top.randomize()) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + /* verilator lint_off WIDTHTRUNC */ +endmodule