Fix same variable on the RHS forced to two different LHSs. (#6269)

This commit is contained in:
Artur Bieniek 2025-08-06 23:37:00 +02:00 committed by GitHub
parent dc049fdd74
commit 5b7188fcaf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 76 additions and 13 deletions

View File

@ -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<AstVar, ForceComponentsVar> m_forceComponentsVar;
AstUser1Allocator<AstVarScope, ForceComponentsVarScope> m_forceComponentsVarScope;
std::unordered_map<const AstVarScope*,
std::pair<std::unordered_set<AstVarScope*>, std::vector<AstVarScope*>>>
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<AstVarScope*>* 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");
}

View File

@ -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()

View File

@ -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