diff --git a/src/V3Width.cpp b/src/V3Width.cpp index d5be92247..6d58119b3 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -6961,6 +6961,40 @@ class WidthVisitor final : public VNVisitor { } userIterateChildren(nodep, nullptr); } + void visit(AstResizeLValue* nodep) override { + // RESIZELVALUE adjusts width of lvalues for assignments/function calls + // The parent context determines the required width + UINFO(9, "visit AstResizeLValue " << nodep << endl); + if (nodep->didWidthAndSet()) return; + + UASSERT_OBJ(nodep->lhsp(), nodep, "ResizeLValue missing lhs expression"); + + // First, process child to know its natural width + if (m_vup->prelim()) { + userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); + } + + // Get the required width from parent context + if (AstNodeDType* const vdtypep = m_vup->dtypeNullp()) { + // Parent specified required width - use it + nodep->dtypeFrom(vdtypep); + } else if (!nodep->dtypep()) { + // No parent context, use child's type + nodep->dtypeFrom(nodep->lhsp()); + } + + // Verify we ended up with a valid datatype + UASSERT_OBJ(nodep->dtypep(), nodep, "ResizeLValue still missing dtype after visiting"); + UASSERT_OBJ(nodep->width() > 0, nodep, "ResizeLValue has invalid width"); + + // Log the transformation for debugging + UINFO(9, " ResizeLValue: " << nodep->lhsp()->width() << " bits -> " << nodep->width() + << " bits" << endl); + + // Final processing + userIterateAndNext(nodep->lhsp(), WidthVP{SELF, FINAL}.p()); + nodep->didWidth(true); + } void visitClass(AstClass* nodep) { if (nodep->didWidthAndSet()) return; diff --git a/test_regress/t/t_resize_lvalue.py b/test_regress/t/t_resize_lvalue.py new file mode 100755 index 000000000..b20365d31 --- /dev/null +++ b/test_regress/t/t_resize_lvalue.py @@ -0,0 +1,18 @@ +#!/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.compile(verilator_flags2=["--binary -Wno-WIDTHEXPAND"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_resize_lvalue.v b/test_regress/t/t_resize_lvalue.v new file mode 100644 index 000000000..7d4fd97a5 --- /dev/null +++ b/test_regress/t/t_resize_lvalue.v @@ -0,0 +1,84 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Alex Solomatnikov. +// SPDX-License-Identifier: CC0-1.0 + +package x_pkg; + typedef bit unsigned [64-1:0] x_reg_data_t ; + virtual class x_reg; + extern virtual task read(output int status, output x_reg_data_t value, input int path); + endclass: x_reg + task x_reg::read(output int status, output x_reg_data_t value, input int path); + endtask: read +endpackage // x_pkg + +package s_c_env_pkg; + import x_pkg::*; + + class r_reg_p_s_reg_n_doorbell extends x_reg; + x_reg_data_t val; + function new(int _val); + val = _val; + endfunction + extern virtual task read(output int status, output x_reg_data_t value, input int path); + endclass : r_reg_p_s_reg_n_doorbell + task r_reg_p_s_reg_n_doorbell::read(output int status, output x_reg_data_t value, input int path); + status = 1; + value = val; + endtask: read + + class r_block_p_s_reg; + rand r_reg_p_s_reg_n_doorbell n_doorbell; + + function new(int _val); + n_doorbell = new(_val); + endfunction + endclass : r_block_p_s_reg + + class r_top_s_reg; + rand r_block_p_s_reg p_s[16]; + endclass + + class s_c_env; + r_top_s_reg r_reg_model; + endclass: s_c_env +endpackage // s_c_env_pkg + +package s_c_sequences_pkg; + import x_pkg::*; + import s_c_env_pkg::*; + class s_c_v_regs_seq; + s_c_env m_env; + virtual task body(); + int unsigned p, rdata; + int status; + if (m_env == null) + m_env = new; + if (m_env.r_reg_model == null) + m_env.r_reg_model = new; + foreach (m_env.r_reg_model.p_s[p]) begin + if (m_env.r_reg_model.p_s[p] == null) begin + m_env.r_reg_model.p_s[p] = new(p); + end + m_env.r_reg_model.p_s[p].n_doorbell.read(status, rdata, 0); + if (status != 1) $stop; + if (rdata != p) $stop; + end + endtask + endclass +endpackage // s_c_sequences_pkg + +module t; + + s_c_sequences_pkg::s_c_v_regs_seq seq; + + initial begin + seq = new; + seq.body(); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule // t