Fix code coverage holes

Fixes #3422
This commit is contained in:
Geza Lore 2022-05-16 20:02:49 +01:00
parent 0e62cd11da
commit 282887d9c6
11 changed files with 336 additions and 355 deletions

View File

@ -514,10 +514,9 @@ private:
visitAlways(nodep, nodep->sensesp(), nodep->keyword());
}
virtual void visit(AstAlwaysPostponed* nodep) override {
if (!nodep->bodysp()) { // Empty always. Remove it now.
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
// Might be empty with later optimizations, so this assertion can be removed,
// but for now it is guaranteed to be not empty.
UASSERT_OBJ(nodep->bodysp(), nodep, "Should not be empty");
visitAlways(nodep, nullptr, VAlwaysKwd::ALWAYS);
}
virtual void visit(AstAlwaysPublic* nodep) override {

View File

@ -119,30 +119,26 @@ private:
pushDeletep(nodep); // Delete it later, AstActives still pointing to it
}
virtual void visit(AstActive* nodep) override {
// Careful if adding variables here, ACTIVES can be under other ACTIVES
// Need to save and restore any member state in AstUntilStable block
UASSERT_OBJ(nodep->hasClocked(), nodep, "Should have been converted by V3Sched");
UASSERT_OBJ(nodep->stmtsp(), nodep, "Should not have been created if empty");
VNRelinker relinker;
nodep->unlinkFrBack(&relinker);
UASSERT_OBJ(nodep->stmtsp(), nodep, "Should not have been created if empty");
AstNode* const stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
if (nodep->hasClocked()) {
// Create 'if' statement, if needed
if (!m_lastSenp || !nodep->sensesp()->sameTree(m_lastSenp)) {
clearLastSen();
m_lastSenp = nodep->sensesp();
// Make a new if statement
m_lastIfp = makeActiveIf(m_lastSenp);
relinker.relink(m_lastIfp);
}
// Move statements to if
m_lastIfp->addIfsp(stmtsp);
} else if (nodep->hasCombo()) {
// Create 'if' statement, if needed
if (!m_lastSenp || !nodep->sensesp()->sameTree(m_lastSenp)) {
clearLastSen();
// Move statements to body
relinker.relink(stmtsp);
} else {
nodep->v3fatalSrc("Should have been removed by V3Sched::schedule");
m_lastSenp = nodep->sensesp();
// Make a new if statement
m_lastIfp = makeActiveIf(m_lastSenp);
relinker.relink(m_lastIfp);
}
// Move statements to if
m_lastIfp->addIfsp(stmtsp);
// Dispose of the AstActive
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
virtual void visit(AstExecGraph* nodep) override {

View File

@ -112,8 +112,8 @@ void OrderGraph::loopsVertexCb(V3GraphVertex* vertexp) {
<< " Example path: " << vvertexp->nodep()->typeName() << endl;
}
if (OrderVarVertex* const vvertexp = dynamic_cast<OrderVarVertex*>(vertexp)) {
std::cerr << vvertexp->varScp()->fileline()->warnOther()
<< " Example path: " << vvertexp->varScp()->prettyName() << endl;
std::cerr << vvertexp->vscp()->fileline()->warnOther()
<< " Example path: " << vvertexp->vscp()->prettyName() << endl;
}
}
@ -924,9 +924,6 @@ class OrderProcess final : VNDeleter {
V3List<OrderMoveDomScope*> m_pomReadyDomScope; // List of ready domain/scope pairs, by loopId
std::map<std::pair<AstNodeModule*, std::string>, unsigned> m_funcNums; // Function ordinals
// STATS
std::array<VDouble0, OrderVEdgeType::_ENUM_END> m_statCut; // Count of each edge type cut
// METHODS
VL_DEBUG_FUNC; // Declare debug()
@ -999,15 +996,7 @@ class OrderProcess final : VNDeleter {
pushDeletep(m_deleteDomainp);
}
~OrderProcess() {
// Stats
for (int type = 0; type < OrderVEdgeType::_ENUM_END; type++) {
const double count = double(m_statCut[type]);
if (count != 0.0) {
V3Stats::addStat(string("Order, cut, ") + OrderVEdgeType(type).ascii(), count);
}
}
}
~OrderProcess() = default;
public:
// Order the logic
@ -1071,16 +1060,18 @@ void OrderProcess::processDomainsIterate(OrderEitherVertex* vertexp) {
OrderEitherVertex* const fromVertexp = static_cast<OrderEitherVertex*>(edgep->fromp());
if (edgep->weight() && fromVertexp->domainMatters()) {
AstSenTree* fromDomainp = fromVertexp->domainp();
UASSERT(!fromDomainp->hasCombo(), "There should be no need for combinational domains");
if (OrderVarVertex* const varVtxp = dynamic_cast<OrderVarVertex*>(fromVertexp)) {
AstVarScope* const vscp = varVtxp->varScp();
AstVarScope* const vscp = varVtxp->vscp();
if (AstSenTree* const externalDomainp = m_externalDomain(vscp)) {
UASSERT(!externalDomainp->hasCombo(),
"There should be no need for combinational domains");
fromDomainp = fromDomainp == m_deleteDomainp
? externalDomainp
: combineDomains(fromDomainp, externalDomainp);
}
}
UINFO(9, " from d=" << cvtToHex(fromDomainp) << " " << fromVertexp << endl);
UASSERT(!fromDomainp->hasCombo(), "There should be no need for combinational domains");
// Irrelevant input vertex (never triggered)
if (fromDomainp == m_deleteDomainp) continue;
@ -1088,29 +1079,22 @@ void OrderProcess::processDomainsIterate(OrderEitherVertex* vertexp) {
// First input to this vertex
if (!domainp) domainp = fromDomainp;
// Once in combo, keep in combo; already as severe as we can get
if (domainp->hasCombo()) break;
// Make a domain that merges the two domains
if (domainp != fromDomainp) domainp = combineDomains(domainp, fromDomainp);
}
}
// Default the domain
// This is a node which has only constant inputs, or is otherwise indeterminate.
// Presumably it has inputs which we never trigger, or nothing it's sensitive to,
// so we can rip it out.
if (!domainp && vertexp->scopep()) domainp = m_deleteDomainp;
// If nothing triggers this vertex, we can delete the corresponding logic
if (!domainp) domainp = m_deleteDomainp;
if (domainp) {
vertexp->domainp(domainp);
UINFO(5, " done d=" << cvtToHex(vertexp->domainp())
<< (domainp == m_deleteDomainp ? " [DEL]"
: vertexp->domainp()->hasCombo() ? " [COMB]"
: vertexp->domainp()->isMulti() ? " [MULT]"
: "")
<< " " << vertexp << endl);
}
// Set the domain of the vertex
vertexp->domainp(domainp);
UINFO(5, " done d=" << cvtToHex(vertexp->domainp())
<< (domainp == m_deleteDomainp ? " [DEL]"
: vertexp->domainp()->hasCombo() ? " [COMB]"
: vertexp->domainp()->isMulti() ? " [MULT]"
: "")
<< " " << vertexp << endl);
}
//######################################################################
@ -1131,7 +1115,7 @@ void OrderProcess::processEdgeReport() {
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (OrderVarVertex* const vvertexp = dynamic_cast<OrderVarVertex*>(itp)) {
string name(vvertexp->varScp()->prettyName());
string name(vvertexp->vscp()->prettyName());
if (dynamic_cast<OrderVarPreVertex*>(itp)) {
name += " {PRE}";
} else if (dynamic_cast<OrderVarPostVertex*>(itp)) {
@ -1141,7 +1125,7 @@ void OrderProcess::processEdgeReport() {
}
std::ostringstream os;
os.setf(std::ios::left);
os << " " << cvtToHex(vvertexp->varScp()) << " " << std::setw(50) << name << " ";
os << " " << cvtToHex(vvertexp->vscp()) << " " << std::setw(50) << name << " ";
AstSenTree* const senTreep = vvertexp->domainp();
if (senTreep == m_deleteDomainp) {
os << "DELETED";
@ -1314,67 +1298,65 @@ AstActive* OrderProcess::processMoveOneLogic(const OrderLogicVertex* lvertexp,
AstNode* nodep = lvertexp->nodep();
AstNodeModule* const modp = scopep->modp();
UASSERT(modp, "nullptr");
if (VN_IS(nodep, Active)) {
// Just ignore sensitivities, we'll deal with them when we move statements that need them
} else { // Normal logic
// Move the logic into a CFunc
// We are move the logic into a CFunc, so unlink it from the AstActive
nodep->unlinkFrBack();
// Process procedures per statement (unless profCFuncs), so we can split CFuncs within
// procedures. Everything else is handled in one go
if (AstNodeProcedure* const procp = VN_CAST(nodep, NodeProcedure)) {
nodep = procp->bodysp();
pushDeletep(procp);
}
// When profCFuncs, create a new function for all logic block
if (v3Global.opt.profCFuncs()) newFuncpr = nullptr;
while (nodep) {
// Split the CFunc if too large (but not when profCFuncs)
if (!v3Global.opt.profCFuncs()
&& (v3Global.opt.outputSplitCFuncs()
&& v3Global.opt.outputSplitCFuncs() < newStmtsr)) {
// Put every statement into a unique function to ease profiling or reduce function
// size
newFuncpr = nullptr;
}
if (!newFuncpr && domainp != m_deleteDomainp) {
const string name = cfuncName(modp, domainp, scopep, nodep);
newFuncpr = new AstCFunc(nodep->fileline(), name, scopep);
newFuncpr->isStatic(false);
newFuncpr->isLoose(true);
newFuncpr->slow(m_slow);
newStmtsr = 0;
scopep->addActivep(newFuncpr);
// Create top call to it
AstCCall* const callp = new AstCCall(nodep->fileline(), newFuncpr);
// Where will we be adding the call?
AstActive* const newActivep = new AstActive(nodep->fileline(), name, domainp);
newActivep->addStmtsp(callp);
if (!activep) {
activep = newActivep;
} else {
activep->addNext(newActivep);
}
UINFO(6, " New " << newFuncpr << endl);
}
AstNode* const nextp = nodep->nextp();
// When processing statements in a procedure, unlink the current statement
if (nodep->backp()) nodep->unlinkFrBack();
// Process procedures per statement (unless profCFuncs), so we can split CFuncs within
// procedures. Everything else is handled in one go
if (AstNodeProcedure* const procp = VN_CAST(nodep, NodeProcedure)) {
nodep = procp->bodysp();
pushDeletep(procp);
if (domainp == m_deleteDomainp) {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else {
newFuncpr->addStmtsp(nodep);
// Add in the number of nodes we're adding
if (v3Global.opt.outputSplitCFuncs()) newStmtsr += nodep->nodeCount();
}
// When profCFuncs, create a new function for all logic block
if (v3Global.opt.profCFuncs()) newFuncpr = nullptr;
while (nodep) {
// Split the CFunc if too large (but not when profCFuncs)
if (!v3Global.opt.profCFuncs()
&& (v3Global.opt.outputSplitCFuncs()
&& v3Global.opt.outputSplitCFuncs() < newStmtsr)) {
// Put every statement into a unique function to ease profiling or reduce function
// size
newFuncpr = nullptr;
}
if (!newFuncpr && domainp != m_deleteDomainp) {
const string name = cfuncName(modp, domainp, scopep, nodep);
newFuncpr = new AstCFunc(nodep->fileline(), name, scopep);
newFuncpr->isStatic(false);
newFuncpr->isLoose(true);
newFuncpr->slow(m_slow);
newStmtsr = 0;
scopep->addActivep(newFuncpr);
// Create top call to it
AstCCall* const callp = new AstCCall(nodep->fileline(), newFuncpr);
// Where will we be adding the call?
AstActive* const newActivep = new AstActive(nodep->fileline(), name, domainp);
newActivep->addStmtsp(callp);
if (!activep) {
activep = newActivep;
} else {
activep->addNext(newActivep);
}
UINFO(6, " New " << newFuncpr << endl);
}
AstNode* const nextp = nodep->nextp();
// When processing statements in a procedure, unlink the current statement
if (nodep->backp()) nodep->unlinkFrBack();
if (domainp == m_deleteDomainp) {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else {
newFuncpr->addStmtsp(nodep);
// Add in the number of nodes we're adding
if (v3Global.opt.outputSplitCFuncs()) newStmtsr += nodep->nodeCount();
}
nodep = nextp;
}
nodep = nextp;
}
return activep;
}
@ -1430,7 +1412,7 @@ void OrderProcess::processMTasks() {
const OrderVarVertex* const pre_varp
= dynamic_cast<const OrderVarVertex*>(edgep->fromp());
if (!pre_varp) continue;
AstVar* const varp = pre_varp->varScp()->varp();
AstVar* const varp = pre_varp->vscp()->varp();
// varp depends on logicp, so logicp produces varp,
// and vice-versa below
varp->addProducingMTaskId(mtaskId);
@ -1440,7 +1422,7 @@ void OrderProcess::processMTasks() {
const OrderVarVertex* const post_varp
= dynamic_cast<const OrderVarVertex*>(edgep->top());
if (!post_varp) continue;
AstVar* const varp = post_varp->varScp()->varp();
AstVar* const varp = post_varp->vscp()->varp();
varp->addConsumingMTaskId(mtaskId);
}
// TODO? We ignore IO vars here, so those will have empty mtask

View File

@ -43,7 +43,6 @@
#include <unordered_map>
class OrderVisitor;
class OrderMoveVertex;
class OrderMoveVertexMaker;
class OrderMoveDomScope;
@ -55,50 +54,8 @@ enum OrderWeights : uint8_t {
WEIGHT_POST = 2, // Post-delayed used var
WEIGHT_PRE = 3, // Breakable pre-delayed used var
WEIGHT_MEDIUM = 8, // Medium weight just so dot graph looks nice
WEIGHT_NORMAL = 32
}; // High weight just so dot graph looks nice
struct OrderVEdgeType {
enum en : uint8_t {
VERTEX_UNKNOWN = 0,
VERTEX_LOGIC,
VERTEX_VARSTD,
VERTEX_VARPRE,
VERTEX_VARPOST,
VERTEX_VARPORD,
VERTEX_MOVE,
EDGE_STD,
EDGE_COMBOCUT,
EDGE_PRECUT,
EDGE_POSTCUT,
_ENUM_END
};
const char* ascii() const {
static const char* const names[]
= {"%E-vedge", "VERTEX_LOGIC", "VERTEX_VARSTD", "VERTEX_VARPRE",
"VERTEX_VARPOST", "VERTEX_VARPORD", "VERTEX_MOVE", "EDGE_STD",
"EDGE_COMBOCUT", "EDGE_PRECUT", "EDGE_POSTCUT", "_ENUM_END"};
return names[m_e];
}
enum en m_e;
inline OrderVEdgeType()
: m_e{VERTEX_UNKNOWN} {}
// cppcheck-suppress noExplicitConstructor
inline OrderVEdgeType(en _e)
: m_e{_e} {}
explicit inline OrderVEdgeType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
operator en() const { return m_e; }
WEIGHT_NORMAL = 32 // High weight just so dot graph looks nice
};
inline bool operator==(const OrderVEdgeType& lhs, const OrderVEdgeType& rhs) {
return lhs.m_e == rhs.m_e;
}
inline bool operator==(const OrderVEdgeType& lhs, OrderVEdgeType::en rhs) {
return lhs.m_e == rhs;
}
inline bool operator==(OrderVEdgeType::en lhs, const OrderVEdgeType& rhs) {
return lhs == rhs.m_e;
}
//######################################################################
// Graph types
@ -107,7 +64,7 @@ class OrderGraph final : public V3Graph {
public:
OrderGraph() = default;
virtual ~OrderGraph() override = default;
// Methods
// METHODS
virtual void loopsVertexCb(V3GraphVertex* vertexp) override;
};
@ -117,156 +74,187 @@ public:
class OrderEitherVertex VL_NOT_FINAL : public V3GraphVertex {
AstScope* const m_scopep; // Scope the vertex is in
AstSenTree* m_domainp; // Clock domain (nullptr = to be computed as we iterate)
protected:
OrderEitherVertex(V3Graph* graphp, const OrderEitherVertex& old)
: V3GraphVertex{graphp, old}
, m_scopep{old.m_scopep}
, m_domainp{old.m_domainp} {}
public:
protected:
// CONSTRUCTOR
OrderEitherVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp)
: V3GraphVertex{graphp}
, m_scopep{scopep}
, m_domainp{domainp} {}
, m_domainp{domainp} {
UASSERT(scopep, "Must not be null");
}
virtual ~OrderEitherVertex() override = default;
virtual OrderEitherVertex* clone(V3Graph* graphp) const override = 0;
// Methods
virtual OrderVEdgeType type() const = 0;
virtual bool domainMatters() = 0; // Must be in same domain when cross edge to this vertex
virtual string dotName() const override { return cvtToHex(m_scopep) + "_"; }
public:
// METHODS
virtual bool domainMatters() = 0;
// ACCESSORS
AstSenTree* domainp() const { return m_domainp; }
void domainp(AstSenTree* domainp) { m_domainp = domainp; }
AstScope* scopep() const { return m_scopep; }
AstSenTree* domainp() const { return m_domainp; }
// LCOV_EXCL_START // Debug code
virtual string dotName() const override { return cvtToHex(m_scopep) + "_"; }
// LCOV_EXCL_STOP
};
class OrderLogicVertex final : public OrderEitherVertex {
AstNode* const m_nodep;
AstSenTree* const m_hybridp;
protected:
OrderLogicVertex(V3Graph* graphp, const OrderLogicVertex& old)
: OrderEitherVertex{graphp, old}
, m_nodep{old.m_nodep}
, m_hybridp{old.m_hybridp} {}
public:
// CONSTRUCTOR
OrderLogicVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp, AstSenTree* hybridp,
AstNode* nodep)
: OrderEitherVertex{graphp, scopep, domainp}
, m_nodep{nodep}
, m_hybridp{hybridp} {
UASSERT_OBJ(!(domainp && hybridp), nodep, "Can't have bot domainp and hybridp set");
UASSERT_OBJ(!(domainp && hybridp), nodep, "Cannot have bot domainp and hybridp set");
}
virtual ~OrderLogicVertex() override = default;
virtual OrderLogicVertex* clone(V3Graph* graphp) const override {
return new OrderLogicVertex(graphp, *this);
}
virtual OrderVEdgeType type() const override { return OrderVEdgeType::VERTEX_LOGIC; }
// METHODS
virtual bool domainMatters() override { return true; }
// ACCESSORS
AstNode* nodep() const { return m_nodep; }
AstSenTree* hybridp() const { return m_hybridp; }
// LCOV_EXCL_START // Debug code
virtual string name() const override {
return (cvtToHex(m_nodep) + "\\n " + cvtToStr(nodep()->typeName()));
}
AstNode* nodep() const { return m_nodep; }
AstSenTree* hybridp() const { return m_hybridp; }
virtual string dotShape() const override {
return VN_IS(m_nodep, Active) ? "doubleoctagon" : "rect";
}
// LCOV_EXCL_STOP
};
class OrderVarVertex VL_NOT_FINAL : public OrderEitherVertex {
AstVarScope* const m_varScp;
protected:
OrderVarVertex(V3Graph* graphp, const OrderVarVertex& old)
: OrderEitherVertex{graphp, old}
, m_varScp{old.m_varScp} {}
AstVarScope* const m_vscp;
public:
OrderVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
// CONSTRUCTOR
OrderVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* vscp)
: OrderEitherVertex{graphp, scopep, nullptr}
, m_varScp{varScp} {}
, m_vscp{vscp} {}
virtual ~OrderVarVertex() override = default;
virtual OrderVarVertex* clone(V3Graph* graphp) const override = 0;
virtual OrderVEdgeType type() const override = 0;
virtual FileLine* fileline() const override { return varScp()->fileline(); }
// ACCESSORS
AstVarScope* varScp() const { return m_varScp; }
virtual string dotShape() const override { return "ellipse"; }
AstVarScope* vscp() const { return m_vscp; }
// LCOV_EXCL_START // Debug code
virtual string dotShape() const override final { return "ellipse"; }
virtual string nameSuffix() const = 0;
virtual string name() const override final {
return cvtToHex(m_vscp) + " " + nameSuffix() + "\\n " + m_vscp->name();
}
// LCOV_EXCL_STOP
};
class OrderVarStdVertex final : public OrderVarVertex {
OrderVarStdVertex(V3Graph* graphp, const OrderVarStdVertex& old)
: OrderVarVertex{graphp, old} {}
public:
// CONSTRUCTOR
OrderVarStdVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
: OrderVarVertex{graphp, scopep, varScp} {}
virtual ~OrderVarStdVertex() override = default;
virtual OrderVarStdVertex* clone(V3Graph* graphp) const override {
return new OrderVarStdVertex(graphp, *this);
}
virtual OrderVEdgeType type() const override { return OrderVEdgeType::VERTEX_VARSTD; }
virtual string name() const override {
return (cvtToHex(varScp()) + "\\n " + varScp()->name());
}
virtual string dotColor() const override { return "grey"; }
virtual bool domainMatters() override { return true; }
};
class OrderVarPreVertex final : public OrderVarVertex {
OrderVarPreVertex(V3Graph* graphp, const OrderVarPreVertex& old)
: OrderVarVertex{graphp, old} {}
// METHODS
virtual bool domainMatters() override { return true; }
// LCOV_EXCL_START // Debug code
virtual string nameSuffix() const override { return ""; }
virtual string dotColor() const override { return "grey"; }
// LCOV_EXCL_STOP
};
class OrderVarPreVertex final : public OrderVarVertex {
public:
// CONSTRUCTOR
OrderVarPreVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
: OrderVarVertex{graphp, scopep, varScp} {}
virtual ~OrderVarPreVertex() override = default;
virtual OrderVarPreVertex* clone(V3Graph* graphp) const override {
return new OrderVarPreVertex(graphp, *this);
}
virtual OrderVEdgeType type() const override { return OrderVEdgeType::VERTEX_VARPRE; }
virtual string name() const override {
return (cvtToHex(varScp()) + " PRE\\n " + varScp()->name());
}
virtual string dotColor() const override { return "green"; }
virtual bool domainMatters() override { return false; }
};
class OrderVarPostVertex final : public OrderVarVertex {
OrderVarPostVertex(V3Graph* graphp, const OrderVarPostVertex& old)
: OrderVarVertex{graphp, old} {}
// METHODS
virtual bool domainMatters() override { return false; }
// LCOV_EXCL_START // Debug code
virtual string nameSuffix() const override { return "PRE"; }
virtual string dotColor() const override { return "green"; }
// LCOV_EXCL_STOP
};
class OrderVarPostVertex final : public OrderVarVertex {
public:
// CONSTRUCTOR
OrderVarPostVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
: OrderVarVertex{graphp, scopep, varScp} {}
virtual OrderVarPostVertex* clone(V3Graph* graphp) const override {
return new OrderVarPostVertex(graphp, *this);
}
virtual OrderVEdgeType type() const override { return OrderVEdgeType::VERTEX_VARPOST; }
virtual ~OrderVarPostVertex() override = default;
virtual string name() const override {
return (cvtToHex(varScp()) + " POST\\n " + varScp()->name());
}
virtual string dotColor() const override { return "red"; }
virtual bool domainMatters() override { return false; }
};
class OrderVarPordVertex final : public OrderVarVertex {
OrderVarPordVertex(V3Graph* graphp, const OrderVarPordVertex& old)
: OrderVarVertex{graphp, old} {}
// METHODS
virtual bool domainMatters() override { return false; }
// LCOV_EXCL_START // Debug code
virtual string nameSuffix() const override { return "POST"; }
virtual string dotColor() const override { return "red"; }
// LCOV_EXCL_STOP
};
class OrderVarPordVertex final : public OrderVarVertex {
public:
// CONSTRUCTOR
OrderVarPordVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
: OrderVarVertex{graphp, scopep, varScp} {}
virtual ~OrderVarPordVertex() override = default;
virtual OrderVarPordVertex* clone(V3Graph* graphp) const override {
return new OrderVarPordVertex(graphp, *this);
}
virtual OrderVEdgeType type() const override { return OrderVEdgeType::VERTEX_VARPORD; }
virtual string name() const override {
return (cvtToHex(varScp()) + " PORD\\n " + varScp()->name());
}
virtual string dotColor() const override { return "blue"; }
// METHODS
virtual bool domainMatters() override { return false; }
// LCOV_EXCL_START // Debug code
virtual string nameSuffix() const override { return "PORD"; }
virtual string dotColor() const override { return "blue"; }
// LCOV_EXCL_STOP
};
//######################################################################
// Edge types
class OrderEdge VL_NOT_FINAL : public V3GraphEdge {
public:
// CONSTRUCTOR
OrderEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight,
bool cutable = false)
: V3GraphEdge{graphp, fromp, top, weight, cutable} {}
virtual ~OrderEdge() override = default;
};
class OrderPostCutEdge final : public OrderEdge {
// Edge created from output of post assignment
public:
// CONSTRUCTOR
OrderPostCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
: OrderEdge{graphp, fromp, top, WEIGHT_COMBO, CUTABLE} {}
virtual ~OrderPostCutEdge() override = default;
// LCOV_EXCL_START // Debug code
virtual string dotColor() const override { return "palegreen"; }
// LCOV_EXCL_STOP
};
class OrderPreCutEdge final : public OrderEdge {
// Edge created from var_PREVAR->consuming logic vertex
// Always breakable, just results in performance loss
// in which case we can't optimize away the pre/post delayed assignments
public:
// CONSTRUCTOR
OrderPreCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
: OrderEdge{graphp, fromp, top, WEIGHT_PRE, CUTABLE} {}
virtual ~OrderPreCutEdge() override = default;
// LCOV_EXCL_START // Debug code
virtual string dotColor() const override { return "khaki"; }
// LCOV_EXCL_STOP
};
//######################################################################
@ -283,7 +271,7 @@ protected:
friend class OrderProcess;
friend class OrderMoveVertexMaker;
// These only contain the "next" item,
// for the head of the list, see the same var name under OrderVisitor
// for the head of the list, see the same var name under OrderProcess
V3ListEnt<OrderMoveVertex*> m_pomWaitingE; // List of nodes needing inputs to become ready
V3ListEnt<OrderMoveVertex*> m_readyVerticesE; // List of ready under domain/scope
public:
@ -294,12 +282,8 @@ public:
, m_state{POM_WAIT}
, m_domScopep{nullptr} {}
virtual ~OrderMoveVertex() override = default;
virtual OrderMoveVertex* clone(V3Graph* graphp) const override {
v3fatalSrc("Unsupported");
return nullptr;
}
// METHODS
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_MOVE; }
virtual string dotColor() const override {
if (logicp()) {
return logicp()->dotColor();
@ -307,13 +291,7 @@ public:
return "";
}
}
virtual FileLine* fileline() const override {
if (logicp()) {
return logicp()->fileline();
} else {
return nullptr;
}
}
virtual string name() const override {
string nm;
if (VL_UNCOVERABLE(!logicp())) { // Avoid crash when debugging
@ -350,9 +328,6 @@ class MTaskMoveVertex final : public V3GraphVertex {
const AstScope* const m_scopep;
const AstSenTree* const m_domainp;
protected:
friend class OrderVisitor;
public:
MTaskMoveVertex(V3Graph* graphp, OrderLogicVertex* logicp, const OrderEitherVertex* varp,
const AstScope* scopep, const AstSenTree* domainp)
@ -364,11 +339,13 @@ public:
UASSERT(!(logicp && varp), "MTaskMoveVertex: logicp and varp may not both be set!\n");
}
virtual ~MTaskMoveVertex() override = default;
virtual MTaskMoveVertex* clone(V3Graph* graphp) const override {
v3fatalSrc("Unsupported");
return nullptr;
}
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_MOVE; }
// ACCESSORS
OrderLogicVertex* logicp() const { return m_logicp; }
const OrderEitherVertex* varp() const { return m_varp; }
const AstScope* scopep() const { return m_scopep; }
const AstSenTree* domainp() const { return m_domainp; }
virtual string dotColor() const override {
if (logicp()) {
return logicp()->dotColor();
@ -389,71 +366,6 @@ public:
}
return nm;
}
// ACCESSORS
OrderLogicVertex* logicp() const { return m_logicp; }
const OrderEitherVertex* varp() const { return m_varp; }
const AstScope* scopep() const { return m_scopep; }
const AstSenTree* domainp() const { return m_domainp; }
};
//######################################################################
// Edge types
class OrderEdge VL_NOT_FINAL : public V3GraphEdge {
protected:
OrderEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, const OrderEdge& old)
: V3GraphEdge{graphp, fromp, top, old} {}
public:
OrderEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight,
bool cutable = false)
: V3GraphEdge{graphp, fromp, top, weight, cutable} {}
virtual ~OrderEdge() override = default;
virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_STD; }
virtual OrderEdge* clone(V3Graph* graphp, V3GraphVertex* fromp,
V3GraphVertex* top) const override {
return new OrderEdge(graphp, fromp, top, *this);
}
};
class OrderPostCutEdge final : public OrderEdge {
// Edge created from output of post assignment
// Breakable if the output var feeds back to input combo logic or another clock pin
// in which case we'll need a change detect loop around this var.
OrderPostCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top,
const OrderPostCutEdge& old)
: OrderEdge{graphp, fromp, top, old} {}
public:
OrderPostCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
: OrderEdge{graphp, fromp, top, WEIGHT_COMBO, CUTABLE} {}
virtual OrderVEdgeType type() const override { return OrderVEdgeType::EDGE_POSTCUT; }
virtual ~OrderPostCutEdge() override = default;
virtual OrderPostCutEdge* clone(V3Graph* graphp, V3GraphVertex* fromp,
V3GraphVertex* top) const override {
return new OrderPostCutEdge(graphp, fromp, top, *this);
}
virtual string dotColor() const override { return "PaleGreen"; }
};
class OrderPreCutEdge final : public OrderEdge {
// Edge created from var_PREVAR->consuming logic vertex
// Always breakable, just results in performance loss
// in which case we can't optimize away the pre/post delayed assignments
OrderPreCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top,
const OrderPreCutEdge& old)
: OrderEdge{graphp, fromp, top, old} {}
public:
OrderPreCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
: OrderEdge{graphp, fromp, top, WEIGHT_PRE, CUTABLE} {}
virtual OrderVEdgeType type() const override { return OrderVEdgeType::EDGE_PRECUT; }
virtual OrderPreCutEdge* clone(V3Graph* graphp, V3GraphVertex* fromp,
V3GraphVertex* top) const override {
return new OrderPreCutEdge(graphp, fromp, top, *this);
}
virtual ~OrderPreCutEdge() override = default;
virtual string dotColor() const override { return "khaki"; }
};
#endif // Guard

View File

@ -1951,7 +1951,7 @@ public:
const OrderVarStdVertex* const ovvp
= dynamic_cast<OrderVarStdVertex*>(edgep->top());
if (!ovvp) continue;
if (ovvp->varScp()->varp()->isSc()) {
if (ovvp->vscp()->varp()->isSc()) {
ovvSetSystemC.insert(ovvp);
} else {
ovvSet.insert(ovvp);

View File

@ -128,7 +128,8 @@ void splitCheck(AstCFunc* ofuncp) {
// Unlink all statements, then add item by item to new sub-functions
AstBegin* const tempp = new AstBegin{ofuncp->fileline(), "[EditWrapper]",
ofuncp->stmtsp()->unlinkFrBackWithNext()};
if (ofuncp->finalsp()) tempp->addStmtsp(ofuncp->finalsp()->unlinkFrBackWithNext());
// Currently we do not use finalsp in V3Sched, if we do, it needs to be handled here
UASSERT_OBJ(!ofuncp->finalsp(), ofuncp, "Should not have any finalps");
while (tempp->stmtsp()) {
AstNode* const itemp = tempp->stmtsp()->unlinkFrBack();
const int stmts = itemp->nodeCount();

View File

@ -69,9 +69,10 @@ public:
AstNode* logicp() const { return m_logicp; }
AstScope* scopep() const { return m_scopep; }
// For graph dumping
// LCOV_EXCL_START // Debug code
string name() const override { return m_logicp->fileline()->ascii(); };
string dotShape() const override { return "rectangle"; }
// LCOV_EXCL_STOP
};
class VarVertex final : public V3GraphVertex {
@ -84,10 +85,11 @@ public:
AstVarScope* vscp() const { return m_vscp; }
AstVar* varp() const { return m_vscp->varp(); }
// For graph dumping
// LCOV_EXCL_START // Debug code
string name() const override { return m_vscp->name(); }
string dotShape() const override { return "ellipse"; }
string dotColor() const override { return "blue"; }
// LCOV_EXCL_STOP
};
class Graph final : public V3Graph {
@ -97,8 +99,9 @@ class Graph final : public V3Graph {
AstNode* const logicp = lvtxp->logicp();
std::cerr << logicp->fileline()->warnOther()
<< " Example path: " << logicp->typeName() << endl;
}
if (VarVertex* const vvtxp = dynamic_cast<VarVertex*>(vtxp)) {
} else {
VarVertex* const vvtxp = dynamic_cast<VarVertex*>(vtxp);
UASSERT(vvtxp, "Cannot be anything else");
AstVarScope* const vscp = vvtxp->vscp();
std::cerr << vscp->fileline()->warnOther()
<< " Example path: " << vscp->prettyName() << endl;
@ -369,11 +372,7 @@ LogicByScope fixCuts(AstNetlist* netlistp, const std::vector<VarVertex*>& cutVer
for (AstVarScope* const vscp : lvtx2Cuts[lvtxp]) {
AstVarRef* const refp = new AstVarRef{flp, vscp, VAccess::READ};
AstSenItem* const nextp = new AstSenItem{flp, VEdgeType::ET_HYBRID, refp};
if (!senItemsp) {
senItemsp = nextp;
} else {
senItemsp->addNext(nextp);
}
senItemsp = VN_AS(AstNode::addNext(senItemsp, nextp), SenItem);
}
AstSenTree* const senTree = new AstSenTree{flp, senItemsp};
// Add logic to result with new sensitivity

View File

@ -59,6 +59,8 @@ public:
SchedSenVertex(V3Graph* graphp, const AstSenItem* senItemp)
: V3GraphVertex{graphp}
, m_senItemp{senItemp} {}
// LCOV_EXCL_START // Debug code
string name() const override {
std::ostringstream os;
V3EmitV::verilogForTree(const_cast<AstSenItem*>(m_senItemp), os);
@ -66,6 +68,7 @@ public:
}
string dotShape() const override { return "doubleoctagon"; }
string dotColor() const override { return "red"; }
// LCOV_EXCL_STOP
};
class SchedLogicVertex final : public V3GraphVertex {
@ -83,10 +86,12 @@ public:
AstSenTree* senTreep() const { return m_senTreep; }
AstNode* logicp() const { return m_logicp; }
// LCOV_EXCL_START // Debug code
string name() const override {
return m_logicp->typeName() + ("\n" + m_logicp->fileline()->ascii());
};
string dotShape() const override { return "rectangle"; }
// LCOV_EXCL_STOP
};
class SchedVarVertex final : public V3GraphVertex {
@ -96,6 +101,8 @@ public:
SchedVarVertex(V3Graph* graphp, AstVarScope* vscp)
: V3GraphVertex{graphp}
, m_vscp{vscp} {}
// LCOV_EXCL_START // Debug code
string name() const override { return m_vscp->name(); }
string dotShape() const override {
return m_vscp->scopep()->isTop() && m_vscp->varp()->isNonOutput() ? "invhouse" : "ellipse";
@ -103,6 +110,7 @@ public:
string dotColor() const override {
return m_vscp->scopep()->isTop() && m_vscp->varp()->isNonOutput() ? "green" : "black";
}
// LCOV_EXCL_STOP
};
class SchedGraphBuilder final : public VNVisitor {
@ -229,18 +237,23 @@ class SchedGraphBuilder final : public VNVisitor {
virtual void visit(AstAssignPost* nodep) override {}
virtual void visit(AstAlwaysPost* nodep) override {}
// LCOV_EXCL_START
// Ignore
virtual void visit(AstInitialStatic* nodep) override { // LCOV_EXCL_START
virtual void visit(AstInitialStatic* nodep) override {
nodep->v3fatalSrc("Should not need ordering");
}
virtual void visit(AstInitial* nodep) override {
virtual void visit(AstInitial* nodep) override { //
nodep->v3fatalSrc("Should not need ordering");
}
virtual void visit(AstFinal* nodep) override {
virtual void visit(AstFinal* nodep) override { //
nodep->v3fatalSrc("Should not need ordering");
} // LCOV_EXCL_STOP
}
virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
// Default - Any other AstActive content not handled above will hit this
virtual void visit(AstNode* nodep) override { //
nodep->v3fatalSrc("Should behandled above");
}
// LCOV_EXCL_STOP
SchedGraphBuilder(const LogicByScope& clockedLogic, const LogicByScope& combinationalLogic,
const LogicByScope& hybridLogic) {

View File

@ -70,7 +70,7 @@ public:
m_drivingRegions = static_cast<RegionFlags>(m_drivingRegions | regions);
}
// For graph dumping
// LCOV_EXCL_START // Debug code
string dotColor() const override {
switch (static_cast<unsigned>(m_drivingRegions)) {
case NONE: return "black";
@ -81,9 +81,10 @@ public:
case INPUT | NBA: return "magenta";
case ACTIVE | NBA: return "cyan";
case INPUT | ACTIVE | NBA: return "gray80"; // don't want white on white background
default: v3fatal("There are only 3 region bits"); return ""; // LCOV_EXCL_LINE
default: v3fatal("There are only 3 region bits"); return "";
}
}
// LCOV_EXCL_STOP
};
class LogicVertex final : public Vertex {

View File

@ -0,0 +1,21 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(vlt_all => 1);
compile(
verilator_flags2 => ["--stats"],
);
# We must not convert these blocks into combinational blocks
file_grep($Self->{stats}, qr/Scheduling, size of class: combinational\s+(\d+)/i, 0);
ok(1);
1;

View File

@ -0,0 +1,57 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Geza Lore.
// SPDX-License-Identifier: CC0-1.0
module t (
clk,
input wire i,
output reg o_0,
output reg o_1,
output reg o_2,
output reg o_3,
output reg o_4,
output reg o_5
);
input clk;
reg a = 0;
reg b = 0;
event e;
// We must not convert these blocks into combinational blocks
always @(i) begin
a <= ~a;
o_0 = i;
end
always @(i) begin
force b = 1;
o_1 = i;
end
always @(i) begin
release b;
o_2 = i;
end
always @(i) begin
-> e;
o_3 = i;
end
always @(i) begin
->> e;
o_4 = i;
end
always @(i) begin
$display("Hello");
o_5 = i;
end
endmodule