From a868e70595f25d0fd539396f2aec46a91936b4d3 Mon Sep 17 00:00:00 2001 From: Rahul Behl Date: Mon, 2 Feb 2026 11:55:55 +0530 Subject: [PATCH 01/13] Support array reduction methods with 'with' clause in constraints Fixes #6455 This commit adds support for array reduction methods (sum, product, and, or, xor) with 'with' clauses in randomization constraints. Previously, expressions like 'array.sum() with (int'(item == value))' were not supported in constraints. Implementation details: - Added handler in ConstraintExprVisitor::visit(AstCMethodHard*) for ARRAY_R_SUM, ARRAY_R_PRODUCT, ARRAY_R_AND, ARRAY_R_OR, ARRAY_R_XOR - Substitutes lambda argument references (item/index) in the with expression - Generates appropriate SMT operations (bvadd, bvmul, bvand, bvor, bvxor) - Uses correct identity elements for each reduction operation - Marks non-constant nodes with user1() flag to ensure proper SMT code generation The implementation follows the same pattern as the existing ARRAY_INSIDE handler, generating SMT expressions at Verilator compile time that are evaluated by the constraint solver at runtime. --- src/V3Randomize.cpp | 86 ++++++++++++++++++ test_regress/t/t_constraint_array_sum_with.py | 21 +++++ test_regress/t/t_constraint_array_sum_with.v | 90 +++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 test_regress/t/t_constraint_array_sum_with.py create mode 100644 test_regress/t/t_constraint_array_sum_with.v diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 2a4dd3d8a..7f8853b20 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1500,6 +1500,92 @@ class ConstraintExprVisitor final : public VNVisitor { return; } + // Handle array reduction methods with 'with' clause + if (nodep->method() == VCMethod::ARRAY_R_SUM + || nodep->method() == VCMethod::ARRAY_R_PRODUCT + || nodep->method() == VCMethod::ARRAY_R_AND || nodep->method() == VCMethod::ARRAY_R_OR + || nodep->method() == VCMethod::ARRAY_R_XOR) { + // Handle array reduction methods with 'with' clause + AstWith* const withp = VN_CAST(nodep->pinsp(), With); + UASSERT_OBJ(withp, nodep, "Array reduction in constraint should have 'with' clause"); + + const bool randArr = nodep->fromp()->user1(); + + // Create loop variable + AstVar* const loopVarp + = new AstVar{fl, VVarType::BLOCKTEMP, "__Vreduce", nodep->findSigned32DType()}; + AstNodeExpr* const idxRefp = new AstVarRef{fl, loopVarp, VAccess::READ}; + + // Create array iteration structure + AstSelLoopVars* const arrayLoopVars + = new AstSelLoopVars{fl, nodep->fromp()->cloneTreePure(false), loopVarp}; + + // Get array element selector + AstNodeExpr* const elemSelp = newSel(fl, nodep->fromp(), idxRefp); + elemSelp->user1(randArr); + + // Clone the 'with' expression and substitute lambda args + AstNode* perElemExprp = withp->exprp()->cloneTreePure(false); + + // Substitute lambda argument references with actual array element/index + perElemExprp->foreach([&](AstLambdaArgRef* refp) { + if (refp->index()) { + refp->replaceWith(idxRefp->cloneTreePure(false)); + } else { + refp->replaceWith(elemSelp->cloneTreePure(false)); + } + VL_DO_DANGLING(refp->deleteTree(), refp); + }); + + // Mark all non-constant nodes in the expression as depending on random variables + // Constants remain unmarked so they'll be formatted as SMT constants + perElemExprp->foreach([](AstNode* nodep) { + if (!VN_IS(nodep, Const)) nodep->user1(true); + }); + + // Determine SMT operation and identity element + std::string smtOp; + std::string identityVal; + if (nodep->method() == VCMethod::ARRAY_R_SUM) { + smtOp = "bvadd"; + identityVal = "#b" + std::string(nodep->dtypep()->width(), '0'); + } else if (nodep->method() == VCMethod::ARRAY_R_PRODUCT) { + smtOp = "bvmul"; + // Identity = 1 in binary (guard against zero width to avoid out-of-bounds) + const size_t w = nodep->dtypep()->width(); + identityVal = (w > 0) ? "#b" + std::string(w - 1, '0') + "1" : "#b0"; + } else if (nodep->method() == VCMethod::ARRAY_R_AND) { + smtOp = "bvand"; + // Identity = all 1s + identityVal = "#b" + std::string(nodep->dtypep()->width(), '1'); + } else if (nodep->method() == VCMethod::ARRAY_R_OR) { + smtOp = "bvor"; + identityVal = "#b" + std::string(nodep->dtypep()->width(), '0'); + } else { // ARRAY_R_XOR + smtOp = "bvxor"; + identityVal = "#b" + std::string(nodep->dtypep()->width(), '0'); + } + + // Build the loop body that accumulates results + AstCStmt* const cstmtp = new AstCStmt{fl}; + cstmtp->add("ret = \"(" + smtOp + " \" + ret + \" \";\n"); + cstmtp->add("ret += "); + cstmtp->add(iterateSubtreeReturnEdits(perElemExprp)); + cstmtp->add(";\n"); + cstmtp->add("ret += \")\";\n"); + + // Create the lambda that generates SMT code + AstCExpr* const cexprp = new AstCExpr{fl}; + cexprp->dtypeSetString(); + cexprp->add("([&]{\nstd::string ret = \"" + identityVal + "\";\n"); + cexprp->add(new AstBegin{fl, "", new AstForeach{fl, arrayLoopVars, cstmtp}, true}); + cexprp->add("return ret;\n})()"); + + nodep->replaceWith(new AstSFormatF{fl, "%@", false, cexprp}); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + return; + } + nodep->v3warn(CONSTRAINTIGN, "Unsupported: randomizing this expression, treating as state"); nodep->user1(false); diff --git a/test_regress/t/t_constraint_array_sum_with.py b/test_regress/t/t_constraint_array_sum_with.py new file mode 100644 index 000000000..db1adb3f9 --- /dev/null +++ b/test_regress/t/t_constraint_array_sum_with.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_array_sum_with.v b/test_regress/t/t_constraint_array_sum_with.v new file mode 100644 index 000000000..c5a22dfdc --- /dev/null +++ b/test_regress/t/t_constraint_array_sum_with.v @@ -0,0 +1,90 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 by Wilson Snyder and Contributors +// SPDX-License-Identifier: CC0-1.0 + +// Test case for array.sum() with (...) in constraints (issue #6455) + +class three_duplicates_only; + rand byte array[5]; + rand byte repeated_value; + + constraint three_duplicates_only_c { + // Ensure exactly 3 occurrences of repeated_value + array.sum() with (int'(item==repeated_value)) == 3; + + // All other values should appear exactly once + foreach(array[i]) { + array[i] != repeated_value -> array.sum() with (int'(item==array[i])) == 1; + } + } + + function void display(); + foreach (array[i]) begin + $display("Array[%0d]: %d", i, unsigned'(array[i])); + end + endfunction + + function bit verify(); + int count_map[byte]; + int repeated_count = 0; + + // Count occurrences + foreach (array[i]) begin + if (!count_map.exists(array[i])) count_map[array[i]] = 0; + count_map[array[i]]++; + end + + // Check repeated_value appears exactly 3 times + if (count_map.exists(repeated_value)) begin + repeated_count = count_map[repeated_value]; + if (repeated_count != 3) begin + $display("ERROR: repeated_value=%0d appears %0d times, expected 3", + repeated_value, repeated_count); + return 0; + end + end else begin + $display("ERROR: repeated_value=%0d doesn't appear in array", repeated_value); + return 0; + end + + // Check all other values appear exactly once + foreach (count_map[val]) begin + if (val != repeated_value && count_map[val] != 1) begin + $display("ERROR: value=%0d appears %0d times, expected 1", val, count_map[val]); + return 0; + end + end + + return 1; + endfunction +endclass + +module t; + three_duplicates_only inst; + + initial begin + inst = new(); + + // Test multiple randomizations + repeat (10) begin + if (inst.randomize() == 0) begin + $display("%%Error: Failed to randomize array."); + $stop; + end + + inst.display(); + + if (!inst.verify()) begin + $display("%%Error: Constraint verification failed."); + $stop; + end + + $display("---"); + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From f24f4ddf7e215ac97b41b8ec0c87613e7529c318 Mon Sep 17 00:00:00 2001 From: Rahul Behl Date: Thu, 5 Feb 2026 12:08:08 +0530 Subject: [PATCH 02/13] Updating contributors --- docs/CONTRIBUTORS | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index c652b4f39..1573079b3 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -211,6 +211,7 @@ Piotr Binkowski Qingyao Sun Quentin Corradi Rafal Kapuscik +Rahul Behl Rasfunk Raynard Qiao Ricardo Barbedo From 8e6edaa03b7932c03880589ecb304f562ea39439 Mon Sep 17 00:00:00 2001 From: Rahul Behl Date: Thu, 5 Feb 2026 14:16:09 +0530 Subject: [PATCH 03/13] Fix: heap-use-after-free in lambda substitution foreach --- src/V3Randomize.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 7f8853b20..5ab2cbde5 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1528,14 +1528,22 @@ class ConstraintExprVisitor final : public VNVisitor { AstNode* perElemExprp = withp->exprp()->cloneTreePure(false); // Substitute lambda argument references with actual array element/index + // Collect nodes to delete after iteration completes to avoid use-after-free + std::vector toDelete; perElemExprp->foreach([&](AstLambdaArgRef* refp) { if (refp->index()) { refp->replaceWith(idxRefp->cloneTreePure(false)); } else { refp->replaceWith(elemSelp->cloneTreePure(false)); } - VL_DO_DANGLING(refp->deleteTree(), refp); + toDelete.push_back(refp); }); + // Safe to delete now that iteration is complete + for (AstLambdaArgRef* refp : toDelete) { + VL_DO_DANGLING(refp->deleteTree(), refp); + } + // Clean up the original template nodes (elemSelp contains idxRefp) + VL_DO_DANGLING(elemSelp->deleteTree(), elemSelp); // Mark all non-constant nodes in the expression as depending on random variables // Constants remain unmarked so they'll be formatted as SMT constants From 95dbd349d648595143ae37fa6decd9b3331b5deb Mon Sep 17 00:00:00 2001 From: github action Date: Thu, 5 Feb 2026 08:56:39 +0000 Subject: [PATCH 04/13] Apply 'make format' --- src/V3Randomize.cpp | 32 ++++++++++++------- test_regress/t/t_constraint_array_sum_with.py | 0 2 files changed, 21 insertions(+), 11 deletions(-) mode change 100644 => 100755 test_regress/t/t_constraint_array_sum_with.py diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 5ab2cbde5..7f0f20d6a 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1528,19 +1528,29 @@ class ConstraintExprVisitor final : public VNVisitor { AstNode* perElemExprp = withp->exprp()->cloneTreePure(false); // Substitute lambda argument references with actual array element/index - // Collect nodes to delete after iteration completes to avoid use-after-free - std::vector toDelete; - perElemExprp->foreach([&](AstLambdaArgRef* refp) { - if (refp->index()) { - refp->replaceWith(idxRefp->cloneTreePure(false)); + // Handle case where perElemExprp itself is a LambdaArgRef + if (AstLambdaArgRef* const rootRefp = VN_CAST(perElemExprp, LambdaArgRef)) { + if (rootRefp->index()) { + perElemExprp = idxRefp->cloneTreePure(false); } else { - refp->replaceWith(elemSelp->cloneTreePure(false)); + perElemExprp = elemSelp->cloneTreePure(false); + } + VL_DO_DANGLING(rootRefp->deleteTree(), rootRefp); + } else { + // Collect nodes to delete after iteration completes + std::vector toDelete; + perElemExprp->foreach([&](AstLambdaArgRef* refp) { + if (refp->index()) { + refp->replaceWith(idxRefp->cloneTreePure(false)); + } else { + refp->replaceWith(elemSelp->cloneTreePure(false)); + } + toDelete.push_back(refp); + }); + // Safe to delete now that iteration is complete + for (AstLambdaArgRef* refp : toDelete) { + VL_DO_DANGLING(refp->deleteTree(), refp); } - toDelete.push_back(refp); - }); - // Safe to delete now that iteration is complete - for (AstLambdaArgRef* refp : toDelete) { - VL_DO_DANGLING(refp->deleteTree(), refp); } // Clean up the original template nodes (elemSelp contains idxRefp) VL_DO_DANGLING(elemSelp->deleteTree(), elemSelp); diff --git a/test_regress/t/t_constraint_array_sum_with.py b/test_regress/t/t_constraint_array_sum_with.py old mode 100644 new mode 100755 From d3461d92757a1912cee14d84672cca7b43278ed6 Mon Sep 17 00:00:00 2001 From: Rahul Behl Date: Tue, 10 Feb 2026 16:23:19 +0530 Subject: [PATCH 05/13] Improve constraint array reduction codegen - Use AstExprStmt and AstCReturn for proper lambda generation - Add --constraint-with-limit option to prevent code explosion on large arrays - Emit CONSTRAINTIGN warning when array size exceeds limit - Updated test to check for all the operators (sum, product, and, or, xor) --- src/V3Options.cpp | 1 + src/V3Options.h | 2 + src/V3Randomize.cpp | 52 +++-- test_regress/t/t_constraint_array_sum_with.v | 203 ++++++++++++++++--- 4 files changed, 215 insertions(+), 43 deletions(-) diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 42108834f..da234a0ab 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1825,6 +1825,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-U", CbPartialMatch, &V3PreShell::undef); DECL_OPTION("-underline-zero", OnOff, &m_underlineZero).undocumented(); // Deprecated DECL_OPTION("-no-unlimited-stack", CbCall, []() {}); // Processed only in bin/verilator shell + DECL_OPTION("-constraint-with-limit", Set, &m_constraintWithLimit).undocumented(); DECL_OPTION("-unroll-count", Set, &m_unrollCount).undocumented(); // Optimization tweak DECL_OPTION("-unroll-limit", Set, &m_unrollLimit); DECL_OPTION("-unroll-stmts", Set, &m_unrollStmts).undocumented(); // Optimization tweak diff --git a/src/V3Options.h b/src/V3Options.h index 935cdd2d9..40a99ff2e 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -352,6 +352,7 @@ private: int m_unrollCount = 64; // main switch: --unroll-count int m_unrollLimit = 16384; // main switch: --unroll-limit int m_unrollStmts = 30000; // main switch: --unroll-stmts + int m_constraintWithLimit = 64; // main switch: --constraint-with-limit int m_verilateJobs = -1; // main switch: --verilate-jobs int m_compLimitBlocks = 0; // compiler selection; number of nested blocks @@ -635,6 +636,7 @@ public: int unrollCount() const { return m_unrollCount; } int unrollLimit() const { return m_unrollLimit; } int unrollStmts() const { return m_unrollStmts; } + int constraintWithLimit() const { return m_constraintWithLimit; } int verilateJobs() const { return m_verilateJobs; } int compLimitBlocks() const { return m_compLimitBlocks; } diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 7f0f20d6a..86201dac1 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1509,6 +1509,24 @@ class ConstraintExprVisitor final : public VNVisitor { AstWith* const withp = VN_CAST(nodep->pinsp(), With); UASSERT_OBJ(withp, nodep, "Array reduction in constraint should have 'with' clause"); + // Check array size against limit to avoid code explosion + if (AstUnpackArrayDType* const adtypep + = VN_CAST(nodep->fromp()->dtypep()->skipRefp(), UnpackArrayDType)) { + const int arraySize = adtypep->elementsConst(); + if (arraySize > v3Global.opt.constraintWithLimit()) { + nodep->v3warn( + CONSTRAINTIGN, + "Constraint array reduction ignored (array size " + cvtToStr(arraySize) + + " exceeds --constraint-with-limit of " + + cvtToStr(v3Global.opt.constraintWithLimit()) + + "), treating as state"); + nodep->user1(false); + if (editFormat(nodep)) return; + nodep->v3fatalSrc("Method not handled in constraints? " << nodep); + return; + } + } + const bool randArr = nodep->fromp()->user1(); // Create loop variable @@ -1537,20 +1555,14 @@ class ConstraintExprVisitor final : public VNVisitor { } VL_DO_DANGLING(rootRefp->deleteTree(), rootRefp); } else { - // Collect nodes to delete after iteration completes - std::vector toDelete; perElemExprp->foreach([&](AstLambdaArgRef* refp) { if (refp->index()) { refp->replaceWith(idxRefp->cloneTreePure(false)); } else { refp->replaceWith(elemSelp->cloneTreePure(false)); } - toDelete.push_back(refp); + VL_DO_DANGLING(pushDeletep(refp), refp); }); - // Safe to delete now that iteration is complete - for (AstLambdaArgRef* refp : toDelete) { - VL_DO_DANGLING(refp->deleteTree(), refp); - } } // Clean up the original template nodes (elemSelp contains idxRefp) VL_DO_DANGLING(elemSelp->deleteTree(), elemSelp); @@ -1592,14 +1604,26 @@ class ConstraintExprVisitor final : public VNVisitor { cstmtp->add(";\n"); cstmtp->add("ret += \")\";\n"); - // Create the lambda that generates SMT code - AstCExpr* const cexprp = new AstCExpr{fl}; - cexprp->dtypeSetString(); - cexprp->add("([&]{\nstd::string ret = \"" + identityVal + "\";\n"); - cexprp->add(new AstBegin{fl, "", new AstForeach{fl, arrayLoopVars, cstmtp}, true}); - cexprp->add("return ret;\n})()"); + // Initialize return value variable + AstCStmt* const initStmtp = new AstCStmt{fl}; + initStmtp->add("std::string ret = \"" + identityVal + "\";\n"); - nodep->replaceWith(new AstSFormatF{fl, "%@", false, cexprp}); + // Create loop statement + AstNode* const loopStmtp + = new AstBegin{fl, "", new AstForeach{fl, arrayLoopVars, cstmtp}, true}; + + // Create return expression + AstCExpr* const retExprp = new AstCExpr{fl, AstCExpr::Pure{}, "ret"}; + retExprp->dtypeSetString(); + + // Build lambda as AstExprStmt with CReturn + AstNode* const lambdaStmtsp = initStmtp; + lambdaStmtsp->addNext(loopStmtp); + lambdaStmtsp->addNext(new AstCReturn{fl, retExprp}); + AstExprStmt* const lambdap = new AstExprStmt{fl, lambdaStmtsp, retExprp->cloneTree(false)}; + lambdap->hasResult(false); // Result comes from CReturn, not resultp + + nodep->replaceWith(new AstSFormatF{fl, "%@", false, lambdap}); VL_DO_DANGLING(nodep->deleteTree(), nodep); return; } diff --git a/test_regress/t/t_constraint_array_sum_with.v b/test_regress/t/t_constraint_array_sum_with.v index c5a22dfdc..cea9ba347 100644 --- a/test_regress/t/t_constraint_array_sum_with.v +++ b/test_regress/t/t_constraint_array_sum_with.v @@ -4,14 +4,14 @@ // SPDX-FileCopyrightText: 2026 by Wilson Snyder and Contributors // SPDX-License-Identifier: CC0-1.0 -// Test case for array.sum() with (...) in constraints (issue #6455) +// Test case for array reduction methods with 'with' clause in constraints (issue #6455) -class three_duplicates_only; +class test_sum; rand byte array[5]; rand byte repeated_value; - constraint three_duplicates_only_c { - // Ensure exactly 3 occurrences of repeated_value + constraint c { + // Ensure exactly 3 occurrences of repeated_value using sum array.sum() with (int'(item==repeated_value)) == 3; // All other values should appear exactly once @@ -20,12 +20,6 @@ class three_duplicates_only; } } - function void display(); - foreach (array[i]) begin - $display("Array[%0d]: %d", i, unsigned'(array[i])); - end - endfunction - function bit verify(); int count_map[byte]; int repeated_count = 0; @@ -40,19 +34,19 @@ class three_duplicates_only; if (count_map.exists(repeated_value)) begin repeated_count = count_map[repeated_value]; if (repeated_count != 3) begin - $display("ERROR: repeated_value=%0d appears %0d times, expected 3", + $display("%%Error: sum test - repeated_value=%0d appears %0d times, expected 3", repeated_value, repeated_count); return 0; end end else begin - $display("ERROR: repeated_value=%0d doesn't appear in array", repeated_value); + $display("%%Error: sum test - repeated_value=%0d doesn't appear in array", repeated_value); return 0; end // Check all other values appear exactly once foreach (count_map[val]) begin if (val != repeated_value && count_map[val] != 1) begin - $display("ERROR: value=%0d appears %0d times, expected 1", val, count_map[val]); + $display("%%Error: sum test - value=%0d appears %0d times, expected 1", val, count_map[val]); return 0; end end @@ -61,28 +55,179 @@ class three_duplicates_only; endfunction endclass +class test_product; + rand bit [3:0] array[4]; + + constraint c { + // Product of non-zero elements should be <= 100 + array.product() with (item != 0 ? int'(item) : 1) <= 100; + // At least 2 non-zero elements + array.sum() with (int'(item != 0)) >= 2; + } + + function bit verify(); + int prod = 1; + int nonzero_count = 0; + + foreach (array[i]) begin + if (array[i] != 0) begin + prod *= array[i]; + nonzero_count++; + end + end + + if (prod > 100) begin + $display("%%Error: product test - product %0d > 100", prod); + return 0; + end + + if (nonzero_count < 2) begin + $display("%%Error: product test - only %0d non-zero elements", nonzero_count); + return 0; + end + + return 1; + endfunction +endclass + +class test_and; + rand bit [7:0] array[3]; + + constraint c { + // AND of all elements with a mask should equal specific value + array.and() with (item & 8'hF0) == 8'h50; + } + + function bit verify(); + bit [7:0] result = 8'hFF; + + foreach (array[i]) begin + result &= (array[i] & 8'hF0); + end + + if (result != 8'h50) begin + $display("%%Error: and test - result 0x%0h != 0x50", result); + return 0; + end + + return 1; + endfunction +endclass + +class test_or; + rand bit [7:0] array[3]; + + constraint c { + // OR of specific bits should set bit 3 + (array.or() with (item & 8'h08)) == 8'h08; + // At least one element has bit 3 set + array.sum() with (int'((item & 8'h08) != 0)) >= 1; + } + + function bit verify(); + bit [7:0] result = 8'h00; + + foreach (array[i]) begin + result |= (array[i] & 8'h08); + end + + if (result != 8'h08) begin + $display("%%Error: or test - result 0x%0h != 0x08", result); + return 0; + end + + return 1; + endfunction +endclass + +class test_xor; + rand bit [7:0] array[4]; + + constraint c { + // XOR of all elements should be non-zero (parity check) + array.xor() with (item) != 0; + // Ensure some variation + array.sum() with (int'(item != 0)) >= 2; + } + + function bit verify(); + bit [7:0] result = 8'h00; + + foreach (array[i]) begin + result ^= array[i]; + end + + if (result == 0) begin + $display("%%Error: xor test - result is 0"); + return 0; + end + + return 1; + endfunction +endclass + module t; - three_duplicates_only inst; + test_sum sum_inst; + test_product product_inst; + test_and and_inst; + test_or or_inst; + test_xor xor_inst; initial begin - inst = new(); - - // Test multiple randomizations - repeat (10) begin - if (inst.randomize() == 0) begin - $display("%%Error: Failed to randomize array."); + // Test sum + sum_inst = new(); + repeat (3) begin + if (sum_inst.randomize() == 0) begin + $display("%%Error: Failed to randomize sum test"); $stop; end - - inst.display(); - - if (!inst.verify()) begin - $display("%%Error: Constraint verification failed."); - $stop; - end - - $display("---"); + if (!sum_inst.verify()) $stop; end + $display("sum test PASSED"); + + // Test product + product_inst = new(); + repeat (3) begin + if (product_inst.randomize() == 0) begin + $display("%%Error: Failed to randomize product test"); + $stop; + end + if (!product_inst.verify()) $stop; + end + $display("product test PASSED"); + + // Test and + and_inst = new(); + repeat (3) begin + if (and_inst.randomize() == 0) begin + $display("%%Error: Failed to randomize and test"); + $stop; + end + if (!and_inst.verify()) $stop; + end + $display("and test PASSED"); + + // Test or + or_inst = new(); + repeat (3) begin + if (or_inst.randomize() == 0) begin + $display("%%Error: Failed to randomize or test"); + $stop; + end + if (!or_inst.verify()) $stop; + end + $display("or test PASSED"); + + // Test xor + xor_inst = new(); + repeat (3) begin + if (xor_inst.randomize() == 0) begin + $display("%%Error: Failed to randomize xor test"); + $stop; + end + if (!xor_inst.verify()) $stop; + end + $display("xor test PASSED"); $write("*-* All Finished *-*\n"); $finish; From 92ff52f16e5a37109cc63b95e42f1e3fd1e4ac56 Mon Sep 17 00:00:00 2001 From: github action Date: Tue, 10 Feb 2026 11:01:52 +0000 Subject: [PATCH 06/13] Apply 'make format' --- src/V3Randomize.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 86201dac1..5abd9db5d 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1514,12 +1514,11 @@ class ConstraintExprVisitor final : public VNVisitor { = VN_CAST(nodep->fromp()->dtypep()->skipRefp(), UnpackArrayDType)) { const int arraySize = adtypep->elementsConst(); if (arraySize > v3Global.opt.constraintWithLimit()) { - nodep->v3warn( - CONSTRAINTIGN, - "Constraint array reduction ignored (array size " + cvtToStr(arraySize) - + " exceeds --constraint-with-limit of " - + cvtToStr(v3Global.opt.constraintWithLimit()) - + "), treating as state"); + nodep->v3warn(CONSTRAINTIGN, "Constraint array reduction ignored (array size " + + cvtToStr(arraySize) + + " exceeds --constraint-with-limit of " + + cvtToStr(v3Global.opt.constraintWithLimit()) + + "), treating as state"); nodep->user1(false); if (editFormat(nodep)) return; nodep->v3fatalSrc("Method not handled in constraints? " << nodep); @@ -1620,7 +1619,8 @@ class ConstraintExprVisitor final : public VNVisitor { AstNode* const lambdaStmtsp = initStmtp; lambdaStmtsp->addNext(loopStmtp); lambdaStmtsp->addNext(new AstCReturn{fl, retExprp}); - AstExprStmt* const lambdap = new AstExprStmt{fl, lambdaStmtsp, retExprp->cloneTree(false)}; + AstExprStmt* const lambdap + = new AstExprStmt{fl, lambdaStmtsp, retExprp->cloneTree(false)}; lambdap->hasResult(false); // Result comes from CReturn, not resultp nodep->replaceWith(new AstSFormatF{fl, "%@", false, lambdap}); From b88fc74c5709aaef2afc982bcfd4e62e19c77a8a Mon Sep 17 00:00:00 2001 From: Rahul Behl Date: Fri, 13 Feb 2026 12:25:26 +0530 Subject: [PATCH 07/13] Addressing review comments - Renamed the command line option to specify the array limit size - Added a new test to test the command line option - Fixed the existing test to verify all the supported operators --- src/V3Options.cpp | 2 +- src/V3Options.h | 4 +- src/V3Randomize.cpp | 6 +- test_regress/t/t_constraint_array_limit.out | 6 + test_regress/t/t_constraint_array_limit.py | 16 +++ test_regress/t/t_constraint_array_limit.v | 38 ++++++ test_regress/t/t_constraint_array_sum_with.v | 122 ++++++++----------- 7 files changed, 118 insertions(+), 76 deletions(-) create mode 100644 test_regress/t/t_constraint_array_limit.out create mode 100644 test_regress/t/t_constraint_array_limit.py create mode 100644 test_regress/t/t_constraint_array_limit.v diff --git a/src/V3Options.cpp b/src/V3Options.cpp index da234a0ab..18aa3c6a0 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1825,7 +1825,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-U", CbPartialMatch, &V3PreShell::undef); DECL_OPTION("-underline-zero", OnOff, &m_underlineZero).undocumented(); // Deprecated DECL_OPTION("-no-unlimited-stack", CbCall, []() {}); // Processed only in bin/verilator shell - DECL_OPTION("-constraint-with-limit", Set, &m_constraintWithLimit).undocumented(); + DECL_OPTION("-constraint-array-limit", Set, &m_constraintArrayLimit).undocumented(); DECL_OPTION("-unroll-count", Set, &m_unrollCount).undocumented(); // Optimization tweak DECL_OPTION("-unroll-limit", Set, &m_unrollLimit); DECL_OPTION("-unroll-stmts", Set, &m_unrollStmts).undocumented(); // Optimization tweak diff --git a/src/V3Options.h b/src/V3Options.h index 40a99ff2e..0bc705f70 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -352,7 +352,7 @@ private: int m_unrollCount = 64; // main switch: --unroll-count int m_unrollLimit = 16384; // main switch: --unroll-limit int m_unrollStmts = 30000; // main switch: --unroll-stmts - int m_constraintWithLimit = 64; // main switch: --constraint-with-limit + int m_constraintArrayLimit = 64; // main switch: --constraint-array-limit int m_verilateJobs = -1; // main switch: --verilate-jobs int m_compLimitBlocks = 0; // compiler selection; number of nested blocks @@ -636,7 +636,7 @@ public: int unrollCount() const { return m_unrollCount; } int unrollLimit() const { return m_unrollLimit; } int unrollStmts() const { return m_unrollStmts; } - int constraintWithLimit() const { return m_constraintWithLimit; } + int constraintArrayLimit() const { return m_constraintArrayLimit; } int verilateJobs() const { return m_verilateJobs; } int compLimitBlocks() const { return m_compLimitBlocks; } diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 5abd9db5d..4921550e6 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1513,11 +1513,11 @@ class ConstraintExprVisitor final : public VNVisitor { if (AstUnpackArrayDType* const adtypep = VN_CAST(nodep->fromp()->dtypep()->skipRefp(), UnpackArrayDType)) { const int arraySize = adtypep->elementsConst(); - if (arraySize > v3Global.opt.constraintWithLimit()) { + if (arraySize > v3Global.opt.constraintArrayLimit()) { nodep->v3warn(CONSTRAINTIGN, "Constraint array reduction ignored (array size " + cvtToStr(arraySize) - + " exceeds --constraint-with-limit of " - + cvtToStr(v3Global.opt.constraintWithLimit()) + + " exceeds --constraint-array-limit of " + + cvtToStr(v3Global.opt.constraintArrayLimit()) + "), treating as state"); nodep->user1(false); if (editFormat(nodep)) return; diff --git a/test_regress/t/t_constraint_array_limit.out b/test_regress/t/t_constraint_array_limit.out new file mode 100644 index 000000000..266f7760e --- /dev/null +++ b/test_regress/t/t_constraint_array_limit.out @@ -0,0 +1,6 @@ +%Warning-CONSTRAINTIGN: t/t_constraint_array_limit.v:15:12: Constraint array reduction ignored (array size 100 exceeds --constraint-array-limit of 64), treating as state + 15 | data.sum() with (item) < 1000; + | ^~~ + ... 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. +%Error: Exiting due to diff --git a/test_regress/t/t_constraint_array_limit.py b/test_regress/t/t_constraint_array_limit.py new file mode 100644 index 000000000..1cd952af3 --- /dev/null +++ b/test_regress/t/t_constraint_array_limit.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expectations 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_constraint_array_limit.v b/test_regress/t/t_constraint_array_limit.v new file mode 100644 index 000000000..91d97ea5e --- /dev/null +++ b/test_regress/t/t_constraint_array_limit.v @@ -0,0 +1,38 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Ravi Behl. +// SPDX-License-Identifier: CC0-1.0 + +// Test that array reduction constraints are ignored when array size exceeds --constraint-array-limit +`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); + +class Packet; + rand int data[100]; // Array size 100 > default limit of 64 + + constraint c { + data.sum() with (item) < 1000; // This should be ignored due to array size + } + + function void verify(); + int i; + i = randomize(); + `checkd(i, 1); + endfunction +endclass + +module t; + initial begin + Packet p; + int success_count; + + p = new; + + // Try randomization -- should fail with a warning + p.verify(); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_constraint_array_sum_with.v b/test_regress/t/t_constraint_array_sum_with.v index cea9ba347..01e7dfa71 100644 --- a/test_regress/t/t_constraint_array_sum_with.v +++ b/test_regress/t/t_constraint_array_sum_with.v @@ -5,6 +5,8 @@ // SPDX-License-Identifier: CC0-1.0 // Test case for array reduction methods with 'with' clause in constraints (issue #6455) +`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); class test_sum; rand byte array[5]; @@ -20,14 +22,18 @@ class test_sum; } } - function bit verify(); + function void verify(); int count_map[byte]; int repeated_count = 0; + int i; + + i = randomize(); + `checkd(i, 1); // Count occurrences - foreach (array[i]) begin - if (!count_map.exists(array[i])) count_map[array[i]] = 0; - count_map[array[i]]++; + foreach (array[idx]) begin + if (!count_map.exists(array[idx])) count_map[array[idx]] = 0; + count_map[array[idx]]++; end // Check repeated_value appears exactly 3 times @@ -36,22 +42,20 @@ class test_sum; if (repeated_count != 3) begin $display("%%Error: sum test - repeated_value=%0d appears %0d times, expected 3", repeated_value, repeated_count); - return 0; + $stop; end end else begin $display("%%Error: sum test - repeated_value=%0d doesn't appear in array", repeated_value); - return 0; + $stop; end // Check all other values appear exactly once foreach (count_map[val]) begin if (val != repeated_value && count_map[val] != 1) begin $display("%%Error: sum test - value=%0d appears %0d times, expected 1", val, count_map[val]); - return 0; + $stop; end end - - return 1; endfunction endclass @@ -65,28 +69,30 @@ class test_product; array.sum() with (int'(item != 0)) >= 2; } - function bit verify(); + function void verify(); int prod = 1; int nonzero_count = 0; + int i; - foreach (array[i]) begin - if (array[i] != 0) begin - prod *= array[i]; + i = randomize(); + `checkd(i, 1); + + foreach (array[idx]) begin + if (array[idx] != 0) begin + prod *= array[idx]; nonzero_count++; end end if (prod > 100) begin $display("%%Error: product test - product %0d > 100", prod); - return 0; + $stop; end if (nonzero_count < 2) begin $display("%%Error: product test - only %0d non-zero elements", nonzero_count); - return 0; + $stop; end - - return 1; endfunction endclass @@ -98,19 +104,21 @@ class test_and; array.and() with (item & 8'hF0) == 8'h50; } - function bit verify(); + function void verify(); bit [7:0] result = 8'hFF; + int i; - foreach (array[i]) begin - result &= (array[i] & 8'hF0); + i = randomize(); + `checkd(i, 1); + + foreach (array[idx]) begin + result &= (array[idx] & 8'hF0); end if (result != 8'h50) begin $display("%%Error: and test - result 0x%0h != 0x50", result); - return 0; + $stop; end - - return 1; endfunction endclass @@ -124,19 +132,21 @@ class test_or; array.sum() with (int'((item & 8'h08) != 0)) >= 1; } - function bit verify(); + function void verify(); bit [7:0] result = 8'h00; + int i; - foreach (array[i]) begin - result |= (array[i] & 8'h08); + i = randomize(); + `checkd(i, 1); + + foreach (array[idx]) begin + result |= (array[idx] & 8'h08); end if (result != 8'h08) begin $display("%%Error: or test - result 0x%0h != 0x08", result); - return 0; + $stop; end - - return 1; endfunction endclass @@ -150,19 +160,21 @@ class test_xor; array.sum() with (int'(item != 0)) >= 2; } - function bit verify(); + function void verify(); bit [7:0] result = 8'h00; + int i; - foreach (array[i]) begin - result ^= array[i]; + i = randomize(); + `checkd(i, 1); + + foreach (array[idx]) begin + result ^= array[idx]; end if (result == 0) begin $display("%%Error: xor test - result is 0"); - return 0; + $stop; end - - return 1; endfunction endclass @@ -176,57 +188,27 @@ module t; initial begin // Test sum sum_inst = new(); - repeat (3) begin - if (sum_inst.randomize() == 0) begin - $display("%%Error: Failed to randomize sum test"); - $stop; - end - if (!sum_inst.verify()) $stop; - end + repeat (20) sum_inst.verify(); $display("sum test PASSED"); // Test product product_inst = new(); - repeat (3) begin - if (product_inst.randomize() == 0) begin - $display("%%Error: Failed to randomize product test"); - $stop; - end - if (!product_inst.verify()) $stop; - end + repeat (20) product_inst.verify(); $display("product test PASSED"); // Test and and_inst = new(); - repeat (3) begin - if (and_inst.randomize() == 0) begin - $display("%%Error: Failed to randomize and test"); - $stop; - end - if (!and_inst.verify()) $stop; - end + repeat (20) and_inst.verify(); $display("and test PASSED"); // Test or or_inst = new(); - repeat (3) begin - if (or_inst.randomize() == 0) begin - $display("%%Error: Failed to randomize or test"); - $stop; - end - if (!or_inst.verify()) $stop; - end + repeat (20) or_inst.verify(); $display("or test PASSED"); // Test xor xor_inst = new(); - repeat (3) begin - if (xor_inst.randomize() == 0) begin - $display("%%Error: Failed to randomize xor test"); - $stop; - end - if (!xor_inst.verify()) $stop; - end + repeat (20) xor_inst.verify(); $display("xor test PASSED"); $write("*-* All Finished *-*\n"); From 5a947114f3789dbe7de3f40621283e94a41d1dee Mon Sep 17 00:00:00 2001 From: github action Date: Fri, 13 Feb 2026 06:57:42 +0000 Subject: [PATCH 08/13] Apply 'make format' --- src/V3Randomize.cpp | 11 ++++++----- test_regress/t/t_constraint_array_limit.py | 0 2 files changed, 6 insertions(+), 5 deletions(-) mode change 100644 => 100755 test_regress/t/t_constraint_array_limit.py diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 4921550e6..32239d08a 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1514,11 +1514,12 @@ class ConstraintExprVisitor final : public VNVisitor { = VN_CAST(nodep->fromp()->dtypep()->skipRefp(), UnpackArrayDType)) { const int arraySize = adtypep->elementsConst(); if (arraySize > v3Global.opt.constraintArrayLimit()) { - nodep->v3warn(CONSTRAINTIGN, "Constraint array reduction ignored (array size " - + cvtToStr(arraySize) - + " exceeds --constraint-array-limit of " - + cvtToStr(v3Global.opt.constraintArrayLimit()) - + "), treating as state"); + nodep->v3warn(CONSTRAINTIGN, + "Constraint array reduction ignored (array size " + + cvtToStr(arraySize) + + " exceeds --constraint-array-limit of " + + cvtToStr(v3Global.opt.constraintArrayLimit()) + + "), treating as state"); nodep->user1(false); if (editFormat(nodep)) return; nodep->v3fatalSrc("Method not handled in constraints? " << nodep); diff --git a/test_regress/t/t_constraint_array_limit.py b/test_regress/t/t_constraint_array_limit.py old mode 100644 new mode 100755 From fec88978f3130de12d2dca48633d1f5a5fad430a Mon Sep 17 00:00:00 2001 From: Rahul Behl Date: Fri, 13 Feb 2026 12:32:54 +0530 Subject: [PATCH 09/13] Updating docs with new command line option --- bin/verilator | 1 + docs/guide/exe_verilator.rst | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/bin/verilator b/bin/verilator index a94b7fe56..8bdff0d52 100755 --- a/bin/verilator +++ b/bin/verilator @@ -345,6 +345,7 @@ detailed descriptions of these arguments. -CFLAGS C++ compiler arguments for makefile --compiler Tune for specified C++ compiler --compiler-include Include additional header in the precompiled one + --constraint-array-limit Maximum array size for constraint array reduction --converge-limit Tune convergence settle time --coverage Enable all coverage --coverage-expr Enable expression coverage diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 7c21ae284..901918f77 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -262,6 +262,17 @@ Summary: limitation that allow only one precompiled header per compilation. Use this instead of ::vlopt:`-CFLAGS` with `-include `. +.. option:: --constraint-array-limit + + Specifies the maximum array size for which array reduction methods + (sum, product, and, or, xor) in constraint expressions will be + expanded. Arrays larger than this limit will have their reduction + constraints ignored with a `CONSTRAINTIGN` warning. This prevents + excessive code generation for very large arrays. + + Defaults to 64. Setting to 0 disables all array reduction constraint + expansion. + .. option:: --converge-limit Rarely needed. Specifies the maximum number of runtime iterations before From dbc9062dcd07d2c36463675155b737db7ce2ce84 Mon Sep 17 00:00:00 2001 From: Rahul Behl Date: Fri, 13 Feb 2026 14:03:43 +0530 Subject: [PATCH 10/13] Fixing t_constraint_array_limit test --- test_regress/t/t_constraint_array_limit.out | 6 ------ test_regress/t/t_constraint_array_limit.py | 10 ++++++++-- test_regress/t/t_constraint_array_limit.v | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) delete mode 100644 test_regress/t/t_constraint_array_limit.out diff --git a/test_regress/t/t_constraint_array_limit.out b/test_regress/t/t_constraint_array_limit.out deleted file mode 100644 index 266f7760e..000000000 --- a/test_regress/t/t_constraint_array_limit.out +++ /dev/null @@ -1,6 +0,0 @@ -%Warning-CONSTRAINTIGN: t/t_constraint_array_limit.v:15:12: Constraint array reduction ignored (array size 100 exceeds --constraint-array-limit of 64), treating as state - 15 | data.sum() with (item) < 1000; - | ^~~ - ... 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. -%Error: Exiting due to diff --git a/test_regress/t/t_constraint_array_limit.py b/test_regress/t/t_constraint_array_limit.py index 1cd952af3..76d489357 100755 --- a/test_regress/t/t_constraint_array_limit.py +++ b/test_regress/t/t_constraint_array_limit.py @@ -9,8 +9,14 @@ import vltest_bootstrap -test.scenarios('linter') +test.scenarios('simulator') -test.lint(fails=test.vlt_all, expect_filename=test.golden_filename) +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile( + fails=True, + verilator_flags2=["--constraint-array-limit", "16"], + expect=r'%Warning-CONSTRAINTIGN:.*exceeds --constraint-array-limit') test.passes() diff --git a/test_regress/t/t_constraint_array_limit.v b/test_regress/t/t_constraint_array_limit.v index 91d97ea5e..e0a00d90b 100644 --- a/test_regress/t/t_constraint_array_limit.v +++ b/test_regress/t/t_constraint_array_limit.v @@ -9,10 +9,10 @@ `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); class Packet; - rand int data[100]; // Array size 100 > default limit of 64 + rand int data[32]; constraint c { - data.sum() with (item) < 1000; // This should be ignored due to array size + data.sum() with (item) < 1000; } function void verify(); From f678960e183109aae42e711b3598fd83928eae82 Mon Sep 17 00:00:00 2001 From: github action Date: Fri, 13 Feb 2026 08:34:42 +0000 Subject: [PATCH 11/13] Apply 'make format' --- test_regress/t/t_constraint_array_limit.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test_regress/t/t_constraint_array_limit.py b/test_regress/t/t_constraint_array_limit.py index 76d489357..d858a8b28 100755 --- a/test_regress/t/t_constraint_array_limit.py +++ b/test_regress/t/t_constraint_array_limit.py @@ -14,9 +14,8 @@ test.scenarios('simulator') if not test.have_solver: test.skip("No constraint solver installed") -test.compile( - fails=True, - verilator_flags2=["--constraint-array-limit", "16"], - expect=r'%Warning-CONSTRAINTIGN:.*exceeds --constraint-array-limit') +test.compile(fails=True, + verilator_flags2=["--constraint-array-limit", "16"], + expect=r'%Warning-CONSTRAINTIGN:.*exceeds --constraint-array-limit') test.passes() From 2f3237a84fe712d485c1527ba42820fc47f14880 Mon Sep 17 00:00:00 2001 From: Rahul Behl Date: Fri, 13 Feb 2026 15:29:09 +0530 Subject: [PATCH 12/13] Adding golden output file --- test_regress/t/t_constraint_array_limit.out | 6 ++++++ test_regress/t/t_constraint_array_limit.py | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 test_regress/t/t_constraint_array_limit.out diff --git a/test_regress/t/t_constraint_array_limit.out b/test_regress/t/t_constraint_array_limit.out new file mode 100644 index 000000000..7eb325933 --- /dev/null +++ b/test_regress/t/t_constraint_array_limit.out @@ -0,0 +1,6 @@ +%Warning-CONSTRAINTIGN: t/t_constraint_array_limit.v:15:12: Constraint array reduction ignored (array size 32 exceeds --constraint-array-limit of 16), treating as state + 15 | data.sum() with (item) < 1000; + | ^~~ + ... 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. +%Error: Exiting due to diff --git a/test_regress/t/t_constraint_array_limit.py b/test_regress/t/t_constraint_array_limit.py index d858a8b28..59b3c275d 100755 --- a/test_regress/t/t_constraint_array_limit.py +++ b/test_regress/t/t_constraint_array_limit.py @@ -14,8 +14,9 @@ test.scenarios('simulator') if not test.have_solver: test.skip("No constraint solver installed") -test.compile(fails=True, - verilator_flags2=["--constraint-array-limit", "16"], - expect=r'%Warning-CONSTRAINTIGN:.*exceeds --constraint-array-limit') +test.compile( + fails=True, + verilator_flags2=["--constraint-array-limit", "16"], + expect_filename=test.golden_filename) test.passes() From c9d77133893b7a71ee9bf032740653b4940668b9 Mon Sep 17 00:00:00 2001 From: github action Date: Fri, 13 Feb 2026 10:01:01 +0000 Subject: [PATCH 13/13] Apply 'make format' --- test_regress/t/t_constraint_array_limit.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test_regress/t/t_constraint_array_limit.py b/test_regress/t/t_constraint_array_limit.py index 59b3c275d..e8b70b0d9 100755 --- a/test_regress/t/t_constraint_array_limit.py +++ b/test_regress/t/t_constraint_array_limit.py @@ -14,9 +14,8 @@ test.scenarios('simulator') if not test.have_solver: test.skip("No constraint solver installed") -test.compile( - fails=True, - verilator_flags2=["--constraint-array-limit", "16"], - expect_filename=test.golden_filename) +test.compile(fails=True, + verilator_flags2=["--constraint-array-limit", "16"], + expect_filename=test.golden_filename) test.passes()