diff --git a/src/V3Force.cpp b/src/V3Force.cpp index 224d5abf2..56b5b0673 100644 --- a/src/V3Force.cpp +++ b/src/V3Force.cpp @@ -147,7 +147,7 @@ private: // AstVar::user1p -> ForceComponentsVar* instance (via m_forceComponentsVar) // AstVarScope::user1p -> ForceComponentsVarScope* instance (via m_forceComponentsVarScope) // AstVarRef::user2 -> Flag indicating not to replace reference - // AstVarScope::user3p -> AstNodeExpr*, the RHS expression + // AstVarScope::user3p -> AstAssign*, the assignment __VforceVal = const VNUser1InUse m_user1InUse; const VNUser2InUse m_user2InUse; const VNUser3InUse m_user3InUse; @@ -197,11 +197,9 @@ public: ForceComponentsVarScope* tryGetForceComponents(AstVarRef* nodep) const { return m_forceComponentsVarScope.tryGet(nodep->varScopep()); } - void setValVscpRhsExpr(AstVarScope* valVscp, AstNodeExpr* rhsExpr) { - valVscp->user3p(rhsExpr); - } - AstNodeExpr* getValVscpRhsExpr(AstVarScope* valVscp) const { - return VN_CAST(valVscp->user3p(), NodeExpr); + void setValVscpAssign(AstVarScope* valVscp, AstAssign* rhsExpr) { valVscp->user3p(rhsExpr); } + AstAssign* getValVscpAssign(AstVarScope* valVscp) const { + return VN_CAST(valVscp->user3p(), Assign); } }; @@ -249,9 +247,9 @@ class ForceConvertVisitor final : public VNVisitor { // Set corresponding value signals to the forced value AstAssign* const setValp = new AstAssign{flp, lhsp->cloneTreePure(false), rhsp->cloneTreePure(false)}; - transformWritenVarScopes(setValp->lhsp(), [this, rhsp](AstVarScope* vscp) { + transformWritenVarScopes(setValp->lhsp(), [this, rhsp, setValp](AstVarScope* vscp) { AstVarScope* const valVscp = m_state.getForceComponents(vscp).m_valVscp; - m_state.setValVscpRhsExpr(valVscp, rhsp); + m_state.setValVscpAssign(valVscp, setValp); rhsp->foreach([valVscp, this](AstVarRef* refp) { m_state.addValVscp(refp, valVscp); }); return valVscp; }); @@ -393,15 +391,15 @@ class ForceReplaceVisitor final : public VNVisitor { 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); - UASSERT_OBJ(rhsp, flp, "RHS of force/release must be an AstNodeExpr"); - rhsp = rhsp->cloneTreePure(false); + AstAssign* assignp = m_state.getValVscpAssign(valVscp); + UASSERT_OBJ(assignp, flp, "Missing stored assignment for forced valVscp"); - ForceState::markNonReplaceable(valp); - rhsp->foreach([](AstVarRef* refp) { ForceState::markNonReplaceable(refp); }); + assignp = assignp->cloneTreePure(false); - m_stmtp->addNextHere(new AstAssign{flp, valp, rhsp}); + assignp->rhsp()->foreach( + [](AstVarRef* refp) { ForceState::markNonReplaceable(refp); }); + + m_stmtp->addNextHere(assignp); } break; } diff --git a/test_regress/t/t_force_struct_partial.py b/test_regress/t/t_force_struct_partial.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_force_struct_partial.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('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_force_struct_partial.v b/test_regress/t/t_force_struct_partial.v new file mode 100644 index 000000000..7490facaa --- /dev/null +++ b/test_regress/t/t_force_struct_partial.v @@ -0,0 +1,31 @@ +// 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) + +typedef struct packed { + logic sig1; + logic sig2; + logic not_forced; +} s1; + +module t(clk); + input clk; + s1 s1inst; + logic a = 1'b0; + logic b; + initial force s1inst.sig1 = a; + always @(posedge clk) begin + force s1inst.sig2 = 1'b1; + force s1inst.sig1 = b; + + `checkh(s1inst.sig1, b); + `checkh(s1inst.sig2, 1'b1); + + $finish; + end +endmodule