Fix independent force of multiply instantiated signals (#7905)
Signed-off-by: Artur Bieniek <abieniek@antmicro.com>
This commit is contained in:
parent
9740203485
commit
b211af9d24
|
|
@ -145,7 +145,7 @@ private:
|
||||||
const VNUser2InUse m_user2InUse;
|
const VNUser2InUse m_user2InUse;
|
||||||
|
|
||||||
std::vector<VarForceInfo> m_varInfos; // Indexed by stable variable ID
|
std::vector<VarForceInfo> m_varInfos; // Indexed by stable variable ID
|
||||||
std::unordered_map<AstVar*, int> m_varToId;
|
std::unordered_map<AstVarScope*, int> m_varToId;
|
||||||
std::unordered_set<AstVar*> m_clockedWrites;
|
std::unordered_set<AstVar*> m_clockedWrites;
|
||||||
std::unordered_map<AstVar*, std::vector<ForceInfo*>> m_rhsDepToForces;
|
std::unordered_map<AstVar*, std::vector<ForceInfo*>> m_rhsDepToForces;
|
||||||
std::unordered_map<AstScope*, ScopeVarCache> m_scopeVarCaches;
|
std::unordered_map<AstScope*, ScopeVarCache> m_scopeVarCaches;
|
||||||
|
|
@ -329,7 +329,7 @@ public:
|
||||||
// Non-bitwise member/struct paths cannot use a real bit range, so map each distinct
|
// Non-bitwise member/struct paths cannot use a real bit range, so map each distinct
|
||||||
// source path onto a synthetic index in VlForceVec and use that index consistently
|
// source path onto a synthetic index in VlForceVec and use that index consistently
|
||||||
// for force, release, and readback.
|
// for force, release, and readback.
|
||||||
VarForceInfo& varInfo = getOrCreateVarInfo(varp);
|
VarForceInfo& varInfo = getOrCreateVarInfo(getOneVarRef(lhsp)->varScopep());
|
||||||
const int index = varInfo.getOrCreateForcePathIndex(lhsp);
|
const int index = varInfo.getOrCreateForcePathIndex(lhsp);
|
||||||
info.m_rangeLsb = index;
|
info.m_rangeLsb = index;
|
||||||
info.m_rangeMsb = index;
|
info.m_rangeMsb = index;
|
||||||
|
|
@ -426,14 +426,16 @@ public:
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
VarForceInfo& getOrCreateVarInfo(AstVar* varp) {
|
VarForceInfo& getOrCreateVarInfo(AstVarScope* vscp) {
|
||||||
const auto it = m_varToId.find(varp);
|
const auto it = m_varToId.find(vscp);
|
||||||
if (it != m_varToId.end()) return m_varInfos[it->second];
|
if (it != m_varToId.end()) return m_varInfos[it->second];
|
||||||
|
|
||||||
m_varToId.emplace(varp, m_varInfos.size());
|
m_varToId.emplace(vscp, m_varInfos.size());
|
||||||
m_varInfos.emplace_back();
|
m_varInfos.emplace_back();
|
||||||
VarForceInfo& info = m_varInfos.back();
|
VarForceInfo& info = m_varInfos.back();
|
||||||
info.m_varp = varp;
|
info.m_varVscp = vscp;
|
||||||
|
info.m_varp = vscp->varp();
|
||||||
|
info.m_scopep = vscp->scopep();
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -442,8 +444,8 @@ public:
|
||||||
|
|
||||||
bool doingAssign() const { return m_doingAssign; }
|
bool doingAssign() const { return m_doingAssign; }
|
||||||
|
|
||||||
const VarForceInfo* getVarInfo(AstVar* varp) const {
|
const VarForceInfo* getVarInfo(AstVarScope* vscp) const {
|
||||||
const auto it = m_varToId.find(varp);
|
const auto it = m_varToId.find(vscp);
|
||||||
return it != m_varToId.end() ? &m_varInfos[it->second] : nullptr;
|
return it != m_varToId.end() ? &m_varInfos[it->second] : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -464,8 +466,7 @@ public:
|
||||||
v3Global.setUsesForce();
|
v3Global.setUsesForce();
|
||||||
varp->setForcedByCode();
|
varp->setForcedByCode();
|
||||||
|
|
||||||
VarForceInfo& info = getOrCreateVarInfo(varp);
|
VarForceInfo& info = getOrCreateVarInfo(vscp);
|
||||||
if (!info.m_scopep) info.m_scopep = vscp->scopep();
|
|
||||||
const int forceId = info.m_forces.size();
|
const int forceId = info.m_forces.size();
|
||||||
FileLine* const flp = varp->fileline();
|
FileLine* const flp = varp->fileline();
|
||||||
AstScope* const scopep = vscp->scopep();
|
AstScope* const scopep = vscp->scopep();
|
||||||
|
|
@ -475,9 +476,11 @@ public:
|
||||||
AstCDType* const forceVecDtypep = new AstCDType{flp, "VlForceVec"};
|
AstCDType* const forceVecDtypep = new AstCDType{flp, "VlForceVec"};
|
||||||
v3Global.rootp()->typeTablep()->addTypesp(forceVecDtypep);
|
v3Global.rootp()->typeTablep()->addTypesp(forceVecDtypep);
|
||||||
|
|
||||||
AstVar* const forceVecVarp = new AstVar{
|
AstVar* const forceVecVarp
|
||||||
flp, VVarType::MEMBER,
|
= new AstVar{flp, VVarType::MEMBER,
|
||||||
varp->name() + (m_doingAssign ? "_VassignVec" : "__VforceVec"), forceVecDtypep};
|
varp->name() + (m_doingAssign ? "_VassignVec" : "__VforceVec") + "__"
|
||||||
|
+ scopep->nameDotless(),
|
||||||
|
forceVecDtypep};
|
||||||
forceVecVarp->funcLocal(false);
|
forceVecVarp->funcLocal(false);
|
||||||
forceVecVarp->isInternal(true);
|
forceVecVarp->isInternal(true);
|
||||||
varp->addNextHere(forceVecVarp);
|
varp->addNextHere(forceVecVarp);
|
||||||
|
|
@ -578,11 +581,11 @@ public:
|
||||||
UASSERT_OBJ(finfo.m_rhsExprp, varp, "Missing RHS expression for ForceInfo");
|
UASSERT_OBJ(finfo.m_rhsExprp, varp, "Missing RHS expression for ForceInfo");
|
||||||
|
|
||||||
// Create per-force temporary storage for the captured RHS value.
|
// Create per-force temporary storage for the captured RHS value.
|
||||||
AstVar* const rhsVarp
|
AstVar* const rhsVarp = new AstVar{
|
||||||
= new AstVar{flp, VVarType::VAR,
|
flp, VVarType::VAR,
|
||||||
varp->name() + (doingAssign() ? "_VassignRHS" : "__VforceRHS")
|
varp->name() + (doingAssign() ? "_VassignRHS" : "__VforceRHS")
|
||||||
+ std::to_string(finfo.m_forceId),
|
+ std::to_string(finfo.m_forceId) + "__" + scopep->nameDotless(),
|
||||||
finfo.m_rhsExprp->dtypep()};
|
finfo.m_rhsExprp->dtypep()};
|
||||||
rhsVarp->noSubst(true);
|
rhsVarp->noSubst(true);
|
||||||
rhsVarp->sigPublic(true);
|
rhsVarp->sigPublic(true);
|
||||||
rhsVarp->setForcedByCode();
|
rhsVarp->setForcedByCode();
|
||||||
|
|
@ -704,8 +707,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
const ForceInfo& getForceInfo(AstAssignForce* forceStmtp) const {
|
const ForceInfo& getForceInfo(AstAssignForce* forceStmtp) const {
|
||||||
AstVar* varp = getOneVarRef(forceStmtp->lhsp())->varp();
|
AstVarScope* const vscp = getOneVarRef(forceStmtp->lhsp())->varScopep();
|
||||||
const VarForceInfo* const varInfo = getVarInfo(varp);
|
const VarForceInfo* const varInfo = getVarInfo(vscp);
|
||||||
UASSERT(varInfo, "Force info not found for variable");
|
UASSERT(varInfo, "Force info not found for variable");
|
||||||
auto it2 = varInfo->m_forces.find(forceStmtp);
|
auto it2 = varInfo->m_forces.find(forceStmtp);
|
||||||
UASSERT(it2 != varInfo->m_forces.end(), "Force statement not found");
|
UASSERT(it2 != varInfo->m_forces.end(), "Force statement not found");
|
||||||
|
|
@ -812,11 +815,10 @@ class ForceDiscoveryVisitor final : public VNVisitorConst {
|
||||||
nodep->scopep()->addVarsp(enVscp);
|
nodep->scopep()->addVarsp(enVscp);
|
||||||
nodep->scopep()->addVarsp(valVscp);
|
nodep->scopep()->addVarsp(valVscp);
|
||||||
|
|
||||||
ForceState::VarForceInfo& info = m_state.getOrCreateVarInfo(varp);
|
ForceState::VarForceInfo& info = m_state.getOrCreateVarInfo(nodep);
|
||||||
info.m_forceRdVscp = rdVscp;
|
info.m_forceRdVscp = rdVscp;
|
||||||
info.m_forceEnVscp = enVscp;
|
info.m_forceEnVscp = enVscp;
|
||||||
info.m_forceValVscp = valVscp;
|
info.m_forceValVscp = valVscp;
|
||||||
info.m_varVscp = nodep;
|
|
||||||
varp->user3p(rdVscp);
|
varp->user3p(rdVscp);
|
||||||
varp->user4p(enVscp);
|
varp->user4p(enVscp);
|
||||||
nodep->user3p(valVscp);
|
nodep->user3p(valVscp);
|
||||||
|
|
@ -906,11 +908,10 @@ class ForceDiscoveryVisitor final : public VNVisitorConst {
|
||||||
if (rdVscp || enVscp || valVscp) {
|
if (rdVscp || enVscp || valVscp) {
|
||||||
UASSERT_OBJ(rdVscp && enVscp && valVscp, nodep,
|
UASSERT_OBJ(rdVscp && enVscp && valVscp, nodep,
|
||||||
"Incomplete pre-existing force helper set");
|
"Incomplete pre-existing force helper set");
|
||||||
ForceState::VarForceInfo& info = m_state.getOrCreateVarInfo(varp);
|
ForceState::VarForceInfo& info = m_state.getOrCreateVarInfo(nodep);
|
||||||
info.m_forceRdVscp = rdVscp;
|
info.m_forceRdVscp = rdVscp;
|
||||||
info.m_forceEnVscp = enVscp;
|
info.m_forceEnVscp = enVscp;
|
||||||
info.m_forceValVscp = valVscp;
|
info.m_forceValVscp = valVscp;
|
||||||
info.m_varVscp = nodep;
|
|
||||||
iterateChildrenConst(nodep);
|
iterateChildrenConst(nodep);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -958,11 +959,10 @@ class ForceDiscoveryVisitor final : public VNVisitorConst {
|
||||||
nodep->user3p(valVscp);
|
nodep->user3p(valVscp);
|
||||||
|
|
||||||
// Register force metadata so later transforms can find these helper vars.
|
// Register force metadata so later transforms can find these helper vars.
|
||||||
ForceState::VarForceInfo& info = m_state.getOrCreateVarInfo(varp);
|
ForceState::VarForceInfo& info = m_state.getOrCreateVarInfo(nodep);
|
||||||
info.m_forceRdVscp = rdVscp;
|
info.m_forceRdVscp = rdVscp;
|
||||||
info.m_forceEnVscp = enVscp;
|
info.m_forceEnVscp = enVscp;
|
||||||
info.m_forceValVscp = valVscp;
|
info.m_forceValVscp = valVscp;
|
||||||
info.m_varVscp = nodep;
|
|
||||||
|
|
||||||
// Build an update block triggered by force-enable changes.
|
// Build an update block triggered by force-enable changes.
|
||||||
AstSenItem* const itemsp = new AstSenItem{flp, VEdgeType::ET_CHANGED,
|
AstSenItem* const itemsp = new AstSenItem{flp, VEdgeType::ET_CHANGED,
|
||||||
|
|
@ -1033,7 +1033,8 @@ class ForceConvertVisitor final : public VNVisitor {
|
||||||
AstVar* const forcedVarp = lhsVarRefp->varp();
|
AstVar* const forcedVarp = lhsVarRefp->varp();
|
||||||
|
|
||||||
const ForceState::ForceInfo& info = m_state.getForceInfo(nodep);
|
const ForceState::ForceInfo& info = m_state.getForceInfo(nodep);
|
||||||
const ForceState::VarForceInfo* const varInfo = m_state.getVarInfo(forcedVarp);
|
const ForceState::VarForceInfo* const varInfo
|
||||||
|
= m_state.getVarInfo(lhsVarRefp->varScopep());
|
||||||
UASSERT_OBJ(varInfo && varInfo->m_forceVecVscp, nodep, "Force info not set up");
|
UASSERT_OBJ(varInfo && varInfo->m_forceVecVscp, nodep, "Force info not set up");
|
||||||
|
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
|
|
@ -1141,7 +1142,8 @@ class ForceConvertVisitor final : public VNVisitor {
|
||||||
AstVarRef* const lhsVarRefp = m_state.getOneVarRef(lhsp);
|
AstVarRef* const lhsVarRefp = m_state.getOneVarRef(lhsp);
|
||||||
AstVar* const releasedVarp = lhsVarRefp->varp();
|
AstVar* const releasedVarp = lhsVarRefp->varp();
|
||||||
|
|
||||||
const ForceState::VarForceInfo* const varInfo = m_state.getVarInfo(releasedVarp);
|
const ForceState::VarForceInfo* const varInfo
|
||||||
|
= m_state.getVarInfo(lhsVarRefp->varScopep());
|
||||||
if (!varInfo) {
|
if (!varInfo) {
|
||||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
return;
|
return;
|
||||||
|
|
@ -1314,7 +1316,7 @@ class ForceReplaceVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
AstVar* const varp = refp->varp();
|
AstVar* const varp = refp->varp();
|
||||||
const ForceState::VarForceInfo* const varInfo = m_state.getVarInfo(varp);
|
const ForceState::VarForceInfo* const varInfo = m_state.getVarInfo(refp->varScopep());
|
||||||
if (!varInfo || varInfo->m_forceRdVscp || varInfo->m_forces.empty()
|
if (!varInfo || varInfo->m_forceRdVscp || varInfo->m_forces.empty()
|
||||||
|| !ForceState::isBitwiseDType(varp) || !varp->dtypep()->isWide()) {
|
|| !ForceState::isBitwiseDType(varp) || !varp->dtypep()->isWide()) {
|
||||||
visit(static_cast<AstNode*>(nodep));
|
visit(static_cast<AstNode*>(nodep));
|
||||||
|
|
@ -1362,7 +1364,7 @@ class ForceReplaceVisitor final : public VNVisitor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AstVar* const varp = baseRefp->varp();
|
AstVar* const varp = baseRefp->varp();
|
||||||
const ForceState::VarForceInfo* const varInfo = m_state.getVarInfo(varp);
|
const ForceState::VarForceInfo* const varInfo = m_state.getVarInfo(baseRefp->varScopep());
|
||||||
// Skip non-forceable reads, reads we intentionally protected earlier, and intermediate
|
// Skip non-forceable reads, reads we intentionally protected earlier, and intermediate
|
||||||
// selections that still evaluate to an unpacked array rather than a scalar element.
|
// selections that still evaluate to an unpacked array rather than a scalar element.
|
||||||
if (ForceState::isNotReplaceable(baseRefp) || !varInfo
|
if (ForceState::isNotReplaceable(baseRefp) || !varInfo
|
||||||
|
|
@ -1389,8 +1391,7 @@ class ForceReplaceVisitor final : public VNVisitor {
|
||||||
if (ForceState::isNotReplaceable(nodep)) return;
|
if (ForceState::isNotReplaceable(nodep)) return;
|
||||||
if (nodep->backp() && VN_IS(nodep->backp(), ArraySel)) return;
|
if (nodep->backp() && VN_IS(nodep->backp(), ArraySel)) return;
|
||||||
|
|
||||||
AstVar* const varp = nodep->varp();
|
const ForceState::VarForceInfo* const varInfo = m_state.getVarInfo(nodep->varScopep());
|
||||||
const ForceState::VarForceInfo* const varInfo = m_state.getVarInfo(varp);
|
|
||||||
if (!varInfo) return;
|
if (!varInfo) return;
|
||||||
|
|
||||||
if (varInfo->m_forceRdVscp) {
|
if (varInfo->m_forceRdVscp) {
|
||||||
|
|
@ -1444,7 +1445,8 @@ class ForceReplaceVisitor final : public VNVisitor {
|
||||||
AstVar* const varp = baseRefp->varp();
|
AstVar* const varp = baseRefp->varp();
|
||||||
if (!ForceState::isBitwiseDType(varp)
|
if (!ForceState::isBitwiseDType(varp)
|
||||||
&& !ForceState::isUnpackedArrayDType(varp->dtypep())) {
|
&& !ForceState::isUnpackedArrayDType(varp->dtypep())) {
|
||||||
const ForceState::VarForceInfo* const varInfo = m_state.getVarInfo(varp);
|
const ForceState::VarForceInfo* const varInfo
|
||||||
|
= m_state.getVarInfo(baseRefp->varScopep());
|
||||||
if (!ForceState::isNotReplaceable(baseRefp) && varInfo) {
|
if (!ForceState::isNotReplaceable(baseRefp) && varInfo) {
|
||||||
const int forcePathIndex = varInfo->findForcePathIndex(exprp);
|
const int forcePathIndex = varInfo->findForcePathIndex(exprp);
|
||||||
if (forcePathIndex >= 0) {
|
if (forcePathIndex >= 0) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('simulator')
|
||||||
|
|
||||||
|
test.compile()
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain
|
||||||
|
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module child(input wire i);
|
||||||
|
/*verilator no_inline_module*/
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module t;
|
||||||
|
child a(.i(1'b0));
|
||||||
|
child b(.i(1'b0));
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
force a.i = 1'b1;
|
||||||
|
|
||||||
|
if (a.i !== 1'b1) $stop;
|
||||||
|
if (b.i !== 1'b0) $stop;
|
||||||
|
if (a.i === b.i) $stop;
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('simulator')
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=["-Wno-IEEEMAYDEPRECATE"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain
|
||||||
|
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module child(input wire drive, output wire observed);
|
||||||
|
/*verilator no_inline_module*/
|
||||||
|
|
||||||
|
logic value;
|
||||||
|
|
||||||
|
assign observed = value;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
value = 1'b0;
|
||||||
|
if (drive) assign value = 1'b1;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module t;
|
||||||
|
wire a_observed;
|
||||||
|
wire b_observed;
|
||||||
|
bit done;
|
||||||
|
|
||||||
|
child a(.drive(1'b1), .observed(a_observed));
|
||||||
|
child b(.drive(1'b0), .observed(b_observed));
|
||||||
|
|
||||||
|
always @(a_observed or b_observed) begin
|
||||||
|
if (!done && a_observed === 1'b1) begin
|
||||||
|
done = 1'b1;
|
||||||
|
if (b_observed !== 1'b0) $stop;
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue