diff --git a/src/V3Cdc.cpp b/src/V3Cdc.cpp index 4ed93e882..0749d2498 100644 --- a/src/V3Cdc.cpp +++ b/src/V3Cdc.cpp @@ -58,21 +58,21 @@ public: }; //###################################################################### -// Support classes +// Graph support classes class CdcEitherVertex : public V3GraphVertex { AstScope* m_scopep; AstNode* m_nodep; AstSenTree* m_srcDomainp; - bool m_srcDomainSet; AstSenTree* m_dstDomainp; + bool m_srcDomainSet; bool m_dstDomainSet; bool m_asyncPath; public: CdcEitherVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep) : V3GraphVertex(graphp), m_scopep(scopep), m_nodep(nodep) - , m_srcDomainp(NULL), m_srcDomainSet(false) - , m_dstDomainp(NULL), m_dstDomainSet(false) + , m_srcDomainp(NULL), m_dstDomainp(NULL) + , m_srcDomainSet(false), m_dstDomainSet(false) , m_asyncPath(false) {} virtual ~CdcEitherVertex() {} // Accessors @@ -121,6 +121,43 @@ public: void clearSafe(AstNode* nodep) { m_safe = false; nodep->user3(true); } }; +//###################################################################### + +class CdcDumpVisitor : public CdcBaseVisitor { +private: + // NODE STATE + //Entire netlist: + // {statement}Node::user3p -> bool, indicating not safe + ofstream* m_ofp; // Output file + string m_prefix; + + virtual void visit(AstNode* nodep, AstNUser*) { + *m_ofp<user3()) *m_ofp<<" %%"; + else *m_ofp<<" "; + *m_ofp<prettyTypeName()<<" "<op1p()->iterateAndNext(*this); + m_prefix = lastPrefix + "2:"; + nodep->op2p()->iterateAndNext(*this); + m_prefix = lastPrefix + "3:"; + nodep->op3p()->iterateAndNext(*this); + m_prefix = lastPrefix + "4:"; + nodep->op4p()->iterateAndNext(*this); + m_prefix = lastPrefix; + } + +public: + // CONSTUCTORS + CdcDumpVisitor(AstNode* nodep, ofstream* ofp, const string& prefix) { + m_ofp = ofp; + m_prefix = prefix; + nodep->accept(*this); + } + virtual ~CdcDumpVisitor() {} +}; + //###################################################################### // Cdc class functions @@ -181,14 +218,151 @@ private: return vertexp; } + void warnAndFile(AstNode* nodep, V3ErrorCode code, const string& msg) { + static bool told_file = false; + nodep->v3warnCode(code,msg); + if (!told_file) { + told_file = 1; + cerr<fileline()<<" "<fileline()->warnIsOff(V3ErrorCode::CDCRSTLOGIC)) { + UINFO(9,"Clear safe "<clearSafe(nodep); + } + } + + string spaces(int level) { string out; while (level--) out+=" "; return out; } + string pad(unsigned column, const string& in) { string out = in; while (out.length()6) m_graph.dump(); + if (debug()>6) m_graph.dumpDotFilePrefixed("cdc_pre"); + m_graph.removeRedundantEdgesSum(&V3GraphEdge::followAlwaysTrue); + m_graph.dumpDotFilePrefixed("cdc_simp"); + // + analyzeReset(); + } + + //---------------------------------------- + // RESET REPORT + + void analyzeReset() { + // Find all async reset wires, and trace backwards + // userClearVertices is very slow, so we use a generation count instead + m_graph.userClearVertices(); // user1: uint32_t - was analyzed generation + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (CdcVarVertex* vvertexp = dynamic_cast(itp)) { + if (vvertexp->cntAsyncRst()) { + m_userGeneration++; // Effectively a userClearVertices() + UINFO(9, " Trace One async: "<user()>=m_userGeneration) return false; // Processed - prevent loop + vertexp->user(m_userGeneration); + + if (CdcLogicVertex* vvertexp = dynamic_cast(vertexp)) { + // Any logic considered bad, at the moment, anyhow + if (!vvertexp->safe() && !mark_outp) mark_outp = vvertexp; + // And keep tracing back so the user can understand what's up + } + 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; + // 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); + //} + return false; + } + } + + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { + CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp(); + CdcEitherVertex* submarkp = traceAsyncRecurse(eFromVertexp, mark); + if (submarkp && !mark_outp) mark_outp = submarkp; + } + + if (mark) vertexp->asyncPath(true); + return mark_outp; + } + + void dumpAsync(CdcVarVertex* vertexp, CdcEitherVertex* markp) { + AstNode* nodep = vertexp->varScp(); + *m_ofp<<"\n"; + *m_ofp<<"\n"; + AstNode* targetp = vertexp->nodep(); + if (V3GraphEdge* edgep = vertexp->outBeginp()) { + CdcEitherVertex* eToVertexp = (CdcEitherVertex*)edgep->top(); + targetp = eToVertexp->nodep(); + } + 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 + vertexp->user(m_userGeneration); + if (!vertexp->asyncPath()) return; // Not part of path + + *m_ofp<nodep(); + string front = pad(40,nodep->fileline()->ascii()+":"); + front += " "+prefix+" "; + if (nodep->castVarScope()) *m_ofp<prettyName()<inBeginp(); edgep; edgep = edgep->inNextp()) { + CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp(); + dumpAsyncRecurse(eFromVertexp, blank+" +--", blank+" | "); + } + } + + //---------------------------------------- + // EDGE REPORTS void edgeReport() { // Make report of all signal names and what clock edges they have @@ -238,8 +412,6 @@ private: } } - string spaces(int level) { string out; while (level--) out+=" "; return out; } - void edgeDomainRecurse(CdcEitherVertex* vertexp, bool traceDests, int level) { // Scan back to inputs/outputs, flops, and compute clock domain information UINFO(8,spaces(level)<<" Tracein "<v3warnCode(code,msg); - if (!told_file) { - told_file = 1; - cerr<fileline()<<" "<fileline()->warnIsOff(V3ErrorCode::CDCRSTLOGIC)) { - UINFO(9,"Clear safe "<clearSafe(nodep); - } - } - // VISITORS virtual void visit(AstNodeModule* nodep, AstNUser*) { m_modp = nodep; @@ -477,158 +626,6 @@ public: //###################################################################### // Cdc class functions -class CdcDumpVisitor : public CdcBaseVisitor { -private: - // NODE STATE - //Entire netlist: - // {statement}Node::user3p -> bool, indicating not safe - ofstream* m_ofp; // Output file - string m_prefix; - - virtual void visit(AstNode* nodep, AstNUser*) { - *m_ofp<user3()) *m_ofp<<" %%"; - else *m_ofp<<" "; - *m_ofp<prettyTypeName()<<" "<op1p()->iterateAndNext(*this); - m_prefix = lastPrefix + "2:"; - nodep->op2p()->iterateAndNext(*this); - m_prefix = lastPrefix + "3:"; - nodep->op3p()->iterateAndNext(*this); - m_prefix = lastPrefix + "4:"; - nodep->op4p()->iterateAndNext(*this); - m_prefix = lastPrefix; - } - -public: - // CONSTUCTORS - CdcDumpVisitor(AstNode* nodep, ofstream* ofp, const string& prefix) { - m_ofp = ofp; - m_prefix = prefix; - nodep->accept(*this); - } - virtual ~CdcDumpVisitor() {} -}; - -//###################################################################### - -void CdcVisitor::analyze() { - UINFO(3,__FUNCTION__<<": "<6) m_graph.dump(); - if (debug()>6) m_graph.dumpDotFilePrefixed("cdc_pre"); - m_graph.removeRedundantEdgesSum(&V3GraphEdge::followAlwaysTrue); - m_graph.dumpDotFilePrefixed("cdc_simp"); - // - analyzeReset(); -} - -void CdcVisitor::analyzeReset() { - // Find all async reset wires, and trace backwards - // userClearVertices is very slow, so we use a generation count instead - m_graph.userClearVertices(); // user1: uint32_t - was analyzed generation - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (CdcVarVertex* vvertexp = dynamic_cast(itp)) { - if (vvertexp->cntAsyncRst()) { - m_userGeneration++; // Effectively a userClearVertices() - UINFO(9, " Trace One async: "<user()>=m_userGeneration) return false; // Processed - prevent loop - vertexp->user(m_userGeneration); - - if (CdcLogicVertex* vvertexp = dynamic_cast(vertexp)) { - // Any logic considered bad, at the moment, anyhow - if (!vvertexp->safe() && !mark_outp) mark_outp = vvertexp; - // And keep tracing back so the user can understand what's up - } - 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; - // Also ok if from flop - if (vvertexp->fromFlop()) return false; - } - - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp(); - CdcEitherVertex* submarkp = traceAsyncRecurse(eFromVertexp, mark); - if (submarkp && !mark_outp) mark_outp = submarkp; - } - - if (mark) vertexp->asyncPath(true); - return mark_outp; -} - -//---------------------------------------------------------------------- - -void CdcVisitor::dumpAsync(CdcVarVertex* vertexp, CdcEitherVertex* markp) { - AstNode* nodep = vertexp->varScp(); - *m_ofp<<"\n"; - *m_ofp<<"\n"; - AstNode* targetp = vertexp->nodep(); - if (V3GraphEdge* edgep = vertexp->outBeginp()) { - CdcEitherVertex* eToVertexp = (CdcEitherVertex*)edgep->top(); - targetp = eToVertexp->nodep(); - } - 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 - vertexp->user(m_userGeneration); - if (!vertexp->asyncPath()) return; // Not part of path - - *m_ofp<nodep(); - string front = pad(40,nodep->fileline()->ascii()+":"); - front += " "+prefix+" "; - if (nodep->castVarScope()) *m_ofp<prettyName()<inBeginp(); edgep; edgep = edgep->inNextp()) { - CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp(); - dumpAsyncRecurse(eFromVertexp, blank+" +--", blank+" | "); - } -} - -//###################################################################### -// Cdc class functions - void V3Cdc::cdcAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<