Clock domain crossing checks
This commit is contained in:
parent
a03a540156
commit
bf860b21d7
2
Changes
2
Changes
|
|
@ -26,6 +26,8 @@ indicates the contributor was also the author of the fix; Thanks!
|
||||||
|
|
||||||
*** Add VARHIDDEN warning when signal name hides module name.
|
*** Add VARHIDDEN warning when signal name hides module name.
|
||||||
|
|
||||||
|
*** Add experimental clock domain crossing checks.
|
||||||
|
|
||||||
**** Support optional cell parenthesis, bug179. [by Byron Bradley]
|
**** Support optional cell parenthesis, bug179. [by Byron Bradley]
|
||||||
|
|
||||||
**** Support for loop i++, ++i, i--, --i, bug175. [by Byron Bradley]
|
**** Support for loop i++, ++i, i--, --i, bug175. [by Byron Bradley]
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,7 @@ descriptions in the next sections for more information.
|
||||||
--bbox-unsup Blackbox unsupported language features
|
--bbox-unsup Blackbox unsupported language features
|
||||||
--bin <filename> Override Verilator binary
|
--bin <filename> Override Verilator binary
|
||||||
--cc Create C++ output
|
--cc Create C++ output
|
||||||
|
--cdc Clock domain crossing analysis
|
||||||
--compiler <compiler-name> Tune for specified C++ compiler
|
--compiler <compiler-name> Tune for specified C++ compiler
|
||||||
--coverage Enable all coverage
|
--coverage Enable all coverage
|
||||||
--coverage-line Enable line coverage
|
--coverage-line Enable line coverage
|
||||||
|
|
@ -254,6 +255,7 @@ descriptions in the next sections for more information.
|
||||||
+define+<var>+<value> Set preprocessor define
|
+define+<var>+<value> Set preprocessor define
|
||||||
+incdir+<dir> Directory to search for includes
|
+incdir+<dir> Directory to search for includes
|
||||||
+libext+<ext>+[ext]... Extensions for finding modules
|
+libext+<ext>+[ext]... Extensions for finding modules
|
||||||
|
+notimingchecks Ignored
|
||||||
|
|
||||||
=head1 ARGUMENTS
|
=head1 ARGUMENTS
|
||||||
|
|
||||||
|
|
@ -308,6 +310,17 @@ output files.
|
||||||
|
|
||||||
Specifies C++ without SystemC output mode; see also --sc and --sp.
|
Specifies C++ without SystemC output mode; see also --sc and --sp.
|
||||||
|
|
||||||
|
=item --cdc
|
||||||
|
|
||||||
|
Experimental. Perform some clock domain crossing checks and issue related
|
||||||
|
warnings. Additional warning information is written to the file
|
||||||
|
{prefix}__cdc.txt. Generally used with --lint-only.
|
||||||
|
|
||||||
|
Currently only checks that asynchronous flop reset terms come from primary
|
||||||
|
inputs (a check that most other CDC tools missed, so it was put here). If
|
||||||
|
you have interest in adding more traditional CDC checks, please contact the
|
||||||
|
authors.
|
||||||
|
|
||||||
=item --compiler I<compiler-name>
|
=item --compiler I<compiler-name>
|
||||||
|
|
||||||
Enables tunings and work-arounds for the specified C++ compiler.
|
Enables tunings and work-arounds for the specified C++ compiler.
|
||||||
|
|
@ -992,6 +1005,7 @@ In certain optimization modes, it also creates:
|
||||||
{prefix}__Syms.cpp // Global symbol table C++
|
{prefix}__Syms.cpp // Global symbol table C++
|
||||||
{prefix}__Syms.h // Global symbol table header
|
{prefix}__Syms.h // Global symbol table header
|
||||||
{prefix}__Trace.cpp // Wave file generation code (--trace)
|
{prefix}__Trace.cpp // Wave file generation code (--trace)
|
||||||
|
{prefix}__cdc.txt // Clock Domain Crossing checks (--cdc)
|
||||||
{prefix}__stats.txt // Statistics (--stats)
|
{prefix}__stats.txt // Statistics (--stats)
|
||||||
|
|
||||||
It also creates internal files that can be mostly ignored:
|
It also creates internal files that can be mostly ignored:
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,7 @@ RAW_OBJS = \
|
||||||
V3Broken.o \
|
V3Broken.o \
|
||||||
V3Case.o \
|
V3Case.o \
|
||||||
V3Cast.o \
|
V3Cast.o \
|
||||||
|
V3Cdc.o \
|
||||||
V3Changed.o \
|
V3Changed.o \
|
||||||
V3Clean.o \
|
V3Clean.o \
|
||||||
V3ClkGater.o \
|
V3ClkGater.o \
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,629 @@
|
||||||
|
//*************************************************************************
|
||||||
|
// DESCRIPTION: Verilator: Clock Domain Crossing Lint
|
||||||
|
//
|
||||||
|
// Code available from: http://www.veripool.org/verilator
|
||||||
|
//
|
||||||
|
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
//
|
||||||
|
// Copyright 2003-2010 by Wilson Snyder. 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.
|
||||||
|
//
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
|
||||||
|
class CdcBaseVisitor : public AstNVisitor {
|
||||||
|
public:
|
||||||
|
static int debug() {
|
||||||
|
static int level = -1;
|
||||||
|
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
// Support classes
|
||||||
|
|
||||||
|
class CdcEitherVertex : public V3GraphVertex {
|
||||||
|
AstScope* m_scopep;
|
||||||
|
AstNode* m_nodep;
|
||||||
|
AstSenTree* m_srcDomainp;
|
||||||
|
bool m_srcDomainSet;
|
||||||
|
AstSenTree* m_dstDomainp;
|
||||||
|
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_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 {
|
||||||
|
bool m_safe;
|
||||||
|
public:
|
||||||
|
CdcLogicVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep, AstSenTree* sensenodep)
|
||||||
|
: CdcEitherVertex(graphp,scopep,nodep), m_safe(true) { srcDomainp(sensenodep); dstDomainp(sensenodep); }
|
||||||
|
virtual ~CdcLogicVertex() {}
|
||||||
|
// Accessors
|
||||||
|
virtual string name() const { return (cvtToStr((void*)nodep())+"@"+scopep()->prettyName()); }
|
||||||
|
virtual string dotColor() const { return safe() ? "black" : "yellow"; }
|
||||||
|
bool safe() const { return m_safe; }
|
||||||
|
void clearSafe(AstNode* nodep) { m_safe = false; nodep->user3(true); }
|
||||||
|
};
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
// Cdc class functions
|
||||||
|
|
||||||
|
class CdcVisitor : public CdcBaseVisitor {
|
||||||
|
private:
|
||||||
|
// NODE STATE
|
||||||
|
//Entire netlist:
|
||||||
|
// AstVarScope::user1p -> CdcVarVertex* for usage var, 0=not set yet
|
||||||
|
// {statement}Node::user1p -> CdcLogicVertex* for this statement
|
||||||
|
// AstNode::user3 -> bool True indicates to print %% (via V3EmitV)
|
||||||
|
AstUser1InUse m_inuser1;
|
||||||
|
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
|
||||||
|
int m_senNumber; // Number of senitems
|
||||||
|
string m_ofFilename; // Output filename
|
||||||
|
ofstream* m_ofp; // Output file
|
||||||
|
|
||||||
|
// 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
|
||||||
|
m_logicVertexp->srcDomainp(m_domainp);
|
||||||
|
m_logicVertexp->srcDomainSet(true);
|
||||||
|
m_logicVertexp->dstDomainp(m_domainp);
|
||||||
|
m_logicVertexp->dstDomainSet(true);
|
||||||
|
}
|
||||||
|
m_senNumber = 0;
|
||||||
|
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()->isIO() && varscp->scopep()->isTop()) {}
|
||||||
|
if (varscp->varp()->isUsedClock()) {}
|
||||||
|
}
|
||||||
|
if (m_senNumber > 1) vertexp->cntAsyncRst(vertexp->cntAsyncRst()+1);
|
||||||
|
return vertexp;
|
||||||
|
}
|
||||||
|
|
||||||
|
string pad(unsigned column, const string& in) {
|
||||||
|
string out = in;
|
||||||
|
while (out.length()<column) out += ' ';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void analyze();
|
||||||
|
void analyzeReset();
|
||||||
|
|
||||||
|
void edgeReport() {
|
||||||
|
// Make report of all signal names and what clock edges they have
|
||||||
|
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";
|
||||||
|
const auto_ptr<ofstream> ofp (V3File::new_ofstream(filename));
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
os<<"\n";
|
||||||
|
report.push_back(os.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort(report.begin(), report.end());
|
||||||
|
for (deque<string>::iterator it = report.begin(); it!=report.end(); ++it) {
|
||||||
|
*ofp << *it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 "<<vertexp<<endl);
|
||||||
|
if (vertexp->user()) return; // Mid-Processed - prevent loop
|
||||||
|
vertexp->user(true);
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CdcEitherVertex* traceAsyncRecurse(CdcEitherVertex* vertexp, uint32_t did_value, bool mark);
|
||||||
|
void dumpAsync(CdcVarVertex* vertexp, CdcEitherVertex* markp);
|
||||||
|
void dumpAsyncRecurse(CdcEitherVertex* vertexp, const string& prefix, const string& blank);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 "<<nodep<<endl);
|
||||||
|
m_logicVertexp->clearSafe(nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VISITORS
|
||||||
|
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
||||||
|
m_modp = nodep;
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
m_modp = NULL;
|
||||||
|
}
|
||||||
|
virtual void visit(AstScope* nodep, AstNUser*) {
|
||||||
|
UINFO(4," SCOPE "<<nodep<<endl);
|
||||||
|
m_scopep = nodep;
|
||||||
|
m_logicVertexp = NULL;
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
m_scopep = NULL;
|
||||||
|
}
|
||||||
|
virtual void visit(AstActive* nodep, AstNUser*) {
|
||||||
|
// Create required blocks and add to module
|
||||||
|
UINFO(4," BLOCK "<<nodep<<endl);
|
||||||
|
m_domainp = nodep->sensesp();
|
||||||
|
if (!m_domainp || m_domainp->hasCombo() || m_domainp->hasClocked()) { // IE not hasSettle/hasInitial
|
||||||
|
iterateNewStmt(nodep);
|
||||||
|
}
|
||||||
|
m_domainp = NULL;
|
||||||
|
}
|
||||||
|
virtual void visit(AstNodeVarRef* nodep, AstNUser*) {
|
||||||
|
if (m_scopep) {
|
||||||
|
if (!m_logicVertexp) nodep->v3fatalSrc("Var ref not under a logic block\n");
|
||||||
|
AstVarScope* varscp = nodep->varScopep();
|
||||||
|
if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp\n");
|
||||||
|
CdcVarVertex* varvertexp = makeVarVertex(varscp);
|
||||||
|
UINFO(5," VARREF to "<<varscp<<endl);
|
||||||
|
// We use weight of one; if we ref the var more than once, when we simplify,
|
||||||
|
// the weight will increase
|
||||||
|
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 {
|
||||||
|
new V3GraphEdge(&m_graph, varvertexp, m_logicVertexp, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void visit(AstAssignDly* nodep, AstNUser*) {
|
||||||
|
m_inDly = true;
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
m_inDly = false;
|
||||||
|
}
|
||||||
|
virtual void visit(AstSenItem* nodep, AstNUser*) {
|
||||||
|
// Note we look at only AstSenItems, not AstSenGate's
|
||||||
|
// The gating term of a AstSenGate is normal logic
|
||||||
|
m_senNumber++;
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
}
|
||||||
|
virtual void visit(AstAlways* nodep, AstNUser*) {
|
||||||
|
iterateNewStmt(nodep);
|
||||||
|
}
|
||||||
|
virtual void visit(AstCFunc* nodep, AstNUser*) {
|
||||||
|
iterateNewStmt(nodep);
|
||||||
|
}
|
||||||
|
virtual void visit(AstSenGate* nodep, AstNUser*) {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
|
||||||
|
iterateNewStmt(nodep);
|
||||||
|
}
|
||||||
|
virtual void visit(AstAssignW* nodep, AstNUser*) {
|
||||||
|
iterateNewStmt(nodep);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Math that shouldn't cause us to clear safe
|
||||||
|
virtual void visit(AstConst* nodep, AstNUser*) { }
|
||||||
|
virtual void visit(AstReplicate* nodep, AstNUser*) {
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
}
|
||||||
|
virtual void visit(AstConcat* nodep, AstNUser*) {
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
}
|
||||||
|
virtual void visit(AstNot* nodep, AstNUser*) {
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
}
|
||||||
|
virtual void visit(AstSel* nodep, AstNUser*) {
|
||||||
|
if (!nodep->lsbp()->castConst()) clearNodeSafe(nodep);
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
}
|
||||||
|
virtual void visit(AstNodeSel* nodep, AstNUser*) {
|
||||||
|
if (!nodep->bitp()->castConst()) clearNodeSafe(nodep);
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignores
|
||||||
|
virtual void visit(AstInitial* nodep, AstNUser*) { }
|
||||||
|
virtual void visit(AstTraceInc* nodep, AstNUser*) { }
|
||||||
|
virtual void visit(AstCoverToggle* nodep, AstNUser*) { }
|
||||||
|
|
||||||
|
//--------------------
|
||||||
|
// Default
|
||||||
|
virtual void visit(AstNodeMath* nodep, AstNUser*) {
|
||||||
|
clearNodeSafe(nodep);
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
}
|
||||||
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||||
|
nodep->iterateChildren(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// CONSTUCTORS
|
||||||
|
CdcVisitor(AstNode* nodep) {
|
||||||
|
m_logicVertexp = NULL;
|
||||||
|
m_scopep = NULL;
|
||||||
|
m_modp = NULL;
|
||||||
|
m_domainp = NULL;
|
||||||
|
m_inDly = false;
|
||||||
|
m_senNumber = 0;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
*m_ofp<<"Each dump below traces a variable from a flop back through logic.\n";
|
||||||
|
*m_ofp<<"First the variable is listed, then the logic that creates it, then all variables\n";
|
||||||
|
*m_ofp<<"feeding that logic, recursively backwards to the sourcing flop(s).\n";
|
||||||
|
*m_ofp<<"%% Indicates nodes considered potentially unsafe.\n";
|
||||||
|
|
||||||
|
nodep->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<<endl; }
|
||||||
|
}
|
||||||
|
virtual ~CdcVisitor() {
|
||||||
|
if (m_ofp) { delete m_ofp; m_ofp = NULL; }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
// 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<<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() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
|
||||||
|
void CdcVisitor::analyze() {
|
||||||
|
UINFO(3,__FUNCTION__<<": "<<endl);
|
||||||
|
//if (debug()>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
|
||||||
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||||
|
if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||||
|
if (vvertexp->cntAsyncRst()) {
|
||||||
|
m_graph.userClearVertices(); // user1: bool - was analyzed
|
||||||
|
UINFO(9, " Trace One async: "<<vvertexp<<endl);
|
||||||
|
// Twice, as we need to detect, then propagate
|
||||||
|
CdcEitherVertex* markp = traceAsyncRecurse(vvertexp, 1, false);
|
||||||
|
if (markp) { // Mark is non-NULL if something bad on this path
|
||||||
|
UINFO(9, " Trace One bad! "<<vvertexp<<endl);
|
||||||
|
traceAsyncRecurse(vvertexp, 2, true);
|
||||||
|
m_graph.userClearVertices(); // user1: bool - was analyzed
|
||||||
|
dumpAsync(vvertexp, markp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CdcEitherVertex* CdcVisitor::traceAsyncRecurse(CdcEitherVertex* vertexp, uint32_t did_value, bool mark) {
|
||||||
|
// Return vertex of any dangerous stuff attached, or NULL if OK
|
||||||
|
// If mark, also mark the output even if nothing dangerous below
|
||||||
|
CdcEitherVertex* mark_outp = NULL;
|
||||||
|
UINFO(9," Trace: "<<vertexp<<endl);
|
||||||
|
|
||||||
|
if (vertexp->user()>=did_value) return false; // Processed - prevent loop
|
||||||
|
vertexp->user(did_value);
|
||||||
|
|
||||||
|
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(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<CdcVarVertex*>(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, did_value, 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: "<<vertexp->cntAsyncRst()<<" Target: "<<targetp->fileline()<<endl;
|
||||||
|
dumpAsyncRecurse(vertexp," +--", " | ");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CdcVisitor::dumpAsyncRecurse(CdcEitherVertex* vertexp, const string& prefix, const string& blank) {
|
||||||
|
// Return true if any dangerous stuff attached
|
||||||
|
// If mark, also mark the output even if nothing dangerous below
|
||||||
|
if (vertexp->user()) return; // Processed - prevent loop
|
||||||
|
vertexp->user(1);
|
||||||
|
if (!vertexp->asyncPath()) return; // Not part of path
|
||||||
|
|
||||||
|
*m_ofp<<V3OutFile::indentSpaces(40)<<" "<<blank<<"\n";
|
||||||
|
|
||||||
|
// 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<<front<<"Variable: "<<nodep->prettyName()<<endl;
|
||||||
|
else {
|
||||||
|
V3EmitV::verilogPrefixedTree(nodep, *m_ofp, prefix+" ", 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+" | ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
// Cdc class functions
|
||||||
|
|
||||||
|
void V3Cdc::cdcAll(AstNetlist* nodep) {
|
||||||
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||||
|
CdcVisitor visitor (nodep);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
// -*- C++ -*-
|
||||||
|
//*************************************************************************
|
||||||
|
// DESCRIPTION: Verilator: Break always into sensitivity block domains
|
||||||
|
//
|
||||||
|
// Code available from: http://www.veripool.org/verilator
|
||||||
|
//
|
||||||
|
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
//
|
||||||
|
// Copyright 2003-2010 by Wilson Snyder. 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.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
|
||||||
|
#ifndef _V3CDC_H_
|
||||||
|
#define _V3CDC_H_ 1
|
||||||
|
#include "config_build.h"
|
||||||
|
#include "verilatedos.h"
|
||||||
|
#include "V3Error.h"
|
||||||
|
#include "V3Ast.h"
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
class V3Cdc {
|
||||||
|
public:
|
||||||
|
static void cdcAll(AstNetlist* nodep);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // Guard
|
||||||
124
src/V3EmitV.cpp
124
src/V3EmitV.cpp
|
|
@ -50,7 +50,8 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||||
|
|
||||||
virtual void puts(const string& str) = 0;
|
virtual void puts(const string& str) = 0;
|
||||||
virtual void putbs(const string& str) = 0;
|
virtual void putbs(const string& str) = 0;
|
||||||
virtual void putfs(AstNode* nodep, const string& str) = 0;
|
virtual void putfs(AstNode* nodep, const string& str) = 0; // Fileline and node %% mark
|
||||||
|
virtual void putqs(AstNode* nodep, const string& str) = 0; // Fileline quiet w/o %% mark
|
||||||
virtual void putsNoTracking(const string& str) = 0;
|
virtual void putsNoTracking(const string& str) = 0;
|
||||||
virtual void putsQuoted(const string& str) {
|
virtual void putsQuoted(const string& str) {
|
||||||
// Quote \ and " for use inside C programs
|
// Quote \ and " for use inside C programs
|
||||||
|
|
@ -68,16 +69,16 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||||
virtual void visit(AstModule* nodep, AstNUser*) {
|
virtual void visit(AstModule* nodep, AstNUser*) {
|
||||||
putfs(nodep, "module "+modClassName(nodep)+";\n");
|
putfs(nodep, "module "+modClassName(nodep)+";\n");
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
putfs(nodep, "endmodule\n");
|
putqs(nodep, "endmodule\n");
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
||||||
putfs(nodep, nodep->isFunction() ? "function":"task");
|
putfs(nodep, nodep->isFunction() ? "function":"task");
|
||||||
puts(" ");
|
puts(" ");
|
||||||
puts(nodep->name());
|
puts(nodep->name());
|
||||||
puts(";\n");
|
puts(";\n");
|
||||||
putfs(nodep, "begin\n");
|
putqs(nodep, "begin\n"); // Only putfs the first time for each visitor; later for same node is putqs
|
||||||
nodep->stmtsp()->iterateAndNext(*this);
|
nodep->stmtsp()->iterateAndNext(*this);
|
||||||
putfs(nodep, "end\n");
|
putqs(nodep, "end\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstBegin* nodep, AstNUser*) {
|
virtual void visit(AstBegin* nodep, AstNUser*) {
|
||||||
|
|
@ -88,17 +89,17 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||||
virtual void visit(AstGenerate* nodep, AstNUser*) {
|
virtual void visit(AstGenerate* nodep, AstNUser*) {
|
||||||
putfs(nodep, "generate\n");
|
putfs(nodep, "generate\n");
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
putfs(nodep, "end\n");
|
putqs(nodep, "end\n");
|
||||||
}
|
}
|
||||||
virtual void visit(AstFinal* nodep, AstNUser*) {
|
virtual void visit(AstFinal* nodep, AstNUser*) {
|
||||||
putfs(nodep, "final begin\n");
|
putfs(nodep, "final begin\n");
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
putfs(nodep, "end\n");
|
putqs(nodep, "end\n");
|
||||||
}
|
}
|
||||||
virtual void visit(AstInitial* nodep, AstNUser*) {
|
virtual void visit(AstInitial* nodep, AstNUser*) {
|
||||||
putfs(nodep,"initial begin\n");
|
putfs(nodep,"initial begin\n");
|
||||||
nodep->iterateChildren(*this);
|
nodep->iterateChildren(*this);
|
||||||
putfs(nodep, "end\n");
|
putqs(nodep, "end\n");
|
||||||
}
|
}
|
||||||
virtual void visit(AstAlways* nodep, AstNUser*) {
|
virtual void visit(AstAlways* nodep, AstNUser*) {
|
||||||
putfs(nodep,"always ");
|
putfs(nodep,"always ");
|
||||||
|
|
@ -106,7 +107,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||||
else nodep->sensesp()->iterateAndNext(*this);
|
else nodep->sensesp()->iterateAndNext(*this);
|
||||||
putbs(" begin\n");
|
putbs(" begin\n");
|
||||||
nodep->bodysp()->iterateAndNext(*this);
|
nodep->bodysp()->iterateAndNext(*this);
|
||||||
putfs(nodep,"end\n");
|
putqs(nodep,"end\n");
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||||
nodep->lhsp()->iterateAndNext(*this);
|
nodep->lhsp()->iterateAndNext(*this);
|
||||||
|
|
@ -139,7 +140,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||||
putfs(nodep,"@(");
|
putfs(nodep,"@(");
|
||||||
for (AstNode* expp=nodep->sensesp(); expp; expp = expp->nextp()) {
|
for (AstNode* expp=nodep->sensesp(); expp; expp = expp->nextp()) {
|
||||||
expp->accept(*this);
|
expp->accept(*this);
|
||||||
if (expp->nextp()) putfs(expp->nextp()," or ");
|
if (expp->nextp()) putqs(expp->nextp()," or ");
|
||||||
}
|
}
|
||||||
puts(")");
|
puts(")");
|
||||||
}
|
}
|
||||||
|
|
@ -165,7 +166,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nodep->itemsp()->iterateAndNext(*this);
|
nodep->itemsp()->iterateAndNext(*this);
|
||||||
putfs(nodep,"endcase\n");
|
putqs(nodep,"endcase\n");
|
||||||
}
|
}
|
||||||
virtual void visit(AstCaseItem* nodep, AstNUser*) {
|
virtual void visit(AstCaseItem* nodep, AstNUser*) {
|
||||||
if (nodep->condsp()) {
|
if (nodep->condsp()) {
|
||||||
|
|
@ -173,7 +174,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||||
} else putbs("default");
|
} else putbs("default");
|
||||||
putfs(nodep,": begin ");
|
putfs(nodep,": begin ");
|
||||||
nodep->bodysp()->iterateAndNext(*this);
|
nodep->bodysp()->iterateAndNext(*this);
|
||||||
putfs(nodep,"end\n");
|
putqs(nodep,"end\n");
|
||||||
}
|
}
|
||||||
virtual void visit(AstComment* nodep, AstNUser*) {
|
virtual void visit(AstComment* nodep, AstNUser*) {
|
||||||
puts((string)"// "+nodep->name()+"\n");
|
puts((string)"// "+nodep->name()+"\n");
|
||||||
|
|
@ -253,7 +254,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||||
m_suppressSemi = false;
|
m_suppressSemi = false;
|
||||||
puts(") begin\n");
|
puts(") begin\n");
|
||||||
nodep->bodysp()->iterateAndNext(*this);
|
nodep->bodysp()->iterateAndNext(*this);
|
||||||
putfs(nodep,"end\n");
|
putqs(nodep,"end\n");
|
||||||
}
|
}
|
||||||
virtual void visit(AstRepeat* nodep, AstNUser*) {
|
virtual void visit(AstRepeat* nodep, AstNUser*) {
|
||||||
putfs(nodep,"repeat (");
|
putfs(nodep,"repeat (");
|
||||||
|
|
@ -277,11 +278,11 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||||
puts(") begin\n");
|
puts(") begin\n");
|
||||||
nodep->ifsp()->iterateAndNext(*this);
|
nodep->ifsp()->iterateAndNext(*this);
|
||||||
if (nodep->elsesp()) {
|
if (nodep->elsesp()) {
|
||||||
putfs(nodep,"end\n");
|
putqs(nodep,"end\n");
|
||||||
putfs(nodep,"else begin\n");
|
putqs(nodep,"else begin\n");
|
||||||
nodep->elsesp()->iterateAndNext(*this);
|
nodep->elsesp()->iterateAndNext(*this);
|
||||||
}
|
}
|
||||||
putfs(nodep,"end\n");
|
putqs(nodep,"end\n");
|
||||||
}
|
}
|
||||||
virtual void visit(AstStop* nodep, AstNUser*) {
|
virtual void visit(AstStop* nodep, AstNUser*) {
|
||||||
putfs(nodep,"$stop;\n");
|
putfs(nodep,"$stop;\n");
|
||||||
|
|
@ -437,12 +438,12 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||||
// Terminals
|
// Terminals
|
||||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||||
putfs(nodep,nodep->hiername());
|
putfs(nodep,nodep->hiername());
|
||||||
puts(nodep->varp()->name());
|
puts(nodep->varp()->prettyName());
|
||||||
}
|
}
|
||||||
virtual void visit(AstVarXRef* nodep, AstNUser*) {
|
virtual void visit(AstVarXRef* nodep, AstNUser*) {
|
||||||
putfs(nodep,nodep->dotted());
|
putfs(nodep,nodep->dotted());
|
||||||
puts(".");
|
puts(".");
|
||||||
puts(nodep->varp()->name());
|
puts(nodep->varp()->prettyName());
|
||||||
}
|
}
|
||||||
virtual void visit(AstConst* nodep, AstNUser*) {
|
virtual void visit(AstConst* nodep, AstNUser*) {
|
||||||
putfs(nodep,nodep->num().ascii(true,true));
|
putfs(nodep,nodep->num().ascii(true,true));
|
||||||
|
|
@ -458,13 +459,13 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||||
putfs(nodep,nodep->verilogKwd());
|
putfs(nodep,nodep->verilogKwd());
|
||||||
puts(" ");
|
puts(" ");
|
||||||
nodep->dtypep()->iterateChildren(*this); puts(" ");
|
nodep->dtypep()->iterateAndNext(*this); puts(" ");
|
||||||
puts(nodep->name());
|
puts(nodep->name());
|
||||||
puts(";\n");
|
puts(";\n");
|
||||||
}
|
}
|
||||||
virtual void visit(AstActive* nodep, AstNUser*) {
|
virtual void visit(AstActive* nodep, AstNUser*) {
|
||||||
m_sensesp = nodep->sensesp();
|
m_sensesp = nodep->sensesp();
|
||||||
nodep->iterateChildren(*this);
|
nodep->stmtsp()->iterateAndNext(*this);
|
||||||
m_sensesp = NULL;
|
m_sensesp = NULL;
|
||||||
}
|
}
|
||||||
virtual void visit(AstVarScope*, AstNUser*) {}
|
virtual void visit(AstVarScope*, AstNUser*) {}
|
||||||
|
|
@ -501,8 +502,8 @@ class EmitVFileVisitor : public EmitVBaseVisitor {
|
||||||
virtual void puts(const string& str) { ofp()->puts(str); }
|
virtual void puts(const string& str) { ofp()->puts(str); }
|
||||||
virtual void putbs(const string& str) { ofp()->putbs(str); }
|
virtual void putbs(const string& str) { ofp()->putbs(str); }
|
||||||
virtual void putfs(AstNode*, const string& str) { putbs(str); }
|
virtual void putfs(AstNode*, const string& str) { putbs(str); }
|
||||||
|
virtual void putqs(AstNode*, const string& str) { putbs(str); }
|
||||||
virtual void putsNoTracking(const string& str) { ofp()->putsNoTracking(str); }
|
virtual void putsNoTracking(const string& str) { ofp()->putsNoTracking(str); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EmitVFileVisitor(AstNode* nodep, V3OutFile* ofp) {
|
EmitVFileVisitor(AstNode* nodep, V3OutFile* ofp) {
|
||||||
m_ofp = ofp;
|
m_ofp = ofp;
|
||||||
|
|
@ -518,12 +519,12 @@ class EmitVStreamVisitor : public EmitVBaseVisitor {
|
||||||
// MEMBERS
|
// MEMBERS
|
||||||
ostream& m_os;
|
ostream& m_os;
|
||||||
// METHODS
|
// METHODS
|
||||||
virtual void puts(const string& str) { m_os<<str; }
|
|
||||||
virtual void putbs(const string& str) { m_os<<str; }
|
|
||||||
virtual void putfs(AstNode*, const string& str) { putbs(str); }
|
|
||||||
virtual void putsNoTracking(const string& str) { m_os<<str; }
|
virtual void putsNoTracking(const string& str) { m_os<<str; }
|
||||||
|
virtual void puts(const string& str) { putsNoTracking(str); }
|
||||||
public:
|
virtual void putbs(const string& str) { puts(str); }
|
||||||
|
virtual void putfs(AstNode*, const string& str) { putbs(str); }
|
||||||
|
virtual void putqs(AstNode*, const string& str) { putbs(str); }
|
||||||
|
public:
|
||||||
EmitVStreamVisitor(AstNode* nodep, ostream& os)
|
EmitVStreamVisitor(AstNode* nodep, ostream& os)
|
||||||
: m_os(os) {
|
: m_os(os) {
|
||||||
nodep->accept(*this);
|
nodep->accept(*this);
|
||||||
|
|
@ -531,6 +532,75 @@ public:
|
||||||
virtual ~EmitVStreamVisitor() {}
|
virtual ~EmitVStreamVisitor() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
// Emit to a stream (perhaps stringstream)
|
||||||
|
|
||||||
|
class EmitVPrefixedFormatter : public V3OutFormatter {
|
||||||
|
ostream& m_os;
|
||||||
|
string m_prefix; // What to print at beginning of each line
|
||||||
|
int m_column; // Rough location; need just zero or non-zero
|
||||||
|
FileLine* m_prefixFl;
|
||||||
|
// METHODS
|
||||||
|
virtual void putcOutput(char chr) {
|
||||||
|
if (chr == '\n') {
|
||||||
|
m_column = 0;
|
||||||
|
m_os<<chr;
|
||||||
|
} else {
|
||||||
|
if (m_column == 0) {
|
||||||
|
m_column = 10;
|
||||||
|
m_os<<m_prefixFl->ascii()+":";
|
||||||
|
m_os<<V3OutFile::indentSpaces(40-(m_prefixFl->ascii().length()+1));
|
||||||
|
m_os<<" ";
|
||||||
|
m_os<<m_prefix;
|
||||||
|
}
|
||||||
|
m_column++;
|
||||||
|
m_os<<chr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
void prefixFl(FileLine* fl) { m_prefixFl = fl; }
|
||||||
|
FileLine* prefixFl() const { return m_prefixFl; }
|
||||||
|
int column() const { return m_column; }
|
||||||
|
EmitVPrefixedFormatter(ostream& os, const string& prefix)
|
||||||
|
: V3OutFormatter("__STREAM", true), m_os(os), m_prefix(prefix) {
|
||||||
|
m_column = 0;
|
||||||
|
m_prefixFl = v3Global.rootp()->fileline(); // NETLIST's fileline instead of NULL to avoid NULL checks
|
||||||
|
}
|
||||||
|
virtual ~EmitVPrefixedFormatter() {
|
||||||
|
if (m_column) puts("\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class EmitVPrefixedVisitor : public EmitVBaseVisitor {
|
||||||
|
// MEMBERS
|
||||||
|
EmitVPrefixedFormatter m_formatter; // Special verilog formatter (Way down the inheritance is another unused V3OutFormatter)
|
||||||
|
bool m_user3mark; // nodep->user3() if set means mark with %%
|
||||||
|
// METHODS
|
||||||
|
virtual void putsNoTracking(const string& str) { m_formatter.putsNoTracking(str); }
|
||||||
|
virtual void puts(const string& str) { m_formatter.puts(str); }
|
||||||
|
// We don't use m_formatter's putbs because the tokens will change filelines
|
||||||
|
// and insert returns at the proper locations
|
||||||
|
virtual void putbs(const string& str) { m_formatter.puts(str); }
|
||||||
|
virtual void putfs(AstNode* nodep, const string& str) { putfsqs(nodep,str,false); }
|
||||||
|
virtual void putqs(AstNode* nodep, const string& str) { putfsqs(nodep,str,true); }
|
||||||
|
void putfsqs(AstNode* nodep, const string& str, bool quiet) {
|
||||||
|
if (m_formatter.prefixFl() != nodep->fileline()) {
|
||||||
|
m_formatter.prefixFl(nodep->fileline());
|
||||||
|
if (m_formatter.column()) puts("\n"); // This in turn will print the m_prefixFl
|
||||||
|
}
|
||||||
|
if (!quiet && nodep->user3()) puts("%%");
|
||||||
|
putbs(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
EmitVPrefixedVisitor(AstNode* nodep, ostream& os, const string& prefix, bool user3mark)
|
||||||
|
: m_formatter(os, prefix), m_user3mark(user3mark) {
|
||||||
|
if (user3mark) { AstUser3InUse::check(); }
|
||||||
|
nodep->accept(*this);
|
||||||
|
}
|
||||||
|
virtual ~EmitVPrefixedVisitor() {}
|
||||||
|
};
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// EmitV class functions
|
// EmitV class functions
|
||||||
|
|
||||||
|
|
@ -556,3 +626,7 @@ void V3EmitV::emitv() {
|
||||||
void V3EmitV::verilogForTree(AstNode* nodep, ostream& os) {
|
void V3EmitV::verilogForTree(AstNode* nodep, ostream& os) {
|
||||||
EmitVStreamVisitor(nodep, os);
|
EmitVStreamVisitor(nodep, os);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void V3EmitV::verilogPrefixedTree(AstNode* nodep, ostream& os, const string& prefix, bool user3mark) {
|
||||||
|
EmitVPrefixedVisitor(nodep, os, prefix, user3mark);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ class V3EmitV {
|
||||||
public:
|
public:
|
||||||
static void emitv();
|
static void emitv();
|
||||||
static void verilogForTree(AstNode* nodep, ostream& os=cout);
|
static void verilogForTree(AstNode* nodep, ostream& os=cout);
|
||||||
|
static void verilogPrefixedTree(AstNode* nodep, ostream& os, const string& prefix, bool user3percent);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ public:
|
||||||
CASEOVERLAP, // Case statements overlap
|
CASEOVERLAP, // Case statements overlap
|
||||||
CASEWITHX, // Case with X values
|
CASEWITHX, // Case with X values
|
||||||
CASEX, // Casex
|
CASEX, // Casex
|
||||||
|
CDCRSTLOGIC, // Logic in async reset path
|
||||||
CMPCONST, // Comparison is constant due to limited range
|
CMPCONST, // Comparison is constant due to limited range
|
||||||
COMBDLY, // Combinatorial delayed assignment
|
COMBDLY, // Combinatorial delayed assignment
|
||||||
STMTDLY, // Delayed statement
|
STMTDLY, // Delayed statement
|
||||||
|
|
@ -95,7 +96,7 @@ public:
|
||||||
// Warnings
|
// Warnings
|
||||||
" FIRST_WARN",
|
" FIRST_WARN",
|
||||||
"BLKANDNBLK",
|
"BLKANDNBLK",
|
||||||
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CMPCONST",
|
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CDCRSTLOGIC", "CMPCONST",
|
||||||
"COMBDLY", "STMTDLY", "SYMRSVDWORD", "GENCLK", "IMPERFECTSCH", "IMPLICIT", "IMPURE",
|
"COMBDLY", "STMTDLY", "SYMRSVDWORD", "GENCLK", "IMPERFECTSCH", "IMPLICIT", "IMPURE",
|
||||||
"LITENDIAN",
|
"LITENDIAN",
|
||||||
"MULTIDRIVEN", "REDEFMACRO",
|
"MULTIDRIVEN", "REDEFMACRO",
|
||||||
|
|
@ -185,7 +186,8 @@ inline void v3errorEnd(ostringstream& sstr) { V3Error::v3errorEnd(sstr); }
|
||||||
|
|
||||||
// These allow errors using << operators: v3error("foo"<<"bar");
|
// These allow errors using << operators: v3error("foo"<<"bar");
|
||||||
// Careful, you can't put () around msg, as you would in most macro definitions
|
// Careful, you can't put () around msg, as you would in most macro definitions
|
||||||
#define v3warn(code,msg) v3errorEnd(((V3Error::v3errorPrep(V3ErrorCode::code)<<msg),V3Error::v3errorStr()));
|
#define v3warnCode(code,msg) v3errorEnd(((V3Error::v3errorPrep(code)<<msg),V3Error::v3errorStr()));
|
||||||
|
#define v3warn(code,msg) v3warnCode(V3ErrorCode::code,msg)
|
||||||
#define v3info(msg) v3warn(INFO,msg)
|
#define v3info(msg) v3warn(INFO,msg)
|
||||||
#define v3fatal(msg) v3warn(FATAL,msg)
|
#define v3fatal(msg) v3warn(FATAL,msg)
|
||||||
#define v3error(msg) v3warn(ERROR,msg)
|
#define v3error(msg) v3warn(ERROR,msg)
|
||||||
|
|
|
||||||
|
|
@ -269,9 +269,11 @@ const char* V3OutFormatter::indentStr(int num) {
|
||||||
static char str[MAXSPACE+20];
|
static char str[MAXSPACE+20];
|
||||||
char* cp = str;
|
char* cp = str;
|
||||||
if (num>MAXSPACE) num=MAXSPACE;
|
if (num>MAXSPACE) num=MAXSPACE;
|
||||||
while (num>=8) {
|
if (!m_verilog) { // verilogPrefixedTree doesn't want tabs
|
||||||
*cp++ = '\t';
|
while (num>=8) {
|
||||||
num -= 8;
|
*cp++ = '\t';
|
||||||
|
num -= 8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
while (num>0) {
|
while (num>0) {
|
||||||
*cp++ = ' ';
|
*cp++ = ' ';
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ private:
|
||||||
stack<int> m_parenVec; // Stack of columns where last ( was
|
stack<int> m_parenVec; // Stack of columns where last ( was
|
||||||
|
|
||||||
int endLevels(const char* strg);
|
int endLevels(const char* strg);
|
||||||
static const char* indentStr(int levels);
|
const char* indentStr(int levels);
|
||||||
void putcNoTracking(char chr);
|
void putcNoTracking(char chr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,12 @@ private:
|
||||||
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void visit(AstUdpTable* nodep, AstNUser*) {
|
||||||
|
if (!v3Global.opt.bboxUnsup()) {
|
||||||
|
nodep->v3error("Unsupported: Verilog 1995 UDP Tables. Use --bbox-unsup to ignore tables.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Save some time
|
// Save some time
|
||||||
virtual void visit(AstNodeAssign*, AstNUser*) {}
|
virtual void visit(AstNodeAssign*, AstNUser*) {}
|
||||||
virtual void visit(AstAlways*, AstNUser*) {}
|
virtual void visit(AstAlways*, AstNUser*) {}
|
||||||
|
|
|
||||||
|
|
@ -340,7 +340,8 @@ private:
|
||||||
virtual void visit(AstUdpTable* nodep, AstNUser*) {
|
virtual void visit(AstUdpTable* nodep, AstNUser*) {
|
||||||
UINFO(5,"UDPTABLE "<<nodep<<endl);
|
UINFO(5,"UDPTABLE "<<nodep<<endl);
|
||||||
if (!v3Global.opt.bboxUnsup()) {
|
if (!v3Global.opt.bboxUnsup()) {
|
||||||
nodep->v3error("Unsupported: Verilog 1995 UDP Tables. Use --bbox-unsup to ignore tables.");
|
// We don't warn until V3Inst, so that UDPs that are in libraries and
|
||||||
|
// never used won't result in any warnings.
|
||||||
} else {
|
} else {
|
||||||
// Massive hack, just tie off all outputs so our analysis can proceed
|
// Massive hack, just tie off all outputs so our analysis can proceed
|
||||||
AstVar* varoutp = NULL;
|
AstVar* varoutp = NULL;
|
||||||
|
|
|
||||||
|
|
@ -660,6 +660,7 @@ void V3Options::parseOptsList(FileLine* fl, int argc, char** argv) {
|
||||||
else if ( onoff (sw, "-bbox-sys", flag/*ref*/) ) { m_bboxSys = flag; }
|
else if ( onoff (sw, "-bbox-sys", flag/*ref*/) ) { m_bboxSys = flag; }
|
||||||
else if ( onoff (sw, "-bbox-unsup", flag/*ref*/) ) { m_bboxUnsup = flag; }
|
else if ( onoff (sw, "-bbox-unsup", flag/*ref*/) ) { m_bboxUnsup = flag; }
|
||||||
else if ( !strcmp (sw, "-cc") ) { m_outFormatOk = true; m_systemC = false; m_systemPerl = false; }
|
else if ( !strcmp (sw, "-cc") ) { m_outFormatOk = true; m_systemC = false; m_systemPerl = false; }
|
||||||
|
else if ( onoff (sw, "-cdc", flag/*ref*/) ) { m_cdc = flag; }
|
||||||
else if ( onoff (sw, "-coverage", flag/*ref*/) ) { coverage(flag); }
|
else if ( onoff (sw, "-coverage", flag/*ref*/) ) { coverage(flag); }
|
||||||
else if ( onoff (sw, "-coverage-line", flag/*ref*/) ){ m_coverageLine = flag; }
|
else if ( onoff (sw, "-coverage-line", flag/*ref*/) ){ m_coverageLine = flag; }
|
||||||
else if ( onoff (sw, "-coverage-toggle", flag/*ref*/) ){ m_coverageToggle = flag; }
|
else if ( onoff (sw, "-coverage-toggle", flag/*ref*/) ){ m_coverageToggle = flag; }
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@ class V3Options {
|
||||||
bool m_autoflush; // main switch: --autoflush
|
bool m_autoflush; // main switch: --autoflush
|
||||||
bool m_bboxSys; // main switch: --bbox-sys
|
bool m_bboxSys; // main switch: --bbox-sys
|
||||||
bool m_bboxUnsup; // main switch: --bbox-unsup
|
bool m_bboxUnsup; // main switch: --bbox-unsup
|
||||||
|
bool m_cdc; // main switch: --cdc
|
||||||
bool m_coverageLine; // main switch: --coverage-block
|
bool m_coverageLine; // main switch: --coverage-block
|
||||||
bool m_coverageToggle;// main switch: --coverage-toggle
|
bool m_coverageToggle;// main switch: --coverage-toggle
|
||||||
bool m_coverageUser; // main switch: --coverage-func
|
bool m_coverageUser; // main switch: --coverage-func
|
||||||
|
|
@ -208,6 +209,7 @@ class V3Options {
|
||||||
bool autoflush() const { return m_autoflush; }
|
bool autoflush() const { return m_autoflush; }
|
||||||
bool bboxSys() const { return m_bboxSys; }
|
bool bboxSys() const { return m_bboxSys; }
|
||||||
bool bboxUnsup() const { return m_bboxUnsup; }
|
bool bboxUnsup() const { return m_bboxUnsup; }
|
||||||
|
bool cdc() const { return m_cdc; }
|
||||||
bool coverage() const { return m_coverageLine || m_coverageToggle || m_coverageUser; }
|
bool coverage() const { return m_coverageLine || m_coverageToggle || m_coverageUser; }
|
||||||
bool coverageLine() const { return m_coverageLine; }
|
bool coverageLine() const { return m_coverageLine; }
|
||||||
bool coverageToggle() const { return m_coverageToggle; }
|
bool coverageToggle() const { return m_coverageToggle; }
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@
|
||||||
#include "V3EmitV.h"
|
#include "V3EmitV.h"
|
||||||
#include "V3Expand.h"
|
#include "V3Expand.h"
|
||||||
#include "V3File.h"
|
#include "V3File.h"
|
||||||
|
#include "V3Cdc.h"
|
||||||
#include "V3Gate.h"
|
#include "V3Gate.h"
|
||||||
#include "V3Graph.h"
|
#include "V3Graph.h"
|
||||||
#include "V3GenClk.h"
|
#include "V3GenClk.h"
|
||||||
|
|
@ -352,6 +353,11 @@ void process () {
|
||||||
V3Dead::deadifyAll(v3Global.rootp(), true);
|
V3Dead::deadifyAll(v3Global.rootp(), true);
|
||||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree"));
|
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree"));
|
||||||
|
|
||||||
|
// Clock domain crossing analysis
|
||||||
|
if (v3Global.opt.cdc()) {
|
||||||
|
V3Cdc::cdcAll(v3Global.rootp());
|
||||||
|
}
|
||||||
|
|
||||||
// Reorder assignments in pipelined blocks
|
// Reorder assignments in pipelined blocks
|
||||||
if (v3Global.opt.oReorder()) {
|
if (v3Global.opt.oReorder()) {
|
||||||
V3Split::splitReorderAll(v3Global.rootp());
|
V3Split::splitReorderAll(v3Global.rootp());
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2009 by Wilson Snyder. 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.
|
||||||
|
|
||||||
|
compile (
|
||||||
|
v_flags => ['--cdc --lint-only'],
|
||||||
|
verilator_make_gcc => 0,
|
||||||
|
fails => 1,
|
||||||
|
expect=>
|
||||||
|
'%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.rst3_bad_n
|
||||||
|
%Error: Exiting due to.*',
|
||||||
|
);
|
||||||
|
|
||||||
|
file_grep ("$Self->{obj_dir}/V$Self->{name}__cdc.txt", qr/CDC Report/);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed into the Public Domain, for any use,
|
||||||
|
// without warranty, 2009 by Wilson Snyder.
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/
|
||||||
|
// Outputs
|
||||||
|
q0, q1, q2, q3, q4, q5,
|
||||||
|
// Inputs
|
||||||
|
clk, d, rst0_n
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
input d;
|
||||||
|
|
||||||
|
// OK -- from primary
|
||||||
|
input rst0_n;
|
||||||
|
output wire q0;
|
||||||
|
Flop flop0 (.q(q0), .rst_n(rst0_n), .clk(clk), .d(d));
|
||||||
|
|
||||||
|
// OK -- from flop
|
||||||
|
reg rst1_n;
|
||||||
|
always @ (posedge clk) rst1_n <= rst0_n;
|
||||||
|
output wire q1;
|
||||||
|
Flop flop1 (.q(q1), .rst_n(rst1_n), .clk(clk), .d(d));
|
||||||
|
|
||||||
|
// Bad - logic
|
||||||
|
wire rst2_bad_n = rst0_n | rst1_n;
|
||||||
|
output wire q2;
|
||||||
|
Flop flop2 (.q(q2), .rst_n(rst2_bad_n), .clk(clk), .d(d));
|
||||||
|
|
||||||
|
// Bad - logic in submodule
|
||||||
|
wire rst3_bad_n;
|
||||||
|
Sub sub (.z(rst3_bad_n), .a(rst0_n), .b(rst1_n));
|
||||||
|
output wire q3;
|
||||||
|
Flop flop3 (.q(q3), .rst_n(rst3_bad_n), .clk(clk), .d(d));
|
||||||
|
|
||||||
|
// OK - bit selection
|
||||||
|
reg [3:0] rst4_n;
|
||||||
|
always @ (posedge clk) rst4_n <= {4{rst0_n}};
|
||||||
|
output wire q4;
|
||||||
|
Flop flop4 (.q(q4), .rst_n(rst4_n[1]), .clk(clk), .d(d));
|
||||||
|
|
||||||
|
// Bad - logic, but waived
|
||||||
|
// verilator lint_off CDCRSTLOGIC
|
||||||
|
wire rst5_waive_n = rst0_n & rst1_n;
|
||||||
|
// verilator lint_on CDCRSTLOGIC
|
||||||
|
output wire q5;
|
||||||
|
Flop flop5 (.q(q5), .rst_n(rst5_waive_n), .clk(clk), .d(d));
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$display("%%Error: Not a runnable test");
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module Flop (
|
||||||
|
input clk,
|
||||||
|
input d,
|
||||||
|
input rst_n,
|
||||||
|
output q);
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) q <= 1'b0;
|
||||||
|
else q <= d;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module Sub (input a, b,
|
||||||
|
output z);
|
||||||
|
wire z = a|b;
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue