Support force assignments to unpacked structs (#7060)
This commit is contained in:
parent
c21498293e
commit
8491d6a80c
329
src/V3Force.cpp
329
src/V3Force.cpp
|
|
@ -47,6 +47,7 @@
|
||||||
#include "V3Force.h"
|
#include "V3Force.h"
|
||||||
|
|
||||||
#include "V3AstUserAllocator.h"
|
#include "V3AstUserAllocator.h"
|
||||||
|
#include "V3UniqueNames.h"
|
||||||
|
|
||||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
||||||
|
|
@ -78,10 +79,12 @@ public:
|
||||||
AstVarScope* const m_rdVscp; // New variable to replace read references with
|
AstVarScope* const m_rdVscp; // New variable to replace read references with
|
||||||
AstVarScope* const m_valVscp; // Forced value
|
AstVarScope* const m_valVscp; // Forced value
|
||||||
AstVarScope* const m_enVscp; // Force enabled signal
|
AstVarScope* const m_enVscp; // Force enabled signal
|
||||||
|
V3UniqueNames m_iterNames; // Names for loop iteration variables
|
||||||
explicit ForceComponentsVarScope(AstVarScope* vscp, ForceComponentsVar& fcv)
|
explicit ForceComponentsVarScope(AstVarScope* vscp, ForceComponentsVar& fcv)
|
||||||
: m_rdVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_rdVarp}}
|
: m_rdVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_rdVarp}}
|
||||||
, m_valVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_valVarp}}
|
, m_valVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_valVarp}}
|
||||||
, m_enVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_enVarp}} {
|
, m_enVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_enVarp}}
|
||||||
|
, m_iterNames{"__VForceIter"} {
|
||||||
m_rdVscp->addNext(m_enVscp);
|
m_rdVscp->addNext(m_enVscp);
|
||||||
m_rdVscp->addNext(m_valVscp);
|
m_rdVscp->addNext(m_valVscp);
|
||||||
vscp->addNextHere(m_rdVscp);
|
vscp->addNextHere(m_rdVscp);
|
||||||
|
|
@ -94,63 +97,30 @@ public:
|
||||||
activeInitp->senTreeStorep(activeInitp->sentreep());
|
activeInitp->senTreeStorep(activeInitp->sentreep());
|
||||||
vscp->scopep()->addBlocksp(activeInitp);
|
vscp->scopep()->addBlocksp(activeInitp);
|
||||||
|
|
||||||
AstVarRef* const enRefp = new AstVarRef{flp, m_enVscp, VAccess::WRITE};
|
// Create statements that update __Rd variable.
|
||||||
|
// These nodes will be copied and used also for __En initialization
|
||||||
|
AstVarRef* const rdRefp = new AstVarRef{flp, m_rdVscp, VAccess::WRITE};
|
||||||
|
std::vector<AstAssign*> assigns;
|
||||||
|
AstNodeStmt* const rdUpdateStmtsp
|
||||||
|
= getForcedUpdateStmtsRecursep(rdRefp, vscp, rdRefp, assigns);
|
||||||
|
|
||||||
AstNodeStmt* toInsertp = nullptr;
|
// To use these statements for __En initialization, replace references to __Rd with
|
||||||
AstNodeStmt* outerStmtp = nullptr;
|
// ones to __En and replace assignments RHS with 0
|
||||||
std::vector<AstNodeExpr*> loopVarRefs;
|
AstNodeStmt* const enInitStmtsp = rdUpdateStmtsp->cloneTree(true);
|
||||||
if (VN_IS(enRefp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
for (size_t i = 0; i < assigns.size(); i++) {
|
||||||
// Create a loop to set all elements of __VforceEn array to 0.
|
// Save copies, because clonep() works only after the last cloneTree
|
||||||
// That loop node is then copied and used for updating elements of __VforceRd array
|
assigns[i] = assigns[i]->clonep();
|
||||||
if (AstUnpackArrayDType* const unpackedp
|
|
||||||
= VN_CAST(m_rdVscp->varp()->dtypep(), UnpackArrayDType)) {
|
|
||||||
std::vector<AstUnpackArrayDType*> dims = unpackedp->unpackDimensions();
|
|
||||||
loopVarRefs.reserve(dims.size());
|
|
||||||
for (size_t i = 0; i < dims.size(); i++) {
|
|
||||||
AstVar* const loopVarp = new AstVar{
|
|
||||||
flp, VVarType::MODULETEMP,
|
|
||||||
m_rdVscp->varp()->name() + "__VwhileIter" + std::to_string(i),
|
|
||||||
VFlagBitPacked{}, 32};
|
|
||||||
m_rdVscp->varp()->addNext(loopVarp);
|
|
||||||
AstVarScope* const loopVarScopep
|
|
||||||
= new AstVarScope{flp, m_rdVscp->scopep(), loopVarp};
|
|
||||||
m_rdVscp->addNext(loopVarScopep);
|
|
||||||
AstVarRef* const readRefp
|
|
||||||
= new AstVarRef{flp, loopVarScopep, VAccess::READ};
|
|
||||||
loopVarRefs.push_back(readRefp);
|
|
||||||
AstNodeStmt* const currInitp
|
|
||||||
= new AstAssign{flp, new AstVarRef{flp, loopVarScopep, VAccess::WRITE},
|
|
||||||
new AstConst{flp, 0}};
|
|
||||||
if (toInsertp) {
|
|
||||||
toInsertp->addNextHere(currInitp);
|
|
||||||
} else {
|
|
||||||
outerStmtp = currInitp;
|
|
||||||
}
|
|
||||||
AstLoop* const currWhilep = new AstLoop{flp};
|
|
||||||
currInitp->addNextHere(currWhilep);
|
|
||||||
AstLoopTest* const loopTestp = new AstLoopTest{
|
|
||||||
flp, currWhilep,
|
|
||||||
new AstNeq{flp, readRefp,
|
|
||||||
new AstConst{
|
|
||||||
flp, static_cast<uint32_t>(dims[i]->elementsConst())}}};
|
|
||||||
currWhilep->addStmtsp(loopTestp);
|
|
||||||
toInsertp = loopTestp;
|
|
||||||
AstAssign* const currIncrp = new AstAssign{
|
|
||||||
flp, new AstVarRef{flp, loopVarScopep, VAccess::WRITE},
|
|
||||||
new AstAdd{flp, readRefp->cloneTree(false), new AstConst{flp, 1}}};
|
|
||||||
currWhilep->addStmtsp(currIncrp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
V3Number zero{m_enVscp, m_enVscp->width()};
|
for (AstAssign* const assignp : assigns) {
|
||||||
AstNodeExpr* const enRhsp = new AstConst{flp, zero};
|
AstVarRef* const lhsVarRefp
|
||||||
AstNodeExpr* enLhsp = applySelects(enRefp, loopVarRefs);
|
= VN_AS(AstNodeVarRef::varRefLValueRecurse(assignp->lhsp()), VarRef);
|
||||||
AstNodeStmt* stmtp = new AstAssign{flp, enLhsp, enRhsp};
|
lhsVarRefp->replaceWith(new AstVarRef{flp, m_enVscp, VAccess::WRITE});
|
||||||
if (toInsertp) {
|
lhsVarRefp->deleteTree();
|
||||||
toInsertp->addNextHere(stmtp);
|
assignp->rhsp()->unlinkFrBack()->deleteTree();
|
||||||
stmtp = outerStmtp;
|
V3Number zero{m_enVscp, assignp->lhsp()->dtypep()->width()};
|
||||||
|
assignp->rhsp(new AstConst{flp, zero});
|
||||||
}
|
}
|
||||||
activeInitp->addStmtsp(new AstInitial{flp, stmtp->cloneTree(true)});
|
activeInitp->addStmtsp(new AstInitial{flp, enInitStmtsp});
|
||||||
{ // Add the combinational override
|
{ // Add the combinational override
|
||||||
// Explicitly list dependencies for update.
|
// Explicitly list dependencies for update.
|
||||||
// Note: rdVscp is also needed to retrigger assignment for the first time.
|
// Note: rdVscp is also needed to retrigger assignment for the first time.
|
||||||
|
|
@ -167,48 +137,105 @@ public:
|
||||||
= new AstActive{flp, "force-update", new AstSenTree{flp, itemsp}};
|
= new AstActive{flp, "force-update", new AstSenTree{flp, itemsp}};
|
||||||
activep->senTreeStorep(activep->sentreep());
|
activep->senTreeStorep(activep->sentreep());
|
||||||
|
|
||||||
// Reuse the statements created for __VforceEn initialization
|
activep->addStmtsp(
|
||||||
// and replace var ref on the LHS and the RHS
|
new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr, rdUpdateStmtsp});
|
||||||
AstVarRef* const rdRefp = new AstVarRef{flp, m_rdVscp, VAccess::WRITE};
|
|
||||||
AstNodeExpr* const rdRhsp = forcedUpdate(vscp, loopVarRefs);
|
|
||||||
enRefp->replaceWith(rdRefp);
|
|
||||||
VL_DO_DANGLING(enRefp->deleteTree(), enRefp);
|
|
||||||
enRhsp->replaceWith(rdRhsp);
|
|
||||||
VL_DO_DANGLING(enRhsp->deleteTree(), enRhsp);
|
|
||||||
|
|
||||||
activep->addStmtsp(new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr, stmtp});
|
|
||||||
vscp->scopep()->addBlocksp(activep);
|
vscp->scopep()->addBlocksp(activep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static AstNodeExpr* applySelects(AstNodeExpr* exprp,
|
AstNodeStmt* getForcedUpdateStmtsRecursep(AstNodeExpr* const lhsp, AstVarScope* const vscp,
|
||||||
const std::vector<AstNodeExpr*>& selectExprs) {
|
AstVarRef* const lhsVarRefp,
|
||||||
for (AstNodeExpr* const sp : selectExprs) {
|
std::vector<AstAssign*>& assigns) {
|
||||||
exprp = new AstArraySel{exprp->fileline(), exprp, sp->cloneTreePure(false)};
|
// Create stataments that update values of __Rd variable.
|
||||||
|
// lhsp is either a reference to that variable or ArraySel or MemberSel on it.
|
||||||
|
// lhsVarRefp is a reference to that variable in lhsp subtree.
|
||||||
|
// assigns is a vector to which all assignments to __Rd are added.
|
||||||
|
FileLine* const flp = lhsp->fileline();
|
||||||
|
const AstNodeDType* const lhsDtypep = lhsp->dtypep()->skipRefp();
|
||||||
|
if (lhsDtypep->isIntegralOrPacked() || VN_IS(lhsDtypep, BasicDType)) {
|
||||||
|
AstAssign* const assignp
|
||||||
|
= new AstAssign{flp, lhsp, forcedUpdate(vscp, lhsp, lhsVarRefp)};
|
||||||
|
assigns.push_back(assignp);
|
||||||
|
return assignp;
|
||||||
|
} else if (const AstStructDType* const structDtypep
|
||||||
|
= VN_CAST(lhsDtypep, StructDType)) {
|
||||||
|
AstNodeStmt* stmtsp = nullptr;
|
||||||
|
bool firstIter = true;
|
||||||
|
for (AstMemberDType* mdtp = structDtypep->membersp(); mdtp;
|
||||||
|
mdtp = VN_AS(mdtp->nextp(), MemberDType)) {
|
||||||
|
AstNodeExpr* const lhsCopyp = firstIter ? lhsp : lhsp->cloneTreePure(false);
|
||||||
|
AstVarRef* const lhsVarRefCopyp
|
||||||
|
= firstIter ? lhsVarRefp : lhsVarRefp->clonep();
|
||||||
|
AstStructSel* const structSelp = new AstStructSel{flp, lhsCopyp, mdtp->name()};
|
||||||
|
structSelp->dtypep(mdtp);
|
||||||
|
AstNodeStmt* const memberStmtp
|
||||||
|
= getForcedUpdateStmtsRecursep(structSelp, vscp, lhsVarRefCopyp, assigns);
|
||||||
|
stmtsp = firstIter ? memberStmtp : stmtsp->addNext(memberStmtp);
|
||||||
|
firstIter = false;
|
||||||
|
}
|
||||||
|
return stmtsp;
|
||||||
|
} else if (const AstUnpackArrayDType* const arrayDtypep
|
||||||
|
= VN_CAST(lhsDtypep, UnpackArrayDType)) {
|
||||||
|
AstVar* const loopVarp
|
||||||
|
= new AstVar{flp, VVarType::MODULETEMP,
|
||||||
|
m_iterNames.get(m_rdVscp->varp()->name()), VFlagBitPacked{}, 32};
|
||||||
|
m_rdVscp->varp()->addNext(loopVarp);
|
||||||
|
AstVarScope* const loopVarScopep
|
||||||
|
= new AstVarScope{flp, m_rdVscp->scopep(), loopVarp};
|
||||||
|
m_rdVscp->addNext(loopVarScopep);
|
||||||
|
AstVarRef* const readRefp = new AstVarRef{flp, loopVarScopep, VAccess::READ};
|
||||||
|
AstNodeStmt* const currInitp = new AstAssign{
|
||||||
|
flp, new AstVarRef{flp, loopVarScopep, VAccess::WRITE}, new AstConst{flp, 0}};
|
||||||
|
AstLoop* const currWhilep = new AstLoop{flp};
|
||||||
|
currInitp->addNextHere(currWhilep);
|
||||||
|
AstLoopTest* const loopTestp = new AstLoopTest{
|
||||||
|
flp, currWhilep,
|
||||||
|
new AstNeq{
|
||||||
|
flp, readRefp,
|
||||||
|
new AstConst{flp, static_cast<uint32_t>(arrayDtypep->elementsConst())}}};
|
||||||
|
currWhilep->addStmtsp(loopTestp);
|
||||||
|
AstArraySel* const lhsSelp
|
||||||
|
= new AstArraySel{flp, lhsp, readRefp->cloneTree(false)};
|
||||||
|
AstNodeStmt* const loopBodyp
|
||||||
|
= getForcedUpdateStmtsRecursep(lhsSelp, vscp, lhsVarRefp, assigns);
|
||||||
|
currWhilep->addStmtsp(loopBodyp);
|
||||||
|
AstAssign* const currIncrp = new AstAssign{
|
||||||
|
flp, new AstVarRef{flp, loopVarScopep, VAccess::WRITE},
|
||||||
|
new AstAdd{flp, readRefp->cloneTree(false), new AstConst{flp, 1}}};
|
||||||
|
currWhilep->addStmtsp(currIncrp);
|
||||||
|
return currInitp;
|
||||||
|
} else {
|
||||||
|
lhsDtypep->v3fatalSrc("Unhandled type");
|
||||||
}
|
}
|
||||||
return exprp;
|
|
||||||
}
|
}
|
||||||
AstNodeExpr* forcedUpdate(AstVarScope* const vscp,
|
static AstNodeExpr* wrapIntoExprp(AstVarRef* const refp, AstNodeExpr* const exprp,
|
||||||
const std::vector<AstNodeExpr*>& selectExprs) const {
|
AstVarRef* const varRefToReplacep) {
|
||||||
|
// Return a copy of exprp in which varRefToReplacep is replaced with refp
|
||||||
|
if (exprp == varRefToReplacep) {
|
||||||
|
return refp;
|
||||||
|
} else {
|
||||||
|
AstNodeExpr* const copiedExprp = exprp->cloneTreePure(false);
|
||||||
|
AstNode* const oldRefp = varRefToReplacep->clonep();
|
||||||
|
varRefToReplacep->clonep()->replaceWith(refp);
|
||||||
|
oldRefp->deleteTree();
|
||||||
|
return copiedExprp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AstNodeExpr* forcedUpdate(AstVarScope* const vscp, AstNodeExpr* exprp = nullptr,
|
||||||
|
AstVarRef* const varRefToReplacep = nullptr) const {
|
||||||
FileLine* const flp = vscp->fileline();
|
FileLine* const flp = vscp->fileline();
|
||||||
AstVarRef* origRefp = new AstVarRef{flp, vscp, VAccess::READ};
|
AstVarRef* origRefp = new AstVarRef{flp, vscp, VAccess::READ};
|
||||||
ForceState::markNonReplaceable(origRefp);
|
ForceState::markNonReplaceable(origRefp);
|
||||||
AstNodeExpr* const origp = applySelects(origRefp, selectExprs);
|
AstNodeExpr* const origExprp = wrapIntoExprp(origRefp, exprp, varRefToReplacep);
|
||||||
|
AstNodeExpr* const enExprp = wrapIntoExprp(new AstVarRef{flp, m_enVscp, VAccess::READ},
|
||||||
|
exprp, varRefToReplacep);
|
||||||
|
AstNodeExpr* const valExprp = wrapIntoExprp(
|
||||||
|
new AstVarRef{flp, m_valVscp, VAccess::READ}, exprp, varRefToReplacep);
|
||||||
if (ForceState::isRangedDType(vscp)) {
|
if (ForceState::isRangedDType(vscp)) {
|
||||||
return new AstOr{
|
return new AstOr{
|
||||||
flp,
|
flp, new AstAnd{flp, enExprp, valExprp},
|
||||||
new AstAnd{
|
new AstAnd{flp, new AstNot{flp, enExprp->cloneTreePure(false)}, origExprp}};
|
||||||
flp,
|
|
||||||
applySelects(new AstVarRef{flp, m_enVscp, VAccess::READ}, selectExprs),
|
|
||||||
applySelects(new AstVarRef{flp, m_valVscp, VAccess::READ}, selectExprs)},
|
|
||||||
new AstAnd{
|
|
||||||
flp,
|
|
||||||
new AstNot{flp, applySelects(new AstVarRef{flp, m_enVscp, VAccess::READ},
|
|
||||||
selectExprs)},
|
|
||||||
origp}};
|
|
||||||
}
|
}
|
||||||
return new AstCond{
|
return new AstCond{flp, enExprp, valExprp, origExprp};
|
||||||
flp, applySelects(new AstVarRef{flp, m_enVscp, VAccess::READ}, selectExprs),
|
|
||||||
applySelects(new AstVarRef{flp, m_valVscp, VAccess::READ}, selectExprs), origp};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -229,39 +256,52 @@ private:
|
||||||
m_valVscps;
|
m_valVscps;
|
||||||
// `valVscp` force components of a forced RHS
|
// `valVscp` force components of a forced RHS
|
||||||
|
|
||||||
static AstNodeDType* getEnVarpDTypep(AstVar* const varp) {
|
static size_t checkIfDTypeSupportedRecurse(const AstNodeDType* const dtypep,
|
||||||
|
const AstVar* const varp) {
|
||||||
|
// Checks if force stmt is supported on all subtypes
|
||||||
|
// and returns number of unpacked elements
|
||||||
|
const AstNodeDType* const dtp = dtypep->skipRefp();
|
||||||
|
if (const AstUnpackArrayDType* const udtp = VN_CAST(dtp, UnpackArrayDType)) {
|
||||||
|
const size_t elemsInSubDType = checkIfDTypeSupportedRecurse(udtp->subDTypep(), varp);
|
||||||
|
return udtp->elementsConst() * elemsInSubDType;
|
||||||
|
} else if (const AstNodeUOrStructDType* const sdtp = VN_CAST(dtp, NodeUOrStructDType)) {
|
||||||
|
size_t elemCount = 0;
|
||||||
|
for (const AstMemberDType* mdtp = sdtp->membersp(); mdtp;
|
||||||
|
mdtp = VN_AS(mdtp->nextp(), MemberDType)) {
|
||||||
|
elemCount += checkIfDTypeSupportedRecurse(mdtp->subDTypep(), varp);
|
||||||
|
}
|
||||||
|
return elemCount;
|
||||||
|
} else if (const AstBasicDType* const bdtp = VN_CAST(dtp, BasicDType)) {
|
||||||
|
if (bdtp->isString() || bdtp->isEvent() || bdtp->keyword() == VBasicDTypeKwd::CHANDLE
|
||||||
|
|| bdtp->keyword() == VBasicDTypeKwd::TIME) {
|
||||||
|
varp->v3warn(E_UNSUPPORTED, "Forcing variable of unsupported type: "
|
||||||
|
<< varp->dtypep()->prettyTypeName());
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
} else if (!dtp->isIntegralOrPacked()) {
|
||||||
|
varp->v3warn(E_UNSUPPORTED, "Forcing variable of unsupported type: "
|
||||||
|
<< varp->dtypep()->prettyTypeName());
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
// All packed types are supported
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static AstNodeDType* getEnVarpDTypep(const AstVar* const varp) {
|
||||||
AstNodeDType* const origDTypep = varp->dtypep()->skipRefp();
|
AstNodeDType* const origDTypep = varp->dtypep()->skipRefp();
|
||||||
|
const size_t unpackElemNum = checkIfDTypeSupportedRecurse(origDTypep, varp);
|
||||||
|
if (unpackElemNum > ELEMENTS_MAX) {
|
||||||
|
varp->v3warn(E_UNSUPPORTED, "Unsupported: Force of variable with "
|
||||||
|
">= "
|
||||||
|
<< ELEMENTS_MAX << " unpacked elements");
|
||||||
|
}
|
||||||
if (VN_IS(origDTypep, UnpackArrayDType)) {
|
if (VN_IS(origDTypep, UnpackArrayDType)) {
|
||||||
size_t elemNum = 1;
|
|
||||||
AstNodeDType* dtp = origDTypep;
|
|
||||||
while (AstUnpackArrayDType* const uDtp = VN_CAST(dtp, UnpackArrayDType)) {
|
|
||||||
dtp = uDtp->subDTypep()->skipRefp();
|
|
||||||
elemNum *= uDtp->elementsConst();
|
|
||||||
}
|
|
||||||
if (elemNum > ELEMENTS_MAX) {
|
|
||||||
varp->v3warn(E_UNSUPPORTED, "Unsupported: Force of unpacked array variable with "
|
|
||||||
">= "
|
|
||||||
<< ELEMENTS_MAX << " elements");
|
|
||||||
}
|
|
||||||
bool complexElem = true;
|
|
||||||
if (const AstBasicDType* const basicp = VN_CAST(dtp, BasicDType)) {
|
|
||||||
complexElem = basicp->isOpaque();
|
|
||||||
}
|
|
||||||
if (complexElem) {
|
|
||||||
varp->v3warn(E_UNSUPPORTED, "Unsupported: Force of unpacked array variable with "
|
|
||||||
"elements of complex data type");
|
|
||||||
}
|
|
||||||
return origDTypep;
|
return origDTypep;
|
||||||
} else if (VN_IS(origDTypep, BasicDType)) {
|
} else if (VN_IS(origDTypep, BasicDType)) {
|
||||||
return isRangedDType(varp) ? origDTypep : varp->findBitDType();
|
return isRangedDType(varp) ? origDTypep : varp->findBitDType();
|
||||||
} else if (VN_IS(origDTypep, PackArrayDType)) {
|
} else if (VN_IS(origDTypep, PackArrayDType)) {
|
||||||
return origDTypep;
|
return origDTypep;
|
||||||
} else if (const AstNodeUOrStructDType* const sdtp
|
} else if (VN_IS(origDTypep, NodeUOrStructDType)) {
|
||||||
= VN_CAST(origDTypep, NodeUOrStructDType)) {
|
|
||||||
if (!sdtp->packed()) {
|
|
||||||
varp->v3warn(E_UNSUPPORTED,
|
|
||||||
"Unsupported: Force of unpacked struct / union variable");
|
|
||||||
}
|
|
||||||
return origDTypep;
|
return origDTypep;
|
||||||
} else {
|
} else {
|
||||||
varp->v3fatalSrc("Unsupported: Force of variable of unhandled data type");
|
varp->v3fatalSrc("Unsupported: Force of variable of unhandled data type");
|
||||||
|
|
@ -275,7 +315,7 @@ public:
|
||||||
VL_UNCOPYABLE(ForceState);
|
VL_UNCOPYABLE(ForceState);
|
||||||
|
|
||||||
// STATIC METHODS
|
// STATIC METHODS
|
||||||
static bool isRangedDType(AstNode* nodep) {
|
static bool isRangedDType(const AstNode* const nodep) {
|
||||||
// If ranged we need a multibit enable to support bit-by-bit part-select forces,
|
// 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.
|
// otherwise forcing a real or other opaque dtype and need a single bit enable.
|
||||||
const AstBasicDType* const basicp = nodep->dtypep()->skipRefp()->basicp();
|
const AstBasicDType* const basicp = nodep->dtypep()->skipRefp()->basicp();
|
||||||
|
|
@ -416,26 +456,14 @@ class ForceConvertVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Replace write refs on RHS
|
// Replace write refs on RHS
|
||||||
if (VN_IS(resetRdp->rhsp(), ArraySel)) {
|
if (VN_IS(resetRdp->rhsp(), ArraySel) || VN_IS(resetRdp->rhsp(), StructSel)) {
|
||||||
std::vector<AstNodeExpr*> selIndices;
|
AstVarRef* const refp
|
||||||
AstNodeExpr* exprp = resetRdp->rhsp();
|
= VN_AS(AstNodeVarRef::varRefLValueRecurse(resetRdp->rhsp()), VarRef);
|
||||||
while (AstArraySel* const selp = VN_CAST(exprp, ArraySel)) {
|
AstVarScope* const vscp = refp->varScopep();
|
||||||
selIndices.push_back(selp->bitp());
|
AstNodeExpr* const origRhsp = resetRdp->rhsp();
|
||||||
exprp = selp->fromp();
|
origRhsp->replaceWith(
|
||||||
}
|
m_state.getForceComponents(vscp).forcedUpdate(vscp, origRhsp, refp));
|
||||||
if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) {
|
VL_DO_DANGLING(origRhsp->deleteTree(), origRhsp);
|
||||||
AstVarScope* const vscp = refp->varScopep();
|
|
||||||
std::vector<AstNodeExpr*> reversedIndices(selIndices.size());
|
|
||||||
std::reverse_copy(selIndices.begin(), selIndices.end(), reversedIndices.begin());
|
|
||||||
AstNodeExpr* const origRhsp = resetRdp->rhsp();
|
|
||||||
origRhsp->replaceWith(
|
|
||||||
m_state.getForceComponents(vscp).forcedUpdate(vscp, reversedIndices));
|
|
||||||
VL_DO_DANGLING(origRhsp->deleteTree(), origRhsp);
|
|
||||||
} else {
|
|
||||||
exprp->v3warn(
|
|
||||||
E_UNSUPPORTED,
|
|
||||||
"Unsupported: Release statement argument is too complex array select");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
resetRdp->rhsp()->foreach([this](AstVarRef* refp) {
|
resetRdp->rhsp()->foreach([this](AstVarRef* refp) {
|
||||||
if (refp->access() != VAccess::WRITE) return;
|
if (refp->access() != VAccess::WRITE) return;
|
||||||
|
|
@ -444,7 +472,7 @@ class ForceConvertVisitor final : public VNVisitor {
|
||||||
refp->access(VAccess::READ);
|
refp->access(VAccess::READ);
|
||||||
ForceState::markNonReplaceable(refp);
|
ForceState::markNonReplaceable(refp);
|
||||||
} else {
|
} else {
|
||||||
refp->replaceWith(m_state.getForceComponents(vscp).forcedUpdate(vscp, {}));
|
refp->replaceWith(m_state.getForceComponents(vscp).forcedUpdate(vscp));
|
||||||
VL_DO_DANGLING(refp->deleteTree(), refp);
|
VL_DO_DANGLING(refp->deleteTree(), refp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -493,7 +521,6 @@ class ForceReplaceVisitor final : public VNVisitor {
|
||||||
AstNodeStmt* m_stmtp = nullptr;
|
AstNodeStmt* m_stmtp = nullptr;
|
||||||
bool m_inLogic = false;
|
bool m_inLogic = false;
|
||||||
bool m_releaseRhs = false; // Inside RHS of assignment created for release statement
|
bool m_releaseRhs = false; // Inside RHS of assignment created for release statement
|
||||||
std::vector<AstNodeExpr*> m_selIndices; // Indices of array select expressions above
|
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
void iterateLogic(AstNode* logicp) {
|
void iterateLogic(AstNode* logicp) {
|
||||||
|
|
@ -529,12 +556,6 @@ class ForceReplaceVisitor final : public VNVisitor {
|
||||||
iterateLogic(nodep);
|
iterateLogic(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstSenItem* nodep) override { iterateLogic(nodep); }
|
void visit(AstSenItem* nodep) override { iterateLogic(nodep); }
|
||||||
void visit(AstArraySel* nodep) override {
|
|
||||||
m_selIndices.push_back(nodep->bitp());
|
|
||||||
iterateChildren(nodep);
|
|
||||||
UASSERT_OBJ(m_selIndices.size(), nodep, "Underflow");
|
|
||||||
m_selIndices.pop_back();
|
|
||||||
}
|
|
||||||
void visit(AstVarRef* nodep) override {
|
void visit(AstVarRef* nodep) override {
|
||||||
if (ForceState::isNotReplaceable(nodep)) return;
|
if (ForceState::isNotReplaceable(nodep)) return;
|
||||||
|
|
||||||
|
|
@ -554,12 +575,26 @@ class ForceReplaceVisitor final : public VNVisitor {
|
||||||
if (ForceState::ForceComponentsVarScope* const fcp
|
if (ForceState::ForceComponentsVarScope* const fcp
|
||||||
= m_state.tryGetForceComponents(nodep)) {
|
= m_state.tryGetForceComponents(nodep)) {
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
std::vector<AstNodeExpr*> reversedIndices(m_selIndices.size());
|
AstVarRef* const lhsRefp = new AstVarRef{flp, fcp->m_rdVscp, VAccess::WRITE};
|
||||||
std::reverse_copy(m_selIndices.begin(), m_selIndices.end(),
|
AstNodeExpr* lhsp;
|
||||||
reversedIndices.begin());
|
AstNodeExpr* rhsp;
|
||||||
AstNodeExpr* const lhsp = ForceState::ForceComponentsVarScope::applySelects(
|
if (nodep->dtypep()->skipRefp()->isIntegralOrPacked()) {
|
||||||
new AstVarRef{flp, fcp->m_rdVscp, VAccess::WRITE}, reversedIndices);
|
rhsp = fcp->forcedUpdate(nodep->varScopep());
|
||||||
AstNodeExpr* const rhsp = fcp->forcedUpdate(nodep->varScopep(), reversedIndices);
|
lhsp = lhsRefp;
|
||||||
|
} else {
|
||||||
|
AstNodeExpr* wholeExprp = nodep;
|
||||||
|
while (VN_IS(wholeExprp->backp(), NodeExpr)) {
|
||||||
|
wholeExprp = VN_AS(wholeExprp->backp(), NodeExpr);
|
||||||
|
// wholeExprp should never be ExprStmt, because:
|
||||||
|
// * if nodep is inside stmtsp() of one, we should sooner get NodeStmt node
|
||||||
|
// * nodep should never be in resultp(), because it is a WRITE reference
|
||||||
|
// and resultp() should be an rvalue
|
||||||
|
UASSERT_OBJ(!VN_IS(wholeExprp, ExprStmt), nodep, "Unexpected AstExprStmt");
|
||||||
|
}
|
||||||
|
lhsp = ForceState::ForceComponentsVarScope::wrapIntoExprp(lhsRefp, wholeExprp,
|
||||||
|
nodep);
|
||||||
|
rhsp = fcp->forcedUpdate(nodep->varScopep(), wholeExprp, nodep);
|
||||||
|
}
|
||||||
m_stmtp->addNextHere(new AstAssign{flp, lhsp, rhsp});
|
m_stmtp->addNextHere(new AstAssign{flp, lhsp, rhsp});
|
||||||
}
|
}
|
||||||
// Emit valVscp update after each write to any VarRef on forced RHS.
|
// Emit valVscp update after each write to any VarRef on forced RHS.
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
%Error-UNSUPPORTED: t/t_force_readwrite_unsup.v:25:18: Unsupported: Signals used via read-write reference cannot be forced
|
%Error-UNSUPPORTED: t/t_force_readwrite_unsup.v:19:18: Unsupported: Signals used via read-write reference cannot be forced
|
||||||
25 | cls.take_ref(a);
|
19 | cls.take_ref(a);
|
||||||
| ^
|
| ^
|
||||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
%Error-UNSUPPORTED: t/t_force_readwrite_unsup.v:26:18: Unsupported: Signals used via read-write reference cannot be forced
|
%Error-UNSUPPORTED: t/t_force_readwrite_unsup.v:20:18: Unsupported: Signals used via read-write reference cannot be forced
|
||||||
26 | cls.take_ref(b);
|
20 | cls.take_ref(b);
|
||||||
| ^
|
| ^
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,6 @@
|
||||||
// SPDX-FileCopyrightText: 2025 Antmicro
|
// SPDX-FileCopyrightText: 2025 Antmicro
|
||||||
// SPDX-License-Identifier: CC0-1.0
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
// DESCRIPTION: Verilator: Verilog Test module
|
|
||||||
//
|
|
||||||
// This file ONLY is placed under the Creative Commons Public Domain
|
|
||||||
// SPDX-FileCopyrightText: 2025 Antmicro
|
|
||||||
// SPDX-License-Identifier: CC0-1.0
|
|
||||||
|
|
||||||
class Cls;
|
class Cls;
|
||||||
task take_ref(ref logic s);
|
task take_ref(ref logic s);
|
||||||
endtask
|
endtask
|
||||||
|
|
|
||||||
|
|
@ -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,83 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// verilog_format: off
|
||||||
|
`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)
|
||||||
|
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||||
|
// verilog_format: on
|
||||||
|
|
||||||
|
module t (
|
||||||
|
input clk
|
||||||
|
);
|
||||||
|
|
||||||
|
integer cyc = 0;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int x;
|
||||||
|
logic y;
|
||||||
|
int arr[5];
|
||||||
|
} struct_t;
|
||||||
|
|
||||||
|
struct_t s_array[3];
|
||||||
|
struct_t my_struct;
|
||||||
|
|
||||||
|
// Test loop
|
||||||
|
always @(posedge clk) begin
|
||||||
|
cyc <= cyc + 1;
|
||||||
|
if (cyc == 0) begin
|
||||||
|
s_array[1].x = 1;
|
||||||
|
s_array[1].arr[2] = 1;
|
||||||
|
my_struct.x <= 1;
|
||||||
|
end
|
||||||
|
else if (cyc == 1) begin
|
||||||
|
`checkh(s_array[1].x, 1);
|
||||||
|
`checkh(s_array[1].arr[2], 1);
|
||||||
|
`checkh(my_struct.x, 1);
|
||||||
|
end
|
||||||
|
else if (cyc == 2) begin
|
||||||
|
force s_array[1].x = 0;
|
||||||
|
force s_array[1].arr[2] = 2;
|
||||||
|
force my_struct.x = 0;
|
||||||
|
end
|
||||||
|
else if (cyc == 3) begin
|
||||||
|
`checkh(s_array[1].x, 0);
|
||||||
|
s_array[1].x = 1;
|
||||||
|
`checkh(s_array[1].arr[2], 2);
|
||||||
|
s_array[1].arr[2] = 3;
|
||||||
|
`checkh(my_struct.x, 0);
|
||||||
|
my_struct.x <= 1;
|
||||||
|
end
|
||||||
|
else if (cyc == 4) begin
|
||||||
|
`checkh(s_array[1].x, 0);
|
||||||
|
`checkh(s_array[1].arr[2], 2);
|
||||||
|
`checkh(my_struct.x, 0);
|
||||||
|
end
|
||||||
|
else if (cyc == 5) begin
|
||||||
|
release s_array[1].x;
|
||||||
|
release s_array[1].arr[2];
|
||||||
|
release my_struct.x;
|
||||||
|
end
|
||||||
|
else if (cyc == 6) begin
|
||||||
|
`checkh(s_array[1].x, 0);
|
||||||
|
s_array[1].x = 1;
|
||||||
|
`checkh(s_array[1].arr[2], 2);
|
||||||
|
s_array[1].arr[2] = 4;
|
||||||
|
`checkh(my_struct.x, 0);
|
||||||
|
my_struct.x <= 1;
|
||||||
|
end
|
||||||
|
else if (cyc == 7) begin
|
||||||
|
`checkh(s_array[1].x, 1);
|
||||||
|
`checkh(s_array[1].arr[2], 4);
|
||||||
|
`checkh(my_struct.x, 1);
|
||||||
|
end
|
||||||
|
else if (cyc == 8) begin
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
@ -1,17 +1,8 @@
|
||||||
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:23:8: Unsupported: Force of unpacked array variable with elements of complex data type
|
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:22:7: Unsupported: Force of variable with >= 1000 unpacked elements
|
||||||
23 | real r_array[2];
|
|
||||||
| ^~~~~~~
|
|
||||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
|
||||||
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:22:7: Unsupported: Force of unpacked array variable with >= 1000 elements
|
|
||||||
22 | bit big_array[40][40][40];
|
22 | bit big_array[40][40][40];
|
||||||
| ^~~~~~~~~
|
| ^~~~~~~~~
|
||||||
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:21:12: Unsupported: Force of unpacked array variable with >= 1000 elements
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
|
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:21:12: Unsupported: Force of variable with >= 1000 unpacked elements
|
||||||
21 | struct_t s_array[3000];
|
21 | struct_t s_array[3000];
|
||||||
| ^~~~~~~
|
| ^~~~~~~
|
||||||
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:21:12: Unsupported: Force of unpacked array variable with elements of complex data type
|
|
||||||
21 | struct_t s_array[3000];
|
|
||||||
| ^~~~~~~
|
|
||||||
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:24:12: Unsupported: Force of unpacked struct / union variable
|
|
||||||
24 | struct_t my_struct;
|
|
||||||
| ^~~~~~~~~
|
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// DESCRIPTION: Verilator: Verilog Test module
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
//
|
//
|
||||||
// This file ONLY is placed under the Creative Commons Public Domain
|
// This file ONLY is placed under the Creative Commons Public Domain
|
||||||
// SPDX-FileCopyrightText: 2025 Antmicro
|
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||||
// SPDX-License-Identifier: CC0-1.0
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
// verilog_format: off
|
// verilog_format: off
|
||||||
|
|
@ -21,7 +21,6 @@ module t (
|
||||||
struct_t s_array[3000];
|
struct_t s_array[3000];
|
||||||
bit big_array[40][40][40];
|
bit big_array[40][40][40];
|
||||||
real r_array[2];
|
real r_array[2];
|
||||||
struct_t my_struct;
|
|
||||||
|
|
||||||
// Test loop
|
// Test loop
|
||||||
always @(posedge clk) begin
|
always @(posedge clk) begin
|
||||||
|
|
@ -30,19 +29,16 @@ module t (
|
||||||
r_array[0] <= 1;
|
r_array[0] <= 1;
|
||||||
big_array[1][2][3] <= 1;
|
big_array[1][2][3] <= 1;
|
||||||
s_array[1].x <= 1;
|
s_array[1].x <= 1;
|
||||||
my_struct.x <= 1;
|
|
||||||
end
|
end
|
||||||
else if (cyc == 1) begin
|
else if (cyc == 1) begin
|
||||||
`checkr(r_array[0], 1);
|
`checkr(r_array[0], 1);
|
||||||
`checkr(big_array[1][2][3], 1);
|
`checkr(big_array[1][2][3], 1);
|
||||||
`checkh(s_array[1].x, 1);
|
`checkh(s_array[1].x, 1);
|
||||||
`checkh(my_struct.x, 1);
|
|
||||||
end
|
end
|
||||||
else if (cyc == 2) begin
|
else if (cyc == 2) begin
|
||||||
force r_array[0] = 0;
|
force r_array[0] = 0;
|
||||||
force big_array[1][2][3] = 0;
|
force big_array[1][2][3] = 0;
|
||||||
force s_array[1].x = 0;
|
force s_array[1].x = 0;
|
||||||
force my_struct.x = 0;
|
|
||||||
end
|
end
|
||||||
else if (cyc == 3) begin
|
else if (cyc == 3) begin
|
||||||
`checkr(r_array[0], 0);
|
`checkr(r_array[0], 0);
|
||||||
|
|
@ -51,20 +47,16 @@ module t (
|
||||||
big_array[1][2][3] <= 1;
|
big_array[1][2][3] <= 1;
|
||||||
`checkh(s_array[1].x, 0);
|
`checkh(s_array[1].x, 0);
|
||||||
s_array[1].x <= 1;
|
s_array[1].x <= 1;
|
||||||
`checkh(my_struct.x, 0);
|
|
||||||
my_struct.x <= 1;
|
|
||||||
end
|
end
|
||||||
else if (cyc == 4) begin
|
else if (cyc == 4) begin
|
||||||
`checkr(r_array[0], 0);
|
`checkr(r_array[0], 0);
|
||||||
`checkr(big_array[1][2][3], 0);
|
`checkr(big_array[1][2][3], 0);
|
||||||
`checkh(s_array[1].x, 0);
|
`checkh(s_array[1].x, 0);
|
||||||
`checkh(my_struct.x, 0);
|
|
||||||
end
|
end
|
||||||
else if (cyc == 5) begin
|
else if (cyc == 5) begin
|
||||||
release r_array[0];
|
release r_array[0];
|
||||||
release big_array[1][2][3];
|
release big_array[1][2][3];
|
||||||
release s_array[1].x;
|
release s_array[1].x;
|
||||||
release my_struct.x;
|
|
||||||
end
|
end
|
||||||
else if (cyc == 6) begin
|
else if (cyc == 6) begin
|
||||||
`checkr(r_array[0], 0);
|
`checkr(r_array[0], 0);
|
||||||
|
|
@ -73,14 +65,11 @@ module t (
|
||||||
big_array[1][2][3] <= 1;
|
big_array[1][2][3] <= 1;
|
||||||
`checkh(s_array[1].x, 0);
|
`checkh(s_array[1].x, 0);
|
||||||
s_array[1].x <= 1;
|
s_array[1].x <= 1;
|
||||||
`checkh(my_struct.x, 0);
|
|
||||||
my_struct.x <= 1;
|
|
||||||
end
|
end
|
||||||
else if (cyc == 7) begin
|
else if (cyc == 7) begin
|
||||||
`checkr(r_array[0], 1);
|
`checkr(r_array[0], 1);
|
||||||
`checkr(big_array[1][2][3], 1);
|
`checkr(big_array[1][2][3], 1);
|
||||||
`checkh(s_array[1].x, 1);
|
`checkh(s_array[1].x, 1);
|
||||||
`checkh(my_struct.x, 1);
|
|
||||||
end
|
end
|
||||||
else if (cyc == 8) begin
|
else if (cyc == 8) begin
|
||||||
$write("*-* All Finished *-*\n");
|
$write("*-* All Finished *-*\n");
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,8 @@
|
||||||
8 | string str /*verilator forceable*/;
|
8 | string str /*verilator forceable*/;
|
||||||
| ^~~
|
| ^~~
|
||||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||||
|
%Error-UNSUPPORTED: t/t_forceable_string_bad.v:8:10: Forcing variable of unsupported type: BASICDTYPE 'string'
|
||||||
|
8 | string str /*verilator forceable*/;
|
||||||
|
| ^~~
|
||||||
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue