diff --git a/tools/netlist/include/NetlistVisitorOptions.hpp b/tools/netlist/include/NetlistVisitorOptions.hpp new file mode 100644 index 000000000..c56174582 --- /dev/null +++ b/tools/netlist/include/NetlistVisitorOptions.hpp @@ -0,0 +1,17 @@ +//------------------------------------------------------------------------------ +//! @file NetlistVisitorOptions.h +//! @brief Options controlling the way the netlist is created. +// +// SPDX-FileCopyrightText: Michael Popoloski +// SPDX-License-Identifier: MIT +//------------------------------------------------------------------------------ +#pragma once + +#include + +/// Hold various options controlling the way the netlist is created. +struct NetlistVisitorOptions { + + /// If enabled, unroll for loops in procedural blocks. + bool unrollForLoops{false}; +}; diff --git a/tools/netlist/include/visitors/GenerateBlockVisitor.hpp b/tools/netlist/include/visitors/GenerateBlockVisitor.hpp index 82af6c13a..48291362f 100644 --- a/tools/netlist/include/visitors/GenerateBlockVisitor.hpp +++ b/tools/netlist/include/visitors/GenerateBlockVisitor.hpp @@ -7,6 +7,7 @@ //------------------------------------------------------------------------------ #pragma once +#include "visitors/ContinuousAssignVisitor.hpp" #include "visitors/ProceduralBlockVisitor.hpp" using namespace slang; @@ -18,8 +19,9 @@ namespace netlist { /// marked as uninstantiated, and are therefore not visited. class GenerateBlockVisitor : public ast::ASTVisitor { public: - explicit GenerateBlockVisitor(ast::Compilation& compilation, Netlist& netlist) : - compilation(compilation), netlist(netlist) {} + explicit GenerateBlockVisitor(ast::Compilation& compilation, Netlist& netlist, + NetlistVisitorOptions const& options) : + compilation(compilation), netlist(netlist), options(options) {} /// Variable declaration. void handle(const ast::VariableSymbol& symbol) { netlist.addVariableDeclaration(symbol); } @@ -29,7 +31,7 @@ class GenerateBlockVisitor : public ast::ASTVisitor { public: - explicit InstanceVisitor(ast::Compilation& compilation, Netlist& netlist) : - compilation(compilation), netlist(netlist) {} + explicit InstanceVisitor(ast::Compilation& compilation, Netlist& netlist, + NetlistVisitorOptions const& options) : + compilation(compilation), netlist(netlist), options(options) {} private: void connectDeclToVar(NetlistNode& declNode, const ast::Symbol& variable) { @@ -207,7 +208,7 @@ class InstanceVisitor : public ast::ASTVisitor { /// Procedural block. void handle(const ast::ProceduralBlockSymbol& symbol) { - ProceduralBlockVisitor visitor(compilation, netlist, + ProceduralBlockVisitor visitor(compilation, netlist, options, ProceduralBlockVisitor::determineEdgeKind(symbol)); symbol.visit(visitor); } @@ -215,7 +216,7 @@ class InstanceVisitor : public ast::ASTVisitor { /// Generate block. void handle(const ast::GenerateBlockSymbol& symbol) { if (!symbol.isUninstantiated) { - GenerateBlockVisitor visitor(compilation, netlist); + GenerateBlockVisitor visitor(compilation, netlist, options); symbol.visit(visitor); } } @@ -231,6 +232,7 @@ class InstanceVisitor : public ast::ASTVisitor { private: ast::Compilation& compilation; Netlist& netlist; + NetlistVisitorOptions const& options; }; } // namespace netlist diff --git a/tools/netlist/include/visitors/NetlistVisitor.h b/tools/netlist/include/visitors/NetlistVisitor.h index 89a2714ea..a6b128cce 100644 --- a/tools/netlist/include/visitors/NetlistVisitor.h +++ b/tools/netlist/include/visitors/NetlistVisitor.h @@ -7,26 +7,13 @@ //------------------------------------------------------------------------------ #pragma once -#include "Config.h" -#include "Debug.h" #include "Netlist.h" -#include "fmt/color.h" -#include "fmt/format.h" +#include "NetlistVisitorOptions.hpp" #include "visitors/InstanceVisitor.hpp" -#include -#include -#include "slang/ast/ASTContext.h" #include "slang/ast/ASTVisitor.h" #include "slang/ast/Compilation.h" -#include "slang/ast/SemanticFacts.h" -#include "slang/ast/Symbol.h" -#include "slang/ast/expressions/AssignmentExpressions.h" -#include "slang/ast/symbols/BlockSymbols.h" #include "slang/ast/symbols/CompilationUnitSymbols.h" -#include "slang/ast/symbols/ValueSymbol.h" -#include "slang/diagnostics/TextDiagnosticClient.h" -#include "slang/util/Util.h" using namespace slang; @@ -35,17 +22,19 @@ namespace netlist { /// The top-level visitor that traverses the AST and builds a netlist connectivity graph. class NetlistVisitor : public ast::ASTVisitor { public: - explicit NetlistVisitor(ast::Compilation& compilation, Netlist& netlist) : - compilation(compilation), netlist(netlist) {} + explicit NetlistVisitor(ast::Compilation& compilation, Netlist& netlist, + NetlistVisitorOptions const& options) : + compilation(compilation), netlist(netlist), options(options) {} void handle(const ast::InstanceSymbol& symbol) { - InstanceVisitor visitor(compilation, netlist); + InstanceVisitor visitor(compilation, netlist, options); symbol.visit(visitor); } private: ast::Compilation& compilation; Netlist& netlist; + NetlistVisitorOptions const& options; }; } // namespace netlist diff --git a/tools/netlist/include/visitors/ProceduralBlockVisitor.hpp b/tools/netlist/include/visitors/ProceduralBlockVisitor.hpp index 927d0bc1b..3bc485c87 100644 --- a/tools/netlist/include/visitors/ProceduralBlockVisitor.hpp +++ b/tools/netlist/include/visitors/ProceduralBlockVisitor.hpp @@ -9,27 +9,27 @@ #include "Debug.h" #include "Netlist.h" -#include +#include "NetlistVisitorOptions.hpp" +#include "visitors/VariableReferenceVisitor.hpp" #include "slang/ast/EvalContext.h" #include "slang/ast/symbols/BlockSymbols.h" -#include "slang/util/IntervalMap.h" using namespace slang; namespace netlist { /// Visit a proceural block. This visitor performs unrolling of loops and -/// evaluation of conditionals where the controlloing conditions can be +/// evaluation of conditionals where the controlling conditions can be /// determined statically. class ProceduralBlockVisitor : public ast::ASTVisitor { public: bool anyErrors = false; explicit ProceduralBlockVisitor(ast::Compilation& compilation, Netlist& netlist, - ast::EdgeKind edgeKind) : + NetlistVisitorOptions const& options, ast::EdgeKind edgeKind) : netlist(netlist), evalCtx(ast::ASTContext(compilation.getRoot(), ast::LookupLocation::max)), - edgeKind(edgeKind) { + options(options), edgeKind(edgeKind) { evalCtx.pushEmptyFrame(); DEBUG_PRINT("Procedural block\n"); } @@ -68,7 +68,7 @@ class ProceduralBlockVisitor : public ast::ASTVisitorvisit(varRefVisitor); } - // Push the condition variables. - for (auto& varRef : varRefVisitor.getVars()) { - condVarsStack.push_back(varRef); - } - // Visit the 'then' and 'else' statements, whose execution is // under the control of the condition variables. stmt.ifTrue.visit(*this); if (stmt.ifFalse) { stmt.ifFalse->visit(*this); } - - // Pop the condition variables. - for (auto& varRef : varRefVisitor.getVars()) { - condVarsStack.pop_back(); - } }; for (auto& cond : stmt.conditions) { @@ -251,19 +242,6 @@ class ProceduralBlockVisitor : public ast::ASTVisitoras(); - - connectDeclToVar(condVarRef, condVarRef.symbol); - for (auto* leftNode : visitorLHS.getVars()) { - - // Add edge from conditional variable to the LHS variable. - connectVarToVar(*condNode, *leftNode); - } - } } private: @@ -303,7 +281,7 @@ class ProceduralBlockVisitor : public ast::ASTVisitor condVarsStack; + NetlistVisitorOptions const& options; ast::EdgeKind edgeKind; }; diff --git a/tools/netlist/include/visitors/VariableReferenceVisitor.hpp b/tools/netlist/include/visitors/VariableReferenceVisitor.hpp index 1b2a454ce..fc7c5b97d 100644 --- a/tools/netlist/include/visitors/VariableReferenceVisitor.hpp +++ b/tools/netlist/include/visitors/VariableReferenceVisitor.hpp @@ -7,6 +7,10 @@ //------------------------------------------------------------------------------ #pragma once +#include "Netlist.h" + +#include "slang/ast/ASTVisitor.h" + using namespace slang; namespace netlist { diff --git a/tools/netlist/netlist.cpp b/tools/netlist/netlist.cpp index 5e660462d..8b9e4d22c 100644 --- a/tools/netlist/netlist.cpp +++ b/tools/netlist/netlist.cpp @@ -13,11 +13,8 @@ #include "fmt/color.h" #include "fmt/format.h" #include "visitors/NetlistVisitor.h" -#include #include #include -#include -#include #include #include "slang/ast/ASTSerializer.h" @@ -27,8 +24,6 @@ #include "slang/driver/Driver.h" #include "slang/text/FormatBuffer.h" #include "slang/text/Json.h" -#include "slang/util/String.h" -#include "slang/util/TimeTrace.h" #include "slang/util/Util.h" #include "slang/util/VersionInfo.h" @@ -212,11 +207,13 @@ int main(int argc, char** argv) { std::optional quiet; std::optional debug; std::optional combLoops; + std::optional unrollForLoops; driver.cmdLine.add("-h,--help", showHelp, "Display available options"); driver.cmdLine.add("--version", showVersion, "Display version information and exit"); driver.cmdLine.add("-q,--quiet", quiet, "Suppress non-essential output"); driver.cmdLine.add("-d,--debug", debug, "Output debugging information"); driver.cmdLine.add("-c,--comb-loops", combLoops, "Detect combinatorial loops"); + driver.cmdLine.add("--unroll-for-loops", unrollForLoops, "Unroll procedural for loops"); std::optional astJsonFile; driver.cmdLine.add( @@ -289,7 +286,9 @@ int main(int argc, char** argv) { // Create the netlist by traversing the AST. Netlist netlist; - NetlistVisitor visitor(*compilation, netlist); + NetlistVisitorOptions options; + options.unrollForLoops = unrollForLoops.value_or(false); + NetlistVisitor visitor(*compilation, netlist, options); compilation->getRoot().visit(visitor); netlist.split(); DEBUG_PRINT("Netlist has {} nodes and {} edges\n", netlist.numNodes(), netlist.numEdges()); diff --git a/tools/netlist/tests/CombLoopsTests.cpp b/tools/netlist/tests/CombLoopsTests.cpp index c0c50600c..ea2bbe31a 100644 --- a/tools/netlist/tests/CombLoopsTests.cpp +++ b/tools/netlist/tests/CombLoopsTests.cpp @@ -8,6 +8,7 @@ #include "CombLoops.h" #include "NetlistTest.h" +#include "Test.h" //===---------------------------------------------------------------------===// // Basic tests diff --git a/tools/netlist/tests/NameTests.cpp b/tools/netlist/tests/NameTests.cpp index ff6039b87..ba966bddc 100644 --- a/tools/netlist/tests/NameTests.cpp +++ b/tools/netlist/tests/NameTests.cpp @@ -7,6 +7,7 @@ //------------------------------------------------------------------------------ #include "NetlistTest.h" +#include "Test.h" //===---------------------------------------------------------------------===// // Tests for name resolution. diff --git a/tools/netlist/tests/NetlistTest.h b/tools/netlist/tests/NetlistTest.h index 0d4b1152c..7f85bee81 100644 --- a/tools/netlist/tests/NetlistTest.h +++ b/tools/netlist/tests/NetlistTest.h @@ -3,15 +3,25 @@ #include "Netlist.h" #include "PathFinder.h" -#include "Test.h" #include "visitors/NetlistVisitor.h" #include +#include "slang/ast/Compilation.h" + using namespace netlist; -inline Netlist createNetlist(Compilation& compilation) { +inline Netlist createNetlist(ast::Compilation& compilation) { + Netlist netlist; + NetlistVisitorOptions options; + NetlistVisitor visitor(compilation, netlist, options); + compilation.getRoot().visit(visitor); + netlist.split(); + return netlist; +} + +inline Netlist createNetlist(ast::Compilation& compilation, NetlistVisitorOptions const& options) { Netlist netlist; - NetlistVisitor visitor(compilation, netlist); + NetlistVisitor visitor(compilation, netlist, options); compilation.getRoot().visit(visitor); netlist.split(); return netlist; diff --git a/tools/netlist/tests/PathTests.cpp b/tools/netlist/tests/PathTests.cpp index 76f9d0e54..7d048eb32 100644 --- a/tools/netlist/tests/PathTests.cpp +++ b/tools/netlist/tests/PathTests.cpp @@ -7,6 +7,7 @@ //------------------------------------------------------------------------------ #include "NetlistTest.h" +#include "Test.h" //===---------------------------------------------------------------------===// // Basic tests @@ -307,14 +308,14 @@ endmodule } //===---------------------------------------------------------------------===// -// Tests for conditional variables in procedural blocks. +// Tests for conditional variables in procedural blocks (Not supported!) //===---------------------------------------------------------------------===// TEST_CASE("Mux") { // Test that the variable in a conditional block is correctly added as a // dependency on the output variable controlled by that block. auto tree = SyntaxTree::fromText(R"( -module mux(input a, input b, input sel, output reg f); + module mux(input a, input b, input sel, output reg f); always @(*) begin if (sel == 1'b0) begin f = a; @@ -322,21 +323,22 @@ module mux(input a, input b, input sel, output reg f); f = b; end end -endmodule + endmodule )"); Compilation compilation; compilation.addSyntaxTree(tree); NO_COMPILATION_ERRORS; auto netlist = createNetlist(compilation); PathFinder pathFinder(netlist); - CHECK(!pathFinder.find(*netlist.lookupPort("mux.sel"), *netlist.lookupPort("mux.f")).empty()); + // Path does not exist! + CHECK(pathFinder.find(*netlist.lookupPort("mux.sel"), *netlist.lookupPort("mux.f")).empty()); } TEST_CASE("Nested muxing") { // Test that the variables in multiple nested levels of conditions are // correctly added as dependencies of the output variable. auto tree = SyntaxTree::fromText(R"( -module mux(input a, input b, input c, + module mux(input a, input b, input c, input sel_a, input sel_b, output reg f); always @(*) begin @@ -349,15 +351,16 @@ module mux(input a, input b, input c, f = c; end end -endmodule + endmodule )"); Compilation compilation; compilation.addSyntaxTree(tree); NO_COMPILATION_ERRORS; auto netlist = createNetlist(compilation); PathFinder pathFinder(netlist); - CHECK(!pathFinder.find(*netlist.lookupPort("mux.sel_a"), *netlist.lookupPort("mux.f")).empty()); - CHECK(!pathFinder.find(*netlist.lookupPort("mux.sel_b"), *netlist.lookupPort("mux.f")).empty()); + // Paths do not exist! + CHECK(pathFinder.find(*netlist.lookupPort("mux.sel_a"), *netlist.lookupPort("mux.f")).empty()); + CHECK(pathFinder.find(*netlist.lookupPort("mux.sel_b"), *netlist.lookupPort("mux.f")).empty()); } //===---------------------------------------------------------------------===// @@ -389,7 +392,9 @@ endmodule Compilation compilation; compilation.addSyntaxTree(tree); NO_COMPILATION_ERRORS; - auto netlist = createNetlist(compilation); + NetlistVisitorOptions options; + options.unrollForLoops = true; + auto netlist = createNetlist(compilation, options); PathFinder pathFinder(netlist); // i_value -> o_value, check it passes through each stage. CHECK(pathFinder @@ -426,7 +431,9 @@ endmodule Compilation compilation; compilation.addSyntaxTree(tree); NO_COMPILATION_ERRORS; - auto netlist = createNetlist(compilation); + NetlistVisitorOptions options; + options.unrollForLoops = true; + auto netlist = createNetlist(compilation, options); PathFinder pathFinder(netlist); // i_value -> o_value, check it passes through each stage. auto path = pathFinder.find(*netlist.lookupPort("chain_nested.i_value"), @@ -476,7 +483,9 @@ endmodule Compilation compilation; compilation.addSyntaxTree(tree); NO_COMPILATION_ERRORS; - auto netlist = createNetlist(compilation); + NetlistVisitorOptions options; + options.unrollForLoops = true; + auto netlist = createNetlist(compilation, options); auto* inPortA = netlist.lookupPort("chain_loop_dual.i_value_a"); auto* inPortB = netlist.lookupPort("chain_loop_dual.i_value_b"); auto* outPortA = netlist.lookupPort("chain_loop_dual.o_value_a"); diff --git a/tools/netlist/tests/SplitTests.cpp b/tools/netlist/tests/SplitTests.cpp index 732c4acd1..184e9eafb 100644 --- a/tools/netlist/tests/SplitTests.cpp +++ b/tools/netlist/tests/SplitTests.cpp @@ -7,6 +7,7 @@ //------------------------------------------------------------------------------ #include "NetlistTest.h" +#include "Test.h" // Test the logic that splits variable declaration nodes into sets of ALIAS // nodes with connections based on their selection bounds. diff --git a/tools/netlist/tests/VariableSelectorsTests.cpp b/tools/netlist/tests/VariableSelectorsTests.cpp index 6b44a094e..ea7ee7bd6 100644 --- a/tools/netlist/tests/VariableSelectorsTests.cpp +++ b/tools/netlist/tests/VariableSelectorsTests.cpp @@ -7,6 +7,7 @@ //------------------------------------------------------------------------------ #include "NetlistTest.h" +#include "Test.h" #include #include "slang/util/Util.h"