diff --git a/include/verilated_types.h b/include/verilated_types.h index 851366b95..c7ef5e35d 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -505,6 +505,12 @@ public: VlQueue& operator=(VlQueue&&) = default; bool operator==(const VlQueue& rhs) const { return m_deque == rhs.m_deque; } bool operator!=(const VlQueue& rhs) const { return m_deque != rhs.m_deque; } + bool operator<(const VlQueue& rhs) const { + for (int index = 0; index < m_deque.size(); ++index) { + if (m_deque[index] < rhs.m_deque[index]) return true; + } + return false; + } // Standard copy constructor works. Verilog: assoca = assocb // Also must allow conversion from a different N_MaxSize queue @@ -1364,6 +1370,12 @@ public: bool neq(const T_Value that[N_Depth]) const { return neq(*this, that); } void assign(const T_Value that[N_Depth]) { std::copy_n(that, N_Depth, m_storage); } void operator=(const T_Value that[N_Depth]) { assign(that); } + bool operator<(const VlUnpacked& that) const { + for (int index = 0; index < N_Depth; ++index) { + if (m_storage[index] < that.m_storage[index]) return true; + } + return false; + } // inside (set membership operator) bool inside(const T_Value& value) const { diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 5abde202e..9af1c604d 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -262,6 +262,23 @@ class EmitCHeader final : public EmitCConstInit { putns(sdtypep, "bool operator!=(const " + EmitCBase::prefixNameProtect(sdtypep) + "& rhs) const {\n"); puts("return !(*this == rhs);\n}\n"); + putns(sdtypep, "\nbool operator<(const " + EmitCBase::prefixNameProtect(sdtypep) + + "& rhs) const {\n"); + puts("return "); + puts("std::tie("); + for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + if (itemp != sdtypep->membersp()) puts(", "); + putns(itemp, itemp->nameProtect()); + } + puts(")\n < std::tie("); + for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + if (itemp != sdtypep->membersp()) puts(", "); + putns(itemp, "rhs." + itemp->nameProtect()); + } + puts(");\n"); + puts("}\n"); puts("};\n"); } diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 0fdb58a16..6baa90919 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -709,7 +709,7 @@ class ConstraintExprVisitor final : public VNVisitor { void visit(AstAssocSel* nodep) override { if (editFormat(nodep)) return; FileLine* const fl = nodep->fileline(); - if (VN_IS(nodep->bitp(), CvtPackString)) { + if (VN_IS(nodep->bitp(), CvtPackString) && VN_IS(nodep->bitp()->dtypep(), BasicDType)) { AstCvtPackString* const stringp = VN_AS(nodep->bitp(), CvtPackString); const size_t stringSize = VN_AS(stringp->lhsp(), Const)->width(); if (stringSize > 128) { @@ -724,22 +724,32 @@ class ConstraintExprVisitor final : public VNVisitor { handle.relink(idxp); editSMT(nodep, nodep->fromp(), idxp); } else { - VNRelinker handle; - const int actual_width = nodep->bitp()->width(); - std::string fmt; - // Normalize to standard bit width - if (actual_width <= 8) { - fmt = "#x%2x"; - } else if (actual_width <= 16) { - fmt = "#x%4x"; - } else { - fmt = "#x%" + std::to_string(VL_WORDS_I(actual_width) * 8) + "x"; - } + if (VN_IS(nodep->bitp()->dtypep(), BasicDType) + || (VN_IS(nodep->bitp()->dtypep(), StructDType) + && VN_AS(nodep->bitp()->dtypep(), StructDType)->packed()) + || VN_IS(nodep->bitp()->dtypep(), EnumDType) + || VN_IS(nodep->bitp()->dtypep(), PackArrayDType)) { + VNRelinker handle; + const int actual_width = nodep->bitp()->width(); + std::string fmt; + // Normalize to standard bit width + if (actual_width <= 8) { + fmt = "#x%2x"; + } else if (actual_width <= 16) { + fmt = "#x%4x"; + } else { + fmt = "#x%" + std::to_string(VL_WORDS_I(actual_width) * 8) + "x"; + } - AstNodeExpr* const idxp - = new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)}; - handle.relink(idxp); - editSMT(nodep, nodep->fromp(), idxp); + AstNodeExpr* const idxp + = new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)}; + handle.relink(idxp); + editSMT(nodep, nodep->fromp(), idxp); + } else { + nodep->bitp()->v3error( + "Illegal non-integral expression or subexpression in random constraint." + " (IEEE 1800-2023 18.3)"); + } } } void visit(AstArraySel* nodep) override { diff --git a/test_regress/t/t_constraint_assoc_arr_bad.out b/test_regress/t/t_constraint_assoc_arr_bad.out index 0e919e3b9..167603034 100644 --- a/test_regress/t/t_constraint_assoc_arr_bad.out +++ b/test_regress/t/t_constraint_assoc_arr_bad.out @@ -3,4 +3,13 @@ | ^~~~~~~~~~~~~~~~~~~~ ... 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: t/t_constraint_assoc_arr_bad.v:30:26: Illegal non-integral expression or subexpression in random constraint. (IEEE 1800-2023 18.3) + 30 | constraint c1 { data[cl] > 0;} + | ^~ +%Error: t/t_constraint_assoc_arr_bad.v:44:44: Illegal non-integral expression or subexpression in random constraint. (IEEE 1800-2023 18.3) + 44 | constraint c2 { foreach (data[i]) data[i] < 100; } + | ^ +%Error: t/t_constraint_assoc_arr_bad.v:58:44: Illegal non-integral expression or subexpression in random constraint. (IEEE 1800-2023 18.3) + 58 | constraint c3 { foreach (data[i]) data[i] > 0; } + | ^ %Error: Exiting due to diff --git a/test_regress/t/t_constraint_assoc_arr_bad.v b/test_regress/t/t_constraint_assoc_arr_bad.v index 7ffeba96a..644b19f30 100755 --- a/test_regress/t/t_constraint_assoc_arr_bad.v +++ b/test_regress/t/t_constraint_assoc_arr_bad.v @@ -4,8 +4,8 @@ // any use, without warranty, 2024 by PlanV GmbH. // SPDX-License-Identifier: CC0-1.0 -class AssocArrayWarningTest; - +// Long String index associative array +class AssocArrayString; rand int string_arr [string]; constraint c { @@ -14,20 +14,79 @@ class AssocArrayWarningTest; function new(); string_arr["a_very_long_string"] = 0; endfunction +endclass +class keyClass; + int id; + function new(); + id = 3; + endfunction +endclass +// Class index associative array +class AssocArrayClass; + rand bit [31:0] data [keyClass]; + keyClass cl; + // constraint c4 { foreach (data[i]) data[i] > 0;} Unsupported index type for an associative array in an iterative constraint. + constraint c1 { data[cl] > 0;} // Illegal index expression of unpacked type in constraint. + function new(); + cl = new(); + data[cl] = 32'd77; + endfunction +endclass + +typedef struct { + int a; + int b; +} UnpackedIndexType; +// Struct (unpacked) index associative array +class AssocArrayUnpackedStruct; + rand bit [31:0] data [UnpackedIndexType]; + constraint c2 { foreach (data[i]) data[i] < 100; } // Illegal non-integral expression in random constraint. + + function new(); + UnpackedIndexType idx; + idx.a = 1; + idx.b = 2; + data[idx] = 32'd25; + endfunction +endclass + +// Array (unpacked) index associative array +typedef logic [2:0] IndexArrayType[3]; +class AssocArrayArrayIndex; + rand bit [31:0] data [IndexArrayType]; + constraint c3 { foreach (data[i]) data[i] > 0; } + + function new(); + IndexArrayType idx; + for (int j = 0; j < 4; j++) begin + idx[j] = 3'd0; + end + data[idx] = 32'd75; + endfunction endclass module t_constraint_assoc_arr_bad; - AssocArrayWarningTest test_obj; + AssocArrayString test_str; + AssocArrayClass test_cls; + AssocArrayUnpackedStruct test_unp_struct; + AssocArrayArrayIndex test_unp_arr; + int success = 0; initial begin - test_obj = new(); - repeat(2) begin - int success; - success = test_obj.randomize(); - if (success != 1) $stop; - end + test_str = new(); + test_cls = new(); + test_unp_struct = new(); + test_unp_arr = new(); + + success += test_str.randomize(); + success += test_cls.randomize(); + success += test_unp_struct.randomize(); + success += test_unp_arr.randomize(); + + if(success != 4) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_constraint_assoc_arr_others.py b/test_regress/t/t_constraint_assoc_arr_others.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_others.py @@ -0,0 +1,21 @@ +#!/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('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_assoc_arr_others.v b/test_regress/t/t_constraint_assoc_arr_others.v new file mode 100755 index 000000000..badbc3387 --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_others.v @@ -0,0 +1,100 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// 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 + +// Enum-based associative array +typedef enum { RED, GREEN, YELLOW } color_t; + +class AssocArrayEnum; + rand bit [7:0] colors [color_t]; + constraint c1 { foreach (colors[i]) colors[i] == 4; } + + function new(); + colors[RED] = 8'd5; + colors[GREEN] = 8'd10; + colors[YELLOW] = 8'd15; + endfunction + + function void self_check(); + foreach (colors[i]) begin + if (colors[i] != 4) $stop; + end + endfunction +endclass + +// Struct (packed) index associative array +typedef struct packed { + bit [2:0] high; + bit [1:0] low; +} PackedIndexType; + +class AssocArrayPackedStruct; + rand bit [31:0] data [PackedIndexType]; + constraint c2 { foreach (data[i]) data[i] == 100; } + + function new(); + PackedIndexType idx; + idx.high = 3'd1; + idx.low = 2'd1; + data[idx] = 32'd50; + endfunction + + function void self_check(); + foreach (data[i]) begin + if (data[i] != 100) $stop; + end + endfunction +endclass + +// Array (packed) index associative array +typedef logic [2:0][7:0] IndexArrayType; + +class AssocArrayArrayIndex; + rand bit [31:0] data [IndexArrayType]; + constraint c3 { foreach (data[i]) data[i] == 0; } + + function new(); + IndexArrayType idx; + idx = 0; + data[idx] = 32'd75; + endfunction + + function void self_check(); + foreach (data[i]) begin + if (data[i] != 0) $stop; + end + endfunction +endclass + +module t_constraint_assoc_array_others; + + AssocArrayEnum enum_arr; + AssocArrayPackedStruct packed_arr; + AssocArrayArrayIndex array_arr; + int success = 0; + + initial begin + // Create instances of the classes + enum_arr = new(); + packed_arr = new(); + array_arr = new(); + + // Randomization and self-check + success = enum_arr.randomize(); + if (success != 1) $stop; + enum_arr.self_check(); + + success = packed_arr.randomize(); + if (success != 1) $stop; + packed_arr.self_check(); + + success = array_arr.randomize(); + if (success != 1) $stop; + array_arr.self_check(); + + $write("*-* All Finished *-*"); + $finish; + end +endmodule