diff --git a/src/V3Cdc.cpp b/src/V3Cdc.cpp index 0749d2498..7a43c4652 100644 --- a/src/V3Cdc.cpp +++ b/src/V3Cdc.cpp @@ -211,8 +211,17 @@ private: UINFO(6,"New vertex "<user1p(vertexp); - if (varscp->varp()->isIO() && varscp->scopep()->isTop()) {} if (varscp->varp()->isUsedClock()) {} + if (varscp->varp()->isPrimaryIO()) { + // Create IO vertex - note it's relative to the pointed to var, not where we are now + // This allows reporting to easily print the input statement + CdcLogicVertex* ioVertexp = new CdcLogicVertex(&m_graph, varscp->scopep(), varscp->varp(), NULL); + if (varscp->varp()->isInput()) { + new V3GraphEdge(&m_graph, ioVertexp, vertexp, 1); + } else { + new V3GraphEdge(&m_graph, vertexp, ioVertexp, 1); + } + } } if (m_senNumber > 1) vertexp->cntAsyncRst(vertexp->cntAsyncRst()+1); return vertexp; @@ -231,9 +240,11 @@ private: void clearNodeSafe(AstNode* nodep) { // Need to not clear if warnings are off (rather than when report it) // as bypassing this warning may turn up another path that isn't warning off'ed. - if (m_logicVertexp && !nodep->fileline()->warnIsOff(V3ErrorCode::CDCRSTLOGIC)) { - UINFO(9,"Clear safe "<clearSafe(nodep); + if (!m_domainp || m_domainp->hasCombo()) { // Source flop logic in a posedge block is OK for reset (not async though) + if (m_logicVertexp && !nodep->fileline()->warnIsOff(V3ErrorCode::CDCRSTLOGIC)) { + UINFO(9,"Clear safe "<clearSafe(nodep); + } } } @@ -283,12 +294,14 @@ private: CdcEitherVertex* traceAsyncRecurse(CdcEitherVertex* vertexp, bool mark) { // Return vertex of any dangerous stuff attached, or NULL if OK - // If mark, also mark the output even if nothing dangerous below + if (vertexp->user()>=m_userGeneration) return false; // Processed - prevent loop + vertexp->user(m_userGeneration); + CdcEitherVertex* mark_outp = NULL; UINFO(9," Trace: "<user()>=m_userGeneration) return false; // Processed - prevent loop - vertexp->user(m_userGeneration); + // Clear out in prep for marking next path + if (!mark) vertexp->asyncPath(false); if (CdcLogicVertex* vvertexp = dynamic_cast(vertexp)) { // Any logic considered bad, at the moment, anyhow @@ -298,13 +311,20 @@ private: else if (CdcVarVertex* vvertexp = dynamic_cast(vertexp)) { if (mark) vvertexp->asyncPath(true); // If primary I/O, it's ok here back - if (vvertexp->varScp()->varp()->isInput()) return false; + if (vvertexp->varScp()->varp()->isPrimaryIn()) { + // Show the source "input" statement if it exists + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { + CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp(); + eFromVertexp->asyncPath(true); + } + return false; + } // Also ok if from flop, but partially trace the flop so more obvious to users if (vvertexp->fromFlop()) { - //for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - // CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp(); - // eFromVertexp->asyncPath(true); - //} + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { + CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp(); + eFromVertexp->asyncPath(true); + } return false; } } @@ -323,42 +343,49 @@ private: AstNode* nodep = vertexp->varScp(); *m_ofp<<"\n"; *m_ofp<<"\n"; - AstNode* targetp = vertexp->nodep(); + CdcEitherVertex* targetp = vertexp; // One example destination flop (of possibly many) if (V3GraphEdge* edgep = vertexp->outBeginp()) { CdcEitherVertex* eToVertexp = (CdcEitherVertex*)edgep->top(); - targetp = eToVertexp->nodep(); + targetp = eToVertexp; } warnAndFile(markp->nodep(),V3ErrorCode::CDCRSTLOGIC,"Logic in path that feeds async reset, via signal: "+nodep->prettyName()); - *m_ofp<<"Fanout: "<cntAsyncRst()<<" Target: "<fileline()<user()>=m_userGeneration) return; // Processed - prevent loop + if (vertexp->user()>=m_userGeneration) return false; // Processed - prevent loop vertexp->user(m_userGeneration); - if (!vertexp->asyncPath()) return; // Not part of path + if (!vertexp->asyncPath() && level!=0) return false; // Not part of path - *m_ofp<inBeginp(); edgep; edgep = edgep->inNextp()) { + CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp(); + if (dumpAsyncRecurse(eFromVertexp, cont, nextsep, level+1)) { + nextsep = " | "; + } + } // Dump single variable/logic block // See also OrderGraph::loopsVertexCb(V3GraphVertex* vertexp) AstNode* nodep = vertexp->nodep(); - string front = pad(40,nodep->fileline()->ascii()+":"); - front += " "+prefix+" "; - if (nodep->castVarScope()) *m_ofp<prettyName()<fileline()->ascii()+":")+" "+prefix+" +- "; + if (nodep->castVarScope()) { + *m_ofp<prettyName()<srcDomainp(), true); if (debug()) { CdcDumpVisitor visitor (nodep, m_ofp, front+"DBG: "); } } - // Now do the other logic in the path - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp(); - dumpAsyncRecurse(eFromVertexp, blank+" +--", blank+" | "); - } + nextsep = " | "; + if (level) *m_ofp<fail()) v3fatalSrc("Can't write "<accept(*this); analyze(); //edgeReport(); // Not useful at the moment - if (0) { *m_ofp<<"\nDBG-test-dumper\n"; V3EmitV::verilogPrefixedTree(nodep, *m_ofp, "DBG ",true); *m_ofp<bodysp()->iterateAndNext(*this); puts(")\n"); + nodep->bodysp()->iterateAndNext(*this); + puts(")"); } // Operators @@ -488,9 +489,9 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { } public: - EmitVBaseVisitor() { + EmitVBaseVisitor(AstSenTree* domainp=NULL) { // Domain for printing one a ALWAYS under a ACTIVE m_suppressSemi = false; - m_sensesp = NULL; + m_sensesp = domainp; } virtual ~EmitVBaseVisitor() {} }; @@ -597,8 +598,9 @@ class EmitVPrefixedVisitor : public EmitVBaseVisitor { } public: - EmitVPrefixedVisitor(AstNode* nodep, ostream& os, const string& prefix, bool user3mark) - : m_formatter(os, prefix), m_user3mark(user3mark) { + EmitVPrefixedVisitor(AstNode* nodep, ostream& os, const string& prefix, + AstSenTree* domainp, bool user3mark) + : EmitVBaseVisitor(domainp), m_formatter(os, prefix), m_user3mark(user3mark) { if (user3mark) { AstUser3InUse::check(); } nodep->accept(*this); } @@ -631,6 +633,7 @@ void V3EmitV::verilogForTree(AstNode* nodep, ostream& os) { EmitVStreamVisitor(nodep, os); } -void V3EmitV::verilogPrefixedTree(AstNode* nodep, ostream& os, const string& prefix, bool user3mark) { - EmitVPrefixedVisitor(nodep, os, prefix, user3mark); +void V3EmitV::verilogPrefixedTree(AstNode* nodep, ostream& os, const string& prefix, + AstSenTree* domainp, bool user3mark) { + EmitVPrefixedVisitor(nodep, os, prefix, domainp, user3mark); } diff --git a/src/V3EmitV.h b/src/V3EmitV.h index 47056839d..6ccfe35b8 100644 --- a/src/V3EmitV.h +++ b/src/V3EmitV.h @@ -33,7 +33,8 @@ class V3EmitV { public: static void emitv(); static void verilogForTree(AstNode* nodep, ostream& os=cout); - static void verilogPrefixedTree(AstNode* nodep, ostream& os, const string& prefix, bool user3percent); + static void verilogPrefixedTree(AstNode* nodep, ostream& os, const string& prefix, + AstSenTree* domainp, bool user3percent); }; #endif // Guard diff --git a/test_regress/t/t_cdc_async_bad.pl b/test_regress/t/t_cdc_async_bad.pl index 6528c54ff..b4d96b848 100755 --- a/test_regress/t/t_cdc_async_bad.pl +++ b/test_regress/t/t_cdc_async_bad.pl @@ -15,6 +15,8 @@ compile ( '%Warning-CDCRSTLOGIC: t/t_cdc_async_bad.v:\d+: Logic in path that feeds async reset, via signal: TOP->v.rst2_bad_n %Warning-CDCRSTLOGIC: Use "/\* verilator lint_off CDCRSTLOGIC \*/" and lint_on around source to disable this message. %Warning-CDCRSTLOGIC: See details in obj_dir/t_cdc_async_bad/Vt_cdc_async_bad__cdc.txt +%Warning-CDCRSTLOGIC: t/t_cdc_async_bad.v:\d+: Logic in path that feeds async reset, via signal: TOP->v.rst6a_bad_n +%Warning-CDCRSTLOGIC: t/t_cdc_async_bad.v:\d+: Logic in path that feeds async reset, via signal: TOP->v.rst6b_bad_n %Warning-CDCRSTLOGIC: t/t_cdc_async_bad.v:\d+: Logic in path that feeds async reset, via signal: TOP->v.rst3_bad_n %Error: Exiting due to.*', ); diff --git a/test_regress/t/t_cdc_async_bad.v b/test_regress/t/t_cdc_async_bad.v index 5587464b9..f7d4c96d5 100644 --- a/test_regress/t/t_cdc_async_bad.v +++ b/test_regress/t/t_cdc_async_bad.v @@ -5,7 +5,7 @@ module t (/*AUTOARG*/ // Outputs - q0, q1, q2, q3, q4, q5, + q0, q1, q2, q3, q4, q5, q6a, q6b, // Inputs clk, d, rst0_n ); @@ -47,6 +47,15 @@ module t (/*AUTOARG*/ output wire q5; Flop flop5 (.q(q5), .rst_n(rst5_waive_n), .clk(clk), .d(d)); + // Bad - for graph test - logic feeds two signals, three destinations + wire rst6_bad_n = rst0_n ^ rst1_n; + wire rst6a_bad_n = rst6_bad_n ^ $c1("0"); // $c prevents optimization + wire rst6b_bad_n = rst6_bad_n ^ $c1("1"); + output wire q6a; + output wire q6b; + Flop flop6a (.q(q6a), .rst_n(rst6a_bad_n), .clk(clk), .d(d)); + Flop flop6v (.q(q6b), .rst_n(rst6b_bad_n), .clk(clk), .d(d)); + initial begin $display("%%Error: Not a runnable test"); $stop;