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");