From a949674a6ba1f4c94040662bb0680a74a1b8cbc9 Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Mon, 28 Sep 2026 14:08:57 +0200 Subject: [PATCH] Support nested array and associative array element member access in constraint 3/4 - associative arrays (#8244) Signed-off-by: Kamil Danecki --- src/V3Randomize.cpp | 20 +++++++++--- .../t/t_constraint_global_arr_nested.v | 32 ++++++++++++++++++- .../t/t_constraint_global_arr_unsup.out | 3 -- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 1784dac3f..b4abd8030 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -2225,6 +2225,15 @@ class ConstraintExprVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(origp), origp); } } + void addStringNamePart(AstNodeSel* nodep, const std::string& fmt = "%32x") { + if (m_nestedAccess) { + AstNodeExpr* const bitp = nodep->bitp()->cloneTreePure(false); + AstNodeExpr* const bitFormatp = new AstSFormatF{bitp->fileline(), fmt, false, bitp}; + AstSFormatF* const herep + = new AstSFormatF{nodep->fileline(), "%s.%s", false, bitFormatp}; + m_nestedAccess->addVarNamePart(herep, nodep->bitp()->name()); + } + } void visit(AstAssocSel* nodep) override { if (editFormat(nodep)) return; FileLine* const fl = nodep->fileline(); @@ -2232,6 +2241,7 @@ class ConstraintExprVisitor final : public VNVisitor { AstNodeExpr* const origp = nodep->cloneTree(false); AstSFormatF* newp = nullptr; if (VN_IS(nodep->bitp(), VarRef) && VN_AS(nodep->bitp(), VarRef)->isString()) { + addStringNamePart(nodep); VNRelinker handle; AstNodeExpr* const idxp = new AstSFormatF{fl, (m_structSel ? "%32x" : "#x%32x"), false, nodep->bitp()->unlinkFrBack(&handle)}; @@ -2247,6 +2257,7 @@ class ConstraintExprVisitor final : public VNVisitor { "Unsupported: Constrained randomization of associative array keys of " << stringSize << "bits, limit is 128 bits"); } + addStringNamePart(nodep); VNRelinker handle; AstNodeExpr* const idxp = new AstSFormatF{fl, (m_structSel ? "%32x" : "#x%32x"), false, stringp->lhsp()->unlinkFrBack(&handle)}; @@ -2258,7 +2269,6 @@ class ConstraintExprVisitor final : public VNVisitor { && 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 @@ -2270,6 +2280,8 @@ class ConstraintExprVisitor final : public VNVisitor { fmt = (m_structSel ? "%" : "#x%") + std::to_string(VL_WORDS_I(actual_width) * 8) + "x"; } + if (m_nestedAccess) addStringNamePart(nodep, fmt); + VNRelinker handle; AstNodeExpr* const idxp = new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)}; handle.relink(idxp); @@ -2278,6 +2290,7 @@ class ConstraintExprVisitor final : public VNVisitor { nodep->bitp()->v3error( "Illegal non-integral expression or subexpression in random constraint." " (IEEE 1800-2023 18.3)"); + if (m_nestedAccess) m_nestedAccess->error(); } } if (newp && m_structSel && newp->name() == "(select %s %s)") { newp->name("%s.%s"); } @@ -2418,6 +2431,7 @@ class ConstraintExprVisitor final : public VNVisitor { } AstNodeSel* arraySelp = VN_CAST(rootNode, ArraySel); + if (!arraySelp) arraySelp = VN_CAST(rootNode, AssocSel); if (arraySelp) { AstNodeDType* const arrayDtp = arraySelp->fromp()->dtypep()->skipRefp(); @@ -2450,10 +2464,6 @@ class ConstraintExprVisitor final : public VNVisitor { return; } } - if (VN_IS(rootNode, AssocSel) || VN_IS(rootNode, ArraySel)) { - nodep->v3warn(E_UNSUPPORTED, - "Unsupported: Array element access in global constraint"); - } // Check if the root variable participates in global constraints if (const AstVarRef* const varRefp = VN_CAST(rootNode, VarRef)) { AstVar* const constrainedVar = varRefp->varp(); diff --git a/test_regress/t/t_constraint_global_arr_nested.v b/test_regress/t/t_constraint_global_arr_nested.v index 3b76cc4af..ddd651a58 100644 --- a/test_regress/t/t_constraint_global_arr_nested.v +++ b/test_regress/t/t_constraint_global_arr_nested.v @@ -50,6 +50,7 @@ class Holder; mode[0] = new; cyclic[0] = new; m_string_assoc["abc"] = new; + m_string_assoc["cba"] = new; foreach (items[i]) items[i] = new; endfunction @@ -132,7 +133,27 @@ class Outer; m_mid_arr2[m_idx + 1].m_obj.m_y == 501; } - // Case 6: randmode + // Case 6: Associative array element member access + constraint c_assoc { + m_assoc[0].m_x == 500; + m_assoc[m_idx].m_x == 502; + } + + constraint c_string_assoc_var { + m_holder.m_string_assoc[m_key].m_x == 43; + m_holder.m_string_assoc[m_key].m_y < 43; + } + + constraint c_string_assoc_literal { + m_holder.m_string_assoc["cba"].m_x == 103; + m_holder.m_string_assoc["cba"].m_y < 103; + } + + constraint c_assoc_nested { + m_assoc_nested[123][1].m_x == 501; + } + + // Case 7: randmode constraint c_mode { m_holder.mode[0].x == 42; } @@ -174,6 +195,12 @@ module t_constraint_global_arr_nested; `checkd(o.m_mid_arr[0].m_obj.m_y, 301); `checkd(o.m_mid_arr[1].m_arr[2].m_y, 400); + `checkd(o.m_assoc[0].m_x, 500); + `checkd(o.m_assoc[1].m_x, 502); + `checkd(o.m_holder.m_string_assoc["abc"].m_x, 43); + `checkd(o.m_holder.m_string_assoc["cba"].m_x, 103); + `checkd(o.m_assoc_nested[123][1].m_x, 501); + `checkd(o.m_holder.mode[0].x, 42); `checkd(o.m_mid_arr2[2].m_obj.m_x, 500); @@ -187,6 +214,9 @@ module t_constraint_global_arr_nested; `check_rand(o, o.m_mid_arr[2].m_arr[2].m_y, o.m_mid_arr[2].m_arr[2].m_y < 400); + `check_rand(o, o.m_holder.m_string_assoc["abc"].m_y, o.m_holder.m_string_assoc["abc"].m_y < 43); + `check_rand(o, o.m_holder.m_string_assoc["cba"].m_y, o.m_holder.m_string_assoc["cba"].m_y < 103); + repeat (4) begin rand_res = randc_o.randomize(); `checkd(rand_res, 1); diff --git a/test_regress/t/t_constraint_global_arr_unsup.out b/test_regress/t/t_constraint_global_arr_unsup.out index 9d4598641..9e29e0c6f 100644 --- a/test_regress/t/t_constraint_global_arr_unsup.out +++ b/test_regress/t/t_constraint_global_arr_unsup.out @@ -17,9 +17,6 @@ %Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:52:14: Unsupported: Randomized index in complex expression 52 | m_mid_arr[m_idx].m_obj.m_x == 123; | ^ -%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:58:24: Unsupported: Array element access in global constraint - 58 | m_mid.m_assoc[i].m_x == 1; - | ^~~ %Error: t/t_constraint_global_arr_unsup.v:58:21: Illegal non-integral expression or subexpression in random constraint. (IEEE 1800-2023 18.3) 58 | m_mid.m_assoc[i].m_x == 1; | ^