Add PROCINITASSIGN on initial assignments to process variables (#2481).

This commit is contained in:
Wilson Snyder
2025-04-30 22:00:06 -04:00
parent 38dd9a344e
commit 8b52bd817f
19 changed files with 292 additions and 59 deletions
+6 -4
View File
@@ -129,6 +129,7 @@ public:
PINNOTFOUND, // instance port name not found in it's module
PKGNODECL, // Error: Package/class needs to be predeclared
PREPROCZERO, // Preprocessor expression with zero
PROCASSINIT, // Procedural assignment versus initialization
PROCASSWIRE, // Procedural assignment on wire
PROFOUTOFDATE, // Profile data out of date
PROTECTED, // detected `pragma protected
@@ -206,7 +207,7 @@ public:
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP",
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NONSTD", "NULLPORT", "PINCONNECTEMPTY",
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSWIRE",
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSINIT", "PROCASSWIRE",
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
"SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR",
"STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
@@ -259,9 +260,10 @@ public:
return (m_e == ASSIGNDLY // More than style, but for backward compatibility
|| m_e == BLKSEQ || m_e == DECLFILENAME || m_e == DEFPARAM || m_e == EOFNEWLINE
|| m_e == GENUNNAMED || m_e == IMPORTSTAR || m_e == INCABSPATH
|| m_e == PINCONNECTEMPTY || m_e == PINNOCONNECT || m_e == SYNCASYNCNET
|| m_e == UNDRIVEN || m_e == UNUSEDGENVAR || m_e == UNUSEDLOOP
|| m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL || m_e == VARHIDDEN);
|| m_e == PINCONNECTEMPTY || m_e == PINNOCONNECT || m_e == PROCASSINIT
|| m_e == SYNCASYNCNET || m_e == UNDRIVEN || m_e == UNUSEDGENVAR
|| m_e == UNUSEDLOOP || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL
|| m_e == VARHIDDEN);
}
// Warnings that are unused only
bool unusedError() const VL_MT_SAFE {
+37 -3
View File
@@ -46,6 +46,8 @@ class UndrivenVarEntry final {
const FileLine* m_alwCombFileLinep = nullptr; // File line of always_comb of var if driven
// within always_comb, else nullptr
const AstNodeVarRef* m_nodep = nullptr; // varref if driven, else nullptr
const AstNode* m_initStaticp = nullptr; // varref if in InitialStatic driven
const AstNode* m_procWritep = nullptr; // varref if written in process
const FileLine* m_nodeFileLinep = nullptr; // File line of varref if driven, else nullptr
bool m_underGen = false; // Under a generate
@@ -129,6 +131,11 @@ public:
m_alwCombp = alwCombp;
m_alwCombFileLinep = fileLinep;
}
const AstNode* initStaticp() const { return m_initStaticp; }
void initStaticp(const AstNode* nodep) { m_initStaticp = nodep; }
const AstNode* procWritep() const { return m_procWritep; }
void procWritep(const AstNode* nodep) { m_procWritep = nodep; }
void underGenerate() { m_underGen = true; }
bool isUnderGen() const { return m_underGen; }
bool isDrivenWhole() const { return m_wholeFlags[FLAG_DRIVEN]; }
@@ -172,6 +179,18 @@ public:
// Combine bits into overall state
AstVar* const nodep = m_varp;
if (initStaticp() && procWritep() && !nodep->isClassMember() && !nodep->isFuncLocal()) {
initStaticp()->v3warn(
PROCASSINIT,
"Procedural assignment to declaration with initial value: "
<< nodep->prettyNameQ() << '\n'
<< initStaticp()->warnMore() << "... Location of variable initialization\n"
<< initStaticp()->warnContextPrimary() << '\n'
<< procWritep()->warnOther() << "... Location of variable process write\n"
<< procWritep()->warnMore()
<< "... Perhaps should initialize instead using a reset in this process\n"
<< procWritep()->warnContextSecondary());
}
if (nodep->isGenVar()) { // Genvar
if (!nodep->isIfaceRef() && !nodep->isUsedParam() && !unusedMatch(nodep)) {
nodep->v3warn(UNUSEDGENVAR, "Genvar is not used: " << nodep->prettyNameQ());
@@ -277,10 +296,12 @@ class UndrivenVisitor final : public VNVisitorConst {
std::array<std::vector<UndrivenVarEntry*>, 3> m_entryps; // Nodes to delete when finished
bool m_inBBox = false; // In black box; mark as driven+used
bool m_inContAssign = false; // In continuous assignment
bool m_inInitialStatic = false; // In InitialStatic
bool m_inProcAssign = false; // In procedural assignment
bool m_inFTaskRef = false; // In function or task call
bool m_inInoutOrRefPin = false; // Connected to pin that is inout
const AstNodeFTask* m_taskp = nullptr; // Current task
const AstAlways* m_alwaysp = nullptr; // Current always of either type
const AstAlways* m_alwaysCombp = nullptr; // Current always if combo, otherwise nullptr
// METHODS
@@ -384,9 +405,8 @@ class UndrivenVisitor final : public VNVisitorConst {
nodep->v3warn(PROCASSWIRE, "Procedural assignment to wire, perhaps intended var"
<< " (IEEE 1800-2023 6.5): "
<< nodep->prettyNameQ());
}
if (m_inContAssign && !nodep->varp()->varType().isContAssignable()
&& !nodep->fileline()->language().systemVerilog()) {
} else if (m_inContAssign && !nodep->varp()->varType().isContAssignable()
&& !nodep->fileline()->language().systemVerilog()) {
nodep->v3warn(CONTASSREG,
"Continuous assignment to reg, perhaps intended wire"
<< " (IEEE 1364-2005 6.1; Verilog only, legal in SV): "
@@ -448,6 +468,13 @@ class UndrivenVisitor final : public VNVisitorConst {
if (m_alwaysCombp)
entryp->drivenAlwaysCombWhole(m_alwaysCombp, m_alwaysCombp->fileline());
}
if (nodep->access().isWriteOrRW()) {
UINFO(1, "ww is=" << m_inInitialStatic << " ipa=" << m_inProcAssign << " " << nodep
<< endl);
if (m_inInitialStatic && !entryp->initStaticp()) entryp->initStaticp(nodep);
if (m_alwaysp && m_inProcAssign && !entryp->procWritep())
entryp->procWritep(nodep);
}
if (m_inBBox || nodep->access().isReadOrRW()
|| fdrv
// Inouts have only isWrite set, as we don't have more
@@ -480,9 +507,16 @@ class UndrivenVisitor final : public VNVisitorConst {
m_inContAssign = true;
iterateChildrenConst(nodep);
}
void visit(AstInitialStatic* nodep) override {
VL_RESTORER(m_inInitialStatic);
m_inInitialStatic = true;
iterateChildrenConst(nodep);
}
void visit(AstAlways* nodep) override {
VL_RESTORER(m_alwaysp);
VL_RESTORER(m_alwaysCombp);
AstNode::user2ClearTree();
m_alwaysp = nodep;
if (nodep->keyword() == VAlwaysKwd::ALWAYS_COMB) {
UINFO(9, " " << nodep << endl);
m_alwaysCombp = nodep;