2012-04-13 01:08:20 +00:00
|
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2010-01-07 21:41:19 +00:00
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Clock Domain Crossing Lint
|
|
|
|
|
//
|
|
|
|
|
// Code available from: http://www.veripool.org/verilator
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2017-01-15 17:09:59 +00:00
|
|
|
|
// Copyright 2003-2017 by Wilson Snyder. This program is free software; you can
|
2010-01-07 21:41:19 +00:00
|
|
|
|
// 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.
|
|
|
|
|
//
|
|
|
|
|
// Verilator is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Cdc's Transformations:
|
|
|
|
|
//
|
|
|
|
|
// Create V3Graph-ish graph
|
|
|
|
|
// Find all negedge reset flops
|
|
|
|
|
// Trace back to previous flop
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdarg>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <deque>
|
|
|
|
|
#include <list>
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Cdc.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
#include "V3Graph.h"
|
|
|
|
|
#include "V3Const.h"
|
|
|
|
|
#include "V3EmitV.h"
|
|
|
|
|
#include "V3File.h"
|
|
|
|
|
|
2011-04-18 14:47:02 +00:00
|
|
|
|
#define CDC_WEIGHT_ASYNC 0x1000 // Weight for edges that feed async logic
|
|
|
|
|
|
2010-01-07 21:41:19 +00:00
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class CdcBaseVisitor : public AstNVisitor {
|
|
|
|
|
public:
|
|
|
|
|
static int debug() {
|
|
|
|
|
static int level = -1;
|
|
|
|
|
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
|
|
|
|
return level;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
2010-01-09 02:31:52 +00:00
|
|
|
|
// Graph support classes
|
2010-01-07 21:41:19 +00:00
|
|
|
|
|
|
|
|
|
class CdcEitherVertex : public V3GraphVertex {
|
|
|
|
|
AstScope* m_scopep;
|
|
|
|
|
AstNode* m_nodep;
|
|
|
|
|
AstSenTree* m_srcDomainp;
|
|
|
|
|
AstSenTree* m_dstDomainp;
|
2011-01-19 02:37:53 +00:00
|
|
|
|
bool m_srcDomainSet:1;
|
|
|
|
|
bool m_dstDomainSet:1;
|
|
|
|
|
bool m_asyncPath:1;
|
2010-01-07 21:41:19 +00:00
|
|
|
|
public:
|
|
|
|
|
CdcEitherVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep)
|
|
|
|
|
: V3GraphVertex(graphp), m_scopep(scopep), m_nodep(nodep)
|
2010-01-09 02:31:52 +00:00
|
|
|
|
, m_srcDomainp(NULL), m_dstDomainp(NULL)
|
|
|
|
|
, m_srcDomainSet(false), m_dstDomainSet(false)
|
2010-01-07 21:41:19 +00:00
|
|
|
|
, m_asyncPath(false) {}
|
|
|
|
|
virtual ~CdcEitherVertex() {}
|
|
|
|
|
// Accessors
|
|
|
|
|
AstScope* scopep() const { return m_scopep; }
|
|
|
|
|
AstNode* nodep() const { return m_nodep; }
|
|
|
|
|
AstSenTree* srcDomainp() const { return m_srcDomainp; }
|
|
|
|
|
void srcDomainp(AstSenTree* nodep) { m_srcDomainp = nodep; }
|
|
|
|
|
bool srcDomainSet() const { return m_srcDomainSet; }
|
|
|
|
|
void srcDomainSet(bool flag) { m_srcDomainSet = flag; }
|
|
|
|
|
AstSenTree* dstDomainp() const { return m_dstDomainp; }
|
|
|
|
|
void dstDomainp(AstSenTree* nodep) { m_dstDomainp = nodep; }
|
|
|
|
|
bool dstDomainSet() const { return m_dstDomainSet; }
|
|
|
|
|
void dstDomainSet(bool flag) { m_dstDomainSet = flag; }
|
|
|
|
|
bool asyncPath() const { return m_asyncPath; }
|
|
|
|
|
void asyncPath(bool flag) { m_asyncPath = flag; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class CdcVarVertex : public CdcEitherVertex {
|
|
|
|
|
AstVarScope* m_varScp;
|
|
|
|
|
int m_cntAsyncRst;
|
|
|
|
|
bool m_fromFlop;
|
|
|
|
|
public:
|
|
|
|
|
CdcVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
|
|
|
|
: CdcEitherVertex(graphp, scopep, varScp), m_varScp(varScp), m_cntAsyncRst(0), m_fromFlop(false) {}
|
|
|
|
|
virtual ~CdcVarVertex() {}
|
|
|
|
|
// Accessors
|
|
|
|
|
AstVarScope* varScp() const { return m_varScp; }
|
|
|
|
|
virtual string name() const { return (cvtToStr((void*)m_varScp)+" "+varScp()->name()); }
|
|
|
|
|
virtual string dotColor() const { return fromFlop() ? "green" : cntAsyncRst() ? "red" : "blue"; }
|
|
|
|
|
int cntAsyncRst() const { return m_cntAsyncRst; }
|
|
|
|
|
void cntAsyncRst(int flag) { m_cntAsyncRst=flag; }
|
|
|
|
|
bool fromFlop() const { return m_fromFlop; }
|
|
|
|
|
void fromFlop(bool flag) { m_fromFlop = flag; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class CdcLogicVertex : public CdcEitherVertex {
|
2011-01-19 02:37:53 +00:00
|
|
|
|
bool m_hazard:1;
|
|
|
|
|
bool m_isFlop:1;
|
2010-01-07 21:41:19 +00:00
|
|
|
|
public:
|
|
|
|
|
CdcLogicVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep, AstSenTree* sensenodep)
|
2011-01-19 02:37:53 +00:00
|
|
|
|
: CdcEitherVertex(graphp,scopep,nodep)
|
|
|
|
|
, m_hazard(false), m_isFlop(false)
|
2010-01-15 14:30:20 +00:00
|
|
|
|
{ srcDomainp(sensenodep); dstDomainp(sensenodep); }
|
2010-01-07 21:41:19 +00:00
|
|
|
|
virtual ~CdcLogicVertex() {}
|
|
|
|
|
// Accessors
|
|
|
|
|
virtual string name() const { return (cvtToStr((void*)nodep())+"@"+scopep()->prettyName()); }
|
2010-01-15 19:04:15 +00:00
|
|
|
|
virtual string dotColor() const { return hazard() ? "black" : "yellow"; }
|
|
|
|
|
bool hazard() const { return m_hazard; }
|
|
|
|
|
void setHazard(AstNode* nodep) { m_hazard = true; nodep->user3(true); }
|
|
|
|
|
void clearHazard() { m_hazard = false; }
|
2010-01-15 14:30:20 +00:00
|
|
|
|
bool isFlop() const { return m_isFlop; }
|
|
|
|
|
void isFlop(bool flag) { m_isFlop = flag; }
|
2010-01-07 21:41:19 +00:00
|
|
|
|
};
|
|
|
|
|
|
2010-01-09 02:31:52 +00:00
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class CdcDumpVisitor : public CdcBaseVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
//Entire netlist:
|
2011-01-19 02:37:53 +00:00
|
|
|
|
// {statement}Node::user3 -> bool, indicating not hazard
|
2010-01-09 02:31:52 +00:00
|
|
|
|
ofstream* m_ofp; // Output file
|
|
|
|
|
string m_prefix;
|
|
|
|
|
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNode* nodep) {
|
2010-01-09 02:31:52 +00:00
|
|
|
|
*m_ofp<<m_prefix;
|
|
|
|
|
if (nodep->user3()) *m_ofp<<" %%";
|
|
|
|
|
else *m_ofp<<" ";
|
|
|
|
|
*m_ofp<<nodep->prettyTypeName()<<" "<<endl;
|
|
|
|
|
string lastPrefix = m_prefix;
|
|
|
|
|
m_prefix = lastPrefix + "1:";
|
|
|
|
|
nodep->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() {}
|
|
|
|
|
};
|
|
|
|
|
|
2010-01-15 14:30:20 +00:00
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class CdcWidthVisitor : public CdcBaseVisitor {
|
|
|
|
|
private:
|
|
|
|
|
int m_maxLineno;
|
2010-01-25 01:53:24 +00:00
|
|
|
|
size_t m_maxFilenameLen;
|
2010-01-15 14:30:20 +00:00
|
|
|
|
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNode* nodep) {
|
2010-01-15 14:30:20 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
// Keeping line+filename lengths separate is much faster than calling ascii().length()
|
|
|
|
|
if (nodep->fileline()->lineno() >= m_maxLineno) {
|
|
|
|
|
m_maxLineno = nodep->fileline()->lineno()+1;
|
|
|
|
|
}
|
|
|
|
|
if (nodep->fileline()->filename().length() >= m_maxFilenameLen) {
|
|
|
|
|
m_maxFilenameLen = nodep->fileline()->filename().length()+1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
2015-10-04 02:33:06 +00:00
|
|
|
|
explicit CdcWidthVisitor(AstNode* nodep) {
|
2010-01-15 14:30:20 +00:00
|
|
|
|
m_maxLineno = 0;
|
|
|
|
|
m_maxFilenameLen = 0;
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
virtual ~CdcWidthVisitor() {}
|
|
|
|
|
// ACCESSORS
|
|
|
|
|
int maxWidth() {
|
2010-01-25 01:53:24 +00:00
|
|
|
|
size_t width=1;
|
2010-01-15 14:30:20 +00:00
|
|
|
|
width += m_maxFilenameLen;
|
|
|
|
|
width += 1; // The :
|
|
|
|
|
width += cvtToStr(m_maxLineno).length();
|
|
|
|
|
width += 1; // Final :
|
2010-01-25 01:53:24 +00:00
|
|
|
|
return (int)width;
|
2010-01-15 14:30:20 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2010-01-07 21:41:19 +00:00
|
|
|
|
//######################################################################
|
|
|
|
|
// Cdc class functions
|
|
|
|
|
|
|
|
|
|
class CdcVisitor : public CdcBaseVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
//Entire netlist:
|
|
|
|
|
// AstVarScope::user1p -> CdcVarVertex* for usage var, 0=not set yet
|
2010-01-15 20:57:48 +00:00
|
|
|
|
// AstVarScope::user2 -> bool Used in sensitivity list
|
2010-01-07 21:41:19 +00:00
|
|
|
|
// {statement}Node::user1p -> CdcLogicVertex* for this statement
|
|
|
|
|
// AstNode::user3 -> bool True indicates to print %% (via V3EmitV)
|
|
|
|
|
AstUser1InUse m_inuser1;
|
2010-01-15 20:57:48 +00:00
|
|
|
|
AstUser2InUse m_inuser2;
|
2010-01-07 21:41:19 +00:00
|
|
|
|
AstUser3InUse m_inuser3;
|
|
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
V3Graph m_graph; // Scoreboard of var usages/dependencies
|
|
|
|
|
CdcLogicVertex* m_logicVertexp; // Current statement being tracked, NULL=ignored
|
|
|
|
|
AstScope* m_scopep; // Current scope being processed
|
|
|
|
|
AstNodeModule* m_modp; // Current module
|
|
|
|
|
AstSenTree* m_domainp; // Current sentree
|
|
|
|
|
bool m_inDly; // In delayed assign
|
2010-01-15 20:57:48 +00:00
|
|
|
|
int m_inSenItem; // Number of senitems
|
2010-01-07 21:41:19 +00:00
|
|
|
|
string m_ofFilename; // Output filename
|
|
|
|
|
ofstream* m_ofp; // Output file
|
2010-01-08 16:12:16 +00:00
|
|
|
|
uint32_t m_userGeneration; // Generation count to avoid slow userClearVertices
|
2010-01-15 14:30:20 +00:00
|
|
|
|
int m_filelineWidth; // Characters in longest fileline
|
2010-01-07 21:41:19 +00:00
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
void iterateNewStmt(AstNode* nodep) {
|
|
|
|
|
if (m_scopep) {
|
|
|
|
|
UINFO(4," STMT "<<nodep<<endl);
|
|
|
|
|
m_logicVertexp = new CdcLogicVertex(&m_graph, m_scopep, nodep, m_domainp);
|
|
|
|
|
if (m_domainp && m_domainp->hasClocked()) { // To/from a flop
|
2010-01-15 14:30:20 +00:00
|
|
|
|
m_logicVertexp->isFlop(true);
|
2010-01-07 21:41:19 +00:00
|
|
|
|
m_logicVertexp->srcDomainp(m_domainp);
|
|
|
|
|
m_logicVertexp->srcDomainSet(true);
|
|
|
|
|
m_logicVertexp->dstDomainp(m_domainp);
|
|
|
|
|
m_logicVertexp->dstDomainSet(true);
|
|
|
|
|
}
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_logicVertexp = NULL;
|
|
|
|
|
|
|
|
|
|
if (0 && debug()>=9) {
|
|
|
|
|
UINFO(9, "Trace Logic:\n");
|
|
|
|
|
nodep->dumpTree(cout, "-log1: ");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CdcVarVertex* makeVarVertex(AstVarScope* varscp) {
|
|
|
|
|
CdcVarVertex* vertexp = (CdcVarVertex*)(varscp->user1p());
|
|
|
|
|
if (!vertexp) {
|
|
|
|
|
UINFO(6,"New vertex "<<varscp<<endl);
|
|
|
|
|
vertexp = new CdcVarVertex(&m_graph, m_scopep, varscp);
|
|
|
|
|
varscp->user1p(vertexp);
|
|
|
|
|
if (varscp->varp()->isUsedClock()) {}
|
2010-01-09 14:05:00 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-01-07 21:41:19 +00:00
|
|
|
|
}
|
2010-01-15 20:57:48 +00:00
|
|
|
|
if (m_inSenItem) {
|
|
|
|
|
varscp->user2(true); // It's like a clock...
|
|
|
|
|
// TODO: In the future we could mark it here and do normal clock tree glitch checks also
|
|
|
|
|
} else if (varscp->user2()) { // It was detected in a sensitivity list earlier
|
|
|
|
|
// And now it's used as logic. So must be a reset.
|
|
|
|
|
vertexp->cntAsyncRst(vertexp->cntAsyncRst()+1);
|
|
|
|
|
}
|
2010-01-07 21:41:19 +00:00
|
|
|
|
return vertexp;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-09 02:31:52 +00:00
|
|
|
|
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<<V3Error::msgPrefix()<<" See details in "<<m_ofFilename<<endl;
|
|
|
|
|
}
|
|
|
|
|
*m_ofp<<"%Warning-"<<code.ascii()<<": "<<nodep->fileline()<<" "<<msg<<endl;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-15 19:04:15 +00:00
|
|
|
|
void setNodeHazard(AstNode* nodep) {
|
2010-01-09 02:31:52 +00:00
|
|
|
|
// 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.
|
2011-01-19 02:37:53 +00:00
|
|
|
|
// We can't modifyWarnOff here, as one instantiation might not be an issue until we find a hitting flop
|
|
|
|
|
// Furthermore, a module like a "Or" module would only get flagged once,
|
|
|
|
|
// even though the signals feeding it are radically different.
|
2010-01-09 14:05:00 +00:00
|
|
|
|
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)) {
|
2011-01-19 02:37:53 +00:00
|
|
|
|
UINFO(8,"Set hazard "<<nodep<<endl);
|
2010-01-15 19:04:15 +00:00
|
|
|
|
m_logicVertexp->setHazard(nodep);
|
2010-01-09 14:05:00 +00:00
|
|
|
|
}
|
2010-01-09 02:31:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string spaces(int level) { string out; while (level--) out+=" "; return out; }
|
|
|
|
|
|
2010-01-07 21:41:19 +00:00
|
|
|
|
string pad(unsigned column, const string& in) {
|
|
|
|
|
string out = in;
|
|
|
|
|
while (out.length()<column) out += ' ';
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-09 02:31:52 +00:00
|
|
|
|
void analyze() {
|
|
|
|
|
UINFO(3,__FUNCTION__<<": "<<endl);
|
|
|
|
|
//if (debug()>6) m_graph.dump();
|
|
|
|
|
if (debug()>6) m_graph.dumpDotFilePrefixed("cdc_pre");
|
2011-04-18 14:47:02 +00:00
|
|
|
|
//
|
|
|
|
|
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); // This will MAX across edge weights
|
|
|
|
|
//
|
2010-01-09 02:31:52 +00:00
|
|
|
|
m_graph.dumpDotFilePrefixed("cdc_simp");
|
|
|
|
|
//
|
|
|
|
|
analyzeReset();
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-15 14:30:20 +00:00
|
|
|
|
int filelineWidth() {
|
|
|
|
|
if (!m_filelineWidth) {
|
|
|
|
|
CdcWidthVisitor visitor (v3Global.rootp());
|
|
|
|
|
m_filelineWidth = visitor.maxWidth();
|
|
|
|
|
}
|
|
|
|
|
return m_filelineWidth;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-09 02:31:52 +00:00
|
|
|
|
//----------------------------------------
|
|
|
|
|
// 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<CdcVarVertex*>(itp)) {
|
|
|
|
|
if (vvertexp->cntAsyncRst()) {
|
|
|
|
|
m_userGeneration++; // Effectively a userClearVertices()
|
2011-01-19 02:37:53 +00:00
|
|
|
|
UINFO(8, " Trace One async: "<<vvertexp<<endl);
|
2010-01-09 02:31:52 +00:00
|
|
|
|
// Twice, as we need to detect, then propagate
|
|
|
|
|
CdcEitherVertex* markp = traceAsyncRecurse(vvertexp, false);
|
|
|
|
|
if (markp) { // Mark is non-NULL if something bad on this path
|
|
|
|
|
UINFO(9, " Trace One bad! "<<vvertexp<<endl);
|
|
|
|
|
m_userGeneration++; // Effectively a userClearVertices()
|
|
|
|
|
traceAsyncRecurse(vvertexp, true);
|
|
|
|
|
m_userGeneration++; // Effectively a userClearVertices()
|
|
|
|
|
dumpAsync(vvertexp, markp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CdcEitherVertex* traceAsyncRecurse(CdcEitherVertex* vertexp, bool mark) {
|
2011-01-19 02:37:53 +00:00
|
|
|
|
// First pass: Return vertex of any hazardous stuff attached, or NULL if OK
|
|
|
|
|
// If first pass returns true, second pass calls asyncPath() on appropriate nodes
|
2012-07-15 16:27:36 +00:00
|
|
|
|
if (vertexp->user()>=m_userGeneration) return NULL; // Processed - prevent loop
|
2010-01-09 14:05:00 +00:00
|
|
|
|
vertexp->user(m_userGeneration);
|
|
|
|
|
|
2010-01-09 02:31:52 +00:00
|
|
|
|
CdcEitherVertex* mark_outp = NULL;
|
|
|
|
|
UINFO(9," Trace: "<<vertexp<<endl);
|
|
|
|
|
|
2010-01-09 14:05:00 +00:00
|
|
|
|
// Clear out in prep for marking next path
|
|
|
|
|
if (!mark) vertexp->asyncPath(false);
|
2010-01-09 02:31:52 +00:00
|
|
|
|
|
|
|
|
|
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
|
|
|
|
// Any logic considered bad, at the moment, anyhow
|
2010-01-15 19:04:15 +00:00
|
|
|
|
if (vvertexp->hazard() && !mark_outp) mark_outp = vvertexp;
|
2010-01-09 02:31:52 +00:00
|
|
|
|
// And keep tracing back so the user can understand what's up
|
|
|
|
|
}
|
|
|
|
|
else if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
|
|
|
|
if (mark) vvertexp->asyncPath(true);
|
|
|
|
|
// If primary I/O, it's ok here back
|
2010-01-09 14:05:00 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
2012-07-15 16:27:36 +00:00
|
|
|
|
return NULL;
|
2010-01-09 14:05:00 +00:00
|
|
|
|
}
|
2010-01-09 02:31:52 +00:00
|
|
|
|
// Also ok if from flop, but partially trace the flop so more obvious to users
|
|
|
|
|
if (vvertexp->fromFlop()) {
|
2010-01-09 14:05:00 +00:00
|
|
|
|
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
|
|
|
|
CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp();
|
|
|
|
|
eFromVertexp->asyncPath(true);
|
|
|
|
|
}
|
2012-07-15 16:27:36 +00:00
|
|
|
|
return NULL;
|
2010-01-09 02:31:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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";
|
2010-01-09 14:05:00 +00:00
|
|
|
|
CdcEitherVertex* targetp = vertexp; // One example destination flop (of possibly many)
|
2010-01-15 14:30:20 +00:00
|
|
|
|
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
2010-01-09 02:31:52 +00:00
|
|
|
|
CdcEitherVertex* eToVertexp = (CdcEitherVertex*)edgep->top();
|
2010-01-15 14:30:20 +00:00
|
|
|
|
if (!eToVertexp) targetp = eToVertexp;
|
|
|
|
|
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(eToVertexp)) {
|
2011-04-18 14:47:02 +00:00
|
|
|
|
if (vvertexp->isFlop() // IE the target flop that is upsetting us
|
|
|
|
|
&& edgep->weight() >= CDC_WEIGHT_ASYNC) { // And this signal feeds an async reset line
|
2010-01-15 14:30:20 +00:00
|
|
|
|
targetp = eToVertexp;
|
2011-04-18 14:47:02 +00:00
|
|
|
|
//UINFO(9," targetasync "<<targetp->name()<<" "<<" from "<<vertexp->name()<<endl);
|
2010-01-15 14:30:20 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} // else it might be random logic that's not relevant
|
2010-01-09 02:31:52 +00:00
|
|
|
|
}
|
2011-04-18 14:47:02 +00:00
|
|
|
|
//UINFO(9," finalflop "<<targetp->name()<<" "<<targetp->nodep()->fileline()<<endl);
|
2010-01-09 02:31:52 +00:00
|
|
|
|
warnAndFile(markp->nodep(),V3ErrorCode::CDCRSTLOGIC,"Logic in path that feeds async reset, via signal: "+nodep->prettyName());
|
2010-01-09 14:05:00 +00:00
|
|
|
|
dumpAsyncRecurse(targetp, "", " ",0);
|
2010-01-09 02:31:52 +00:00
|
|
|
|
}
|
2010-01-09 14:05:00 +00:00
|
|
|
|
bool dumpAsyncRecurse(CdcEitherVertex* vertexp, const string& prefix, const string& sep, int level) {
|
|
|
|
|
// level=0 is special, indicates to dump destination flop
|
|
|
|
|
// Return true if printed anything
|
2010-01-15 19:04:15 +00:00
|
|
|
|
// If mark, also mark the output even if nothing hazardous below
|
2010-01-09 14:05:00 +00:00
|
|
|
|
if (vertexp->user()>=m_userGeneration) return false; // Processed - prevent loop
|
2010-01-09 02:31:52 +00:00
|
|
|
|
vertexp->user(m_userGeneration);
|
2010-01-09 14:05:00 +00:00
|
|
|
|
if (!vertexp->asyncPath() && level!=0) return false; // Not part of path
|
2010-01-09 02:31:52 +00:00
|
|
|
|
|
2010-01-09 14:05:00 +00:00
|
|
|
|
// Other logic in the path
|
|
|
|
|
string cont = prefix+sep;
|
|
|
|
|
string nextsep = " ";
|
|
|
|
|
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
|
|
|
|
CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp();
|
|
|
|
|
if (dumpAsyncRecurse(eFromVertexp, cont, nextsep, level+1)) {
|
|
|
|
|
nextsep = " | ";
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-01-09 02:31:52 +00:00
|
|
|
|
|
|
|
|
|
// Dump single variable/logic block
|
|
|
|
|
// See also OrderGraph::loopsVertexCb(V3GraphVertex* vertexp)
|
|
|
|
|
AstNode* nodep = vertexp->nodep();
|
2010-01-15 14:30:20 +00:00
|
|
|
|
string front = pad(filelineWidth(),nodep->fileline()->ascii()+":")+" "+prefix+" +- ";
|
2010-01-09 14:05:00 +00:00
|
|
|
|
if (nodep->castVarScope()) {
|
|
|
|
|
*m_ofp<<front<<"Variable: "<<nodep->prettyName()<<endl;
|
|
|
|
|
}
|
2010-01-09 02:31:52 +00:00
|
|
|
|
else {
|
2010-01-15 14:30:20 +00:00
|
|
|
|
V3EmitV::verilogPrefixedTree(nodep, *m_ofp, prefix+" +- ", filelineWidth(),
|
|
|
|
|
vertexp->srcDomainp(), true);
|
2010-01-09 02:31:52 +00:00
|
|
|
|
if (debug()) {
|
|
|
|
|
CdcDumpVisitor visitor (nodep, m_ofp, front+"DBG: ");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-09 14:05:00 +00:00
|
|
|
|
nextsep = " | ";
|
2010-01-15 14:30:20 +00:00
|
|
|
|
if (level) *m_ofp<<V3OutFile::indentSpaces(filelineWidth())<<" "<<prefix<<nextsep<<"\n";
|
2010-01-15 19:04:15 +00:00
|
|
|
|
|
|
|
|
|
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
|
|
|
|
// Now that we've printed a path with this hazard, don't bother to print any more
|
|
|
|
|
// Otherwise, we'd get a path for almost every destination flop
|
|
|
|
|
vvertexp->clearHazard();
|
|
|
|
|
}
|
2010-01-09 14:05:00 +00:00
|
|
|
|
return true;
|
2010-01-09 02:31:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//----------------------------------------
|
|
|
|
|
// EDGE REPORTS
|
2010-01-07 21:41:19 +00:00
|
|
|
|
|
|
|
|
|
void edgeReport() {
|
|
|
|
|
// Make report of all signal names and what clock edges they have
|
2011-04-20 14:11:35 +00:00
|
|
|
|
//
|
|
|
|
|
// Due to flattening, many interesting direct-connect signals are
|
|
|
|
|
// lost, so we can't make a report showing I/Os for a low level
|
|
|
|
|
// module. Disabling flattening though makes us consider each
|
|
|
|
|
// signal in it's own unique clock domain.
|
|
|
|
|
|
2010-01-07 21:41:19 +00:00
|
|
|
|
UINFO(3,__FUNCTION__<<": "<<endl);
|
|
|
|
|
|
|
|
|
|
// Trace all sources and sinks
|
|
|
|
|
for (int traceDests=0; traceDests<2; ++traceDests) {
|
|
|
|
|
UINFO(9, " Trace Direction "<<(traceDests?"dst":"src")<<endl);
|
|
|
|
|
m_graph.userClearVertices(); // user1: bool - was analyzed
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
|
|
|
|
UINFO(9, " Trace One edge: "<<vvertexp<<endl);
|
|
|
|
|
edgeDomainRecurse(vvertexp, traceDests, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string filename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__cdc_edges.txt";
|
2015-10-04 17:41:45 +00:00
|
|
|
|
const VL_UNIQUE_PTR<ofstream> ofp (V3File::new_ofstream(filename));
|
2010-01-07 21:41:19 +00:00
|
|
|
|
if (ofp->fail()) v3fatalSrc("Can't write "<<filename);
|
|
|
|
|
*ofp<<"Edge Report for "<<v3Global.opt.prefix()<<endl;
|
|
|
|
|
|
|
|
|
|
deque<string> report; // Sort output by name
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
|
|
|
|
AstVar* varp = vvertexp->varScp()->varp();
|
|
|
|
|
if (1) { // varp->isPrimaryIO()
|
|
|
|
|
const char* whatp = "wire";
|
|
|
|
|
if (varp->isPrimaryIO()) whatp = (varp->isInout()?"inout":varp->isInput()?"input":"output");
|
|
|
|
|
|
|
|
|
|
ostringstream os;
|
|
|
|
|
os.setf(ios::left);
|
2011-04-20 14:11:35 +00:00
|
|
|
|
// Module name - doesn't work due to flattening having lost the original
|
|
|
|
|
// so we assume the modulename matches the filebasename
|
|
|
|
|
string fname = vvertexp->varScp()->fileline()->filebasename() + ":";
|
|
|
|
|
os<<" "<<setw(20)<<fname;
|
2010-01-07 21:41:19 +00:00
|
|
|
|
os<<" "<<setw(8)<<whatp;
|
|
|
|
|
os<<" "<<setw(40)<<vvertexp->varScp()->prettyName();
|
|
|
|
|
os<<" SRC=";
|
|
|
|
|
if (vvertexp->srcDomainp()) V3EmitV::verilogForTree(vvertexp->srcDomainp(), os);
|
|
|
|
|
os<<" DST=";
|
|
|
|
|
if (vvertexp->dstDomainp()) V3EmitV::verilogForTree(vvertexp->dstDomainp(), os);
|
2010-01-15 14:30:20 +00:00
|
|
|
|
os<<setw(0);
|
|
|
|
|
os<<endl;
|
2010-01-07 21:41:19 +00:00
|
|
|
|
report.push_back(os.str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-05-24 00:19:51 +00:00
|
|
|
|
stable_sort(report.begin(), report.end());
|
2010-01-07 21:41:19 +00:00
|
|
|
|
for (deque<string>::iterator it = report.begin(); it!=report.end(); ++it) {
|
|
|
|
|
*ofp << *it;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void edgeDomainRecurse(CdcEitherVertex* vertexp, bool traceDests, int level) {
|
|
|
|
|
// Scan back to inputs/outputs, flops, and compute clock domain information
|
|
|
|
|
UINFO(8,spaces(level)<<" Tracein "<<vertexp<<endl);
|
2010-01-08 16:12:16 +00:00
|
|
|
|
if (vertexp->user()>=m_userGeneration) return; // Mid-Processed - prevent loop
|
|
|
|
|
vertexp->user(m_userGeneration);
|
|
|
|
|
|
2010-01-07 21:41:19 +00:00
|
|
|
|
// Variables from flops already are domained
|
|
|
|
|
if (traceDests ? vertexp->dstDomainSet() : vertexp->srcDomainSet()) return; // Fully computed
|
|
|
|
|
|
|
|
|
|
typedef set<AstSenTree*> SenSet;
|
|
|
|
|
SenSet senouts; // List of all sensitivities for new signal
|
|
|
|
|
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
|
|
|
|
if (vvertexp) {} // Unused
|
|
|
|
|
}
|
|
|
|
|
else if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
|
|
|
|
// If primary I/O, give it domain of the input
|
|
|
|
|
AstVar* varp = vvertexp->varScp()->varp();
|
|
|
|
|
if (varp->isPrimaryIO() && varp->isInput() && !traceDests) {
|
|
|
|
|
senouts.insert(new AstSenTree(varp->fileline(), new AstSenItem(varp->fileline(), AstSenItem::Combo())));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now combine domains of sources/dests
|
|
|
|
|
if (traceDests) {
|
|
|
|
|
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
|
|
|
|
CdcEitherVertex* eToVertexp = (CdcEitherVertex*)edgep->top();
|
|
|
|
|
edgeDomainRecurse(eToVertexp, traceDests, level+1);
|
|
|
|
|
if (eToVertexp->dstDomainp()) senouts.insert(eToVertexp->dstDomainp());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
|
|
|
|
CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp();
|
|
|
|
|
edgeDomainRecurse(eFromVertexp, traceDests, level+1);
|
|
|
|
|
if (eFromVertexp->srcDomainp()) senouts.insert(eFromVertexp->srcDomainp());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert list of senses into one sense node
|
|
|
|
|
AstSenTree* senoutp = NULL;
|
|
|
|
|
bool senedited = false;
|
|
|
|
|
for (SenSet::iterator it=senouts.begin(); it!=senouts.end(); ++it) {
|
|
|
|
|
if (!senoutp) senoutp = *it;
|
|
|
|
|
else {
|
|
|
|
|
if (!senedited) {
|
|
|
|
|
senedited = true;
|
|
|
|
|
senoutp = senoutp->cloneTree(true);
|
|
|
|
|
}
|
|
|
|
|
senoutp->addSensesp((*it)->sensesp()->cloneTree(true));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// If multiple domains need to do complicated optimizations
|
|
|
|
|
if (senedited) {
|
|
|
|
|
senoutp = V3Const::constifyExpensiveEdit(senoutp)->castSenTree();
|
|
|
|
|
}
|
|
|
|
|
if (traceDests) {
|
|
|
|
|
vertexp->dstDomainSet(true); // Note it's set - domainp may be null, so can't use that
|
|
|
|
|
vertexp->dstDomainp(senoutp);
|
|
|
|
|
if (debug()>=9) {
|
|
|
|
|
UINFO(9,spaces(level)+" Tracedst "<<vertexp);
|
|
|
|
|
if (senoutp) V3EmitV::verilogForTree(senoutp, cout); cout<<endl;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
vertexp->srcDomainSet(true); // Note it's set - domainp may be null, so can't use that
|
|
|
|
|
vertexp->srcDomainp(senoutp);
|
|
|
|
|
if (debug()>=9) {
|
|
|
|
|
UINFO(9,spaces(level)+" Tracesrc "<<vertexp);
|
|
|
|
|
if (senoutp) V3EmitV::verilogForTree(senoutp, cout); cout<<endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeModule* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
m_modp = nodep;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_modp = NULL;
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstScope* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
UINFO(4," SCOPE "<<nodep<<endl);
|
|
|
|
|
m_scopep = nodep;
|
|
|
|
|
m_logicVertexp = NULL;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_scopep = NULL;
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstActive* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
// Create required blocks and add to module
|
|
|
|
|
UINFO(4," BLOCK "<<nodep<<endl);
|
2010-01-15 20:57:48 +00:00
|
|
|
|
AstNode::user2ClearTree();
|
2010-01-07 21:41:19 +00:00
|
|
|
|
m_domainp = nodep->sensesp();
|
|
|
|
|
if (!m_domainp || m_domainp->hasCombo() || m_domainp->hasClocked()) { // IE not hasSettle/hasInitial
|
|
|
|
|
iterateNewStmt(nodep);
|
|
|
|
|
}
|
|
|
|
|
m_domainp = NULL;
|
2010-01-15 20:57:48 +00:00
|
|
|
|
AstNode::user2ClearTree();
|
2010-01-07 21:41:19 +00:00
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeVarRef* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
if (m_scopep) {
|
2017-04-29 00:09:27 +00:00
|
|
|
|
if (!m_logicVertexp) nodep->v3fatalSrc("Var ref not under a logic block");
|
2010-01-07 21:41:19 +00:00
|
|
|
|
AstVarScope* varscp = nodep->varScopep();
|
2017-04-29 00:09:27 +00:00
|
|
|
|
if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
|
2010-01-07 21:41:19 +00:00
|
|
|
|
CdcVarVertex* varvertexp = makeVarVertex(varscp);
|
|
|
|
|
UINFO(5," VARREF to "<<varscp<<endl);
|
2011-04-18 14:47:02 +00:00
|
|
|
|
// We use weight of one for normal edges,
|
|
|
|
|
// Weight of CDC_WEIGHT_ASYNC to indicate feeds async (for reporting)
|
|
|
|
|
// When simplify we'll take the MAX weight
|
2010-01-07 21:41:19 +00:00
|
|
|
|
if (nodep->lvalue()) {
|
|
|
|
|
new V3GraphEdge(&m_graph, m_logicVertexp, varvertexp, 1);
|
|
|
|
|
if (m_inDly) {
|
|
|
|
|
varvertexp->fromFlop(true);
|
|
|
|
|
varvertexp->srcDomainp(m_domainp);
|
|
|
|
|
varvertexp->srcDomainSet(true);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2011-04-18 14:47:02 +00:00
|
|
|
|
if (varvertexp->cntAsyncRst()) {
|
|
|
|
|
//UINFO(9," edgeasync "<<varvertexp->name()<<" to "<<m_logicVertexp<<endl);
|
|
|
|
|
new V3GraphEdge(&m_graph, varvertexp, m_logicVertexp, CDC_WEIGHT_ASYNC);
|
|
|
|
|
} else {
|
|
|
|
|
//UINFO(9," edgena "<<varvertexp->name()<<" to "<<m_logicVertexp<<endl);
|
|
|
|
|
new V3GraphEdge(&m_graph, varvertexp, m_logicVertexp, 1);
|
|
|
|
|
}
|
2010-01-07 21:41:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstAssignDly* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
m_inDly = true;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_inDly = false;
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstSenItem* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
// Note we look at only AstSenItems, not AstSenGate's
|
|
|
|
|
// The gating term of a AstSenGate is normal logic
|
2010-01-15 20:57:48 +00:00
|
|
|
|
m_inSenItem = true;
|
2010-01-07 21:41:19 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
2010-01-15 20:57:48 +00:00
|
|
|
|
m_inSenItem = false;
|
2010-01-07 21:41:19 +00:00
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstAlways* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
iterateNewStmt(nodep);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstAlwaysPublic* nodep) {
|
2010-04-06 00:01:17 +00:00
|
|
|
|
// CDC doesn't care about public variables
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstCFunc* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
iterateNewStmt(nodep);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstSenGate* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
// First handle the clock part will be handled in a minute by visit AstSenItem
|
|
|
|
|
// The logic gating term is delt with as logic
|
|
|
|
|
iterateNewStmt(nodep);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstAssignAlias* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
iterateNewStmt(nodep);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstAssignW* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
iterateNewStmt(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-15 19:04:15 +00:00
|
|
|
|
// Math that shouldn't cause us to clear hazard
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstConst* nodep) { }
|
|
|
|
|
virtual void visit(AstReplicate* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstConcat* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNot* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstSel* nodep) {
|
2010-01-15 19:04:15 +00:00
|
|
|
|
if (!nodep->lsbp()->castConst()) setNodeHazard(nodep);
|
2010-01-07 21:41:19 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeSel* nodep) {
|
2010-01-15 19:04:15 +00:00
|
|
|
|
if (!nodep->bitp()->castConst()) setNodeHazard(nodep);
|
2010-01-07 21:41:19 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ignores
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstInitial* nodep) { }
|
|
|
|
|
virtual void visit(AstTraceInc* nodep) { }
|
|
|
|
|
virtual void visit(AstCoverToggle* nodep) { }
|
|
|
|
|
virtual void visit(AstNodeDType* nodep) { }
|
2010-01-07 21:41:19 +00:00
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
// Default
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeMath* nodep) {
|
2010-01-15 19:04:15 +00:00
|
|
|
|
setNodeHazard(nodep);
|
2010-01-07 21:41:19 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNode* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
2015-10-04 02:33:06 +00:00
|
|
|
|
explicit CdcVisitor(AstNode* nodep) {
|
2010-01-07 21:41:19 +00:00
|
|
|
|
m_logicVertexp = NULL;
|
|
|
|
|
m_scopep = NULL;
|
|
|
|
|
m_modp = NULL;
|
|
|
|
|
m_domainp = NULL;
|
|
|
|
|
m_inDly = false;
|
2010-01-15 20:57:48 +00:00
|
|
|
|
m_inSenItem = 0;
|
2010-01-08 16:12:16 +00:00
|
|
|
|
m_userGeneration = 0;
|
2010-01-15 14:30:20 +00:00
|
|
|
|
m_filelineWidth = 0;
|
2010-01-07 21:41:19 +00:00
|
|
|
|
|
|
|
|
|
// Make report of all signal names and what clock edges they have
|
|
|
|
|
string filename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__cdc.txt";
|
|
|
|
|
m_ofp = V3File::new_ofstream(filename);
|
|
|
|
|
if (m_ofp->fail()) v3fatalSrc("Can't write "<<filename);
|
|
|
|
|
m_ofFilename = filename;
|
|
|
|
|
*m_ofp<<"CDC Report for "<<v3Global.opt.prefix()<<endl;
|
2010-01-09 14:05:00 +00:00
|
|
|
|
*m_ofp<<"Each dump below traces logic from inputs/source flops to destination flop(s).\n";
|
|
|
|
|
*m_ofp<<"First source logic is listed, then a variable that logic generates,\n";
|
|
|
|
|
*m_ofp<<"repeating recursively forwards to the destination flop(s).\n";
|
2010-01-15 19:04:15 +00:00
|
|
|
|
*m_ofp<<"%% Indicates the operator considered potentially hazardous.\n";
|
2010-01-07 21:41:19 +00:00
|
|
|
|
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
analyze();
|
2011-04-20 14:11:35 +00:00
|
|
|
|
if (debug()>=1) edgeReport(); // Not useful to users at the moment
|
2010-01-07 21:41:19 +00:00
|
|
|
|
|
2010-01-15 14:30:20 +00:00
|
|
|
|
if (0) { *m_ofp<<"\nDBG-test-dumper\n"; V3EmitV::verilogPrefixedTree(nodep, *m_ofp, "DBG ",40,NULL,true); *m_ofp<<endl; }
|
2010-01-07 21:41:19 +00:00
|
|
|
|
}
|
|
|
|
|
virtual ~CdcVisitor() {
|
|
|
|
|
if (m_ofp) { delete m_ofp; m_ofp = NULL; }
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Cdc class functions
|
|
|
|
|
|
|
|
|
|
void V3Cdc::cdcAll(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
CdcVisitor visitor (nodep);
|
|
|
|
|
}
|