From 994ef82e76eda9aae72343689db54c699fb64a69 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Tue, 17 Feb 2026 01:46:46 +0100 Subject: [PATCH] Support unique constraint on explicit array element subsets (#7057) (#7064) --- src/V3Randomize.cpp | 55 ++++++++++++++ test_regress/t/t_randomize_unique_elem.py | 21 ++++++ test_regress/t/t_randomize_unique_elem.v | 89 +++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100755 test_regress/t/t_randomize_unique_elem.py create mode 100644 test_regress/t/t_randomize_unique_elem.v diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 83f9dd6c6..b0fbd17dd 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -604,6 +604,14 @@ class RandomizeMarkVisitor final : public VNVisitor { handleRandomizeArgument(argp->exprp(), fromVarp, false); } } + void visit(AstConstraintUnique* nodep) override { + VL_RESTORER(m_stmtp); + VL_RESTORER(m_constraintExprGenp); + m_stmtp = nodep; + m_constraintExprGenp = nodep; + iterateChildren(nodep); + if (!nodep->backp()) VL_DO_DANGLING(nodep->deleteTree(), nodep); + } void visit(AstConstraintExpr* nodep) override { VL_RESTORER(m_constraintExprGenp); m_constraintExprGenp = nodep; @@ -2218,6 +2226,50 @@ class RandomizeVisitor final : public VNVisitor { } return false; } + // Expand unique{a,b,c} with explicit elements into pairwise != constraints. + // Whole-array unique{arr} is left for ConstraintExprVisitor's rand_unique handling. + static void expandUniqueElementList(AstNode* itemsp) { + AstNode* itemp = itemsp; + while (itemp) { + AstNode* const nextp = itemp->nextp(); + AstConstraintUnique* const uniquep = VN_CAST(itemp, ConstraintUnique); + if (!uniquep) { + itemp = nextp; + continue; + } + + std::vector exprItems; + bool hasArrayVarRef = false; + for (AstNode* rp = uniquep->rangesp(); rp; rp = rp->nextp()) { + if (AstVarRef* const vrp = VN_CAST(rp, VarRef)) { + if (VN_IS(vrp->varp()->dtypep()->skipRefp(), UnpackArrayDType)) { + hasArrayVarRef = true; + continue; + } + } + exprItems.push_back(VN_AS(rp, NodeExpr)); + } + + if (exprItems.size() >= 2) { + FileLine* const fl = uniquep->fileline(); + for (size_t i = 0; i < exprItems.size(); i++) { + for (size_t j = i + 1; j < exprItems.size(); j++) { + AstNodeExpr* const lhsp = exprItems[i]->cloneTree(false); + AstNodeExpr* const rhsp = exprItems[j]->cloneTree(false); + AstNeq* const neqp = new AstNeq{fl, lhsp, rhsp}; + neqp->user1(true); + AstConstraintExpr* const cexprp = new AstConstraintExpr{fl, neqp}; + uniquep->addNextHere(cexprp); + } + } + if (!hasArrayVarRef) { + uniquep->unlinkFrBack(); + VL_DO_DANGLING(uniquep->deleteTree(), uniquep); + } + } + itemp = nextp; + } + } void createRandomGenerator(AstClass* const classp) { if (classp->user3p()) return; if (classp->extendsp()) { @@ -3232,6 +3284,7 @@ class RandomizeVisitor final : public VNVisitor { resizeAllTaskp->addStmtsp(resizeTaskRefp->makeStmt()); } + if (constrp->itemsp()) expandUniqueElementList(constrp->itemsp()); ConstraintExprVisitor{classp, m_memberMap, constrp->itemsp(), nullptr, genp, randModeVarp, m_writtenVars}; if (constrp->itemsp()) { @@ -3503,6 +3556,7 @@ class RandomizeVisitor final : public VNVisitor { AstNode* const capturedTreep = withp->exprp()->unlinkFrBackWithNext(); randomizeFuncp->addStmtsp(capturedTreep); { + expandUniqueElementList(capturedTreep); ConstraintExprVisitor{nullptr, m_memberMap, capturedTreep, randomizeFuncp, stdrand, nullptr, m_writtenVars}; } @@ -3642,6 +3696,7 @@ class RandomizeVisitor final : public VNVisitor { AstNode* const capturedTreep = withp->exprp()->unlinkFrBackWithNext(); randomizeFuncp->addStmtsp(capturedTreep); { + expandUniqueElementList(capturedTreep); ConstraintExprVisitor{classp, m_memberMap, capturedTreep, randomizeFuncp, localGenp, randModeVarp, m_writtenVars}; } diff --git a/test_regress/t/t_randomize_unique_elem.py b/test_regress/t/t_randomize_unique_elem.py new file mode 100755 index 000000000..db1adb3f9 --- /dev/null +++ b/test_regress/t/t_randomize_unique_elem.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_randomize_unique_elem.v b/test_regress/t/t_randomize_unique_elem.v new file mode 100644 index 000000000..ca88d3a71 --- /dev/null +++ b/test_regress/t/t_randomize_unique_elem.v @@ -0,0 +1,89 @@ +// 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 unique constraint on explicit array element subsets (IEEE 18.5.9). +// bit [3:0] keeps the value space small so collisions are near-certain +// without proper constraint enforcement. + +class UniqueElemSubset; + rand bit [3:0] arr[10]; + + constraint unique_subset_con { + unique { arr[2], arr[3], arr[4], arr[5], arr[6] }; + } + + function new(); + endfunction + + function bit check_unique(); + for (int i = 2; i <= 6; i++) + for (int j = i + 1; j <= 6; j++) + if (arr[i] == arr[j]) return 0; + return 1; + endfunction +endclass + +class UniqueElemFour; + rand bit [3:0] data[8]; + + constraint unique_data_con { + unique { data[1], data[2], data[3], data[4] }; + } + + function new(); + endfunction + + function bit check_unique(); + for (int i = 1; i <= 4; i++) + for (int j = i + 1; j <= 4; j++) + if (data[i] == data[j]) return 0; + return 1; + endfunction +endclass + +class UniqueElemSingle; + rand bit [3:0] val[4]; + + constraint unique_single_con { + unique { val[0] }; + } + + function new(); + endfunction +endclass + +module t; + UniqueElemSubset ues; + UniqueElemFour uef; + UniqueElemSingle uesgl; + + initial begin + ues = new(); + repeat (20) begin + `checkd(ues.randomize(), 1) + `checkd(ues.check_unique(), 1) + end + + uef = new(); + repeat (20) begin + `checkd(uef.randomize(), 1) + `checkd(uef.check_unique(), 1) + end + + uesgl = new(); + repeat (5) begin + `checkd(uesgl.randomize(), 1) + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule