// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Graph tests // // Code available from: https://verilator.org // //************************************************************************* // // Copyright 2003-2024 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 // //************************************************************************* #define VL_MT_DISABLED_CODE_UNIT 1 #include "config_build.h" #include "verilatedos.h" #include "V3Global.h" #include "V3Graph.h" VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### //###################################################################### // Test class class V3GraphTest VL_NOT_FINAL { protected: // MEMBERS V3Graph m_graph; // METHODS - for children virtual void runTest() = 0; // Run the test virtual string name() = 0; // Name of the test // Utilities void dumpSelf() { if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("v3graphtest_" + name()); } public: V3GraphTest() = default; virtual ~V3GraphTest() = default; void run() { runTest(); } }; //###################################################################### //###################################################################### // Vertices and nodes class V3GraphTestVertex VL_NOT_FINAL : public V3GraphVertex { VL_RTTI_IMPL(V3GraphTestVertex, V3GraphVertex) const string m_name; public: V3GraphTestVertex(V3Graph* graphp, const string& name) : V3GraphVertex{graphp} , m_name{name} {} ~V3GraphTestVertex() override = default; // ACCESSORS string name() const override VL_MT_STABLE { return m_name; } }; class V3GraphTestVarVertex final : public V3GraphTestVertex { public: V3GraphTestVarVertex(V3Graph* graphp, const string& name) : V3GraphTestVertex{graphp, name} {} ~V3GraphTestVarVertex() override = default; // ACCESSORS string dotColor() const override { return "blue"; } }; //###################################################################### //###################################################################### // Test vertices and nodes class V3GraphTestStrong final : public V3GraphTest { public: string name() override { return "strong"; } void runTest() override { V3Graph* gp = &m_graph; // Verify we break edges at a good point // A simple alg would make 3 breaks, below only requires b->i to break V3GraphTestVertex* i = new V3GraphTestVarVertex{gp, "*INPUTS*"}; V3GraphTestVertex* a = new V3GraphTestVarVertex{gp, "a"}; V3GraphTestVertex* b = new V3GraphTestVarVertex{gp, "b"}; V3GraphTestVertex* g1 = new V3GraphTestVarVertex{gp, "g1"}; V3GraphTestVertex* g2 = new V3GraphTestVarVertex{gp, "g2"}; V3GraphTestVertex* g3 = new V3GraphTestVarVertex{gp, "g3"}; V3GraphTestVertex* q = new V3GraphTestVarVertex{gp, "q"}; new V3GraphEdge{gp, i, a, 2, true}; new V3GraphEdge{gp, a, b, 2, true}; new V3GraphEdge{gp, b, g1, 2, true}; new V3GraphEdge{gp, b, g2, 2, true}; new V3GraphEdge{gp, b, g3, 2, true}; new V3GraphEdge{gp, g1, a, 2, true}; new V3GraphEdge{gp, g3, g2, 2, true}; new V3GraphEdge{gp, g2, g3, 2, true}; new V3GraphEdge{gp, g1, q, 2, true}; new V3GraphEdge{gp, g2, q, 2, true}; new V3GraphEdge{gp, g3, q, 2, true}; gp->stronglyConnected(&V3GraphEdge::followAlwaysTrue); dumpSelf(); UASSERT(i->color() != a->color() && a->color() != g2->color() && g2->color() != q->color(), "SelfTest: Separate colors not assigned"); UASSERT(a->color() == b->color() && a->color() == g1->color(), "SelfTest: Strongly connected nodes not colored together"); UASSERT(g2->color() == g3->color(), "SelfTest: Strongly connected nodes not colored together"); } }; class V3GraphTestAcyc final : public V3GraphTest { public: string name() override { return "acyc"; } void runTest() override { V3Graph* gp = &m_graph; // Verify we break edges at a good point // A simple alg would make 3 breaks, below only requires b->i to break V3GraphTestVertex* i = new V3GraphTestVarVertex{gp, "*INPUTS*"}; V3GraphTestVertex* a = new V3GraphTestVarVertex{gp, "a"}; V3GraphTestVertex* b = new V3GraphTestVarVertex{gp, "b"}; V3GraphTestVertex* g1 = new V3GraphTestVarVertex{gp, "g1"}; V3GraphTestVertex* g2 = new V3GraphTestVarVertex{gp, "g2"}; V3GraphTestVertex* g3 = new V3GraphTestVarVertex{gp, "g3"}; new V3GraphEdge{gp, i, a, 2, true}; new V3GraphEdge{gp, a, b, 2, true}; new V3GraphEdge{gp, b, g1, 2, true}; new V3GraphEdge{gp, b, g2, 2, true}; new V3GraphEdge{gp, b, g3, 2, true}; new V3GraphEdge{gp, g1, a, 2, true}; new V3GraphEdge{gp, g2, a, 2, true}; new V3GraphEdge{gp, g3, a, 2, true}; gp->acyclic(&V3GraphEdge::followAlwaysTrue); gp->order(); dumpSelf(); } }; class V3GraphTestVars final : public V3GraphTest { public: string name() override { return "vars"; } void runTest() override { V3Graph* const gp = &m_graph; V3GraphTestVertex* const clk = new V3GraphTestVarVertex{gp, "$clk"}; V3GraphTestVertex* const a = new V3GraphTestVarVertex{gp, "$a"}; V3GraphTestVertex* const a_dly = new V3GraphTestVarVertex{gp, "$a_dly"}; V3GraphTestVertex* const a_dlyblk = new V3GraphTestVarVertex{gp, "$a_dlyblk"}; V3GraphTestVertex* const b = new V3GraphTestVarVertex{gp, "$b"}; V3GraphTestVertex* const b_dly = new V3GraphTestVarVertex{gp, "$b_dly"}; V3GraphTestVertex* const b_dlyblk = new V3GraphTestVarVertex{gp, "$b_dlyblk"}; V3GraphTestVertex* const c = new V3GraphTestVarVertex{gp, "$c"}; V3GraphTestVertex* const i = new V3GraphTestVarVertex{gp, "$i"}; V3GraphTestVertex* const ap = new V3GraphTestVarVertex{gp, "$a_pre"}; V3GraphTestVertex* const bp = new V3GraphTestVarVertex{gp, "$b_pre"}; V3GraphTestVertex* const cp = new V3GraphTestVarVertex{gp, "$c_pre"}; V3GraphTestVertex* n; // Logical order between clk, and posedge blocks // implemented by special CLK prod/cons? // Required order between first x_DLY<=x_pre and final x<=x_DLY // implemented by producer/consumer on a_dly signals // Required order between first x_DLY<=x_pre and x_DLY<=setters // implemented by fake dependency on _dlyblk // Required order between x_DLY<=setters and final x<=x_DLY // implemented by producer/consumer on a_dly signals // Desired order between different _DLY blocks so we can elim temporaries // implemented by cutable "pre" signal dependencies n = new V3GraphTestVertex{gp, "*INPUTS*"}; { new V3GraphEdge{gp, n, clk, 2}; new V3GraphEdge{gp, n, i, 2}; } V3GraphTestVertex* const posedge = n = new V3GraphTestVertex{gp, "*posedge clk*"}; { new V3GraphEdge{gp, clk, n, 2}; } // AssignPre's VarRefs on LHS: generate special BLK // normal: VarRefs on LHS: generate normal // underSBlock: VarRefs on RHS: consume 'pre' (required to save cutable tests) n = new V3GraphTestVertex{gp, "a_dly
acyclic(&V3GraphEdge::followAlwaysTrue);
gp->order();
// Test debug function
gp->dumpEdges(std::cout, ap);
dumpSelf();
}
};
//======================================================================
class V3GraphTestImport final : public V3GraphTest {
#ifdef GRAPH_IMPORT
void dotImport();
#else
void dotImport() {}
#endif
public:
string name() override { return "import"; }
void runTest() override {
V3Graph* const gp = &m_graph;
dotImport();
dumpSelf();
gp->acyclic(&V3GraphEdge::followAlwaysTrue);
dumpSelf();
gp->rank(&V3GraphEdge::followAlwaysTrue);
dumpSelf();
}
};
// clang-format off
#ifdef GRAPH_IMPORT
# include "graph_export.cpp"
#endif
// clang-format on
//======================================================================
void V3Graph::selfTest() {
// Execute all of the tests
UINFO(2, __FUNCTION__ << ": " << endl);
{ V3GraphTestStrong{}.run(); }
{ V3GraphTestAcyc{}.run(); }
{ V3GraphTestVars{}.run(); }
{ V3GraphTestImport{}.run(); }
}