diff --git a/include/verilated_random.cpp b/include/verilated_random.cpp index 13c6a058a..3dc3b24b0 100644 --- a/include/verilated_random.cpp +++ b/include/verilated_random.cpp @@ -464,10 +464,10 @@ bool VlRandomizer::parseSolution(std::iostream& f) { const size_t start = hex_index.find_first_not_of(" "); if (start == std::string::npos || hex_index.substr(start, 2) != "#x") { VL_FATAL_MT(__FILE__, __LINE__, "randomize", - "Error: hex_index contains invalid format"); + "hex_index contains invalid format"); continue; } - const int index = std::stoi(hex_index.substr(start + 2), nullptr, 16); + const long long index = std::stoll(hex_index.substr(start + 2), nullptr, 16); oss << "[" << index << "]"; } const std::string indexed_name = oss.str(); @@ -481,7 +481,7 @@ bool VlRandomizer::parseSolution(std::iostream& f) { idx = ss.str(); } else { VL_FATAL_MT(__FILE__, __LINE__, "randomize", - "Error: indexed_name not found in m_arr_vars"); + "indexed_name not found in m_arr_vars"); } } varr.set(idx, value); diff --git a/include/verilated_random.h b/include/verilated_random.h index 4dd840149..90d92aa9e 100644 --- a/include/verilated_random.h +++ b/include/verilated_random.h @@ -29,6 +29,7 @@ #include #include + //============================================================================= // VlRandomExpr and subclasses represent expressions for the constraint solver. class ArrayInfo final { @@ -38,12 +39,15 @@ public: void* const m_datap; // Reference to the array variable data const int m_index; // Flattened (1D) index of the array element const std::vector m_indices; // Multi-dimensional indices of the array element + const std::vector m_idxWidths; // Multi-dimensional indices' bit widths - ArrayInfo(const std::string& name, void* datap, int index, const std::vector& indices) + ArrayInfo(const std::string& name, void* datap, int index, const std::vector& indices, + const std::vector& idxWidths) : m_name(name) , m_datap(datap) , m_index(index) - , m_indices(indices) {} + , m_indices(indices) + , m_idxWidths(idxWidths) {} }; using ArrayInfoMap = std::map>; @@ -90,25 +94,30 @@ public: return count; } }; - template -class VlRandomQueueVar final : public VlRandomVar { +class VlRandomArrayVarTemplate final : public VlRandomVar { public: - VlRandomQueueVar(const char* name, int width, void* datap, int dimension, - std::uint32_t randModeIdx) + VlRandomArrayVarTemplate(const char* name, int width, void* datap, int dimension, + std::uint32_t randModeIdx) : VlRandomVar{name, width, datap, dimension, randModeIdx} {} void* datap(int idx) const override { const std::string indexed_name = name() + std::to_string(idx); const auto it = m_arrVarsRefp->find(indexed_name); - if (it != m_arrVarsRefp->end()) return it->second->m_datap; - return &static_cast(VlRandomVar::datap(idx))->atWrite(idx); + if (it != m_arrVarsRefp->end()) { + return it->second->m_datap; + } else { + VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars"); + return nullptr; + } } - void emitSelect(std::ostream& s, const std::vector& indices) const { + void emitSelect(std::ostream& s, const std::vector& indices, + const std::vector& idxWidths) const { for (size_t idx = 0; idx < indices.size(); ++idx) s << "(select "; s << name(); for (size_t idx = 0; idx < indices.size(); ++idx) { s << " #x"; - for (int j = 28; j >= 0; j -= 4) { + const size_t bit_width = idxWidths[idx]; + for (int j = bit_width - 4; j >= 0; j -= 4) { s << "0123456789abcdef"[(indices[idx] >> j) & 0xf]; } s << ")"; @@ -121,15 +130,28 @@ public: const auto it = m_arrVarsRefp->find(indexed_name); if (it != m_arrVarsRefp->end()) { const std::vector& indices = it->second->m_indices; - emitSelect(s, indices); + const std::vector& idxWidths = it->second->m_idxWidths; + emitSelect(s, indices, idxWidths); + } else { + VL_FATAL_MT(__FILE__, __LINE__, "randomize", + "indexed_name not found in m_arr_vars"); } } } void emitType(std::ostream& s) const override { - if (dimension() > 0) { - for (int i = 0; i < dimension(); ++i) s << "(Array (_ BitVec 32) "; - s << "(_ BitVec " << width() << ")"; - for (int i = 0; i < dimension(); ++i) s << ")"; + const std::string indexed_name = name() + std::to_string(0); + const auto it = m_arrVarsRefp->find(indexed_name); + if (it != m_arrVarsRefp->end()) { + const std::vector& idxWidths = it->second->m_idxWidths; + if (dimension() > 0) { + for (int i = 0; i < dimension(); ++i) { + s << "(Array (_ BitVec " << idxWidths[i] << ") "; + } + s << "(_ BitVec " << width() << ")"; + for (int i = 0; i < dimension(); ++i) { s << ")"; } + } + } else { + VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars"); } } int totalWidth() const override { @@ -144,66 +166,10 @@ public: const auto it = m_arrVarsRefp->find(indexed_name); if (it != m_arrVarsRefp->end()) { const std::vector& indices = it->second->m_indices; - emitSelect(s, indices); - } - s << ')'; - } -}; - -template -class VlRandomArrayVar final : public VlRandomVar { -public: - VlRandomArrayVar(const char* name, int width, void* datap, int dimension, - std::uint32_t randModeIdx) - : VlRandomVar{name, width, datap, dimension, randModeIdx} {} - void* datap(int idx) const override { - const std::string indexed_name = name() + std::to_string(idx); - const auto it = m_arrVarsRefp->find(indexed_name); - if (it != m_arrVarsRefp->end()) return it->second->m_datap; - return &static_cast(VlRandomVar::datap(idx))->operator[](idx); - } - void emitSelect(std::ostream& s, const std::vector& indices) const { - for (size_t idx = 0; idx < indices.size(); ++idx) s << "(select "; - s << name(); - for (size_t idx = 0; idx < indices.size(); ++idx) { - s << " #x"; - for (int j = 28; j >= 0; j -= 4) { - s << "0123456789abcdef"[(indices[idx] >> j) & 0xf]; - } - s << ")"; - } - } - void emitGetValue(std::ostream& s) const override { - const int elementCounts = countMatchingElements(*m_arrVarsRefp, name()); - for (int i = 0; i < elementCounts; i++) { - const std::string indexed_name = name() + std::to_string(i); - const auto it = m_arrVarsRefp->find(indexed_name); - if (it != m_arrVarsRefp->end()) { - const std::vector& indices = it->second->m_indices; - emitSelect(s, indices); - } - } - } - void emitType(std::ostream& s) const override { - if (dimension() > 0) { - for (int i = 0; i < dimension(); ++i) s << "(Array (_ BitVec 32) "; - s << "(_ BitVec " << width() << ")"; - for (int i = 0; i < dimension(); ++i) s << ")"; - } - } - int totalWidth() const override { - const int elementCounts = countMatchingElements(*m_arrVarsRefp, name()); - return width() * elementCounts; - } - void emitExtract(std::ostream& s, int i) const override { - const int j = i / width(); - i = i % width(); - s << " ((_ extract " << i << ' ' << i << ')'; - const std::string indexed_name = name() + std::to_string(j); - const auto it = m_arrVarsRefp->find(indexed_name); - if (it != m_arrVarsRefp->end()) { - const std::vector& indices = it->second->m_indices; - emitSelect(s, indices); + const std::vector& idxWidths = it->second->m_idxWidths; + emitSelect(s, indices, idxWidths); + } else { + VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars"); } s << ')'; } @@ -217,6 +183,7 @@ class VlRandomizer final { std::map> m_vars; // Solver-dependent // variables ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration + std::map seen_values; // Record String Index to avoid conflicts const VlQueue* m_randmode; // rand_mode state; // PRIVATE METHODS @@ -231,6 +198,47 @@ public: // METHODS // Finds the next solution satisfying the constraints bool next(VlRNG& rngr); + + template + typename std::enable_if::value>::type + process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index, + const std::string& base_name, size_t& idx_width) { + integral_index = static_cast(key); + indexed_name = base_name + "[" + std::to_string(integral_index) + "]"; + idx_width = sizeof(T_Key) * 8; + } + template + typename std::enable_if::value>::type + process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index, + const std::string& base_name, size_t& idx_width) { + integral_index = string_to_integral(key); + indexed_name = base_name + "[" + std::to_string(integral_index) + "]"; + idx_width = 64; // 64-bit mask + } + template + typename std::enable_if::value + && !std::is_same::value>::type + process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index, + const std::string& base_name, size_t& idx_width) { + VL_FATAL_MT(__FILE__, __LINE__, "randomize", + "Unsupported: Only integral and string index of associative array is " + "supported currently."); + } + + uint64_t string_to_integral(const std::string& str) { + uint64_t result = 0; + for (char c : str) { result = (result << 8) | static_cast(c); } + +#ifdef VL_DEBUG + if (seen_values.count(result) > 0 && seen_values[result] != str) + VL_WARN_MT(__FILE__, __LINE__, "randomize", + "Conflict detected: Different strings mapped to the same 64-bit index."); + seen_values[result] = str; +#endif + + return result; + } + template void write_var(T& var, int width, const char* name, int dimension, std::uint32_t randmodeIdx = std::numeric_limits::max()) { @@ -243,25 +251,38 @@ public: void write_var(VlQueue& var, int width, const char* name, int dimension, std::uint32_t randmodeIdx = std::numeric_limits::max()) { if (m_vars.find(name) != m_vars.end()) return; - m_vars[name] = std::make_shared>>( + m_vars[name] = std::make_shared>>( name, width, &var, dimension, randmodeIdx); if (dimension > 0) { idx = 0; - record_arr_table(var, name, dimension, {}); + record_arr_table(var, name, dimension, {}, {}); } } template void write_var(VlUnpacked& var, int width, const char* name, int dimension, std::uint32_t randmodeIdx = std::numeric_limits::max()) { if (m_vars.find(name) != m_vars.end()) return; - m_vars[name] = std::make_shared>>( + m_vars[name] = std::make_shared>>( name, width, &var, dimension, randmodeIdx); if (dimension > 0) { idx = 0; - record_arr_table(var, name, dimension, {}); + record_arr_table(var, name, dimension, {}, {}); } } - int idx = 0; + template + void write_var(VlAssocArray& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { + if (m_vars.find(name) != m_vars.end()) return; + m_vars[name] + = std::make_shared>>( + name, width, &var, dimension, randmodeIdx); + if (dimension > 0) { + idx = 0; + record_arr_table(var, name, dimension, {}, {}); + } + } + + int idx; std::string generateKey(const std::string& name, int idx) { if (!name.empty() && name[0] == '\\') { const size_t space_pos = name.find(' '); @@ -272,45 +293,61 @@ public: return (bracket_pos != std::string::npos ? name.substr(0, bracket_pos) : name) + std::to_string(idx); } + template void record_arr_table(T& var, const std::string name, int dimension, - std::vector indices) { + std::vector indices, std::vector idxWidths) { const std::string key = generateKey(name, idx); - m_arr_vars[key] = std::make_shared(name, &var, idx, indices); - idx += 1; + m_arr_vars[key] = std::make_shared(name, &var, idx, indices, idxWidths); + ++idx; } template void record_arr_table(VlQueue& var, const std::string name, int dimension, - std::vector indices) { + std::vector indices, std::vector idxWidths) { if ((dimension > 0) && (var.size() != 0)) { + idxWidths.push_back(32); for (size_t i = 0; i < var.size(); ++i) { const std::string indexed_name = name + "[" + std::to_string(i) + "]"; indices.push_back(i); - record_arr_table(var.atWrite(i), indexed_name, dimension - 1, indices); + record_arr_table(var.atWrite(i), indexed_name, dimension - 1, indices, idxWidths); indices.pop_back(); } - } else { - const std::string key = generateKey(name, idx); - m_arr_vars[key] = std::make_shared(name, &var, idx, indices); - ++idx; } } template void record_arr_table(VlUnpacked& var, const std::string name, int dimension, - std::vector indices) { + std::vector indices, std::vector idxWidths) { if ((dimension > 0) && (N_Depth != 0)) { + idxWidths.push_back(32); for (size_t i = 0; i < N_Depth; ++i) { const std::string indexed_name = name + "[" + std::to_string(i) + "]"; indices.push_back(i); - record_arr_table(var.operator[](i), indexed_name, dimension - 1, indices); + record_arr_table(var.operator[](i), indexed_name, dimension - 1, indices, + idxWidths); indices.pop_back(); } - } else { - const std::string key = generateKey(name, idx); - m_arr_vars[key] = std::make_shared(name, &var, idx, indices); - idx += 1; } } + template + void record_arr_table(VlAssocArray& var, const std::string name, int dimension, + std::vector indices, std::vector idxWidths) { + if ((dimension > 0) && (var.size() != 0)) { + for (auto it = var.begin(); it != var.end(); ++it) { + const T_Key& key = it->first; + const T_Value& value = it->second; + std::string indexed_name; + size_t integral_index; + size_t idx_width; + process_key(key, indexed_name, integral_index, name, idx_width); + idxWidths.push_back(idx_width); + indices.push_back(integral_index); + record_arr_table(var.at(key), indexed_name, dimension - 1, indices, idxWidths); + idxWidths.pop_back(); + indices.pop_back(); + } + } + } + void hard(std::string&& constraint); void clear(); void set_randmode(const VlQueue& randmode) { m_randmode = &randmode; } diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 5237268e6..a97853dd2 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -4191,6 +4191,7 @@ public: } string emitVerilog() override { return "%k(%l%f[%r])"; } string emitC() override { return "%li%k[%ri]"; } + string emitSMT() const override { return "(select %l %r)"; } bool cleanOut() const override { return true; } bool cleanLhs() const override { return false; } bool cleanRhs() const override { return true; } diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index b6507c960..4067cff6b 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -641,7 +641,7 @@ class ConstraintExprVisitor final : public VNVisitor { "write_var"}; uint32_t dimension = 0; if (VN_IS(varp->dtypep(), UnpackArrayDType) || VN_IS(varp->dtypep(), DynArrayDType) - || VN_IS(varp->dtypep(), QueueDType)) { + || VN_IS(varp->dtypep(), QueueDType) || VN_IS(varp->dtypep(), AssocArrayDType)) { const std::pair dims = varp->dtypep()->dimensions(/*includeBasic=*/true); const uint32_t unpackedDimensions = dims.second; @@ -656,7 +656,7 @@ class ConstraintExprVisitor final : public VNVisitor { size_t width = varp->width(); AstNodeDType* tmpDtypep = varp->dtypep(); while (VN_IS(tmpDtypep, UnpackArrayDType) || VN_IS(tmpDtypep, DynArrayDType) - || VN_IS(tmpDtypep, QueueDType)) + || VN_IS(tmpDtypep, QueueDType) || VN_IS(tmpDtypep, AssocArrayDType)) tmpDtypep = tmpDtypep->subDTypep(); width = tmpDtypep->width(); methodp->addPinsp( @@ -724,6 +724,44 @@ class ConstraintExprVisitor final : public VNVisitor { editSMT(nodep, nodep->fromp(), lsbp, msbp); } + void visit(AstAssocSel* nodep) override { + if (editFormat(nodep)) return; + FileLine* const fl = nodep->fileline(); + if (VN_IS(nodep->bitp(), CvtPackString)) { + // Extract and truncate the string index to fit within 64 bits + AstCvtPackString* const stringp = VN_AS(nodep->bitp(), CvtPackString); + VNRelinker handle; + AstNodeExpr* const strIdxp = new AstSFormatF{ + fl, "#x%16x", false, + new AstAnd{fl, stringp->lhsp()->unlinkFrBack(&handle), + new AstConst(fl, AstConst::Unsized64{}, 0xFFFFFFFFFFFFFFFF)}}; + handle.relink(strIdxp); + editSMT(nodep, nodep->fromp(), strIdxp); + } 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 if (actual_width <= 32) { + fmt = "#x%8x"; + } else if (actual_width <= 64) { + fmt = "#x%16x"; + } else { + nodep->v3warn(CONSTRAINTIGN, + "Unsupported: Associative array index " + "widths of more than 64 bits during constraint randomization."); + return; + } + AstNodeExpr* const idxp + = new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)}; + handle.relink(idxp); + editSMT(nodep, nodep->fromp(), idxp); + } + } void visit(AstArraySel* nodep) override { if (editFormat(nodep)) return; FileLine* const fl = nodep->fileline(); diff --git a/test_regress/t/t_constraint_assoc_arr_bad.out b/test_regress/t/t_constraint_assoc_arr_bad.out new file mode 100644 index 000000000..a048591fc --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_bad.out @@ -0,0 +1,9 @@ +%Warning-CONSTRAINTIGN: t/t_constraint_assoc_arr_bad.v:14:22: Unsupported: Associative array index widths of more than 64 bits during constraint randomization. + 14 | bit_index_arr[79'd66] == 65; + | ^ + ... 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. +%Warning-CONSTRAINTIGN: t/t_constraint_assoc_arr_bad.v:15:24: Unsupported: Associative array index widths of more than 64 bits during constraint randomization. + 15 | logic_index_arr[65'd3] == 70; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_constraint_assoc_arr_bad.py b/test_regress/t/t_constraint_assoc_arr_bad.py new file mode 100755 index 000000000..efe8cc01c --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_bad.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('simulator') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_constraint_assoc_arr_bad.v b/test_regress/t/t_constraint_assoc_arr_bad.v new file mode 100644 index 000000000..2050f2643 --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_bad.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 PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + + +class AssocArrayWarningTest; + + rand int bit_index_arr [bit[78:0]]; + rand int logic_index_arr [logic[64:0]]; + + constraint c { + bit_index_arr[79'd66] == 65; + logic_index_arr[65'd3] == 70; + } + function new(); + bit_index_arr = '{79'd66:0}; + logic_index_arr = '{65'd3:0}; + endfunction + +endclass + +module t_constraint_assoc_arr_bad; + + AssocArrayWarningTest test_obj; + + initial begin + test_obj = new(); + repeat(2) begin + int success; + success = test_obj.randomize(); + if (success != 1) $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_array_constraints.py b/test_regress/t/t_constraint_assoc_arr_basic.py similarity index 100% rename from test_regress/t/t_randomize_array_constraints.py rename to test_regress/t/t_constraint_assoc_arr_basic.py diff --git a/test_regress/t/t_constraint_assoc_arr_basic.v b/test_regress/t/t_constraint_assoc_arr_basic.v new file mode 100644 index 000000000..105cdb96a --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_basic.v @@ -0,0 +1,180 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +class constrained_associative_array_basic; + + rand int int_index_arr [int]; + rand int string_index_arr [string]; + /* verilator lint_off SIDEEFFECT */ + // Constraints for both arrays + constraint int_index_constraints { + foreach (int_index_arr[i]) int_index_arr[i] inside {10, 20, 30, 40, 50}; + } + constraint string_index_constraints { + string_index_arr["Alice"] == 35; + string_index_arr["Bob"] inside {50, 60}; + string_index_arr["Charlie"] > 25; + } + + // Constructor to initialize arrays + function new(); + int_index_arr = '{1: 0, 8: 0, 7: 0}; + string_index_arr = '{"Alice": 25, "Bob": 50, "Charlie": 45}; + endfunction + + // Function to check and display the arrays + function void self_check(); + foreach (int_index_arr[i]) begin + if (!(int_index_arr[i] inside {10, 20, 30, 40, 50})) $stop; + end + foreach (string_index_arr[name]) begin + if ((name == "Alice" && string_index_arr[name] != 35) || + (name == "Bob" && !(string_index_arr[name] inside {50, 60})) || + (name == "Charlie" && string_index_arr[name] <= 25)) $stop; + end + endfunction + +endclass + +class constrained_1d_associative_array; + + rand int string_index_arr [string]; + rand int int_index_arr [int]; + rand int shortint_index_arr [shortint]; + rand int longint_index_arr[longint]; + rand int byte_index_arr [byte]; + rand int bit_index_arr [bit[5:0]]; + rand int logic_index_arr [logic[3:0]]; + rand int bit_index_arr_1 [bit[55:0]]; + + // Constraints + constraint associative_array_constraints { + string_index_arr["key1"] == 100; + string_index_arr["key2"] inside {200, 300, 400}; + int_index_arr[40000] + int_index_arr[2000000000] == 2; + shortint_index_arr[2000] == 200; + longint_index_arr[64'd4000000000] == 300; + byte_index_arr[8'd255] == 50; + bit_index_arr[6'd30] - bit_index_arr_1[56'd66] == 3; + logic_index_arr[4'b0011] == 70; + } + + function new(); + string_index_arr = '{"key1":0, "key2":0}; + int_index_arr = '{40000:0, 2000000000:0}; + shortint_index_arr = '{2000:0}; + longint_index_arr = '{64'd4000000000:0}; + byte_index_arr = '{8'd255:0}; + bit_index_arr = '{6'd30:0}; + bit_index_arr_1 = '{56'd66:0}; + logic_index_arr = '{4'd3:0}; + endfunction + + function void self_check(); + if (string_index_arr["key1"] != 100) $stop; + if (!(string_index_arr["key2"] inside {200, 300, 400})) $stop; + if ((int_index_arr[40000] + int_index_arr[2000000000]) != 2) $stop; + if (shortint_index_arr[2000] != 200) $stop; + if (longint_index_arr[64'd4000000000] != 300) $stop; + if (byte_index_arr[8'd255] != 50) $stop; + if (bit_index_arr[6'd30] - bit_index_arr_1[56'd66] != 3) $stop; + if (logic_index_arr[4'd3] != 70) $stop; + endfunction + + function void debug_display(); + $display("string_index_arr[\"key1\"] = %0d", string_index_arr["key1"]); + $display("string_index_arr[\"key2\"] = %0d", string_index_arr["key2"]); + $display("int_index_arr[40000] = %0d", int_index_arr[40000]); + $display("int_index_arr[2000000000] = %0d", int_index_arr[2000000000]); + $display("shortint_index_arr[2000] = %0d", shortint_index_arr[2000]); + $display("longint_index_arr[4000000000] = %0d", longint_index_arr[64'd4000000000]); + $display("byte_index_arr[255] = %0d", byte_index_arr[8'd255]); + $display("bit_index_arr[30] = %0d", bit_index_arr[6'd30]); + $display("bit_index_arr_1[66] = %0d", bit_index_arr_1[56'd66]); + $display("logic_index_arr[3] = %0d", logic_index_arr[4'd3]); + endfunction + +endclass + +class constrained_2d_associative_array; + + rand int string_int_index_arr [string][int]; + rand int int_bit_index_arr [int][bit[5:0]]; + rand int string_bit_index_arr [string][bit[7:0]]; + rand int unpacked_assoc_array_2d [string][2]; + + // Constraints + constraint associative_array_constraints { + string_int_index_arr["key1"][2000] == 100; + string_int_index_arr["key2"][3000] inside {200, 300, 400}; + int_bit_index_arr[40000][6'd30] == 60; + int_bit_index_arr[50000][6'd40] inside {100, 200}; + string_bit_index_arr["key3"][8'd100] == 150; + string_bit_index_arr["key4"][8'd200] inside {250, 350}; + unpacked_assoc_array_2d["key5"][0] == 7; + } + + function new(); + string_int_index_arr = '{"key1":'{2000:0}, "key2":'{3000:0}}; + int_bit_index_arr = '{40000:'{6'd30:0}, 50000:'{6'd40:0}}; + string_bit_index_arr = '{"key3":'{8'd100:0}, "key4":'{8'd200:0}}; + unpacked_assoc_array_2d["key5"][0] = 0; + unpacked_assoc_array_2d["key5"][1] = 0; + endfunction + + function void self_check(); + if (string_int_index_arr["key1"][2000] != 100) $stop; + if (!(string_int_index_arr["key2"][3000] inside {200, 300, 400})) $stop; + if (int_bit_index_arr[40000][6'd30] != 60) $stop; + if (!(int_bit_index_arr[50000][6'd40] inside {100, 200})) $stop; + if (string_bit_index_arr["key3"][8'd100] != 150) $stop; + if (!(string_bit_index_arr["key4"][8'd200] inside {250, 350})) $stop; + if (unpacked_assoc_array_2d["key5"][0] != 7) $stop; + endfunction + + function void debug_display(); + $display("string_int_index_arr[\"key1\"][2000] = %0d", string_int_index_arr["key1"][2000]); + $display("string_int_index_arr[\"key2\"][3000] = %0d", string_int_index_arr["key2"][3000]); + $display("int_bit_index_arr[40000][30] = %0d", int_bit_index_arr[40000][6'd30]); + $display("int_bit_index_arr[50000][40] = %0d", int_bit_index_arr[50000][6'd40]); + $display("string_bit_index_arr[\"key3\"][100] = %0d", string_bit_index_arr["key3"][8'd100]); + $display("string_bit_index_arr[\"key4\"][200] = %0d", string_bit_index_arr["key4"][8'd200]); + $display("unpacked_assoc_array_2d[\"key5\"][0] = %0d", unpacked_assoc_array_2d["key5"][0]); + endfunction + /* verilator lint_off SIDEEFFECT */ +endclass + +module t_constraint_assoc_arr_basic; + + constrained_associative_array_basic my_array; + constrained_1d_associative_array my_1d_array; + constrained_2d_associative_array my_2d_array; + int success; + + initial begin + my_array = new(); + success = my_array.randomize(); + if (success == 0) $stop; + my_array.self_check(); + + my_1d_array = new(); + success = my_1d_array.randomize(); + if (success == 0) $stop; + my_1d_array.self_check(); + + my_1d_array = new(); + success = my_1d_array.randomize(); + if (success == 0) $stop; + my_1d_array.self_check(); + + // my_1d_array.debug_display(); + // my_2d_array.debug_display(); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_constraint_unpacked_array.py b/test_regress/t/t_constraint_unpacked_array.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_constraint_unpacked_array.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_randomize_array_constraints.v b/test_regress/t/t_constraint_unpacked_array.v similarity index 99% rename from test_regress/t/t_randomize_array_constraints.v rename to test_regress/t/t_constraint_unpacked_array.v index f56cab447..1d7ecfbc6 100755 --- a/test_regress/t/t_randomize_array_constraints.v +++ b/test_regress/t/t_constraint_unpacked_array.v @@ -125,7 +125,7 @@ class con_rand_3d_array_test; endclass -module t_randomize_array_constraints; +module t_constraint_unpacked_array; con_rand_1d_array_test rand_test_1; con_rand_2d_array_test rand_test_2; con_rand_3d_array_test rand_test_3;