Add --clk and related optimizations, msg1533.

This commit is contained in:
Wilson Snyder
2015-03-12 19:20:46 -04:00
parent 005b4b7609
commit 486d69da5f
26 changed files with 456 additions and 23 deletions
+4
View File
@@ -176,6 +176,10 @@ string AstNode::prettyName(const string& namein) {
pretty += ".";
pos += 7;
}
else if (0==strncmp(pos,"->",2)) {
pretty += ".";
pos += 2;
}
else if (0==strncmp(pos,"__PVT__",7)) {
pretty += "";
pos += 7;
+36 -2
View File
@@ -257,7 +257,9 @@ public:
VAR_PUBLIC_FLAT_RW, // V3LinkParse moves to AstVar::sigPublic
VAR_ISOLATE_ASSIGNMENTS, // V3LinkParse moves to AstVar::attrIsolateAssign
VAR_SC_BV, // V3LinkParse moves to AstVar::attrScBv
VAR_SFORMAT // V3LinkParse moves to AstVar::attrSFormat
VAR_SFORMAT, // V3LinkParse moves to AstVar::attrSFormat
VAR_CLOCKER, // V3LinkParse moves to AstVar::attrClocker
VAR_NO_CLOCKER // V3LinkParse moves to AstVar::attrClocker
};
enum en m_e;
const char* ascii() const {
@@ -270,7 +272,8 @@ public:
"MEMBER_BASE",
"VAR_BASE", "VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC",
"VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD","VAR_PUBLIC_FLAT_RW",
"VAR_ISOLATE_ASSIGNMENTS", "VAR_SC_BV", "VAR_SFORMAT"
"VAR_ISOLATE_ASSIGNMENTS", "VAR_SC_BV", "VAR_SFORMAT", "VAR_CLOCKER",
"VAR_NO_CLOCKER"
};
return names[m_e];
};
@@ -485,6 +488,37 @@ public:
//######################################################################
class AstVarAttrClocker {
public:
enum en {
CLOCKER_UNKNOWN=0,
CLOCKER_YES,
CLOCKER_NO,
_ENUM_END
};
enum en m_e;
// CONSTRUCTOR - note defaults to *UNKNOWN*
inline AstVarAttrClocker () : m_e(CLOCKER_UNKNOWN) {}
inline AstVarAttrClocker (en _e) : m_e(_e) {}
explicit inline AstVarAttrClocker (int _e) : m_e(static_cast<en>(_e)) {}
operator en () const { return m_e; }
AstVarAttrClocker invert() const {
if (m_e==CLOCKER_YES) return CLOCKER_NO;
else if (m_e==CLOCKER_NO) return CLOCKER_YES;
else return m_e;
}
const char* ascii() const {
static const char* names[] = {
"","clker","non_clker"};
return names[m_e]; }
};
inline bool operator== (AstVarAttrClocker lhs, AstVarAttrClocker rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstVarAttrClocker lhs, AstVarAttrClocker::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstVarAttrClocker::en lhs, AstVarAttrClocker rhs) { return (lhs == rhs.m_e); }
inline ostream& operator<<(ostream& os, AstVarAttrClocker rhs) { return os<<rhs.ascii(); }
//######################################################################
class VAlwaysKwd {
public:
enum en {
+4 -2
View File
@@ -942,6 +942,7 @@ private:
bool m_isIfaceParent:1; // dtype is reference to interface present in this module
bool m_noSubst:1; // Do not substitute out references
bool m_trace:1; // Trace this variable
AstVarAttrClocker m_attrClocker;
void init() {
m_input=false; m_output=false; m_tristate=false; m_declOutput=false;
@@ -952,8 +953,7 @@ private:
m_funcLocal=false; m_funcReturn=false;
m_attrClockEn=false; m_attrScBv=false; m_attrIsolateAssign=false; m_attrSFormat=false;
m_fileDescr=false; m_isConst=false; m_isStatic=false; m_isPulldown=false; m_isPullup=false;
m_isIfaceParent=false;
m_noSubst=false;
m_isIfaceParent=false; m_attrClocker=AstVarAttrClocker::CLOCKER_UNKNOWN; m_noSubst=false;
m_trace=false;
}
public:
@@ -1026,6 +1026,7 @@ public:
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
void attrClockEn(bool flag) { m_attrClockEn = flag; }
void attrClocker(AstVarAttrClocker flag) { m_attrClocker = flag; }
void attrFileDescr(bool flag) { m_fileDescr = flag; }
void attrScClocked(bool flag) { m_scClocked = flag; }
void attrScBv(bool flag) { m_attrScBv = flag; }
@@ -1105,6 +1106,7 @@ public:
bool attrScClocked() const { return m_scClocked; }
bool attrSFormat() const { return m_attrSFormat; }
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
AstVarAttrClocker attrClocker() const { return m_attrClocker; }
virtual string verilogKwd() const;
void propagateAttrFrom(AstVar* fromp) {
// This is getting connected to fromp; keep attributes
+3 -2
View File
@@ -65,6 +65,7 @@ public:
CASEWITHX, // Case with X values
CASEX, // Casex
CDCRSTLOGIC, // Logic in async reset path
CLKDATA, // Clock used as data
CMPCONST, // Comparison is constant due to limited range
COMBDLY, // Combinatorial delayed assignment
DEFPARAM, // Style: Defparam
@@ -119,8 +120,8 @@ public:
" EC_FIRST_WARN",
"ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN",
"BLKANDNBLK", "BLKSEQ",
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CDCRSTLOGIC", "CMPCONST",
"COMBDLY", "DEFPARAM", "DECLFILENAME",
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CDCRSTLOGIC", "CLKDATA",
"CMPCONST", "COMBDLY", "DEFPARAM", "DECLFILENAME",
"ENDLABEL", "GENCLK",
"IFDEPTH", "IMPERFECTSCH", "IMPLICIT", "IMPURE",
"INCABSPATH", "INITIALDLY",
+10
View File
@@ -237,6 +237,16 @@ private:
m_varp->attrScBv(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_CLOCKER) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->attrClocker(AstVarAttrClocker::CLOCKER_YES);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_NO_CLOCKER) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->attrClocker(AstVarAttrClocker::CLOCKER_NO);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
}
virtual void visit(AstAlwaysPublic* nodep, AstNUser*) {
+24 -1
View File
@@ -157,12 +157,27 @@ bool V3Options::isFuture(const string& flag) const {
bool V3Options::isLibraryFile(const string& filename) const {
return m_libraryFiles.find(filename) != m_libraryFiles.end();
}
void V3Options::addLibraryFile(const string& filename) {
if (m_libraryFiles.find(filename) == m_libraryFiles.end()) {
m_libraryFiles.insert(filename);
}
}
bool V3Options::isClocker(const string& signame) const {
return m_clockers.find(signame) != m_clockers.end();
}
void V3Options::addClocker(const string& signame) {
if (m_clockers.find(signame) == m_clockers.end()) {
m_clockers.insert(signame);
}
}
bool V3Options::isNoClocker(const string& signame) const {
return m_noClockers.find(signame) != m_noClockers.end();
}
void V3Options::addNoClocker(const string& signame) {
if (m_noClockers.find(signame) == m_noClockers.end()) {
m_noClockers.insert(signame);
}
}
void V3Options::addVFile(const string& filename) {
// We use a list for v files, because it's legal to have includes
// in a specific order and multiple of them.
@@ -798,6 +813,14 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
shift;
V3Options::addLibraryFile(parseFileArg(optdir,argv[i]));
}
else if ( !strcmp (sw, "-clk") && (i+1)<argc ) {
shift;
V3Options::addClocker(argv[i]);
}
else if ( !strcmp (sw, "-no-clk") && (i+1)<argc ) {
shift;
V3Options::addNoClocker(argv[i]);
}
else if ( !strcmp (sw, "-V") ) {
showVersion(true);
exit(0);
+6
View File
@@ -53,6 +53,8 @@ class V3Options {
V3StringSet m_ldLibs; // argument: user LDFLAGS
V3StringSet m_futures; // argument: -Wfuture- list
V3StringSet m_libraryFiles; // argument: Verilog -v files
V3StringSet m_clockers; // argument: Verilog -clk signals
V3StringSet m_noClockers; // argument: Verilog -noclk signals
V3StringList m_vFiles; // argument: Verilog files to read
DebugSrcMap m_debugSrcs; // argument: --debugi-<srcfile>=<level>
@@ -184,6 +186,8 @@ class V3Options {
void addCFlags(const string& filename);
void addLdLibs(const string& filename);
void addLibraryFile(const string& filename);
void addClocker(const string& signame);
void addNoClocker(const string& signame);
void addVFile(const string& filename);
// ACCESSORS (options)
@@ -270,6 +274,8 @@ class V3Options {
bool isFuture(const string& flag) const;
bool isLibraryFile(const string& filename) const;
bool isClocker(const string& signame) const;
bool isNoClocker(const string& signame) const;
// ACCESSORS (optimization options)
bool oAcycSimp() const { return m_oAcycSimp; }
+210 -1
View File
@@ -241,6 +241,185 @@ struct OrderVarFanoutCmp {
}
};
//######################################################################
// The class is used for propagating the clocker attribute for further
// avoiding marking clock signals as circular.
// Transformation:
// while (newClockerMarked)
// check all assignments
// if RHS is marked as clocker:
// mark LHS as clocker as well.
// newClockerMarked = true;
//
// In addition it also check whether clock and data signals are mixed, and
// produce a CLKDATA warning if so.
//
class OrderClkMarkVisitor : public AstNVisitor {
private:
bool m_hasClk; // flag indicating whether there is clock signal on rhs
bool m_inClocked; // Currently inside a sequential block
bool m_newClkMarked; // Flag for deciding whether a new run is needed
bool m_inAss; // Currently inside of a assignment
int m_childClkWidth; // If in hasClk, width of clock signal in child
int m_rightClkWidth; // Clk width on the RHS
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
m_hasClk = false;
if (AstVarRef* varrefp = nodep->rhsp()->castVarRef()) {
this->visit(varrefp, NULL);
m_rightClkWidth = varrefp->width();
if (varrefp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) {
if (m_inClocked) {
varrefp->v3warn(CLKDATA, "Clock used as data (on rhs of assignment) in sequential block "<<varrefp<<endl);
} else {
m_hasClk = true;
UINFO(5, "node is already marked as clocker "<<varrefp<<endl);
}
}
} else {
m_inAss = true;
m_childClkWidth = 0;
nodep->rhsp()->iterateAndNext(*this);
m_rightClkWidth = m_childClkWidth;
m_inAss = false;
}
// do the marking
if (m_hasClk) {
if (nodep->lhsp()->width() > m_rightClkWidth) {
nodep->v3warn(CLKDATA, "Clock is assigned to part of data signal "<< nodep->lhsp()<<endl);
UINFO(4, "CLKDATA: lhs with width " << nodep->lhsp()->width() <<endl);
UINFO(4, " but rhs clock with width " << m_rightClkWidth <<endl);
return; // skip the marking
}
AstVarRef* lhsp = nodep->lhsp()->castVarRef();
if (lhsp && (lhsp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_UNKNOWN)) {
lhsp->varp()->attrClocker(AstVarAttrClocker::CLOCKER_YES); // mark as clocker
m_newClkMarked = true; // enable a further run since new clocker is marked
UINFO(5, "node is newly marked as clocker by assignment "<<lhsp<<endl);
}
}
}
virtual void visit(AstVarRef* nodep, AstNUser*) {
if (m_inAss && nodep->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) {
if (m_inClocked) {
nodep->v3warn(CLKDATA, "Clock used as data (on rhs of assignment) in sequential block "<<nodep->prettyName());
} else {
m_hasClk = true;
m_childClkWidth = nodep->width(); // Pass up
UINFO(5, "node is already marked as clocker "<<nodep<<endl);
}
}
}
virtual void visit(AstConcat* nodep, AstNUser* wp) {
if (m_inAss) {
nodep->lhsp()->iterateAndNext(*this);
int lw = m_childClkWidth;
nodep->rhsp()->iterateAndNext(*this);
int rw = m_childClkWidth;
m_childClkWidth = lw + rw; // Pass up
}
}
virtual void visit(AstNodeSel* nodep, AstNUser* wp) {
if (m_inAss) {
nodep->iterateChildren(*this);
// Pass up result width
if (m_childClkWidth > nodep->width()) m_childClkWidth = nodep->width();
}
}
virtual void visit(AstSel* nodep, AstNUser*) {
if (m_inAss) {
nodep->iterateChildren(*this);
if (m_childClkWidth > nodep->width()) m_childClkWidth = nodep->width();
}
}
virtual void visit(AstReplicate* nodep, AstNUser*) {
if (m_inAss) {
nodep->iterateChildren(*this);
if (nodep->rhsp()->castConst()) {
m_childClkWidth = m_childClkWidth * nodep->rhsp()->castConst()->toUInt();
} else {
m_childClkWidth = nodep->width(); // can not check in this case.
}
}
}
virtual void visit(AstActive* nodep, AstNUser*) {
m_inClocked = nodep->hasClocked();
nodep->iterateChildren(*this);
m_inClocked = false;
}
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
OrderClkMarkVisitor(AstNode* nodep) {
m_hasClk = false;
m_inClocked = false;
m_inAss = false;
m_childClkWidth = 0;
m_rightClkWidth = 0;
do {
m_newClkMarked = false;
nodep->accept(*this);
} while (m_newClkMarked);
}
virtual ~OrderClkMarkVisitor() {}
};
//######################################################################
// The class used to check if the assignment has clocker inside.
class OrderClkAssVisitor : public AstNVisitor {
private:
bool m_clkAss; // There is signals marked as clocker in the assignment
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
if (AstVarRef* varrefp = nodep->lhsp()->castVarRef() )
if (varrefp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) {
m_clkAss = true;
UINFO(6, "node was marked as clocker "<<varrefp<<endl);
}
nodep->rhsp()->iterateChildren(*this);
}
virtual void visit(AstVarRef* nodep, AstNUser*) {
if (nodep->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) {
m_clkAss = true;
UINFO(6, "node was marked as clocker "<<nodep<<endl);
}
}
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
OrderClkAssVisitor(AstNode* nodep) {
m_clkAss = false;
nodep->accept(*this);
}
virtual ~OrderClkAssVisitor() {}
// METHODS
bool isClkAss() {return m_clkAss;}
};
//######################################################################
// Order class functions
@@ -280,6 +459,7 @@ private:
AstActive* m_activep; // Current activation block
bool m_inSenTree; // Underneath AstSenItem; any varrefs are clocks
bool m_inClocked; // Underneath clocked block
bool m_inClkAss; // Underneath AstAssign
bool m_inPre; // Underneath AstAssignPre
bool m_inPost; // Underneath AstAssignPost
OrderLogicVertex* m_activeSenVxp; // Sensitivity vertex
@@ -356,6 +536,15 @@ private:
return NULL;
}
bool isClkAssign(AstNodeAssign* nodep) {
if (AstVarRef* varrefp = nodep->lhsp()->castVarRef()) {
if (varrefp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) {
return true;
}
}
return false;
}
void process();
void processCircular();
typedef deque<OrderEitherVertex*> VertexVec;
@@ -650,6 +839,10 @@ private:
// clock_enable attribute: user's worring about it for us
con = false;
}
if (m_inClkAss && (varscp->varp()->attrClocker()) != AstVarAttrClocker::CLOCKER_YES) {
con = false;
UINFO(4, "nodep used as clock_enable "<<varscp<<" in "<<m_logicVxp->nodep()<<endl);
}
}
if (gen) varscp->user4(varscp->user4() | VU_GEN);
if (con) varscp->user4(varscp->user4() | VU_CON);
@@ -672,7 +865,12 @@ private:
<< varVxp << endl);
varVxp->isDelayed(true);
} else {
new OrderComboCutEdge(&m_graph, m_logicVxp, varVxp);
// If the lhs is a clocker, avoid marking that as circular by
// putting a hard edge instead of normal cuttable
if (varscp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES)
new OrderEdge(&m_graph, m_logicVxp, varVxp, WEIGHT_NORMAL);
else
new OrderComboCutEdge(&m_graph, m_logicVxp, varVxp);
}
// For m_inPost:
// Add edge consumed_var_POST->logic_vertex
@@ -758,17 +956,26 @@ private:
iterateNewStmt(nodep);
}
virtual void visit(AstAssignW* nodep, AstNUser*) {
OrderClkAssVisitor visitor(nodep);
m_inClkAss = visitor.isClkAss();
iterateNewStmt(nodep);
m_inClkAss = false;
}
virtual void visit(AstAssignPre* nodep, AstNUser*) {
OrderClkAssVisitor visitor(nodep);
m_inClkAss = visitor.isClkAss();
m_inPre = true;
iterateNewStmt(nodep);
m_inPre = false;
m_inClkAss = false;
}
virtual void visit(AstAssignPost* nodep, AstNUser*) {
OrderClkAssVisitor visitor(nodep);
m_inClkAss = visitor.isClkAss();
m_inPost = true;
iterateNewStmt(nodep);
m_inPost = false;
m_inClkAss = false;
}
virtual void visit(AstCoverToggle* nodep, AstNUser*) {
iterateNewStmt(nodep);
@@ -799,6 +1006,7 @@ public:
m_activep = NULL;
m_inSenTree = false;
m_inClocked = false;
m_inClkAss = false;
m_inPre = m_inPost = false;
m_comboDomainp = NULL;
m_deleteDomainp = NULL;
@@ -1449,6 +1657,7 @@ void OrderVisitor::process() {
void V3Order::orderAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
OrderClkMarkVisitor markVisitor(nodep);
OrderVisitor visitor;
visitor.main(nodep);
}
+6
View File
@@ -243,6 +243,12 @@ private:
UINFO(6," New scope "<<varscp<<endl);
if (m_aboveCellp && !m_aboveCellp->isTrace()) varscp->trace(false);
nodep->user1p(varscp);
if (v3Global.opt.isClocker(varscp->prettyName())) {
nodep->attrClocker(AstVarAttrClocker::CLOCKER_YES);
}
if (v3Global.opt.isNoClocker(varscp->prettyName())) {
nodep->attrClocker(AstVarAttrClocker::CLOCKER_NO);
}
if (!m_scopep) nodep->v3fatalSrc("No scope for var");
m_varScopes.insert(make_pair(make_pair(nodep, m_scopep), varscp));
m_scopep->addVarp(varscp);
+2
View File
@@ -674,6 +674,8 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"/*verilator public_flat_rw*/" { FL; return yVL_PUBLIC_FLAT_RW; } // The @(edge) is converted by the preproc
"/*verilator public_module*/" { FL; return yVL_PUBLIC_MODULE; }
"/*verilator sc_clock*/" { FL; return yVL_CLOCK; }
"/*verilator clocker*/" { FL; return yVL_CLOCKER; }
"/*verilator no_clocker*/" { FL; return yVL_NO_CLOCKER; }
"/*verilator sc_bv*/" { FL; return yVL_SC_BV; }
"/*verilator sformat*/" { FL; return yVL_SFORMAT; }
"/*verilator systemc_clock*/" { FL; return yVL_CLOCK; }
+4
View File
@@ -489,6 +489,8 @@ class AstSenTree;
%token<fl> yD_WRITE "$write"
%token<fl> yVL_CLOCK "/*verilator sc_clock*/"
%token<fl> yVL_CLOCKER "/*verilator clocker*/"
%token<fl> yVL_NO_CLOCKER "/*verilator no_clocker*/"
%token<fl> yVL_CLOCK_ENABLE "/*verilator clock_enable*/"
%token<fl> yVL_COVERAGE_BLOCK_OFF "/*verilator coverage_block_off*/"
%token<fl> yVL_FULL_CASE "/*verilator full_case*/"
@@ -1927,6 +1929,8 @@ sigAttrList<nodep>:
sigAttr<nodep>:
yVL_CLOCK { $$ = new AstAttrOf($1,AstAttrType::VAR_CLOCK); }
| yVL_CLOCKER { $$ = new AstAttrOf($1,AstAttrType::VAR_CLOCKER); }
| yVL_NO_CLOCKER { $$ = new AstAttrOf($1,AstAttrType::VAR_NO_CLOCKER); }
| yVL_CLOCK_ENABLE { $$ = new AstAttrOf($1,AstAttrType::VAR_CLOCK_ENABLE); }
| yVL_PUBLIC { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC); v3Global.dpi(true); }
| yVL_PUBLIC_FLAT { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT); v3Global.dpi(true); }