2012-04-13 01:08:20 +00:00
|
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Gate optimizations, such as wire elimination
|
|
|
|
|
//
|
2008-04-25 12:14:27 +00:00
|
|
|
|
// Code available from: http://www.veripool.org/verilator
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2016-01-07 01:36:41 +00:00
|
|
|
|
// Copyright 2003-2016 by Wilson Snyder. This program is free software; you can
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 21:07:57 +00:00
|
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
|
|
|
|
// 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.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Gate's Transformations:
|
2008-06-10 01:25:10 +00:00
|
|
|
|
//
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Extract a graph of the *entire* netlist with cells expanded
|
|
|
|
|
// Perform constant optimization across the graph
|
|
|
|
|
// Create VARSCOPEs for any variables we can rip out
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
2006-12-18 19:20:45 +00:00
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2008-06-30 17:11:25 +00:00
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdarg>
|
2006-08-26 11:35:28 +00:00
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <list>
|
2015-02-12 00:36:34 +00:00
|
|
|
|
#include <map>
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Gate.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
#include "V3Graph.h"
|
|
|
|
|
#include "V3Const.h"
|
|
|
|
|
#include "V3Stats.h"
|
2013-02-21 01:14:15 +00:00
|
|
|
|
#include "V3Hashed.h"
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
typedef list<AstNodeVarRef*> GateVarRefList;
|
|
|
|
|
|
2016-02-05 01:47:55 +00:00
|
|
|
|
#define GATE_DEDUP_MAX_DEPTH 20
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class GateBaseVisitor : public AstNVisitor {
|
|
|
|
|
public:
|
2009-01-21 21:56:50 +00:00
|
|
|
|
static int debug() {
|
|
|
|
|
static int level = -1;
|
|
|
|
|
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
|
|
|
|
return level;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
};
|
|
|
|
|
|
2013-02-21 01:14:15 +00:00
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class GateLogicVertex;
|
|
|
|
|
class GateVarVertex;
|
|
|
|
|
class GateGraphBaseVisitor {
|
|
|
|
|
public:
|
|
|
|
|
virtual AstNUser* visit(GateLogicVertex* vertexp, AstNUser* vup=NULL) =0;
|
|
|
|
|
virtual AstNUser* visit(GateVarVertex* vertexp, AstNUser* vup=NULL) =0;
|
2013-05-01 02:55:03 +00:00
|
|
|
|
virtual ~GateGraphBaseVisitor() {}
|
2013-02-21 01:14:15 +00:00
|
|
|
|
};
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//######################################################################
|
|
|
|
|
// Support classes
|
|
|
|
|
|
|
|
|
|
class GateEitherVertex : public V3GraphVertex {
|
|
|
|
|
AstScope* m_scopep;
|
|
|
|
|
bool m_reducible; // True if this node should be able to be eliminated
|
2013-02-21 01:14:15 +00:00
|
|
|
|
bool m_dedupable; // True if this node should be able to be deduped
|
2006-08-26 11:35:28 +00:00
|
|
|
|
bool m_consumed; // Output goes to something meaningful
|
|
|
|
|
public:
|
|
|
|
|
GateEitherVertex(V3Graph* graphp, AstScope* scopep)
|
2013-02-21 01:14:15 +00:00
|
|
|
|
: V3GraphVertex(graphp), m_scopep(scopep), m_reducible(true), m_dedupable(true), m_consumed(false) {}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
virtual ~GateEitherVertex() {}
|
2013-02-16 13:07:18 +00:00
|
|
|
|
// ACCESSORS
|
2006-08-26 11:35:28 +00:00
|
|
|
|
virtual string dotStyle() const { return m_consumed?"":"dotted"; }
|
|
|
|
|
AstScope* scopep() const { return m_scopep; }
|
|
|
|
|
bool reducible() const { return m_reducible; }
|
2013-02-21 01:14:15 +00:00
|
|
|
|
bool dedupable() const { return m_dedupable; }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
void setConsumed(const char* consumedReason) {
|
|
|
|
|
m_consumed = true;
|
|
|
|
|
//UINFO(0,"\t\tSetConsumed "<<consumedReason<<" "<<this<<endl);
|
|
|
|
|
}
|
|
|
|
|
bool consumed() const { return m_consumed; }
|
|
|
|
|
void clearReducible(const char* nonReducibleReason) {
|
|
|
|
|
m_reducible = false;
|
|
|
|
|
//UINFO(0," NR: "<<nonReducibleReason<<" "<<name()<<endl);
|
|
|
|
|
}
|
2013-02-21 01:14:15 +00:00
|
|
|
|
void clearDedupable(const char* nonDedupableReason) {
|
|
|
|
|
m_dedupable = false;
|
|
|
|
|
//UINFO(0," ND: "<<nonDedupableReason<<" "<<name()<<endl);
|
|
|
|
|
}
|
|
|
|
|
void clearReducibleAndDedupable(const char* nonReducibleReason) {
|
|
|
|
|
clearReducible(nonReducibleReason);
|
|
|
|
|
clearDedupable(nonReducibleReason);
|
|
|
|
|
}
|
|
|
|
|
virtual AstNUser* accept(GateGraphBaseVisitor& v, AstNUser* vup=NULL) =0;
|
|
|
|
|
// Returns only the result from the LAST vertex iterated over
|
|
|
|
|
AstNUser* iterateInEdges(GateGraphBaseVisitor& v, AstNUser* vup=NULL) {
|
2013-02-22 22:14:27 +00:00
|
|
|
|
AstNUser* retp = NULL;
|
2013-02-21 01:14:15 +00:00
|
|
|
|
for (V3GraphEdge* edgep = inBeginp(); edgep; edgep = edgep->inNextp()) {
|
|
|
|
|
retp = dynamic_cast<GateEitherVertex*>(edgep->fromp())->accept(v, vup);
|
|
|
|
|
}
|
|
|
|
|
return retp;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class GateVarVertex : public GateEitherVertex {
|
|
|
|
|
AstVarScope* m_varScp;
|
|
|
|
|
bool m_isTop;
|
|
|
|
|
bool m_isClock;
|
2010-12-31 12:51:14 +00:00
|
|
|
|
AstNode* m_rstSyncNodep; // Used as reset and not in SenItem, in clocked always
|
|
|
|
|
AstNode* m_rstAsyncNodep; // Used as reset and in SenItem, in clocked always
|
2006-08-26 11:35:28 +00:00
|
|
|
|
public:
|
|
|
|
|
GateVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
|
|
|
|
: GateEitherVertex(graphp, scopep), m_varScp(varScp), m_isTop(false)
|
2010-12-31 12:51:14 +00:00
|
|
|
|
, m_isClock(false), m_rstSyncNodep(NULL), m_rstAsyncNodep(NULL) {}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
virtual ~GateVarVertex() {}
|
2013-02-16 13:07:18 +00:00
|
|
|
|
// ACCESSORS
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstVarScope* varScp() const { return m_varScp; }
|
|
|
|
|
virtual string name() const { return (cvtToStr((void*)m_varScp)+" "+varScp()->name()); }
|
|
|
|
|
virtual string dotColor() const { return "blue"; }
|
|
|
|
|
bool isTop() const { return m_isTop; }
|
2010-12-31 12:51:14 +00:00
|
|
|
|
void setIsTop() { m_isTop = true; }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
bool isClock() const { return m_isClock; }
|
2010-12-31 12:51:14 +00:00
|
|
|
|
void setIsClock() { m_isClock = true; setConsumed("isclk"); }
|
|
|
|
|
AstNode* rstSyncNodep() const { return m_rstSyncNodep; }
|
|
|
|
|
void rstSyncNodep(AstNode* nodep) { m_rstSyncNodep=nodep; }
|
|
|
|
|
AstNode* rstAsyncNodep() const { return m_rstAsyncNodep; }
|
|
|
|
|
void rstAsyncNodep(AstNode* nodep) { m_rstAsyncNodep=nodep; }
|
2013-02-16 13:07:18 +00:00
|
|
|
|
// METHODS
|
|
|
|
|
void propagateAttrClocksFrom(GateVarVertex* fromp) {
|
|
|
|
|
// Propagate clock and general attribute onto this node
|
|
|
|
|
varScp()->varp()->propagateAttrFrom(fromp->varScp()->varp());
|
|
|
|
|
if (fromp->isClock()) {
|
|
|
|
|
varScp()->varp()->usedClock(true);
|
|
|
|
|
setIsClock();
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-21 01:14:15 +00:00
|
|
|
|
AstNUser* accept(GateGraphBaseVisitor& v, AstNUser* vup=NULL) { return v.visit(this,vup); }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class GateLogicVertex : public GateEitherVertex {
|
|
|
|
|
AstNode* m_nodep;
|
|
|
|
|
AstActive* m_activep; // Under what active; NULL is ok (under cfunc or such)
|
|
|
|
|
bool m_slow; // In slow block
|
|
|
|
|
public:
|
|
|
|
|
GateLogicVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep, AstActive* activep, bool slow)
|
|
|
|
|
: GateEitherVertex(graphp,scopep), m_nodep(nodep), m_activep(activep), m_slow(slow) {}
|
|
|
|
|
virtual ~GateLogicVertex() {}
|
2013-02-16 13:07:18 +00:00
|
|
|
|
// ACCESSORS
|
2009-12-05 15:38:49 +00:00
|
|
|
|
virtual string name() const { return (cvtToStr((void*)m_nodep)+"@"+scopep()->prettyName()); }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
virtual string dotColor() const { return "yellow"; }
|
|
|
|
|
AstNode* nodep() const { return m_nodep; }
|
|
|
|
|
AstActive* activep() const { return m_activep; }
|
|
|
|
|
bool slow() const { return m_slow; }
|
2013-02-21 01:14:15 +00:00
|
|
|
|
AstNUser* accept(GateGraphBaseVisitor& v, AstNUser* vup=NULL) { return v.visit(this,vup); }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Is this a simple math expression with a single input and single output?
|
|
|
|
|
|
|
|
|
|
class GateOkVisitor : public GateBaseVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// RETURN STATE
|
|
|
|
|
bool m_isSimple; // Set false when we know it isn't simple
|
|
|
|
|
GateVarRefList m_rhsVarRefs; // VarRefs on rhs of assignment
|
|
|
|
|
AstNode* m_substTreep; // What to replace the variable with
|
|
|
|
|
// STATE
|
|
|
|
|
bool m_buffersOnly; // Set when we only allow simple buffering, no equations (for clocks)
|
|
|
|
|
AstNodeVarRef* m_lhsVarRef; // VarRef on lhs of assignment (what we're replacing)
|
2013-02-21 01:14:15 +00:00
|
|
|
|
bool m_dedupe; // Set when we use isGateDedupable instead of isGateOptimizable
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
void clearSimple(const char* because) {
|
|
|
|
|
if (m_isSimple) {
|
|
|
|
|
m_isSimple = false;
|
|
|
|
|
UINFO(9, "Clear simple "<<because<<endl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// VISITORS
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeVarRef* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
// We only allow a LHS ref for the var being set, and a RHS ref for something else being read.
|
|
|
|
|
if (nodep->varScopep()->varp()->isSc()) {
|
|
|
|
|
clearSimple("SystemC sig"); // Don't want to eliminate the VL_ASSIGN_SI's
|
|
|
|
|
}
|
|
|
|
|
if (nodep->lvalue()) {
|
|
|
|
|
if (m_lhsVarRef) clearSimple(">1 lhs varRefs");
|
|
|
|
|
m_lhsVarRef = nodep;
|
|
|
|
|
} else {
|
|
|
|
|
if (m_rhsVarRefs.size()>1) {
|
|
|
|
|
AstNodeVarRef* lastRefp = m_rhsVarRefs.back();
|
|
|
|
|
if (0) { // Diable the multiple-input optimization
|
|
|
|
|
clearSimple(">1 rhs varRefs");
|
|
|
|
|
} else {
|
|
|
|
|
if (m_buffersOnly) clearSimple(">1 rhs varRefs");
|
|
|
|
|
if (!nodep->varScopep()->varp()->gateMultiInputOptimizable()
|
|
|
|
|
// We didn't check multiInput on the first varref, so check it here
|
|
|
|
|
|| !lastRefp->varScopep()->varp()->gateMultiInputOptimizable()) {
|
|
|
|
|
clearSimple("!gateMultiInputOptimizable");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_rhsVarRefs.push_back(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeAssign* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_substTreep = nodep->rhsp();
|
|
|
|
|
if (!nodep->lhsp()->castNodeVarRef())
|
2013-07-30 01:53:43 +00:00
|
|
|
|
clearSimple("ASSIGN(non-VARREF)");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
else nodep->iterateChildren(*this);
|
|
|
|
|
// We don't push logic other then assignments/NOTs into SenItems
|
|
|
|
|
// This avoids a mess in computing what exactly a POSEDGE is
|
|
|
|
|
// V3Const cleans up any NOTs by flipping the edges for us
|
|
|
|
|
if (m_buffersOnly
|
|
|
|
|
&& !(nodep->rhsp()->castVarRef()
|
2014-04-01 00:29:35 +00:00
|
|
|
|
// Avoid making non-clocked logic into clocked,
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// as it slows down the verilator_sim_benchmark
|
|
|
|
|
|| (nodep->rhsp()->castNot()
|
|
|
|
|
&& nodep->rhsp()->castNot()->lhsp()->castVarRef()
|
|
|
|
|
&& nodep->rhsp()->castNot()->lhsp()->castVarRef()->varp()->isUsedClock())
|
|
|
|
|
)) {
|
|
|
|
|
clearSimple("Not a buffer (goes to a clock)");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//--------------------
|
|
|
|
|
// Default
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNode* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// *** Special iterator
|
|
|
|
|
if (!m_isSimple) return; // Fastpath
|
2013-02-21 01:14:15 +00:00
|
|
|
|
if (!(m_dedupe ? nodep->isGateDedupable() : nodep->isGateOptimizable())
|
2011-12-16 03:23:11 +00:00
|
|
|
|
|| !nodep->isPure()
|
|
|
|
|
|| nodep->isBrancher()) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
UINFO(5, "Non optimizable type: "<<nodep<<endl);
|
|
|
|
|
clearSimple("Non optimizable type");
|
|
|
|
|
}
|
|
|
|
|
else nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
2013-02-21 01:14:15 +00:00
|
|
|
|
GateOkVisitor(AstNode* nodep, bool buffersOnly, bool dedupe) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_isSimple = true;
|
|
|
|
|
m_substTreep = NULL;
|
|
|
|
|
m_buffersOnly = buffersOnly;
|
|
|
|
|
m_lhsVarRef = NULL;
|
2013-02-21 01:14:15 +00:00
|
|
|
|
m_dedupe = dedupe;
|
2007-01-31 21:49:13 +00:00
|
|
|
|
// Iterate
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->accept(*this);
|
2007-01-31 21:49:13 +00:00
|
|
|
|
// Check results
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (!m_substTreep) {
|
|
|
|
|
clearSimple("No assignment found\n");
|
|
|
|
|
}
|
2014-06-09 01:36:18 +00:00
|
|
|
|
for (GateVarRefList::const_iterator it = m_rhsVarRefs.begin();
|
|
|
|
|
it != m_rhsVarRefs.end(); ++it) {
|
2007-01-31 21:49:13 +00:00
|
|
|
|
if (m_lhsVarRef && m_lhsVarRef->varScopep() == (*it)->varScopep()) {
|
|
|
|
|
clearSimple("Circular logic\n"); // Oh my, we'll get a UNOPTFLAT much later.
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (debug()>=9 && !m_isSimple) {
|
|
|
|
|
nodep->dumpTree(cout,"\tgate!Ok: ");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual ~GateOkVisitor() {}
|
|
|
|
|
// PUBLIC METHODS
|
|
|
|
|
bool isSimple() const { return m_isSimple; }
|
|
|
|
|
AstNode* substTree() const { return m_substTreep; }
|
|
|
|
|
const GateVarRefList& rhsVarRefs() const {
|
|
|
|
|
return m_rhsVarRefs;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Gate class functions
|
|
|
|
|
|
|
|
|
|
class GateVisitor : public GateBaseVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
//Entire netlist:
|
2008-11-25 14:03:49 +00:00
|
|
|
|
// AstVarScope::user1p -> GateVarVertex* for usage var, 0=not set yet
|
|
|
|
|
// {statement}Node::user1p -> GateLogicVertex* for this statement
|
2010-12-31 12:51:14 +00:00
|
|
|
|
// AstVarScope::user2 -> bool: Signal used in SenItem in *this* always statement
|
|
|
|
|
// AstVar::user2 -> bool: Warned about SYNCASYNCNET
|
2008-11-25 14:03:49 +00:00
|
|
|
|
AstUser1InUse m_inuser1;
|
2010-12-31 12:51:14 +00:00
|
|
|
|
AstUser2InUse m_inuser2;
|
2007-01-31 21:49:13 +00:00
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// STATE
|
|
|
|
|
V3Graph m_graph; // Scoreboard of var usages/dependencies
|
|
|
|
|
GateLogicVertex* m_logicVertexp; // Current statement being tracked, NULL=ignored
|
|
|
|
|
AstScope* m_scopep; // Current scope being processed
|
2009-11-07 11:20:20 +00:00
|
|
|
|
AstNodeModule* m_modp; // Current module
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstActive* m_activep; // Current active
|
|
|
|
|
bool m_activeReducible; // Is activation block reducible?
|
|
|
|
|
bool m_inSenItem; // Underneath AstSenItem; any varrefs are clocks
|
|
|
|
|
bool m_inSlow; // Inside a slow structure
|
|
|
|
|
V3Double0 m_statSigs; // Statistic tracking
|
|
|
|
|
V3Double0 m_statRefs; // Statistic tracking
|
2013-02-21 01:14:15 +00:00
|
|
|
|
V3Double0 m_statDedupLogic; // Statistic tracking
|
2014-11-06 02:09:35 +00:00
|
|
|
|
V3Double0 m_statAssignMerged; // Statistic tracking
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
void iterateNewStmt(AstNode* nodep, const char* nonReducibleReason, const char* consumeReason) {
|
|
|
|
|
if (m_scopep) {
|
|
|
|
|
UINFO(4," STMT "<<nodep<<endl);
|
|
|
|
|
// m_activep is null under AstCFunc's, that's ok.
|
|
|
|
|
m_logicVertexp = new GateLogicVertex(&m_graph, m_scopep, nodep, m_activep, m_inSlow);
|
|
|
|
|
if (nonReducibleReason) {
|
2013-02-21 01:14:15 +00:00
|
|
|
|
m_logicVertexp->clearReducibleAndDedupable(nonReducibleReason);
|
|
|
|
|
} else if (!m_activeReducible) {
|
|
|
|
|
m_logicVertexp->clearReducible("Block Unreducible"); // Sequential logic is dedupable
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
if (consumeReason) m_logicVertexp->setConsumed(consumeReason);
|
|
|
|
|
if (nodep->castSenItem()) m_logicVertexp->setConsumed("senItem");
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_logicVertexp = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GateVarVertex* makeVarVertex(AstVarScope* varscp) {
|
2008-11-25 14:03:49 +00:00
|
|
|
|
GateVarVertex* vertexp = (GateVarVertex*)(varscp->user1p());
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (!vertexp) {
|
|
|
|
|
UINFO(6,"New vertex "<<varscp<<endl);
|
|
|
|
|
vertexp = new GateVarVertex(&m_graph, m_scopep, varscp);
|
2008-11-25 14:03:49 +00:00
|
|
|
|
varscp->user1p(vertexp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (varscp->varp()->isSigPublic()) {
|
|
|
|
|
// Public signals shouldn't be changed, pli code might be messing with them
|
2013-02-21 01:14:15 +00:00
|
|
|
|
vertexp->clearReducibleAndDedupable("SigPublic");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
vertexp->setConsumed("SigPublic");
|
|
|
|
|
}
|
|
|
|
|
if (varscp->varp()->isIO() && varscp->scopep()->isTop()) {
|
|
|
|
|
// We may need to convert to/from sysc/reg sigs
|
|
|
|
|
vertexp->setIsTop();
|
2013-02-21 01:14:15 +00:00
|
|
|
|
vertexp->clearReducibleAndDedupable("isTop");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
vertexp->setConsumed("isTop");
|
|
|
|
|
}
|
|
|
|
|
if (varscp->varp()->isUsedClock()) vertexp->setConsumed("clock");
|
|
|
|
|
}
|
|
|
|
|
return vertexp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void optimizeSignals(bool allowMultiIn);
|
2015-02-12 00:36:34 +00:00
|
|
|
|
bool elimLogicOkOutputs(GateLogicVertex* consumeVertexp, const GateOkVisitor& okVisitor);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
void optimizeElimVar(AstVarScope* varscp, AstNode* logicp, AstNode* consumerp);
|
2010-12-31 12:51:14 +00:00
|
|
|
|
void warnSignals();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
void consumedMark();
|
|
|
|
|
void consumedMarkRecurse(GateEitherVertex* vertexp);
|
|
|
|
|
void consumedMove();
|
|
|
|
|
void replaceAssigns();
|
2013-02-21 01:14:15 +00:00
|
|
|
|
void dedupe();
|
2014-11-06 02:09:35 +00:00
|
|
|
|
void mergeAssigns();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
// VISITORS
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNetlist* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
//if (debug()>6) m_graph.dump();
|
|
|
|
|
if (debug()>6) m_graph.dumpDotFilePrefixed("gate_pre");
|
2015-04-27 10:47:29 +00:00
|
|
|
|
warnSignals(); // Before loss of sync/async pointers
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_graph.removeRedundantEdgesSum(&V3GraphEdge::followAlwaysTrue);
|
|
|
|
|
m_graph.dumpDotFilePrefixed("gate_simp");
|
|
|
|
|
// Find gate interconnect and optimize
|
|
|
|
|
m_graph.userClearVertices(); // vertex->user(): bool. True indicates we've set it as consumed
|
|
|
|
|
// Get rid of buffers first,
|
|
|
|
|
optimizeSignals(false);
|
|
|
|
|
// Then propagate more complicated equations
|
|
|
|
|
optimizeSignals(true);
|
2013-02-21 01:14:15 +00:00
|
|
|
|
// Remove redundant logic
|
|
|
|
|
if (v3Global.opt.oDedupe()) dedupe();
|
2014-11-06 02:09:35 +00:00
|
|
|
|
if (v3Global.opt.oAssemble()) mergeAssigns();
|
2015-04-27 10:47:29 +00:00
|
|
|
|
// Consumption warnings
|
2006-08-26 11:35:28 +00:00
|
|
|
|
consumedMark();
|
|
|
|
|
m_graph.dumpDotFilePrefixed("gate_opt");
|
|
|
|
|
// Rewrite assignments
|
|
|
|
|
consumedMove();
|
|
|
|
|
replaceAssigns();
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeModule* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_modp = nodep;
|
|
|
|
|
m_activeReducible = true;
|
|
|
|
|
nodep->iterateChildren(*this);
|
2008-11-12 20:32:09 +00:00
|
|
|
|
m_modp = NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstScope* nodep) {
|
2006-08-26 11:35:28 +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) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Create required blocks and add to module
|
|
|
|
|
UINFO(4," BLOCK "<<nodep<<endl);
|
|
|
|
|
m_activeReducible = !(nodep->hasClocked()); // Seq logic outputs aren't reducible
|
|
|
|
|
m_activep = nodep;
|
2010-12-31 12:51:14 +00:00
|
|
|
|
AstNode::user2ClearTree();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
2010-12-31 12:51:14 +00:00
|
|
|
|
AstNode::user2ClearTree();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_activep = NULL;
|
|
|
|
|
m_activeReducible = true;
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeVarRef* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
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");
|
2010-12-31 12:51:14 +00:00
|
|
|
|
GateVarVertex* vvertexp = makeVarVertex(varscp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
UINFO(5," VARREF to "<<varscp<<endl);
|
2010-12-31 12:51:14 +00:00
|
|
|
|
if (m_inSenItem) vvertexp->setIsClock();
|
|
|
|
|
// For SYNCASYNCNET
|
|
|
|
|
if (m_inSenItem) varscp->user2(true);
|
|
|
|
|
else if (m_activep && m_activep->hasClocked() && !nodep->lvalue()) {
|
|
|
|
|
if (varscp->user2()) {
|
|
|
|
|
if (!vvertexp->rstSyncNodep()) vvertexp->rstSyncNodep(nodep);
|
|
|
|
|
} else {
|
|
|
|
|
if (!vvertexp->rstAsyncNodep()) vvertexp->rstAsyncNodep(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-11-30 22:12:53 +00:00
|
|
|
|
// We use weight of one; if we ref the var more than once, when we simplify,
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// the weight will increase
|
2008-06-10 01:25:10 +00:00
|
|
|
|
if (nodep->lvalue()) {
|
2010-12-31 12:51:14 +00:00
|
|
|
|
new V3GraphEdge(&m_graph, m_logicVertexp, vvertexp, 1);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
} else {
|
2010-12-31 12:51:14 +00:00
|
|
|
|
new V3GraphEdge(&m_graph, vvertexp, m_logicVertexp, 1);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstAlways* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
iterateNewStmt(nodep, (nodep->isJustOneBodyStmt()?NULL:"Multiple Stmts"), NULL);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstAlwaysPublic* nodep) {
|
2010-04-06 00:01:17 +00:00
|
|
|
|
bool lastslow = m_inSlow;
|
|
|
|
|
m_inSlow = true;
|
|
|
|
|
iterateNewStmt(nodep, "AlwaysPublic", NULL);
|
|
|
|
|
m_inSlow = lastslow;
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstCFunc* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
iterateNewStmt(nodep, "User C Function", "User C Function");
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstSenItem* nodep) {
|
2009-01-07 14:37:59 +00:00
|
|
|
|
// Note we look at only AstSenItems, not AstSenGate's
|
|
|
|
|
// The gating term of a AstSenGate is normal logic
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_inSenItem = true;
|
2009-01-07 14:37:59 +00:00
|
|
|
|
if (m_logicVertexp) { // Already under logic; presumably a SenGate
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
} else { // Standalone item, probably right under a SenTree
|
|
|
|
|
iterateNewStmt(nodep, NULL, NULL);
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_inSenItem = false;
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstSenGate* nodep) {
|
2009-01-07 14:37:59 +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, "Clock gater", "Clock gater");
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstInitial* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
bool lastslow = m_inSlow;
|
|
|
|
|
m_inSlow = true;
|
|
|
|
|
iterateNewStmt(nodep, (nodep->isJustOneBodyStmt()?NULL:"Multiple Stmts"), NULL);
|
|
|
|
|
m_inSlow = lastslow;
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstAssignAlias* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
iterateNewStmt(nodep, NULL, NULL);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstAssignW* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
iterateNewStmt(nodep, NULL, NULL);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstCoverToggle* nodep) {
|
2008-12-12 20:34:02 +00:00
|
|
|
|
iterateNewStmt(nodep, "CoverToggle", "CoverToggle");
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstTraceInc* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
bool lastslow = m_inSlow;
|
|
|
|
|
m_inSlow = true;
|
|
|
|
|
iterateNewStmt(nodep, "Tracing", "Tracing");
|
|
|
|
|
m_inSlow = lastslow;
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstConcat* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (nodep->backp()->castNodeAssign() && nodep->backp()->castNodeAssign()->lhsp()==nodep) {
|
|
|
|
|
nodep->v3fatalSrc("Concat on LHS of assignment; V3Const should have deleted it\n");
|
|
|
|
|
}
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
// Default
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNode* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
if (nodep->isOutputter() && m_logicVertexp) m_logicVertexp->setConsumed("outputter");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
2015-10-04 02:33:06 +00:00
|
|
|
|
explicit GateVisitor(AstNode* nodep) {
|
2013-02-21 01:14:15 +00:00
|
|
|
|
AstNode::user1ClearTree();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_logicVertexp = NULL;
|
|
|
|
|
m_scopep = NULL;
|
|
|
|
|
m_modp = NULL;
|
|
|
|
|
m_activep = NULL;
|
|
|
|
|
m_activeReducible = true;
|
|
|
|
|
m_inSenItem = false;
|
|
|
|
|
m_inSlow = false;
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
virtual ~GateVisitor() {
|
|
|
|
|
V3Stats::addStat("Optimizations, Gate sigs deleted", m_statSigs);
|
|
|
|
|
V3Stats::addStat("Optimizations, Gate inputs replaced", m_statRefs);
|
2013-02-21 01:14:15 +00:00
|
|
|
|
V3Stats::addStat("Optimizations, Gate sigs deduped", m_statDedupLogic);
|
2014-11-06 02:09:35 +00:00
|
|
|
|
V3Stats::addStat("Optimizations, Gate assign merged", m_statAssignMerged);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GateVisitor::optimizeSignals(bool allowMultiIn) {
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
|
|
|
|
if (vvertexp->inEmpty()) {
|
2013-02-21 01:14:15 +00:00
|
|
|
|
vvertexp->clearReducibleAndDedupable("inEmpty"); // Can't deal with no sources
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (!vvertexp->isTop() // Ok if top inputs are driverless
|
2010-01-21 23:20:47 +00:00
|
|
|
|
&& !vvertexp->varScp()->varp()->valuep()
|
2006-08-26 11:35:28 +00:00
|
|
|
|
&& !vvertexp->varScp()->varp()->isSigPublic()) {
|
2008-11-22 19:59:22 +00:00
|
|
|
|
UINFO(4, "No drivers "<<vvertexp->varScp()<<endl);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (0) {
|
|
|
|
|
// If we warned here after constant propagation, what the user considered
|
|
|
|
|
// reasonable logic may have disappeared. Issuing a warning would
|
2010-12-31 12:51:14 +00:00
|
|
|
|
// thus be confusing. V3Undriven now handles this.
|
2006-08-26 11:35:28 +00:00
|
|
|
|
vvertexp->varScp()->varp()->v3warn(UNDRIVEN,"Signal has no drivers "
|
|
|
|
|
<<vvertexp->scopep()->prettyName()<<"."
|
|
|
|
|
<<vvertexp->varScp()->varp()->prettyName());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (!vvertexp->inSize1()) {
|
2013-02-21 01:14:15 +00:00
|
|
|
|
vvertexp->clearReducibleAndDedupable("size!1"); // Can't deal with more than one src
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
// Reduce it?
|
|
|
|
|
if (!vvertexp->reducible()) {
|
|
|
|
|
UINFO(8, "SigNotRed "<<vvertexp->name()<<endl);
|
|
|
|
|
} else {
|
|
|
|
|
UINFO(8, "Sig "<<vvertexp->name()<<endl);
|
|
|
|
|
GateLogicVertex* logicVertexp = dynamic_cast<GateLogicVertex*>
|
|
|
|
|
(vvertexp->inBeginp()->fromp());
|
|
|
|
|
UINFO(8, " From "<<logicVertexp->name()<<endl);
|
|
|
|
|
AstNode* logicp = logicVertexp->nodep();
|
|
|
|
|
if (logicVertexp->reducible()) {
|
|
|
|
|
// Can we eliminate?
|
2013-02-21 01:14:15 +00:00
|
|
|
|
GateOkVisitor okVisitor(logicp, vvertexp->isClock(), false);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
bool multiInputs = okVisitor.rhsVarRefs().size() > 1;
|
|
|
|
|
// Was it ok?
|
|
|
|
|
bool doit = okVisitor.isSimple();
|
|
|
|
|
if (doit && multiInputs) {
|
|
|
|
|
if (!allowMultiIn) doit = false;
|
|
|
|
|
// Doit if one input, or not used, or used only once, ignoring traces
|
|
|
|
|
int n=0;
|
|
|
|
|
for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
|
|
|
|
GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(edgep->top());
|
|
|
|
|
if (!consumeVertexp->slow()) { // Not tracing or other slow path junk
|
|
|
|
|
if (edgep->top()->outBeginp()) { // Destination is itself used
|
|
|
|
|
n += edgep->weight();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (n>1) {
|
|
|
|
|
doit = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-01-31 21:49:13 +00:00
|
|
|
|
// Process it
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (!doit) {
|
|
|
|
|
if (allowMultiIn && (debug()>=9)) {
|
|
|
|
|
UINFO(9, "Not ok simp"<<okVisitor.isSimple()<<" mi"<<multiInputs
|
|
|
|
|
<<" ob"<<vvertexp->outBeginp()<<" on"<<(vvertexp->outBeginp()?vvertexp->outBeginp()->outNextp():0)
|
|
|
|
|
<<" "<<vvertexp->name()
|
|
|
|
|
<<endl);
|
|
|
|
|
for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
|
|
|
|
GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(edgep->top());
|
|
|
|
|
UINFO(9, " edge "<<edgep<<" to: "<<consumeVertexp->nodep()<<endl);
|
|
|
|
|
}
|
|
|
|
|
for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
|
|
|
|
GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(edgep->fromp());
|
|
|
|
|
UINFO(9, " edge "<<edgep<<" from: "<<consumeVertexp->nodep()<<endl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
AstNode* substp = okVisitor.substTree();
|
|
|
|
|
if (debug()>=5) logicp->dumpTree(cout,"\telimVar: ");
|
|
|
|
|
if (debug()>=5) substp->dumpTree(cout,"\t subst: ");
|
2011-08-05 01:15:24 +00:00
|
|
|
|
++m_statSigs;
|
2015-02-12 00:36:34 +00:00
|
|
|
|
bool removedAllUsages = true;
|
|
|
|
|
for (V3GraphEdge* edgep = vvertexp->outBeginp();
|
|
|
|
|
edgep; ) {
|
2008-06-12 16:03:47 +00:00
|
|
|
|
GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(edgep->top());
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstNode* consumerp = consumeVertexp->nodep();
|
2015-02-12 00:36:34 +00:00
|
|
|
|
if (!elimLogicOkOutputs(consumeVertexp, okVisitor/*ref*/)) {
|
|
|
|
|
// Cannot optimize this replacement
|
|
|
|
|
removedAllUsages = false;
|
|
|
|
|
edgep = edgep->outNextp();
|
|
|
|
|
} else {
|
|
|
|
|
optimizeElimVar(vvertexp->varScp(), substp, consumerp);
|
|
|
|
|
// If the new replacement referred to a signal,
|
|
|
|
|
// Correct the graph to point to this new generating variable
|
|
|
|
|
const GateVarRefList& rhsVarRefs = okVisitor.rhsVarRefs();
|
|
|
|
|
for (GateVarRefList::const_iterator it = rhsVarRefs.begin();
|
|
|
|
|
it != rhsVarRefs.end(); ++it) {
|
|
|
|
|
AstVarScope* newvarscp = (*it)->varScopep();
|
|
|
|
|
UINFO(9," Point-to-new vertex "<<newvarscp<<endl);
|
|
|
|
|
GateVarVertex* varvertexp = makeVarVertex(newvarscp);
|
|
|
|
|
new V3GraphEdge(&m_graph, varvertexp, consumeVertexp, 1);
|
|
|
|
|
// Propagate clock attribute onto generating node
|
|
|
|
|
varvertexp->propagateAttrClocksFrom(vvertexp);
|
|
|
|
|
}
|
|
|
|
|
// Remove the edge
|
2015-10-04 17:16:35 +00:00
|
|
|
|
edgep->unlinkDelete(); VL_DANGLING(edgep);
|
2015-02-12 00:36:34 +00:00
|
|
|
|
++m_statRefs;
|
|
|
|
|
edgep = vvertexp->outBeginp();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-02-12 00:36:34 +00:00
|
|
|
|
if (removedAllUsages) {
|
|
|
|
|
// Remove input links
|
|
|
|
|
while (V3GraphEdge* edgep = vvertexp->inBeginp()) {
|
2015-10-04 17:16:35 +00:00
|
|
|
|
edgep->unlinkDelete(); VL_DANGLING(edgep);
|
2015-02-12 00:36:34 +00:00
|
|
|
|
}
|
|
|
|
|
// Clone tree so we remember it for tracing, and keep the pointer
|
|
|
|
|
// to the "ALWAYS" part of the tree as part of this statement
|
|
|
|
|
// That way if a later signal optimization that retained a pointer to the always
|
|
|
|
|
// can optimize it further
|
|
|
|
|
logicp->unlinkFrBack();
|
|
|
|
|
vvertexp->varScp()->valuep(logicp);
|
|
|
|
|
logicp = NULL;
|
|
|
|
|
// Mark the vertex so we don't mark it as being unconsumed in the next step
|
|
|
|
|
vvertexp->user(true);
|
|
|
|
|
logicVertexp->user(true);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-12 00:36:34 +00:00
|
|
|
|
bool GateVisitor::elimLogicOkOutputs(GateLogicVertex* consumeVertexp, const GateOkVisitor& okVisitor) {
|
|
|
|
|
// Return true if can optimize
|
|
|
|
|
// Return false if the consuming logic has an output signal that the replacement logic has as an input
|
|
|
|
|
typedef set<AstVarScope*> VarScopeSet;
|
|
|
|
|
// Use map to find duplicates between two lists
|
|
|
|
|
VarScopeSet varscopes;
|
|
|
|
|
// Replacement logic usually has shorter input list, so faster to build list based on it
|
|
|
|
|
const GateVarRefList& rhsVarRefs = okVisitor.rhsVarRefs();
|
|
|
|
|
for (GateVarRefList::const_iterator it = rhsVarRefs.begin();
|
|
|
|
|
it != rhsVarRefs.end(); ++it) {
|
|
|
|
|
AstVarScope* vscp = (*it)->varScopep();
|
|
|
|
|
if (varscopes.find(vscp) == varscopes.end()) varscopes.insert(vscp);
|
|
|
|
|
}
|
|
|
|
|
for (V3GraphEdge* edgep = consumeVertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
|
|
|
|
GateVarVertex* consVVertexp = dynamic_cast<GateVarVertex*>(edgep->top());
|
|
|
|
|
AstVarScope* vscp = consVVertexp->varScp();
|
|
|
|
|
if (varscopes.find(vscp) != varscopes.end()) {
|
|
|
|
|
UINFO(9," Block-unopt, insertion generates input vscp "<<vscp<<endl);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
void GateVisitor::replaceAssigns() {
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
|
|
|
|
// Take the Comments/assigns that were moved to the VarScope and change them to a
|
|
|
|
|
// simple value assignment
|
|
|
|
|
AstVarScope* vscp = vvertexp->varScp();
|
|
|
|
|
if (vscp->valuep() && !vscp->valuep()->castNodeMath()) {
|
|
|
|
|
//if (debug()>9) vscp->dumpTree(cout, "-vscPre: ");
|
|
|
|
|
while (AstNode* delp=vscp->valuep()->castComment()) {
|
2015-10-04 17:16:35 +00:00
|
|
|
|
delp->unlinkFrBack()->deleteTree(); VL_DANGLING(delp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
if (AstInitial* delp=vscp->valuep()->castInitial()) {
|
|
|
|
|
AstNode* bodyp=delp->bodysp();
|
|
|
|
|
bodyp->unlinkFrBackWithNext();
|
|
|
|
|
delp->replaceWith(bodyp);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
delp->deleteTree(); VL_DANGLING(delp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
if (AstAlways* delp=vscp->valuep()->castAlways()) {
|
|
|
|
|
AstNode* bodyp=delp->bodysp();
|
|
|
|
|
bodyp->unlinkFrBackWithNext();
|
|
|
|
|
delp->replaceWith(bodyp);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
delp->deleteTree(); VL_DANGLING(delp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
if (AstNodeAssign* delp=vscp->valuep()->castNodeAssign()) {
|
|
|
|
|
AstNode* rhsp=delp->rhsp();
|
|
|
|
|
rhsp->unlinkFrBack();
|
|
|
|
|
delp->replaceWith(rhsp);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
delp->deleteTree(); VL_DANGLING(delp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
//if (debug()>9) {vscp->dumpTree(cout, "-vscDone: "); cout<<endl;}
|
|
|
|
|
if (!vscp->valuep()->castNodeMath()
|
|
|
|
|
|| vscp->valuep()->nextp()) {
|
2009-01-26 12:57:59 +00:00
|
|
|
|
vscp->dumpTree(cerr, "vscStrange: ");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
vscp->v3fatalSrc("Value of varscope not mathematical\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GateVisitor::consumedMark() {
|
|
|
|
|
// Propagate consumed signals backwards to all producers into a consumed node
|
|
|
|
|
m_graph.userClearVertices();
|
|
|
|
|
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
|
|
|
|
GateEitherVertex* evertexp = (GateEitherVertex*)vertexp;
|
|
|
|
|
if (!evertexp->user() && evertexp->consumed()) {
|
|
|
|
|
consumedMarkRecurse(evertexp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GateVisitor::consumedMarkRecurse(GateEitherVertex* vertexp) {
|
|
|
|
|
if (vertexp->user()) return; // Already marked
|
|
|
|
|
vertexp->user(true);
|
|
|
|
|
if (!vertexp->consumed()) vertexp->setConsumed("propagated");
|
|
|
|
|
// Walk sources and mark them too
|
|
|
|
|
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
|
|
|
|
GateEitherVertex* eFromVertexp = (GateEitherVertex*)edgep->fromp();
|
|
|
|
|
consumedMarkRecurse(eFromVertexp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GateVisitor::consumedMove() {
|
|
|
|
|
// Remove unused logic (logic that doesn't hit a combo block or a display statement)
|
|
|
|
|
// We need the "usually" block logic to do a better job at this
|
|
|
|
|
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
|
|
|
|
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(vertexp)) {
|
|
|
|
|
if (!vvertexp->consumed() && !vvertexp->user()) {
|
|
|
|
|
UINFO(8, "Unconsumed "<<vvertexp->varScp()<<endl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (GateLogicVertex* lvertexp = dynamic_cast<GateLogicVertex*>(vertexp)) {
|
|
|
|
|
AstNode* nodep = lvertexp->nodep();
|
|
|
|
|
AstActive* oldactp = lvertexp->activep(); // NULL under cfunc
|
|
|
|
|
if (!lvertexp->consumed() && oldactp) {
|
|
|
|
|
// Eventually: Move the statement to a new active block with "tracing-on" sensitivity
|
|
|
|
|
UINFO(8," Remove unconsumed "<<nodep<<endl);
|
|
|
|
|
nodep->unlinkFrBack();
|
2015-10-04 17:16:35 +00:00
|
|
|
|
pushDeletep(nodep); VL_DANGLING(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-31 12:51:14 +00:00
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GateVisitor::warnSignals() {
|
|
|
|
|
AstNode::user2ClearTree();
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
|
|
|
|
AstVarScope* vscp = vvertexp->varScp();
|
|
|
|
|
AstNode* sp = vvertexp->rstSyncNodep();
|
|
|
|
|
AstNode* ap = vvertexp->rstAsyncNodep();
|
|
|
|
|
if (ap && sp && !vscp->varp()->user2()) {
|
|
|
|
|
// This is somewhat wrong, as marking one flop as ok for sync
|
|
|
|
|
// may mean a different flop now fails. However it's a pain to
|
|
|
|
|
// then report a warning in a new place - we should report them all at once.
|
|
|
|
|
// Instead we'll disable if any disabled
|
|
|
|
|
if (!vscp->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET)
|
|
|
|
|
&& !ap->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET)
|
|
|
|
|
&& !sp->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET)
|
|
|
|
|
) {
|
|
|
|
|
vscp->varp()->user2(true); // Warn only once per signal
|
2012-05-22 01:24:17 +00:00
|
|
|
|
vscp->v3warn(SYNCASYNCNET,"Signal flopped as both synchronous and async: "<<vscp->prettyName()<<endl
|
|
|
|
|
<<ap->warnMore()<<"... Location of async usage"<<endl
|
|
|
|
|
<<sp->warnMore()<<"... Location of sync usage"<<endl);
|
2010-12-31 12:51:14 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//######################################################################
|
|
|
|
|
// Push constant into expressions and reevaluate
|
|
|
|
|
|
|
|
|
|
class GateElimVisitor : public GateBaseVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// STATE
|
|
|
|
|
AstVarScope* m_elimVarScp; // Variable being eliminated
|
|
|
|
|
AstNode* m_replaceTreep; // What to replace the variable with
|
|
|
|
|
bool m_didReplace; // Did we do any replacements
|
|
|
|
|
// VISITORS
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeVarRef* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (nodep->varScopep() == m_elimVarScp) {
|
|
|
|
|
// Substitute in the new tree
|
|
|
|
|
// It's possible we substitute into something that will be reduced more later
|
|
|
|
|
// however, as we never delete the top Always/initial statement, all should be well.
|
|
|
|
|
m_didReplace = true;
|
|
|
|
|
if (nodep->lvalue()) nodep->v3fatalSrc("Can't replace lvalue assignments with const var");
|
|
|
|
|
AstNode* substp = m_replaceTreep->cloneTree(false);
|
|
|
|
|
if (nodep->castNodeVarRef()
|
|
|
|
|
&& substp->castNodeVarRef()
|
|
|
|
|
&& nodep->same(substp)) {
|
|
|
|
|
// Prevent a infinite loop...
|
|
|
|
|
substp->v3fatalSrc("Replacing node with itself; perhaps circular logic?");
|
|
|
|
|
}
|
2011-01-17 19:10:01 +00:00
|
|
|
|
// Which fileline() to use?
|
|
|
|
|
// If replacing with logic, an error/warning is likely to want to point to the logic
|
|
|
|
|
// IE what we're replacing with.
|
|
|
|
|
// However a VARREF should point to the original as it's otherwise confusing
|
|
|
|
|
// to throw warnings that point to a PIN rather than where the pin us used.
|
|
|
|
|
if (substp->castVarRef()) substp->fileline(nodep->fileline());
|
2013-02-21 01:14:15 +00:00
|
|
|
|
// Make the substp an rvalue like nodep. This facilitate the hashing in dedupe.
|
|
|
|
|
if (AstNodeVarRef* varrefp = substp->castNodeVarRef()) varrefp->lvalue(false);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->replaceWith(substp);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->deleteTree(); VL_DANGLING(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNode* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
|
|
|
|
virtual ~GateElimVisitor() {}
|
|
|
|
|
GateElimVisitor(AstNode* nodep, AstVarScope* varscp, AstNode* replaceTreep) {
|
|
|
|
|
m_didReplace = false;
|
|
|
|
|
m_elimVarScp = varscp;
|
|
|
|
|
m_replaceTreep = replaceTreep;
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
bool didReplace() const { return m_didReplace; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* consumerp) {
|
|
|
|
|
if (debug()>=5) consumerp->dumpTree(cout,"\telimUsePre: ");
|
|
|
|
|
GateElimVisitor elimVisitor (consumerp, varscp, substp);
|
|
|
|
|
if (elimVisitor.didReplace()) {
|
|
|
|
|
if (debug()>=9) consumerp->dumpTree(cout,"\telimUseCns: ");
|
|
|
|
|
//Caution: Can't let V3Const change our handle to consumerp, such as by
|
|
|
|
|
// optimizing away this assignment, etc.
|
2009-10-15 00:13:04 +00:00
|
|
|
|
consumerp = V3Const::constifyEdit(consumerp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (debug()>=5) consumerp->dumpTree(cout,"\telimUseDne: ");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-21 01:14:15 +00:00
|
|
|
|
//######################################################################
|
|
|
|
|
// Auxiliary hash class for GateDedupeVarVisitor
|
|
|
|
|
|
|
|
|
|
class GateDedupeHash : public V3HashedUserCheck {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Ast*::user2p -> parent AstNodeAssign* for this rhsp
|
|
|
|
|
// Ast*::user3p -> AstNode* checked in test for duplicate
|
|
|
|
|
// Ast*::user5p -> AstNode* checked in test for duplicate
|
|
|
|
|
// AstUser2InUse m_inuser2; (Allocated for use in GateVisitor)
|
|
|
|
|
AstUser3InUse m_inuser3;
|
|
|
|
|
AstUser5InUse m_inuser5;
|
|
|
|
|
V3Hashed m_hashed; // Hash, contains rhs of assigns
|
|
|
|
|
|
|
|
|
|
void hash(AstNode* nodep) {
|
|
|
|
|
// !NULL && the object is hashable
|
|
|
|
|
if (nodep && !nodep->sameHash().isIllegal()) {
|
|
|
|
|
m_hashed.hash(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bool sameHash(AstNode* node1p, AstNode* node2p) {
|
|
|
|
|
return (node1p && node2p
|
|
|
|
|
&& !node1p->sameHash().isIllegal()
|
|
|
|
|
&& !node2p->sameHash().isIllegal()
|
|
|
|
|
&& m_hashed.sameNodes(node1p,node2p));
|
|
|
|
|
}
|
|
|
|
|
bool same(AstNUser* node1p, AstNUser* node2p) {
|
|
|
|
|
return node1p == node2p || sameHash((AstNode*)node1p,(AstNode*)node2p);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
bool check(AstNode* node1p,AstNode* node2p) {
|
|
|
|
|
return same(node1p->user3p(),node2p->user3p()) && same(node1p->user5p(),node2p->user5p())
|
|
|
|
|
&& node1p->user2p()->castNode()->type() == node2p->user2p()->castNode()->type()
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstNodeAssign* hashAndFindDupe(AstNodeAssign* assignp, AstNode* extra1p, AstNode* extra2p) {
|
|
|
|
|
AstNode *rhsp = assignp->rhsp();
|
|
|
|
|
rhsp->user2p(assignp);
|
|
|
|
|
rhsp->user3p(extra1p);
|
|
|
|
|
rhsp->user5p(extra2p);
|
|
|
|
|
|
|
|
|
|
hash(extra1p);
|
|
|
|
|
hash(extra2p);
|
|
|
|
|
|
|
|
|
|
V3Hashed::iterator inserted = m_hashed.hashAndInsert(rhsp);
|
|
|
|
|
V3Hashed::iterator dupit = m_hashed.findDuplicate(rhsp, this);
|
|
|
|
|
// Even though rhsp was just inserted, V3Hashed::findDuplicate doesn't
|
|
|
|
|
// return anything in the hash that has the same pointer (V3Hashed.cpp::findDuplicate)
|
|
|
|
|
// So dupit is either a different, duplicate rhsp, or the end of the hash.
|
|
|
|
|
if (dupit != m_hashed.end()) {
|
|
|
|
|
m_hashed.erase(inserted);
|
|
|
|
|
return m_hashed.iteratorNodep(dupit)->user2p()->castNode()->castNodeAssign();
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Have we seen the rhs of this assign before?
|
|
|
|
|
|
|
|
|
|
class GateDedupeVarVisitor : public GateBaseVisitor {
|
2013-02-21 01:26:53 +00:00
|
|
|
|
// Given a node, it is visited to try to find the AstNodeAssign under it that can used for dedupe.
|
|
|
|
|
// Right now, only the following node trees are supported for dedupe.
|
2013-02-21 01:14:15 +00:00
|
|
|
|
// 1. AstNodeAssign
|
|
|
|
|
// 2. AstAlways -> AstNodeAssign
|
2013-02-21 01:26:53 +00:00
|
|
|
|
// (Note, the assign must also be the only node under the always)
|
2013-02-21 01:14:15 +00:00
|
|
|
|
// 3. AstAlways -> AstNodeIf -> AstNodeAssign
|
2013-02-21 01:26:53 +00:00
|
|
|
|
// (Note, the IF must be the only node under the always,
|
|
|
|
|
// and the assign must be the only node under the if, other than the ifcond)
|
|
|
|
|
// Any other ordering or node type, except for an AstComment, makes it not dedupable
|
2013-02-21 01:14:15 +00:00
|
|
|
|
private:
|
|
|
|
|
// STATE
|
2013-02-21 01:26:53 +00:00
|
|
|
|
GateDedupeHash m_hash; // Hash used to find dupes of rhs of assign
|
|
|
|
|
AstNodeAssign* m_assignp; // Assign found for dedupe
|
2013-02-21 01:14:15 +00:00
|
|
|
|
AstNode* m_ifCondp; // IF condition that assign is under
|
|
|
|
|
bool m_always; // Assign is under an always
|
2013-02-21 01:26:53 +00:00
|
|
|
|
bool m_dedupable; // Determined the assign to be dedupable
|
2013-02-21 01:14:15 +00:00
|
|
|
|
|
|
|
|
|
// VISITORS
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeAssign* assignp) {
|
2013-02-21 01:26:53 +00:00
|
|
|
|
if (m_dedupable) {
|
|
|
|
|
// I think we could safely dedupe an always block with multiple non-blocking statements, but erring on side of caution here
|
|
|
|
|
if (!m_assignp) {
|
|
|
|
|
m_assignp = assignp;
|
|
|
|
|
} else {
|
|
|
|
|
m_dedupable = false;
|
2013-02-21 01:14:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstAlways* alwaysp) {
|
2013-02-21 01:26:53 +00:00
|
|
|
|
if (m_dedupable) {
|
|
|
|
|
if (!m_always) {
|
|
|
|
|
m_always = true;
|
|
|
|
|
alwaysp->bodysp()->iterateAndNext(*this);
|
|
|
|
|
} else {
|
|
|
|
|
m_dedupable = false;
|
|
|
|
|
}
|
2013-02-21 01:14:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Ugly support for latches of the specific form -
|
|
|
|
|
// always @(...)
|
|
|
|
|
// if (...)
|
|
|
|
|
// foo = ...; // or foo <= ...;
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeIf* ifp) {
|
2013-02-21 01:26:53 +00:00
|
|
|
|
if (m_dedupable) {
|
|
|
|
|
if (m_always && !m_ifCondp && !ifp->elsesp()) { //we're under an always, this is the first IF, and there's no else
|
2013-02-21 01:14:15 +00:00
|
|
|
|
m_ifCondp = ifp->condp();
|
2013-02-21 01:26:53 +00:00
|
|
|
|
ifp->ifsp()->iterateAndNext(*this);
|
|
|
|
|
} else {
|
|
|
|
|
m_dedupable = false;
|
2013-02-21 01:14:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-21 01:26:53 +00:00
|
|
|
|
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstComment*) {} // NOP
|
2013-02-21 01:14:15 +00:00
|
|
|
|
//--------------------
|
|
|
|
|
// Default
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNode*) {
|
2013-02-21 01:26:53 +00:00
|
|
|
|
m_dedupable = false;
|
|
|
|
|
}
|
2013-02-21 01:14:15 +00:00
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
2014-04-30 02:59:38 +00:00
|
|
|
|
GateDedupeVarVisitor() {
|
|
|
|
|
m_assignp = NULL;
|
|
|
|
|
m_ifCondp = NULL;
|
|
|
|
|
m_always = false;
|
|
|
|
|
m_dedupable = true;
|
|
|
|
|
}
|
2013-02-21 01:14:15 +00:00
|
|
|
|
// PUBLIC METHODS
|
|
|
|
|
AstNodeVarRef* findDupe(AstNode* nodep, AstVarScope* consumerVarScopep, AstActive* activep) {
|
2013-02-21 01:26:53 +00:00
|
|
|
|
m_assignp = NULL;
|
2013-02-21 01:14:15 +00:00
|
|
|
|
m_ifCondp = NULL;
|
2013-02-21 01:26:53 +00:00
|
|
|
|
m_always = false;
|
|
|
|
|
m_dedupable = true;
|
2013-02-21 01:14:15 +00:00
|
|
|
|
nodep->accept(*this);
|
2013-02-21 01:26:53 +00:00
|
|
|
|
if (m_dedupable && m_assignp) {
|
|
|
|
|
AstNode* lhsp = m_assignp->lhsp();
|
|
|
|
|
// Possible todo, handle more complex lhs expressions
|
|
|
|
|
if (AstNodeVarRef* lhsVarRefp = lhsp->castNodeVarRef()) {
|
|
|
|
|
if (lhsVarRefp->varScopep() != consumerVarScopep) consumerVarScopep->v3fatalSrc("Consumer doesn't match lhs of assign");
|
|
|
|
|
if (AstNodeAssign* dup = m_hash.hashAndFindDupe(m_assignp,activep,m_ifCondp)) {
|
|
|
|
|
return (AstNodeVarRef*) dup->lhsp();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
2013-02-21 01:14:15 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Recurse through the graph, looking for duplicate expressions on the rhs of an assign
|
|
|
|
|
|
|
|
|
|
class GateDedupeGraphVisitor : public GateGraphBaseVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// AstVarScope::user2p -> bool: already visited
|
|
|
|
|
// AstUser2InUse m_inuser2; (Allocated for use in GateVisitor)
|
|
|
|
|
V3Double0 m_numDeduped; // Statistic tracking
|
|
|
|
|
GateDedupeVarVisitor m_varVisitor; // Looks for a dupe of the logic
|
2016-02-05 01:47:55 +00:00
|
|
|
|
int m_depth; // Iteration depth
|
2013-02-21 01:14:15 +00:00
|
|
|
|
|
2016-02-05 01:47:55 +00:00
|
|
|
|
virtual AstNUser* visit(GateVarVertex* vvertexp, AstNUser*) {
|
2013-02-21 01:14:15 +00:00
|
|
|
|
// Check that we haven't been here before
|
2016-02-05 01:47:55 +00:00
|
|
|
|
if (m_depth > GATE_DEDUP_MAX_DEPTH) return NULL; // Break loops; before user2 set so hit this vertex later
|
2013-02-21 01:14:15 +00:00
|
|
|
|
if (vvertexp->varScp()->user2()) return NULL;
|
|
|
|
|
vvertexp->varScp()->user2(true);
|
|
|
|
|
|
2016-02-05 01:47:55 +00:00
|
|
|
|
m_depth++;
|
|
|
|
|
if (vvertexp->inSize1()) {
|
|
|
|
|
AstNodeVarRef* dupVarRefp = (AstNodeVarRef*) vvertexp->iterateInEdges(*this, (AstNUser*) vvertexp);
|
2013-02-21 01:14:15 +00:00
|
|
|
|
|
2016-02-05 01:47:55 +00:00
|
|
|
|
if (dupVarRefp) {
|
|
|
|
|
V3GraphEdge* edgep = vvertexp->inBeginp();
|
|
|
|
|
GateLogicVertex* lvertexp = (GateLogicVertex*)edgep->fromp();
|
|
|
|
|
if (!vvertexp->dedupable()) vvertexp->varScp()->v3fatalSrc("GateLogicVertex* visit should have returned NULL if consumer var vertex is not dedupable.");
|
|
|
|
|
GateOkVisitor okVisitor(lvertexp->nodep(), false, true);
|
|
|
|
|
if (okVisitor.isSimple()) {
|
|
|
|
|
AstVarScope* dupVarScopep = dupVarRefp->varScopep();
|
|
|
|
|
GateVarVertex* dupVvertexp = (GateVarVertex*) (dupVarScopep->user1p());
|
|
|
|
|
UINFO(4,"replacing " << vvertexp << " with " << dupVvertexp << endl);
|
|
|
|
|
++m_numDeduped;
|
|
|
|
|
// Replace all of this varvertex's consumers with dupVarRefp
|
|
|
|
|
for (V3GraphEdge* outedgep = vvertexp->outBeginp(); outedgep; ) {
|
|
|
|
|
GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(outedgep->top());
|
|
|
|
|
AstNode* consumerp = consumeVertexp->nodep();
|
|
|
|
|
GateElimVisitor elimVisitor(consumerp,vvertexp->varScp(),dupVarRefp);
|
|
|
|
|
outedgep = outedgep->relinkFromp(dupVvertexp);
|
|
|
|
|
}
|
2013-02-21 01:14:15 +00:00
|
|
|
|
|
2016-02-05 01:47:55 +00:00
|
|
|
|
// Propagate attributes
|
|
|
|
|
dupVvertexp->propagateAttrClocksFrom(vvertexp);
|
|
|
|
|
// Remove inputs links
|
|
|
|
|
while (V3GraphEdge* inedgep = vvertexp->inBeginp()) {
|
|
|
|
|
inedgep->unlinkDelete(); VL_DANGLING(inedgep);
|
|
|
|
|
}
|
|
|
|
|
// replaceAssigns() does the deleteTree on lvertexNodep in a later step
|
|
|
|
|
AstNode* lvertexNodep = lvertexp->nodep();
|
|
|
|
|
lvertexNodep->unlinkFrBack();
|
|
|
|
|
vvertexp->varScp()->valuep(lvertexNodep);
|
|
|
|
|
lvertexNodep = NULL;
|
|
|
|
|
vvertexp->user(true);
|
|
|
|
|
lvertexp->user(true);
|
2013-02-21 01:14:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-05 01:47:55 +00:00
|
|
|
|
m_depth--;
|
2013-02-21 01:14:15 +00:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-05 01:47:55 +00:00
|
|
|
|
// Given iterated logic, starting at vup which was consumer's GateVarVertex
|
|
|
|
|
// Returns a varref that has the same logic input; or NULL if none
|
2013-02-21 01:14:15 +00:00
|
|
|
|
virtual AstNUser* visit(GateLogicVertex* lvertexp, AstNUser* vup) {
|
|
|
|
|
lvertexp->iterateInEdges(*this);
|
|
|
|
|
|
|
|
|
|
GateVarVertex* consumerVvertexpp = (GateVarVertex*) vup;
|
|
|
|
|
if (lvertexp->dedupable() && consumerVvertexpp->dedupable()) {
|
|
|
|
|
AstNode* nodep = lvertexp->nodep();
|
|
|
|
|
AstVarScope* consumerVarScopep = consumerVvertexpp->varScp();
|
|
|
|
|
// TODO: Doing a simple pointer comparison of activep won't work
|
|
|
|
|
// optimally for statements under generated clocks. Statements under
|
|
|
|
|
// different generated clocks will never compare as equal, even if the
|
|
|
|
|
// generated clocks are deduped into one clock.
|
|
|
|
|
AstActive* activep = lvertexp->activep();
|
|
|
|
|
return (AstNUser*) m_varVisitor.findDupe(nodep, consumerVarScopep, activep);
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2016-02-05 01:47:55 +00:00
|
|
|
|
GateDedupeGraphVisitor() {
|
|
|
|
|
m_depth = 0;
|
|
|
|
|
}
|
2013-02-21 01:14:15 +00:00
|
|
|
|
void dedupeTree(GateVarVertex* vvertexp) {
|
|
|
|
|
vvertexp->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
V3Double0 numDeduped() { return m_numDeduped; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GateVisitor::dedupe() {
|
|
|
|
|
AstNode::user2ClearTree();
|
|
|
|
|
GateDedupeGraphVisitor deduper;
|
|
|
|
|
// Traverse starting from each of the clocks
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
|
|
|
|
if (vvertexp->isClock()) {
|
|
|
|
|
deduper.dedupeTree(vvertexp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Traverse starting from each of the outputs
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
|
|
|
|
if (vvertexp->isTop() && vvertexp->varScp()->varp()->isOutput()) {
|
|
|
|
|
deduper.dedupeTree(vvertexp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_statDedupLogic += deduper.numDeduped();
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-06 02:09:35 +00:00
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Recurse through the graph, try to merge assigns
|
|
|
|
|
|
|
|
|
|
class GateMergeAssignsGraphVisitor : public GateGraphBaseVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
AstNodeAssign* m_assignp;
|
|
|
|
|
AstActive* m_activep;
|
|
|
|
|
GateLogicVertex* m_logicvp;
|
|
|
|
|
V3Graph* m_graphp;
|
|
|
|
|
V3Double0 m_numMergedAssigns; // Statistic tracking
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// assemble two Sel into one if possible
|
|
|
|
|
AstSel* merge(AstSel* pre, AstSel* cur) {
|
|
|
|
|
AstVarRef* preVarRefp = pre->fromp()->castVarRef();
|
|
|
|
|
AstVarRef* curVarRefp = cur->fromp()->castVarRef();
|
|
|
|
|
if (!preVarRefp || !curVarRefp || !curVarRefp->same(preVarRefp)) return NULL; // not the same var
|
|
|
|
|
AstConst* pstart = pre->lsbp()->castConst();
|
|
|
|
|
AstConst* pwidth = pre->widthp()->castConst();
|
|
|
|
|
AstConst* cstart = cur->lsbp()->castConst();
|
|
|
|
|
AstConst* cwidth = cur->widthp()->castConst();
|
|
|
|
|
if (!pstart || !pwidth || !cstart || !cwidth) return NULL; // too complicated
|
|
|
|
|
if (cur->lsbConst()+cur->widthConst() == pre->lsbConst())
|
|
|
|
|
return new AstSel(curVarRefp->fileline(), curVarRefp->cloneTree(false), cur->lsbConst(), pre->widthConst()+cur->widthConst());
|
|
|
|
|
else return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual AstNUser* visit(GateVarVertex *vvertexp, AstNUser*) {
|
|
|
|
|
for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; ) {
|
|
|
|
|
V3GraphEdge* oldedgep = edgep;
|
|
|
|
|
edgep = edgep->inNextp(); // for recursive since the edge could be deleted
|
|
|
|
|
if (GateLogicVertex* lvertexp = dynamic_cast<GateLogicVertex*>(oldedgep->fromp())) {
|
|
|
|
|
if (AstNodeAssign* assignp = lvertexp->nodep()->castNodeAssign()) {
|
|
|
|
|
//if (lvertexp->outSize1() && assignp->lhsp()->castSel()) {
|
|
|
|
|
if (assignp->lhsp()->castSel() && lvertexp->outSize1()) {
|
|
|
|
|
UINFO(9, "assing to the nodep["<<assignp->lhsp()->castSel()->lsbConst()<<"]"<<endl);
|
|
|
|
|
// first assign with Sel-lhs
|
|
|
|
|
if (!m_activep) m_activep = lvertexp->activep();
|
|
|
|
|
if (!m_logicvp) m_logicvp = lvertexp;
|
|
|
|
|
if (!m_assignp) m_assignp = assignp;
|
|
|
|
|
|
|
|
|
|
// not under the same active
|
|
|
|
|
if (m_activep != lvertexp->activep()) {
|
|
|
|
|
m_activep = lvertexp->activep();
|
|
|
|
|
m_logicvp = lvertexp;
|
|
|
|
|
m_assignp = assignp;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstSel* preselp = m_assignp->lhsp()->castSel();
|
|
|
|
|
AstSel* curselp = assignp->lhsp()->castSel();
|
|
|
|
|
if (!preselp || !curselp) continue;
|
|
|
|
|
|
|
|
|
|
if (AstSel* newselp = merge(preselp, curselp)) {
|
|
|
|
|
UINFO(5, "assemble to new sel: "<<newselp<<endl);
|
|
|
|
|
// replace preSel with newSel
|
2015-10-04 17:16:35 +00:00
|
|
|
|
preselp->replaceWith(newselp); preselp->deleteTree(); VL_DANGLING(preselp);
|
2014-11-06 02:09:35 +00:00
|
|
|
|
// create new rhs for pre assignment
|
|
|
|
|
AstNode* newrhsp = new AstConcat(m_assignp->rhsp()->fileline(), m_assignp->rhsp()->cloneTree(false), assignp->rhsp()->cloneTree(false));
|
|
|
|
|
AstNode* oldrhsp = m_assignp->rhsp();
|
2015-10-04 17:16:35 +00:00
|
|
|
|
oldrhsp->replaceWith(newrhsp); oldrhsp->deleteTree(); VL_DANGLING(oldrhsp);
|
2014-11-06 02:09:35 +00:00
|
|
|
|
m_assignp->dtypeChgWidthSigned(m_assignp->width()+assignp->width(), m_assignp->width()+assignp->width(), AstNumeric::fromBool(true));
|
|
|
|
|
// don't need to delete, will be handled
|
2015-10-04 17:16:35 +00:00
|
|
|
|
//assignp->unlinkFrBack(); assignp->deleteTree(); VL_DANGLING(assignp);
|
2014-11-06 02:09:35 +00:00
|
|
|
|
|
|
|
|
|
// update the graph
|
|
|
|
|
{
|
|
|
|
|
// delete all inedges to lvertexp
|
|
|
|
|
if (!lvertexp->inEmpty()) {
|
|
|
|
|
for (V3GraphEdge* ledgep = lvertexp->inBeginp(); ledgep; ) {
|
|
|
|
|
V3GraphEdge* oedgep = ledgep;
|
|
|
|
|
ledgep = ledgep->inNextp();
|
|
|
|
|
GateEitherVertex* fromvp = dynamic_cast<GateEitherVertex*>(oedgep->fromp());
|
|
|
|
|
new V3GraphEdge(m_graphp, fromvp, m_logicvp, 1);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
oedgep->unlinkDelete(); VL_DANGLING(oedgep);
|
2014-11-06 02:09:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// delete all outedges to lvertexp, only one
|
2015-10-04 17:16:35 +00:00
|
|
|
|
oldedgep->unlinkDelete(); VL_DANGLING(oldedgep);
|
2014-11-06 02:09:35 +00:00
|
|
|
|
}
|
|
|
|
|
++m_numMergedAssigns;
|
|
|
|
|
} else {
|
|
|
|
|
m_assignp = assignp;
|
|
|
|
|
m_logicvp = lvertexp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual AstNUser* visit(GateLogicVertex* lvertexp, AstNUser* vup) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2015-10-04 02:33:06 +00:00
|
|
|
|
explicit GateMergeAssignsGraphVisitor(V3Graph* graphp) {
|
2014-11-06 02:09:35 +00:00
|
|
|
|
m_assignp = NULL;
|
|
|
|
|
m_activep = NULL;
|
|
|
|
|
m_logicvp = NULL;
|
|
|
|
|
m_numMergedAssigns = 0;
|
|
|
|
|
m_graphp = graphp;
|
|
|
|
|
}
|
|
|
|
|
void mergeAssignsTree(GateVarVertex* vvertexp) {
|
|
|
|
|
vvertexp->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
V3Double0 numMergedAssigns() { return m_numMergedAssigns; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GateVisitor::mergeAssigns() {
|
|
|
|
|
GateMergeAssignsGraphVisitor merger(&m_graph);
|
|
|
|
|
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
|
|
|
|
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
|
|
|
|
merger.mergeAssignsTree(vvertexp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_statAssignMerged += merger.numMergedAssigns();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//######################################################################
|
|
|
|
|
// Convert VARSCOPE(ASSIGN(default, VARREF)) to just VARSCOPE(default)
|
|
|
|
|
|
|
|
|
|
class GateDeassignVisitor : public GateBaseVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// VISITORS
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstVarScope* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (AstNodeAssign* assp = nodep->valuep()->castNodeAssign()) {
|
|
|
|
|
UINFO(5," Removeassign "<<assp<<endl);
|
|
|
|
|
AstNode* valuep = assp->rhsp();
|
|
|
|
|
valuep->unlinkFrBack();
|
|
|
|
|
assp->replaceWith(valuep);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
assp->deleteTree(); VL_DANGLING(assp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Speedups
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstVar* nodep) {}
|
|
|
|
|
virtual void visit(AstActive* nodep) {}
|
|
|
|
|
virtual void visit(AstNode* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
2015-10-04 02:33:06 +00:00
|
|
|
|
explicit GateDeassignVisitor(AstNode* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
virtual ~GateDeassignVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Gate class functions
|
|
|
|
|
|
|
|
|
|
void V3Gate::gateAll(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
GateVisitor visitor (nodep);
|
|
|
|
|
GateDeassignVisitor deassign (nodep);
|
2015-03-12 23:47:54 +00:00
|
|
|
|
V3Global::dumpCheckGlobalTree("gate.tree", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|