From 5b7188fcafcdfdf0743b126f1b8a4686704d9cc5 Mon Sep 17 00:00:00 2001 From: Artur Bieniek Date: Wed, 6 Aug 2025 23:37:00 +0200 Subject: [PATCH] Fix same variable on the RHS forced to two different LHSs. (#6269) --- src/V3Force.cpp | 38 ++++++++++++++------- test_regress/t/t_force_rhs_ref_multi_lhs.py | 18 ++++++++++ test_regress/t/t_force_rhs_ref_multi_lhs.v | 33 ++++++++++++++++++ 3 files changed, 76 insertions(+), 13 deletions(-) create mode 100755 test_regress/t/t_force_rhs_ref_multi_lhs.py create mode 100644 test_regress/t/t_force_rhs_ref_multi_lhs.v diff --git a/src/V3Force.cpp b/src/V3Force.cpp index a09e3288d..52c16e211 100644 --- a/src/V3Force.cpp +++ b/src/V3Force.cpp @@ -148,15 +148,16 @@ private: // AstVar::user1p -> ForceComponentsVar* instance (via m_forceComponentsVar) // AstVarScope::user1p -> ForceComponentsVarScope* instance (via m_forceComponentsVarScope) // AstVarRef::user2 -> Flag indicating not to replace reference - // AstVarScope::user3 -> AstVarScope*, a `valVscp` force component for each VarScope of - // forced RHS - // AstVarScope::user4p -> AstNodeExpr*, the RHS expression + // AstVarScope::user3p -> AstNodeExpr*, the RHS expression const VNUser1InUse m_user1InUse; const VNUser2InUse m_user2InUse; const VNUser3InUse m_user3InUse; - const VNUser4InUse m_user4InUse; AstUser1Allocator m_forceComponentsVar; AstUser1Allocator m_forceComponentsVarScope; + std::unordered_map, std::vector>> + m_valVscps; + // `valVscp` force components of a forced RHS public: // CONSTRUCTORS @@ -172,11 +173,21 @@ public: } static bool isNotReplaceable(const AstVarRef* const nodep) { return nodep->user2(); } static void markNonReplaceable(AstVarRef* const nodep) { nodep->user2SetOnce(); } - static AstVarScope* getValVscp(AstVarRef* const refp) { - return VN_CAST(refp->varScopep()->user3p(), VarScope); + + // Get all ValVscps for a VarScope + const std::vector* getValVscps(AstVarRef* const refp) const { + auto it = m_valVscps.find(refp->varScopep()); + if (it != m_valVscps.end()) return &(it->second.second); + return nullptr; } - static void setValVscp(AstNodeVarRef* const refp, AstVarScope* const vscp) { - refp->varScopep()->user3p(vscp); + + // Add a ValVscp for a VarScope + void addValVscp(AstVarRef* const refp, AstVarScope* const valVscp) { + if (m_valVscps[refp->varScopep()].first.find(valVscp) + != m_valVscps[refp->varScopep()].first.end()) + return; + m_valVscps[refp->varScopep()].first.emplace(valVscp); + m_valVscps[refp->varScopep()].second.push_back(valVscp); } // METHODS @@ -188,10 +199,10 @@ public: return m_forceComponentsVarScope.tryGet(nodep->varScopep()); } void setValVscpRhsExpr(AstVarScope* valVscp, AstNodeExpr* rhsExpr) { - valVscp->user4p(rhsExpr); + valVscp->user3p(rhsExpr); } AstNodeExpr* getValVscpRhsExpr(AstVarScope* valVscp) const { - return VN_CAST(valVscp->user4p(), NodeExpr); + return VN_CAST(valVscp->user3p(), NodeExpr); } }; @@ -242,7 +253,7 @@ class ForceConvertVisitor final : public VNVisitor { transformWritenVarScopes(setValp->lhsp(), [this, rhsp](AstVarScope* vscp) { AstVarScope* const valVscp = m_state.getForceComponents(vscp).m_valVscp; m_state.setValVscpRhsExpr(valVscp, rhsp->cloneTreePure(false)); - rhsp->foreach([valVscp](AstVarRef* refp) { ForceState::setValVscp(refp, valVscp); }); + rhsp->foreach([valVscp, this](AstVarRef* refp) { m_state.addValVscp(refp, valVscp); }); return valVscp; }); @@ -380,7 +391,8 @@ class ForceReplaceVisitor final : public VNVisitor { m_stmtp->addNextHere(new AstAssign{flp, lhsp, rhsp}); } // Emit valVscp update after each write to any VarRef on forced RHS. - if (AstVarScope* const valVscp = ForceState::getValVscp(nodep)) { + if (!m_state.getValVscps(nodep)) break; + for (AstVarScope* const valVscp : *m_state.getValVscps(nodep)) { FileLine* const flp = nodep->fileline(); AstVarRef* const valp = new AstVarRef{flp, valVscp, VAccess::WRITE}; AstNodeExpr* rhsp = m_state.getValVscpRhsExpr(valVscp); @@ -396,7 +408,7 @@ class ForceReplaceVisitor final : public VNVisitor { } default: if (!m_inLogic) return; - if (m_state.tryGetForceComponents(nodep) || ForceState::getValVscp(nodep)) { + if (m_state.tryGetForceComponents(nodep) || m_state.getValVscps(nodep)) { nodep->v3error( "Unsupported: Signals used via read-write reference cannot be forced"); } diff --git a/test_regress/t/t_force_rhs_ref_multi_lhs.py b/test_regress/t/t_force_rhs_ref_multi_lhs.py new file mode 100755 index 000000000..4ff4dfaa5 --- /dev/null +++ b/test_regress/t/t_force_rhs_ref_multi_lhs.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('vlt') + +test.compile(verilator_flags2=["--exe", "--main", "--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_force_rhs_ref_multi_lhs.v b/test_regress/t/t_force_rhs_ref_multi_lhs.v new file mode 100644 index 000000000..10711caec --- /dev/null +++ b/test_regress/t/t_force_rhs_ref_multi_lhs.v @@ -0,0 +1,33 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0) + +module t; + bit [1:0] a; + bit [1:0] b; + + bit [1:0] d; + + initial begin + a = 0; + force b = a; + force d = a; + a = 2; + #1; + `checkh(a, 2); + `checkh(b, 2); + `checkh(d, 2); + a = 3; + #1; + `checkh(a, 3); + `checkh(b, 3); + `checkh(d, 3); + #1 $finish; + end + +endmodule