2022-01-01 18:24:19 +01:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
2021-12-17 18:56:33 +01:00
|
|
|
// DESCRIPTION: Verilator: Covert forceable signals, process force/release
|
2022-01-01 18:24:19 +01:00
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
|
2022-01-01 18:24:19 +01:00
|
|
|
// 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
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2021-12-17 18:56:33 +01:00
|
|
|
// V3Force's Transformations:
|
2022-01-01 18:24:19 +01:00
|
|
|
//
|
2021-12-17 18:56:33 +01:00
|
|
|
// For each forceable net with name "<name>":
|
|
|
|
|
// add 3 extra signals:
|
|
|
|
|
// - <name>__VforceRd: a net with same type as signal
|
|
|
|
|
// - <name>__VforceEn: a var with same type as signal, which is the bitwise force enable
|
2025-03-04 16:12:02 +01:00
|
|
|
// - <name>__VforceVal: a var with same type as signal, which is the forced value
|
2021-12-17 18:56:33 +01:00
|
|
|
// add an initial statement:
|
|
|
|
|
// initial <name>__VforceEn = 0;
|
|
|
|
|
// add a continuous assignment:
|
2025-03-04 16:12:02 +01:00
|
|
|
// assign <name>__VforceRd = <name>__VforceEn ? <name>__VforceVal : <name>;
|
2021-12-17 18:56:33 +01:00
|
|
|
// replace all READ references to <name> with a read reference to <name>_VforceRd
|
2022-01-01 18:24:19 +01:00
|
|
|
//
|
2021-12-17 18:56:33 +01:00
|
|
|
// Replace each AstAssignForce with 3 assignments:
|
|
|
|
|
// - <lhs>__VforceEn = 1
|
2025-03-04 16:12:02 +01:00
|
|
|
// - <lhs>__VforceVal = <rhs>
|
2021-12-17 18:56:33 +01:00
|
|
|
// - <lhs>__VforceRd = <rhs>
|
2022-01-01 18:24:19 +01:00
|
|
|
//
|
2021-12-17 18:56:33 +01:00
|
|
|
// Replace each AstRelease with 1 or 2 assignments:
|
|
|
|
|
// - <lhs>__VforceEn = 0
|
|
|
|
|
// - <lhs>__VforceRd = <lhs> // iff lhs is a net
|
2022-01-01 18:24:19 +01:00
|
|
|
//
|
2025-03-04 16:12:02 +01:00
|
|
|
// After each WRITE of forced LHS
|
|
|
|
|
// reevaluate <lhs>__VforceRd to support immediate force/release
|
|
|
|
|
//
|
|
|
|
|
// After each WRITE of forced RHS
|
|
|
|
|
// reevaluate <lhs>__VforceVal to support VarRef rollback after release
|
2022-01-01 18:24:19 +01:00
|
|
|
//*************************************************************************
|
|
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2022-01-01 18:24:19 +01:00
|
|
|
#include "V3Force.h"
|
|
|
|
|
|
2021-12-17 18:56:33 +01:00
|
|
|
#include "V3AstUserAllocator.h"
|
2022-01-01 18:24:19 +01:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2022-01-01 18:24:19 +01:00
|
|
|
//######################################################################
|
2021-12-17 18:56:33 +01:00
|
|
|
// Convert force/release statements and signals marked 'forceable'
|
2022-01-01 18:24:19 +01:00
|
|
|
|
2025-03-04 16:12:02 +01:00
|
|
|
class ForceState final {
|
2022-01-01 18:24:19 +01:00
|
|
|
// TYPES
|
2024-01-20 21:06:46 +01:00
|
|
|
struct ForceComponentsVar final {
|
2021-12-17 18:56:33 +01:00
|
|
|
AstVar* const m_rdVarp; // New variable to replace read references with
|
|
|
|
|
AstVar* const m_valVarp; // Forced value
|
2023-02-05 02:37:36 +01:00
|
|
|
AstVar* const m_enVarp; // Force enabled signal
|
2021-12-17 18:56:33 +01:00
|
|
|
explicit ForceComponentsVar(AstVar* varp)
|
|
|
|
|
: m_rdVarp{new AstVar{varp->fileline(), VVarType::WIRE, varp->name() + "__VforceRd",
|
|
|
|
|
varp->dtypep()}}
|
|
|
|
|
, m_valVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforceVal",
|
2023-02-05 02:37:36 +01:00
|
|
|
varp->dtypep()}}
|
2025-03-04 16:12:02 +01:00
|
|
|
, m_enVarp{new AstVar{
|
|
|
|
|
varp->fileline(), VVarType::VAR, varp->name() + "__VforceEn",
|
|
|
|
|
(ForceState::isRangedDType(varp) ? varp->dtypep() : varp->findBitDType())}} {
|
2021-12-17 18:56:33 +01:00
|
|
|
m_rdVarp->addNext(m_enVarp);
|
|
|
|
|
m_rdVarp->addNext(m_valVarp);
|
|
|
|
|
varp->addNextHere(m_rdVarp);
|
2022-01-01 18:24:19 +01:00
|
|
|
}
|
2021-12-17 18:56:33 +01:00
|
|
|
};
|
2022-01-01 18:24:19 +01:00
|
|
|
|
2025-03-04 16:12:02 +01:00
|
|
|
public:
|
2024-01-20 21:06:46 +01:00
|
|
|
struct ForceComponentsVarScope final {
|
2021-12-17 18:56:33 +01:00
|
|
|
AstVarScope* const m_rdVscp; // New variable to replace read references with
|
|
|
|
|
AstVarScope* const m_valVscp; // Forced value
|
2025-03-04 16:12:02 +01:00
|
|
|
AstVarScope* const m_enVscp; // Force enabled signal
|
2021-12-17 18:56:33 +01:00
|
|
|
explicit ForceComponentsVarScope(AstVarScope* vscp, ForceComponentsVar& fcv)
|
|
|
|
|
: m_rdVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_rdVarp}}
|
2025-03-04 16:12:02 +01:00
|
|
|
, m_valVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_valVarp}}
|
|
|
|
|
, m_enVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_enVarp}} {
|
2021-12-17 18:56:33 +01:00
|
|
|
m_rdVscp->addNext(m_enVscp);
|
|
|
|
|
m_rdVscp->addNext(m_valVscp);
|
|
|
|
|
vscp->addNextHere(m_rdVscp);
|
2022-01-01 18:24:19 +01:00
|
|
|
|
2021-12-17 18:56:33 +01:00
|
|
|
FileLine* const flp = vscp->fileline();
|
2022-01-01 18:24:19 +01:00
|
|
|
|
2021-12-17 18:56:33 +01:00
|
|
|
{ // Add initialization of the enable signal
|
|
|
|
|
AstVarRef* const lhsp = new AstVarRef{flp, m_enVscp, VAccess::WRITE};
|
|
|
|
|
V3Number zero{m_enVscp, m_enVscp->width()};
|
|
|
|
|
zero.setAllBits0();
|
2022-10-12 11:19:21 +02:00
|
|
|
AstNodeExpr* const rhsp = new AstConst{flp, zero};
|
2021-12-17 18:56:33 +01:00
|
|
|
AstAssign* const assignp = new AstAssign{flp, lhsp, rhsp};
|
|
|
|
|
AstActive* const activep = new AstActive{
|
|
|
|
|
flp, "force-init",
|
2025-03-04 16:12:02 +01:00
|
|
|
new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Static{}}}};
|
2025-08-18 01:14:34 +02:00
|
|
|
activep->senTreeStorep(activep->sentreep());
|
2025-03-04 16:12:02 +01:00
|
|
|
|
2021-12-17 18:56:33 +01:00
|
|
|
activep->addStmtsp(new AstInitial{flp, assignp});
|
2022-09-15 20:43:56 +02:00
|
|
|
vscp->scopep()->addBlocksp(activep);
|
2021-12-17 18:56:33 +01:00
|
|
|
}
|
|
|
|
|
{ // Add the combinational override
|
|
|
|
|
AstVarRef* const lhsp = new AstVarRef{flp, m_rdVscp, VAccess::WRITE};
|
2025-03-04 16:12:02 +01:00
|
|
|
AstNodeExpr* const rhsp = forcedUpdate(vscp);
|
2023-02-05 02:37:36 +01:00
|
|
|
|
2025-03-04 16:12:02 +01:00
|
|
|
// Explicitly list dependencies for update.
|
|
|
|
|
// Note: rdVscp is also needed to retrigger assignment for the first time.
|
|
|
|
|
AstSenItem* const itemsp = new AstSenItem{
|
|
|
|
|
flp, VEdgeType::ET_CHANGED, new AstVarRef{flp, m_rdVscp, VAccess::READ}};
|
|
|
|
|
itemsp->addNext(new AstSenItem{flp, VEdgeType::ET_CHANGED,
|
|
|
|
|
new AstVarRef{flp, m_valVscp, VAccess::READ}});
|
|
|
|
|
itemsp->addNext(new AstSenItem{flp, VEdgeType::ET_CHANGED,
|
|
|
|
|
new AstVarRef{flp, m_enVscp, VAccess::READ}});
|
|
|
|
|
AstVarRef* const origp = new AstVarRef{flp, vscp, VAccess::READ};
|
|
|
|
|
ForceState::markNonReplaceable(origp);
|
2025-09-09 23:39:44 +02:00
|
|
|
itemsp->addNext(new AstSenItem{flp, VEdgeType::ET_CHANGED, origp});
|
2021-12-17 18:56:33 +01:00
|
|
|
AstActive* const activep
|
2025-03-04 16:12:02 +01:00
|
|
|
= new AstActive{flp, "force-update", new AstSenTree{flp, itemsp}};
|
2025-08-18 01:14:34 +02:00
|
|
|
activep->senTreeStorep(activep->sentreep());
|
2025-03-04 16:12:02 +01:00
|
|
|
activep->addStmtsp(new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr,
|
|
|
|
|
new AstAssign{flp, lhsp, rhsp}});
|
2022-09-15 20:43:56 +02:00
|
|
|
vscp->scopep()->addBlocksp(activep);
|
2022-01-01 18:24:19 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-03-04 16:12:02 +01:00
|
|
|
AstNodeExpr* forcedUpdate(AstVarScope* const vscp) const {
|
|
|
|
|
FileLine* const flp = vscp->fileline();
|
|
|
|
|
AstVarRef* const origp = new AstVarRef{flp, vscp, VAccess::READ};
|
|
|
|
|
ForceState::markNonReplaceable(origp);
|
|
|
|
|
if (ForceState::isRangedDType(vscp)) {
|
|
|
|
|
return new AstOr{
|
|
|
|
|
flp,
|
|
|
|
|
new AstAnd{flp, new AstVarRef{flp, m_enVscp, VAccess::READ},
|
|
|
|
|
new AstVarRef{flp, m_valVscp, VAccess::READ}},
|
|
|
|
|
new AstAnd{flp, new AstNot{flp, new AstVarRef{flp, m_enVscp, VAccess::READ}},
|
|
|
|
|
origp}};
|
|
|
|
|
}
|
|
|
|
|
return new AstCond{flp, new AstVarRef{flp, m_enVscp, VAccess::READ},
|
|
|
|
|
new AstVarRef{flp, m_valVscp, VAccess::READ}, origp};
|
|
|
|
|
}
|
2021-12-17 18:56:33 +01:00
|
|
|
};
|
|
|
|
|
|
2025-03-04 16:12:02 +01:00
|
|
|
private:
|
2021-12-17 18:56:33 +01:00
|
|
|
// NODE STATE
|
|
|
|
|
// AstVar::user1p -> ForceComponentsVar* instance (via m_forceComponentsVar)
|
|
|
|
|
// AstVarScope::user1p -> ForceComponentsVarScope* instance (via m_forceComponentsVarScope)
|
|
|
|
|
// AstVarRef::user2 -> Flag indicating not to replace reference
|
2025-10-08 15:39:50 +02:00
|
|
|
// AstVarScope::user3p -> AstAssign*, the assignment <lhs>__VforceVal = <rhs>
|
2021-12-17 18:56:33 +01:00
|
|
|
const VNUser1InUse m_user1InUse;
|
|
|
|
|
const VNUser2InUse m_user2InUse;
|
2025-03-04 16:12:02 +01:00
|
|
|
const VNUser3InUse m_user3InUse;
|
2021-12-17 18:56:33 +01:00
|
|
|
AstUser1Allocator<AstVar, ForceComponentsVar> m_forceComponentsVar;
|
|
|
|
|
AstUser1Allocator<AstVarScope, ForceComponentsVarScope> m_forceComponentsVarScope;
|
2025-08-06 23:37:00 +02:00
|
|
|
std::unordered_map<const AstVarScope*,
|
|
|
|
|
std::pair<std::unordered_set<AstVarScope*>, std::vector<AstVarScope*>>>
|
|
|
|
|
m_valVscps;
|
|
|
|
|
// `valVscp` force components of a forced RHS
|
2021-12-17 18:56:33 +01:00
|
|
|
|
2025-03-04 16:12:02 +01:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
|
|
|
|
ForceState() = default;
|
|
|
|
|
VL_UNCOPYABLE(ForceState);
|
|
|
|
|
|
|
|
|
|
// STATIC METHODS
|
2023-02-05 02:37:36 +01:00
|
|
|
static bool isRangedDType(AstNode* nodep) {
|
|
|
|
|
// If ranged we need a multibit enable to support bit-by-bit part-select forces,
|
|
|
|
|
// otherwise forcing a real or other opaque dtype and need a single bit enable.
|
|
|
|
|
const AstBasicDType* const basicp = nodep->dtypep()->skipRefp()->basicp();
|
|
|
|
|
return basicp && basicp->isRanged();
|
|
|
|
|
}
|
2025-03-04 16:12:02 +01:00
|
|
|
static bool isNotReplaceable(const AstVarRef* const nodep) { return nodep->user2(); }
|
|
|
|
|
static void markNonReplaceable(AstVarRef* const nodep) { nodep->user2SetOnce(); }
|
2025-08-06 23:37:00 +02:00
|
|
|
|
|
|
|
|
// 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;
|
2025-03-04 16:12:02 +01:00
|
|
|
}
|
2025-08-06 23:37:00 +02:00
|
|
|
|
|
|
|
|
// 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);
|
2025-03-04 16:12:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// METHODS
|
2021-12-17 18:56:33 +01:00
|
|
|
const ForceComponentsVarScope& getForceComponents(AstVarScope* vscp) {
|
|
|
|
|
AstVar* const varp = vscp->varp();
|
|
|
|
|
return m_forceComponentsVarScope(vscp, vscp, m_forceComponentsVar(varp, varp));
|
2022-01-01 18:24:19 +01:00
|
|
|
}
|
2025-03-04 16:12:02 +01:00
|
|
|
ForceComponentsVarScope* tryGetForceComponents(AstVarRef* nodep) const {
|
|
|
|
|
return m_forceComponentsVarScope.tryGet(nodep->varScopep());
|
|
|
|
|
}
|
2025-10-08 15:39:50 +02:00
|
|
|
void setValVscpAssign(AstVarScope* valVscp, AstAssign* rhsExpr) { valVscp->user3p(rhsExpr); }
|
|
|
|
|
AstAssign* getValVscpAssign(AstVarScope* valVscp) const {
|
|
|
|
|
return VN_CAST(valVscp->user3p(), Assign);
|
2025-07-11 03:12:44 +02:00
|
|
|
}
|
2025-03-04 16:12:02 +01:00
|
|
|
};
|
2022-01-01 18:24:19 +01:00
|
|
|
|
2025-03-04 16:12:02 +01:00
|
|
|
class ForceConvertVisitor final : public VNVisitor {
|
|
|
|
|
// STATE
|
|
|
|
|
ForceState& m_state;
|
|
|
|
|
|
|
|
|
|
// STATIC METHODS
|
2021-12-17 18:56:33 +01:00
|
|
|
// Replace each AstNodeVarRef in the given 'nodep' that writes a variable by transforming the
|
|
|
|
|
// referenced AstVarScope with the given function.
|
2025-03-04 16:12:02 +01:00
|
|
|
static void transformWritenVarScopes(AstNode* nodep,
|
|
|
|
|
std::function<AstVarScope*(AstVarScope*)> f) {
|
2021-12-17 18:56:33 +01:00
|
|
|
UASSERT_OBJ(nodep->backp(), nodep, "Must have backp, otherwise will be lost if replaced");
|
2022-10-20 14:48:44 +02:00
|
|
|
nodep->foreach([&f](AstNodeVarRef* refp) {
|
2021-12-17 18:56:33 +01:00
|
|
|
if (refp->access() != VAccess::WRITE) return;
|
|
|
|
|
// TODO: this is not strictly speaking safe for some complicated lvalues, eg.:
|
|
|
|
|
// 'force foo[a(cnt)] = 1;', where 'cnt' is an out parameter, but it will
|
|
|
|
|
// do for now...
|
|
|
|
|
refp->replaceWith(
|
|
|
|
|
new AstVarRef{refp->fileline(), f(refp->varScopep()), VAccess::WRITE});
|
2022-07-31 19:13:55 +02:00
|
|
|
VL_DO_DANGLING(refp->deleteTree(), refp);
|
2021-12-17 18:56:33 +01:00
|
|
|
});
|
2022-01-01 18:24:19 +01:00
|
|
|
}
|
|
|
|
|
|
2025-03-04 16:12:02 +01:00
|
|
|
// VISITORS
|
2021-12-17 18:56:33 +01:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2022-01-01 18:24:19 +01:00
|
|
|
|
2021-12-17 18:56:33 +01:00
|
|
|
void visit(AstAssignForce* nodep) override {
|
|
|
|
|
// The AstAssignForce node will be removed for sure
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const lhsp = nodep->lhsp(); // The LValue we are forcing
|
|
|
|
|
AstNodeExpr* const rhsp = nodep->rhsp(); // The value we are forcing it to
|
2024-05-08 14:36:24 +02:00
|
|
|
VNRelinker relinker;
|
|
|
|
|
nodep->unlinkFrBack(&relinker);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2022-01-01 18:24:19 +01:00
|
|
|
|
2021-12-17 18:56:33 +01:00
|
|
|
// Set corresponding enable signals to ones
|
2025-03-04 16:12:02 +01:00
|
|
|
V3Number ones{lhsp, ForceState::isRangedDType(lhsp) ? lhsp->width() : 1};
|
2021-12-17 18:56:33 +01:00
|
|
|
ones.setAllBits1();
|
|
|
|
|
AstAssign* const setEnp
|
2023-09-17 04:50:54 +02:00
|
|
|
= new AstAssign{flp, lhsp->cloneTreePure(false), new AstConst{rhsp->fileline(), ones}};
|
2021-12-17 18:56:33 +01:00
|
|
|
transformWritenVarScopes(setEnp->lhsp(), [this](AstVarScope* vscp) {
|
2025-03-04 16:12:02 +01:00
|
|
|
return m_state.getForceComponents(vscp).m_enVscp;
|
2021-12-17 18:56:33 +01:00
|
|
|
});
|
|
|
|
|
// Set corresponding value signals to the forced value
|
|
|
|
|
AstAssign* const setValp
|
2023-09-17 04:50:54 +02:00
|
|
|
= new AstAssign{flp, lhsp->cloneTreePure(false), rhsp->cloneTreePure(false)};
|
2025-10-08 15:39:50 +02:00
|
|
|
transformWritenVarScopes(setValp->lhsp(), [this, rhsp, setValp](AstVarScope* vscp) {
|
2025-03-04 16:12:02 +01:00
|
|
|
AstVarScope* const valVscp = m_state.getForceComponents(vscp).m_valVscp;
|
2025-10-08 15:39:50 +02:00
|
|
|
m_state.setValVscpAssign(valVscp, setValp);
|
2025-08-06 23:37:00 +02:00
|
|
|
rhsp->foreach([valVscp, this](AstVarRef* refp) { m_state.addValVscp(refp, valVscp); });
|
2025-03-04 16:12:02 +01:00
|
|
|
return valVscp;
|
2021-12-17 18:56:33 +01:00
|
|
|
});
|
2025-03-04 16:12:02 +01:00
|
|
|
|
|
|
|
|
// Set corresponding read signal directly as well, in case something in the same
|
|
|
|
|
// process reads it later
|
2021-12-17 18:56:33 +01:00
|
|
|
AstAssign* const setRdp = new AstAssign{flp, lhsp->unlinkFrBack(), rhsp->unlinkFrBack()};
|
|
|
|
|
transformWritenVarScopes(setRdp->lhsp(), [this](AstVarScope* vscp) {
|
2025-03-04 16:12:02 +01:00
|
|
|
return m_state.getForceComponents(vscp).m_rdVscp;
|
2021-12-17 18:56:33 +01:00
|
|
|
});
|
2022-01-01 18:24:19 +01:00
|
|
|
|
2021-12-17 18:56:33 +01:00
|
|
|
setEnp->addNext(setValp);
|
|
|
|
|
setEnp->addNext(setRdp);
|
|
|
|
|
relinker.relink(setEnp);
|
2022-01-01 18:24:19 +01:00
|
|
|
}
|
|
|
|
|
|
2021-12-17 18:56:33 +01:00
|
|
|
void visit(AstRelease* nodep) override {
|
2024-05-08 14:36:24 +02:00
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
AstNodeExpr* const lhsp = nodep->lhsp(); // The LValue we are releasing
|
2021-12-17 18:56:33 +01:00
|
|
|
// The AstRelease node will be removed for sure
|
|
|
|
|
VNRelinker relinker;
|
|
|
|
|
nodep->unlinkFrBack(&relinker);
|
2024-05-08 14:36:24 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2022-01-01 18:24:19 +01:00
|
|
|
|
2021-12-17 18:56:33 +01:00
|
|
|
// Set corresponding enable signals to zero
|
2025-03-04 16:12:02 +01:00
|
|
|
V3Number zero{lhsp, ForceState::isRangedDType(lhsp) ? lhsp->width() : 1};
|
2021-12-17 18:56:33 +01:00
|
|
|
zero.setAllBits0();
|
|
|
|
|
AstAssign* const resetEnp
|
2023-09-17 04:50:54 +02:00
|
|
|
= new AstAssign{flp, lhsp->cloneTreePure(false), new AstConst{lhsp->fileline(), zero}};
|
2021-12-17 18:56:33 +01:00
|
|
|
transformWritenVarScopes(resetEnp->lhsp(), [this](AstVarScope* vscp) {
|
2025-03-04 16:12:02 +01:00
|
|
|
return m_state.getForceComponents(vscp).m_enVscp;
|
2021-12-17 18:56:33 +01:00
|
|
|
});
|
2025-07-11 03:12:44 +02:00
|
|
|
|
|
|
|
|
// IEEE 1800-2023 10.6.2: When released, then if the variable is not driven by a continuous
|
|
|
|
|
// assignment and does not currently have an active procedural continuous assignment, the
|
|
|
|
|
// variable shall not immediately change value and shall maintain its current value until
|
|
|
|
|
// the next procedural assignment to the variable is executed. Releasing a variable that is
|
|
|
|
|
// driven by a continuous assignment or currently has an active assign procedural
|
|
|
|
|
// continuous assignment shall reestablish that assignment and schedule a reevaluation in
|
|
|
|
|
// the continuous assignment's scheduling region.
|
2021-12-17 18:56:33 +01:00
|
|
|
AstAssign* const resetRdp
|
2025-06-28 21:45:45 +02:00
|
|
|
= new AstAssign{flp, lhsp->cloneTreePure(false), lhsp->unlinkFrBack()};
|
2021-12-17 18:56:33 +01:00
|
|
|
// Replace write refs on the LHS
|
2022-10-20 14:48:44 +02:00
|
|
|
resetRdp->lhsp()->foreach([this](AstNodeVarRef* refp) {
|
2021-12-17 18:56:33 +01:00
|
|
|
if (refp->access() != VAccess::WRITE) return;
|
|
|
|
|
AstVarScope* const vscp = refp->varScopep();
|
2025-03-04 16:12:02 +01:00
|
|
|
AstVarScope* const newVscp = vscp->varp()->isContinuously()
|
|
|
|
|
? m_state.getForceComponents(vscp).m_rdVscp
|
|
|
|
|
: vscp;
|
2025-06-28 21:45:45 +02:00
|
|
|
AstVarRef* const newpRefp = new AstVarRef{refp->fileline(), newVscp, VAccess::WRITE};
|
2021-12-17 18:56:33 +01:00
|
|
|
refp->replaceWith(newpRefp);
|
2022-07-31 19:13:55 +02:00
|
|
|
VL_DO_DANGLING(refp->deleteTree(), refp);
|
2021-12-17 18:56:33 +01:00
|
|
|
});
|
|
|
|
|
// Replace write refs on RHS
|
2022-10-20 14:48:44 +02:00
|
|
|
resetRdp->rhsp()->foreach([this](AstNodeVarRef* refp) {
|
2021-12-17 18:56:33 +01:00
|
|
|
if (refp->access() != VAccess::WRITE) return;
|
|
|
|
|
AstVarScope* const vscp = refp->varScopep();
|
2023-05-12 12:57:12 +02:00
|
|
|
if (vscp->varp()->isContinuously()) {
|
2025-03-04 16:12:02 +01:00
|
|
|
AstVarRef* const newpRefp = new AstVarRef{refp->fileline(), vscp, VAccess::READ};
|
|
|
|
|
ForceState::markNonReplaceable(newpRefp);
|
2023-05-12 12:57:12 +02:00
|
|
|
refp->replaceWith(newpRefp);
|
|
|
|
|
} else {
|
2025-03-04 16:12:02 +01:00
|
|
|
refp->replaceWith(m_state.getForceComponents(vscp).forcedUpdate(vscp));
|
2023-05-12 12:57:12 +02:00
|
|
|
}
|
2025-03-04 16:12:02 +01:00
|
|
|
|
2022-07-31 19:13:55 +02:00
|
|
|
VL_DO_DANGLING(refp->deleteTree(), refp);
|
2021-12-17 18:56:33 +01:00
|
|
|
});
|
2022-01-01 18:24:19 +01:00
|
|
|
|
2023-05-12 12:57:12 +02:00
|
|
|
resetRdp->addNext(resetEnp);
|
|
|
|
|
relinker.relink(resetRdp);
|
2022-01-01 18:24:19 +01:00
|
|
|
}
|
|
|
|
|
|
2021-12-19 20:45:06 +01:00
|
|
|
void visit(AstVarScope* nodep) override {
|
|
|
|
|
// If this signal is marked externally forceable, create the public force signals
|
|
|
|
|
if (nodep->varp()->isForceable()) {
|
2025-03-04 16:12:02 +01:00
|
|
|
const ForceState::ForceComponentsVarScope& fc = m_state.getForceComponents(nodep);
|
2023-11-05 17:58:22 +01:00
|
|
|
fc.m_enVscp->varp()->sigUserRWPublic(true);
|
|
|
|
|
fc.m_valVscp->varp()->sigUserRWPublic(true);
|
2021-12-19 20:45:06 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-04 16:12:02 +01:00
|
|
|
public:
|
2021-12-17 18:56:33 +01:00
|
|
|
// CONSTRUCTOR
|
2025-08-21 10:43:37 +02:00
|
|
|
// cppcheck-suppress constParameterCallback
|
|
|
|
|
ForceConvertVisitor(AstNetlist* nodep, ForceState& state)
|
2025-03-04 16:12:02 +01:00
|
|
|
: m_state{state} {
|
2021-12-17 18:56:33 +01:00
|
|
|
// Transform all force and release statements
|
|
|
|
|
iterateAndNextNull(nodep->modulesp());
|
2025-03-04 16:12:02 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ForceReplaceVisitor final : public VNVisitor {
|
|
|
|
|
// STATE
|
|
|
|
|
const ForceState& m_state;
|
|
|
|
|
AstNodeStmt* m_stmtp = nullptr;
|
|
|
|
|
bool m_inLogic = false;
|
2021-12-17 18:56:33 +01:00
|
|
|
|
2025-03-04 16:12:02 +01:00
|
|
|
// METHODS
|
|
|
|
|
void iterateLogic(AstNode* logicp) {
|
|
|
|
|
VL_RESTORER(m_inLogic);
|
|
|
|
|
m_inLogic = true;
|
|
|
|
|
iterateChildren(logicp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
void visit(AstNodeStmt* nodep) override {
|
|
|
|
|
VL_RESTORER(m_stmtp);
|
|
|
|
|
m_stmtp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstCFunc* nodep) override { iterateLogic(nodep); }
|
|
|
|
|
void visit(AstCoverToggle* nodep) override { iterateLogic(nodep); }
|
|
|
|
|
void visit(AstNodeProcedure* nodep) override { iterateLogic(nodep); }
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
void visit(AstAlways* nodep) override {
|
|
|
|
|
// TODO: this is the old behavioud prior to moving AssignW under Always.
|
|
|
|
|
// Review if this is appropriate or if we are missing something...
|
|
|
|
|
if (nodep->keyword() == VAlwaysKwd::CONT_ASSIGN) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
iterateLogic(nodep);
|
|
|
|
|
}
|
2025-03-04 16:12:02 +01:00
|
|
|
void visit(AstSenItem* nodep) override { iterateLogic(nodep); }
|
|
|
|
|
void visit(AstVarRef* nodep) override {
|
|
|
|
|
if (ForceState::isNotReplaceable(nodep)) return;
|
|
|
|
|
|
|
|
|
|
switch (nodep->access()) {
|
|
|
|
|
case VAccess::READ: {
|
|
|
|
|
// Replace VarRef from forced LHS with rdVscp.
|
|
|
|
|
if (ForceState::ForceComponentsVarScope* const fcp
|
|
|
|
|
= m_state.tryGetForceComponents(nodep)) {
|
|
|
|
|
nodep->varp(fcp->m_rdVscp->varp());
|
|
|
|
|
nodep->varScopep(fcp->m_rdVscp);
|
2022-01-01 18:24:19 +01:00
|
|
|
}
|
2025-03-04 16:12:02 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case VAccess::WRITE: {
|
|
|
|
|
if (!m_inLogic) return;
|
|
|
|
|
// Emit rdVscp update after each write to any VarRef on forced LHS.
|
|
|
|
|
if (ForceState::ForceComponentsVarScope* const fcp
|
|
|
|
|
= m_state.tryGetForceComponents(nodep)) {
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
AstVarRef* const lhsp = new AstVarRef{flp, fcp->m_rdVscp, VAccess::WRITE};
|
|
|
|
|
AstNodeExpr* const rhsp = fcp->forcedUpdate(nodep->varScopep());
|
|
|
|
|
m_stmtp->addNextHere(new AstAssign{flp, lhsp, rhsp});
|
|
|
|
|
}
|
|
|
|
|
// Emit valVscp update after each write to any VarRef on forced RHS.
|
2025-08-06 23:37:00 +02:00
|
|
|
if (!m_state.getValVscps(nodep)) break;
|
|
|
|
|
for (AstVarScope* const valVscp : *m_state.getValVscps(nodep)) {
|
2025-03-04 16:12:02 +01:00
|
|
|
FileLine* const flp = nodep->fileline();
|
2025-10-08 15:39:50 +02:00
|
|
|
AstAssign* assignp = m_state.getValVscpAssign(valVscp);
|
|
|
|
|
UASSERT_OBJ(assignp, flp, "Missing stored assignment for forced valVscp");
|
|
|
|
|
|
|
|
|
|
assignp = assignp->cloneTreePure(false);
|
2025-03-04 16:12:02 +01:00
|
|
|
|
2025-10-08 15:39:50 +02:00
|
|
|
assignp->rhsp()->foreach(
|
|
|
|
|
[](AstVarRef* refp) { ForceState::markNonReplaceable(refp); });
|
2025-03-04 16:12:02 +01:00
|
|
|
|
2025-10-08 15:39:50 +02:00
|
|
|
m_stmtp->addNextHere(assignp);
|
2025-03-04 16:12:02 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
2025-07-31 12:48:37 +02:00
|
|
|
if (!m_inLogic) return;
|
2025-08-06 23:37:00 +02:00
|
|
|
if (m_state.tryGetForceComponents(nodep) || m_state.getValVscps(nodep)) {
|
2025-09-20 14:19:42 +02:00
|
|
|
nodep->v3warn(
|
|
|
|
|
E_UNSUPPORTED,
|
2025-07-31 12:48:37 +02:00
|
|
|
"Unsupported: Signals used via read-write reference cannot be forced");
|
|
|
|
|
}
|
2025-03-04 16:12:02 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2022-01-01 18:24:19 +01:00
|
|
|
}
|
2025-03-04 16:12:02 +01:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2022-01-01 18:24:19 +01:00
|
|
|
|
|
|
|
|
public:
|
2025-03-04 16:12:02 +01:00
|
|
|
// CONSTRUCTOR
|
|
|
|
|
explicit ForceReplaceVisitor(AstNetlist* nodep, const ForceState& state)
|
|
|
|
|
: m_state{state} {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-01-01 18:24:19 +01:00
|
|
|
};
|
|
|
|
|
//######################################################################
|
2021-12-17 18:56:33 +01:00
|
|
|
//
|
2022-01-01 18:24:19 +01:00
|
|
|
|
|
|
|
|
void V3Force::forceAll(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2021-12-17 18:56:33 +01:00
|
|
|
if (!v3Global.hasForceableSignals()) return;
|
2025-03-04 16:12:02 +01:00
|
|
|
{
|
|
|
|
|
ForceState state;
|
|
|
|
|
{ ForceConvertVisitor{nodep, state}; }
|
|
|
|
|
{ ForceReplaceVisitor{nodep, state}; }
|
|
|
|
|
V3Global::dumpCheckGlobalTree("force", 0, dumpTreeEitherLevel() >= 3);
|
|
|
|
|
}
|
2022-01-01 18:24:19 +01:00
|
|
|
}
|