From 54dfd0a702c9fbc393e7fc974c85841c7391f72b Mon Sep 17 00:00:00 2001 From: Andrew Pullin Date: Fri, 23 Jan 2026 14:43:03 -0800 Subject: [PATCH] Fix #521: Allow variable indices in outer packed dimensions In multi-dimensional packed arrays, allow variable indices in the outer (prefix) dimensions, not just the final dimension. For example: logic [3:0][3:0] a; for (int i=0; i<4; i++) a[i][3] = 1; // Previously error, now works The fix checks if any packed prefix indices are non-constant. If so, use collapse_array_exprs() to compute the bit offset as an expression rather than requiring constant indices. This removes an artificial restriction that had no justification in the IEEE standard, as noted by maintainers in the GitHub issue. Co-Authored-By: Claude Opus 4.5 --- elab_lval.cc | 67 +++++++++++++++++++++++++++++++++++--- ivtest/ivltests/br_gh521.v | 19 +++++++++++ ivtest/regress-sv.list | 1 + 3 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 ivtest/ivltests/br_gh521.v diff --git a/elab_lval.cc b/elab_lval.cc index 4f1859c43..f3858d86a 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -528,9 +528,8 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, bool need_const_idx, bool is_force) const { - listprefix_indices; - bool rc = calculate_packed_indices_(des, scope, lv->sig(), prefix_indices); - if (!rc) return false; + NetNet*reg = lv->sig(); + ivl_assert(*this, reg); const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); @@ -539,8 +538,66 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, ivl_assert(*this, index_tail.msb != 0); ivl_assert(*this, index_tail.lsb == 0); - NetNet*reg = lv->sig(); - ivl_assert(*this, reg); + // First, check if packed prefix indices contain any variable expressions. + // Build the list of packed indices (excluding unpacked dimensions). + list packed_index; + packed_index = name_tail.index; + for (size_t idx = 0 ; idx < reg->unpacked_dimensions() ; idx += 1) + packed_index.pop_front(); + + // For multi-dimensional packed arrays, check if the prefix indices + // (all but the final) contain any non-constant expressions. + bool has_variable_prefix = false; + if (packed_index.size() > 1) { + list::const_iterator icur = packed_index.begin(); + for (size_t idx = 0 ; (idx+1) < packed_index.size() ; idx += 1, ++icur) { + NetExpr*texpr = elab_and_eval(des, scope, icur->msb, -1, false); + if (texpr == 0 || !dynamic_cast(texpr)) { + has_variable_prefix = true; + } + delete texpr; + if (has_variable_prefix) break; + } + } + + // If prefix indices are variable, handle using expression-based path. + if (has_variable_prefix) { + if (need_const_idx) { + cerr << get_fileline() << ": error: '" << reg->name() + << "' index must be a constant in this context." + << endl; + des->errors += 1; + return false; + } + + if ((reg->type()==NetNet::UNRESOLVED_WIRE) && !is_force) { + ivl_assert(*this, reg->coerced_to_uwire()); + report_mixed_assignment_conflict_("bit select"); + des->errors += 1; + return false; + } + + // Use collapse_array_exprs to compute the bit offset as an expression. + NetExpr*base_expr = collapse_array_exprs(des, scope, this, reg, packed_index); + if (base_expr == 0) { + des->errors += 1; + return false; + } + + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_bit_: " + << "Variable packed prefix, base_expr=" << *base_expr + << endl; + } + + lv->set_part(base_expr, 1); + return true; + } + + // All prefix indices are constant. Use the existing code path. + listprefix_indices; + bool rc = calculate_packed_indices_(des, scope, reg, prefix_indices); + if (!rc) return false; // Bit selects have a single select expression. Evaluate the // constant value and treat it as a part select with a bit diff --git a/ivtest/ivltests/br_gh521.v b/ivtest/ivltests/br_gh521.v new file mode 100644 index 000000000..fb8bf40e2 --- /dev/null +++ b/ivtest/ivltests/br_gh521.v @@ -0,0 +1,19 @@ +// Test for GitHub issue #521 +// Loop index should be allowed in outer dimension of multi-dimensional packed arrays +module test; + logic [3:0][3:0] a; + + initial begin + a = 0; + for (int i=0; i<4; i++) + a[i][3] = 1; + + // Each 4-bit sub-array has bit 3 set to 1, so each nibble is 0x8 + if (a !== 16'h8888) begin + $display("FAILED: a = %h, expected 8888", a); + $finish; + end + + $display("PASSED"); + end +endmodule diff --git a/ivtest/regress-sv.list b/ivtest/regress-sv.list index ded8b92a1..76846780a 100644 --- a/ivtest/regress-sv.list +++ b/ivtest/regress-sv.list @@ -212,6 +212,7 @@ br_gh477 normal,-g2009 ivltests br_gh478 normal,-g2009 ivltests br_gh498 normal,-g2009 ivltests br_gh508a normal,-g2009 ivltests +br_gh521 normal,-g2012 ivltests br_gh527 normal,-g2009 ivltests br_gh530 CO,-g2009 ivltests br_gh540 normal,-g2009 ivltests