diff --git a/src/V3EmitCMake.cpp b/src/V3EmitCMake.cpp index e45864ead..cac63a4e4 100644 --- a/src/V3EmitCMake.cpp +++ b/src/V3EmitCMake.cpp @@ -179,30 +179,30 @@ class CMakeEmitter final { *of << "# User .cpp files (from .cpp's on Verilator command line)\n"; cmake_set_raw(*of, name + "_USER_CLASSES", cmake_list(v3Global.opt.cppFiles())); - if (const V3HierBlockPlan* const planp = v3Global.hierPlanp()) { + if (const V3HierGraph* const graphp = v3Global.hierGraphp()) { *of << "# Verilate hierarchical blocks\n"; // Sorted hierarchical blocks in order of leaf-first. - const V3HierBlockPlan::HierVector& hierBlocks = planp->hierBlocksSorted(); *of << "get_target_property(TOP_TARGET_NAME \"${TARGET}\" NAME)\n"; - for (V3HierBlockPlan::HierVector::const_iterator it = hierBlocks.begin(); - it != hierBlocks.end(); ++it) { - const V3HierBlock* hblockp = *it; - const V3HierBlock::HierBlockSet& children = hblockp->children(); + for (const V3GraphVertex& vtx : vlstd::reverse_view(graphp->vertices())) { + const V3HierBlock* const hblockp = vtx.as(); const string prefix = hblockp->hierPrefix(); *of << "add_library(" << prefix << " STATIC)\n"; *of << "target_link_libraries(${TOP_TARGET_NAME} PRIVATE " << prefix << ")\n"; - if (!children.empty()) { + if (!hblockp->outEmpty()) { *of << "target_link_libraries(" << prefix << " INTERFACE"; - for (const V3HierBlock* const childp : children) { - *of << " " << childp->hierPrefix(); + for (const V3GraphEdge& edge : hblockp->outEdges()) { + const V3HierBlock* const dependencyp = edge.top()->as(); + *of << " " << dependencyp->hierPrefix(); } *of << ")\n"; } *of << "verilate(" << prefix << " PREFIX " << prefix << " TOP_MODULE " << hblockp->modp()->name() << " DIRECTORY " << v3Global.opt.makeDir() + "/" + prefix << " SOURCES "; - for (const V3HierBlock* const childp : children) { - *of << " " << v3Global.opt.makeDir() + "/" + childp->hierWrapperFilename(true); + for (const V3GraphEdge& edge : hblockp->outEdges()) { + const V3HierBlock* const dependencyp = edge.top()->as(); + *of << " " + << v3Global.opt.makeDir() + "/" + dependencyp->hierWrapperFilename(true); } *of << " "; const string vFile = hblockp->vFileIfNecessary(); @@ -219,12 +219,13 @@ class CMakeEmitter final { *of << "verilate(${TOP_TARGET_NAME} PREFIX " << v3Global.opt.prefix() << " TOP_MODULE " << v3Global.rootp()->topModulep()->name() << " DIRECTORY " << v3Global.opt.makeDir() << " SOURCES "; - for (const auto& itr : *planp) { - *of << " " << v3Global.opt.makeDir() + "/" + itr.second.hierWrapperFilename(true); + for (const V3GraphVertex& vtx : graphp->vertices()) { + const V3HierBlock* const hblockp = vtx.as(); + *of << " " << v3Global.opt.makeDir() + "/" + hblockp->hierWrapperFilename(true); } *of << " " << cmake_list(v3Global.opt.vFiles()); *of << " VERILATOR_ARGS "; - *of << "-f " << planp->topCommandArgsFilename(true); + *of << "-f " << graphp->topCommandArgsFilename(true); *of << ")\n"; } } diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index bf73ff12d..338bae385 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -803,7 +803,7 @@ public: //###################################################################### class EmitMkHierVerilation final { - const V3HierBlockPlan* const m_planp; + const V3HierGraph* const m_graphp; const string m_makefile; // path of this makefile void emitCommonOpts(V3OutMkFile& of) const { const string cwd = V3Os::filenameRealPath("."); @@ -843,11 +843,9 @@ class EmitMkHierVerilation final { of.puts("# Libraries of hierarchical blocks\n"); of.puts("VM_HIER_LIBS := \\\n"); - const V3HierBlockPlan::HierVector blocks - = m_planp->hierBlocksSorted(); // leaf comes first - // List in order of leaf-last order so that linker can resolve dependency - for (const auto& block : vlstd::reverse_view(blocks)) { - of.puts(" " + block->hierLibFilename(true) + " \\\n"); + for (const V3GraphVertex& vtx : m_graphp->vertices()) { + const V3HierBlock* const blockp = vtx.as(); + of.puts(" " + blockp->hierLibFilename(true) + " \\\n"); } of.puts("\n"); @@ -866,28 +864,31 @@ class EmitMkHierVerilation final { // Top level module { - const string argsFile = v3Global.hierPlanp()->topCommandArgsFilename(false); + const string argsFile = v3Global.hierGraphp()->topCommandArgsFilename(false); of.puts("\n# Verilate the top module\n"); of.puts(v3Global.opt.prefix() + ".mk: $(VM_HIER_INPUT_FILES) $(VM_HIER_VERILOG_LIBS) "); of.puts(V3Os::filenameNonDir(argsFile) + " "); - for (const auto& itr : *m_planp) of.puts(itr.second.hierWrapperFilename(true) + " "); + for (const V3GraphVertex& vtx : m_graphp->vertices()) { + const V3HierBlock* const blockp = vtx.as(); + of.puts(blockp->hierWrapperFilename(true) + " "); + } of.puts("\n"); emitLaunchVerilator(of, argsFile); } // Rules to process hierarchical blocks of.puts("\n# Verilate hierarchical blocks\n"); - for (const V3HierBlock* const blockp : m_planp->hierBlocksSorted()) { + for (const V3GraphVertex& vtx : m_graphp->vertices()) { + const V3HierBlock* const blockp = vtx.as(); const string prefix = blockp->hierPrefix(); const string argsFilename = blockp->commandArgsFilename(false); of.puts(blockp->hierGeneratedFilenames(true)); of.puts(": $(VM_HIER_INPUT_FILES) $(VM_HIER_VERILOG_LIBS) "); of.puts(V3Os::filenameNonDir(argsFilename) + " "); - const V3HierBlock::HierBlockSet& children = blockp->children(); - for (V3HierBlock::HierBlockSet::const_iterator child = children.begin(); - child != children.end(); ++child) { - of.puts((*child)->hierWrapperFilename(true) + " "); + for (const V3GraphEdge& edge : blockp->outEdges()) { + const V3HierBlock* const dependencyp = edge.top()->as(); + of.puts(dependencyp->hierWrapperFilename(true) + " "); } of.puts("\n"); emitLaunchVerilator(of, argsFilename); @@ -897,9 +898,9 @@ class EmitMkHierVerilation final { of.puts(": "); of.puts(blockp->hierMkFilename(true)); of.puts(" "); - for (V3HierBlock::HierBlockSet::const_iterator child = children.begin(); - child != children.end(); ++child) { - of.puts((*child)->hierLibFilename(true)); + for (const V3GraphEdge& edge : blockp->outEdges()) { + const V3HierBlock* const dependencyp = edge.top()->as(); + of.puts(dependencyp->hierLibFilename(true)); of.puts(" "); } of.puts("\n\t$(MAKE) -f " + blockp->hierMkFilename(false) + " -C " + prefix); @@ -910,8 +911,8 @@ class EmitMkHierVerilation final { } public: - explicit EmitMkHierVerilation(const V3HierBlockPlan* planp) - : m_planp{planp} + explicit EmitMkHierVerilation(const V3HierGraph* graphp) + : m_graphp{graphp} , m_makefile{v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "_hier.mk"} { V3OutMkFile of{m_makefile}; emit(of); @@ -926,7 +927,7 @@ void V3EmitMk::emitmk() { const EmitMk emitter; } -void V3EmitMk::emitHierVerilation(const V3HierBlockPlan* planp) { +void V3EmitMk::emitHierVerilation(const V3HierGraph* graphp) { UINFO(2, __FUNCTION__ << ":"); - EmitMkHierVerilation{planp}; + EmitMkHierVerilation{graphp}; } diff --git a/src/V3EmitMk.h b/src/V3EmitMk.h index 4270fad3e..9c81bbeea 100644 --- a/src/V3EmitMk.h +++ b/src/V3EmitMk.h @@ -20,7 +20,7 @@ #include "config_build.h" #include "verilatedos.h" -class V3HierBlockPlan; +class V3HierGraph; //============================================================================ @@ -30,7 +30,7 @@ public: static const size_t PARALLEL_FILE_CNT_THRESHOLD = 128; static void emitmk() VL_MT_DISABLED; - static void emitHierVerilation(const V3HierBlockPlan* planp) VL_MT_DISABLED; + static void emitHierVerilation(const V3HierGraph* planp) VL_MT_DISABLED; }; #endif // Guard diff --git a/src/V3EmitMkJson.cpp b/src/V3EmitMkJson.cpp index 65507d1f6..f9bd18a6d 100644 --- a/src/V3EmitMkJson.cpp +++ b/src/V3EmitMkJson.cpp @@ -111,64 +111,69 @@ class V3EmitMkJsonEmitter final { .putList("user_classes", cppFiles) .end(); - if (const V3HierBlockPlan* const planp = v3Global.hierPlanp()) { - // Sorted hierarchical blocks in order of leaf-first. - const V3HierBlockPlan::HierVector& hierBlocks = planp->hierBlocksSorted(); - + if (const V3HierGraph* const graphp = v3Global.hierGraphp()) { of.begin("submodules", '['); - for (V3HierBlockPlan::HierVector::const_iterator it = hierBlocks.begin(); - it != hierBlocks.end(); ++it) { - const V3HierBlock* hblockp = *it; - const V3HierBlock::HierBlockSet& children = hblockp->children(); - - std::vector childDeps; + // Enumerate in dependency order, leaves first. TODO: Shouldn't + // really have to, but verilator-config.cmake.in depends on order. + for (const V3GraphVertex& vtx : vlstd::reverse_view(graphp->vertices())) { + const V3HierBlock* const hblockp = vtx.as(); + std::vector deps; std::vector sources; - - for (const V3HierBlock* const childp : children) { - childDeps.emplace_back(childp->hierPrefix()); - sources.emplace_back(makeDir + "/" + childp->hierWrapperFilename(true)); + for (const V3GraphEdge& edge : hblockp->outEdges()) { + const V3HierBlock* const dependencyp = edge.top()->as(); + deps.emplace_back(dependencyp->hierPrefix()); + sources.emplace_back(makeDir + "/" + dependencyp->hierWrapperFilename(true)); } - const string vFile = hblockp->vFileIfNecessary(); + const std::string vFile = hblockp->vFileIfNecessary(); if (!vFile.empty()) sources.emplace_back(vFile); - for (const auto& i : v3Global.opt.vFiles()) - sources.emplace_back( - V3Os::filenameSlashPath(V3Os::filenameRealPath(i.filename()))); - - std::vector cflags; - cflags.emplace_back("-fPIC"); + for (const VFileLibName& i : v3Global.opt.vFiles()) { + const std::string fname = i.filename(); + sources.emplace_back(V3Os::filenameSlashPath(V3Os::filenameRealPath(fname))); + } of.begin() .put("prefix", hblockp->hierPrefix()) .put("top", hblockp->modp()->name()) - .putList("deps", childDeps) + .putList("deps", deps) .put("directory", makeDir + "/" + hblockp->hierPrefix()) .putList("sources", sources) - .putList("cflags", cflags) + .putList("cflags", {"-fPIC"}) .put("verilator_args", V3Os::filenameSlashPath(V3Os::filenameRealPath( hblockp->commandArgsFilename(true)))) .end(); } - std::vector sources; - for (const auto& itr : *planp) - sources.emplace_back(makeDir + "/" + itr.second.hierWrapperFilename(true)); + // Top level reintegration + { + // TODO: When this reintegration "submodule" is built with the bundled + // CMake script, it will overwrite the original json manifest containing the + // "subodule" list we are creating here with one that doesn't have "submodule". + // Good luck debugging, suggest 'message(${MANIFEST})' in verilated-config.cmake. + std::vector sources; + for (const V3GraphVertex& vtx : graphp->vertices()) { + const V3HierBlock* const blockp = vtx.as(); + sources.emplace_back(makeDir + "/" + blockp->hierWrapperFilename(true)); + } - for (const auto& itr : v3Global.opt.vFiles()) - sources.emplace_back( - V3Os::filenameSlashPath(V3Os::filenameRealPath(itr.filename()))); + for (const VFileLibName& i : v3Global.opt.vFiles()) { + const std::string fname = i.filename(); + sources.emplace_back(V3Os::filenameSlashPath(V3Os::filenameRealPath(fname))); + } - of.begin() - .put("prefix", v3Global.opt.prefix()) - .put("top", v3Global.rootp()->topModulep()->name()) - .put("directory", makeDir) - .putList("sources", sources) - .put("verilator_args", V3Os::filenameSlashPath(V3Os::filenameRealPath( - planp->topCommandArgsFilename(true)))) - .end() - .end(); + of.begin() + .put("prefix", v3Global.opt.prefix()) + .put("top", v3Global.rootp()->topModulep()->name()) + .put("directory", makeDir) + .putList("sources", sources) + .put("verilator_args", V3Os::filenameSlashPath(V3Os::filenameRealPath( + graphp->topCommandArgsFilename(true)))) + .end(); + } + + of.end(); // submodules } } diff --git a/src/V3File.h b/src/V3File.h index 9ed5523d8..e9e740afd 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -339,11 +339,10 @@ public: V3OutJsonFile& put(bool value) { return putNamed("", value ? "true" : "false", false); } V3OutJsonFile& put(int value) { return putNamed("", std::to_string(value), false); } - template - V3OutJsonFile& putList(const std::string& name, const T& list) { + V3OutJsonFile& putList(const std::string& name, const std::vector& list) { if (list.empty()) return *this; begin(name, '['); - for (auto it = list.begin(); it != list.end(); ++it) put(*it); + for (const std::string& str : list) put(str); return end(); } diff --git a/src/V3Global.cpp b/src/V3Global.cpp index 8cbc03372..56eda099b 100644 --- a/src/V3Global.cpp +++ b/src/V3Global.cpp @@ -55,7 +55,7 @@ void V3Global::boot() { void V3Global::shutdown() { V3PreShell::shutdown(); - VL_DO_CLEAR(delete m_hierPlanp, m_hierPlanp = nullptr); // delete nullptr is safe + VL_DO_CLEAR(delete m_hierGraphp, m_hierGraphp = nullptr); // delete nullptr is safe VL_DO_CLEAR(delete m_threadPoolp, m_threadPoolp = nullptr); // delete nullptr is safe #ifdef VL_LEAK_CHECKS if (m_rootp) VL_DO_CLEAR(m_rootp->deleteTree(), m_rootp = nullptr); diff --git a/src/V3Global.h b/src/V3Global.h index 5f876f95c..ec9e1f660 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -37,7 +37,7 @@ #include class AstNetlist; -class V3HierBlockPlan; +class V3HierGraph; class V3ThreadPool; //====================================================================== @@ -97,12 +97,12 @@ constexpr bool operator==(VWidthMinUsage::en lhs, const VWidthMinUsage& rhs) { class V3Global final { // Globals - AstNetlist* m_rootp = nullptr; // Root of entire netlist, - // created by makeInitNetlist(} so static constructors run first - V3HierBlockPlan* m_hierPlanp = nullptr; // Hierarchical Verilation plan, - // nullptr unless hier_block, set via hierPlanp(V3HierBlockPlan*} - V3ThreadPool* m_threadPoolp = nullptr; // Thread Pool, - // nullptr unless 'verilatedJobs' is known, set via threadPoolp(V3ThreadPool*) + // Root of entire netlist, created by makeInitNetlist(} so static constructors run first + AstNetlist* m_rootp = nullptr; + // Hierarchical block graph (plan) iff hierarchical verilation is performed + V3HierGraph* m_hierGraphp = nullptr; + // Thread Pool, nullptr unless 'verilatedJobs' is known, set via threadPoolp(V3ThreadPool*) + V3ThreadPool* m_threadPoolp = nullptr; VWidthMinUsage m_widthMinUsage = VWidthMinUsage::LINT_WIDTH; // What AstNode::widthMin() is used for @@ -199,11 +199,8 @@ public: void setHasForceableSignals() { m_hasForceableSignals = true; } bool hasSCTextSections() const VL_MT_SAFE { return m_hasSCTextSections; } void setHasSCTextSections() { m_hasSCTextSections = true; } - V3HierBlockPlan* hierPlanp() const { return m_hierPlanp; } - void hierPlanp(V3HierBlockPlan* plan) { - UASSERT(!m_hierPlanp, "call once"); - m_hierPlanp = plan; - } + V3HierGraph* hierGraphp() const { return m_hierGraphp; } + void hierGraphp(V3HierGraph* graphp) { m_hierGraphp = graphp; } bool useParallelBuild() const { return m_useParallelBuild; } void useParallelBuild(bool flag) { m_useParallelBuild = flag; } bool useRandomizeMethods() const { return m_useRandomizeMethods; } diff --git a/src/V3HierBlock.cpp b/src/V3HierBlock.cpp index 81a2fe807..9eaa94a49 100644 --- a/src/V3HierBlock.cpp +++ b/src/V3HierBlock.cpp @@ -126,7 +126,7 @@ static void V3HierWriteCommonInputs(const V3HierBlock* hblockp, std::ostream* of //###################################################################### -V3HierBlock::StrGParams V3HierBlock::stringifyParams(const V3HierBlockParams::GParams& gparams, +V3HierBlock::StrGParams V3HierBlock::stringifyParams(const std::vector& gparams, bool forGOption) { StrGParams strParams; for (const AstVar* const gparam : gparams) { @@ -171,18 +171,19 @@ VStringList V3HierBlock::commandArgs(bool forMkJson) const { opts.push_back(" --protect-key " + v3Global.opt.protectKeyDefaulted()); opts.push_back(" --hierarchical-child " + cvtToStr(v3Global.opt.threads())); - const StrGParams gparamsStr = stringifyParams(params().gparams(), true); + const StrGParams gparamsStr = stringifyParams(m_params, true); for (const StrGParam& param : gparamsStr) { const string name = param.first; const string value = param.second; opts.push_back("-G" + name + "=" + value + ""); } - if (!params().gTypeParams().empty()) + if (!m_typeParams.empty()) { opts.push_back(" --hierarchical-params-file " + typeParametersFilename()); + } const int blockThreads = V3Control::getHierWorkers(m_modp->origName()); if (blockThreads > 1) { - if (hasParent()) { + if (!inEmpty()) { V3Control::getHierWorkersFileLine(m_modp->origName()) ->v3warn(E_UNSUPPORTED, "Specifying workers for nested hierarchical blocks"); } else { @@ -202,14 +203,13 @@ VStringList V3HierBlock::commandArgs(bool forMkJson) const { VStringList V3HierBlock::hierBlockArgs() const { VStringList opts; - const StrGParams gparamsStr = stringifyParams(params().gparams(), false); + const StrGParams gparamsStr = stringifyParams(m_params, false); opts.push_back("--hierarchical-block "); string s = modp()->origName(); // origName s += "," + modp()->name(); // mangledName - for (StrGParams::const_iterator paramIt = gparamsStr.begin(); paramIt != gparamsStr.end(); - ++paramIt) { - s += "," + paramIt->first; - s += "," + paramIt->second; + for (const StrGParam& pair : gparamsStr) { + s += "," + pair.first; + s += "," + pair.second; } opts.back() += s; return opts; @@ -254,8 +254,9 @@ void V3HierBlock::writeCommandArgsFile(bool forMkJson) const { *of << "--cc\n"; if (!forMkJson) { - for (const V3HierBlock* const hierblockp : m_children) { - *of << v3Global.opt.makeDir() << "/" << hierblockp->hierWrapperFilename(true) << "\n"; + for (const V3GraphEdge& edge : outEdges()) { + const V3HierBlock* const dependencyp = edge.top()->as(); + *of << v3Global.opt.makeDir() << "/" << dependencyp->hierWrapperFilename(true) << "\n"; } *of << "-Mdir " << v3Global.opt.makeDir() << "/" << hierPrefix() << " \n"; } @@ -263,8 +264,9 @@ void V3HierBlock::writeCommandArgsFile(bool forMkJson) const { const VStringList& commandOpts = commandArgs(false); for (const string& opt : commandOpts) *of << opt << "\n"; *of << hierBlockArgs().front() << "\n"; - for (const V3HierBlock* const hierblockp : m_children) { - *of << hierblockp->hierBlockArgs().front() << "\n"; + for (const V3GraphEdge& edge : outEdges()) { + const V3HierBlock* const dependencyp = edge.top()->as(); + *of << dependencyp->hierBlockArgs().front() << "\n"; } *of << v3Global.opt.allArgsStringForHierBlock(false) << "\n"; } @@ -278,13 +280,13 @@ string V3HierBlock::typeParametersFilename() const { } void V3HierBlock::writeParametersFile() const { - if (m_params.gTypeParams().empty()) return; + if (m_typeParams.empty()) return; VHashSha256 hash{"type params"}; const string moduleName = "Vhsh" + hash.digestSymbol(); const std::unique_ptr of{V3File::new_ofstream(typeParametersFilename())}; *of << "module " << moduleName << ";\n"; - for (AstParamTypeDType* const gparam : m_params.gTypeParams()) { + for (AstParamTypeDType* const gparam : m_typeParams) { AstTypedef* tdefp = new AstTypedef{new FileLine{FileLine::builtInFilename()}, gparam->name(), nullptr, VFlagChildDType{}, gparam->skipRefp()->cloneTreePure(true)}; @@ -297,176 +299,113 @@ void V3HierBlock::writeParametersFile() const { } //###################################################################### -// Collect how hierarchical blocks are used +// Construct graph of hierarchical blocks class HierBlockUsageCollectVisitor final : public VNVisitorConst { // NODE STATE - // AstNode::user1() -> bool. Processed + // AstNode::user1() -> bool. Already visited const VNUser1InUse m_inuser1; // STATE - using ModuleSet = std::unordered_set; - V3HierBlockPlan* const m_planp; + V3HierGraph* const m_graphp = new V3HierGraph{}; // The graph of hierarchical blocks + // Map from hier blocks to the corresponding V3HierBlock graph vertex + std::unordered_map m_mod2vtx; AstModule* m_modp = nullptr; // The current module - AstModule* m_hierBlockp = nullptr; // The nearest parent module that is a hierarchical block - ModuleSet m_referred; // Modules that have hier_block pragma - V3HierBlockParams m_params; + std::vector m_params; // Overridden value parameters of current module + std::vector m_typeParams; // Type parameters of current module + // Hierarchical blocks instanciated (possibly indirectly) by current hierarchical block + std::vector m_childrenp; + // VISITORSs + void visit(AstNodeModule*) override {} // Ignore all non AstModule void visit(AstModule* nodep) override { - // Don't visit twice + // Visit each module once if (nodep->user1SetOnce()) return; - UINFO(5, "Checking " << nodep->prettyNameQ() << " from " - << (m_hierBlockp ? m_hierBlockp->prettyNameQ() : "null"s)); + + UINFO(5, "Visiting " << nodep->prettyNameQ()); VL_RESTORER(m_modp); - AstModule* const prevHierBlockp = m_hierBlockp; - ModuleSet prevReferred; - V3HierBlockParams previousParams; m_modp = nodep; - if (nodep->hierBlock()) { - m_hierBlockp = nodep; - prevReferred.swap(m_referred); - } - previousParams.swap(m_params); + // If not a hierarchical block, just iterate and return + if (!nodep->hierBlock()) { + iterateChildrenConst(nodep); + return; + } + + // This is a hierarchical block, gather parts + VL_RESTORER(m_params); + VL_RESTORER(m_typeParams); + VL_RESTORER(m_childrenp); + m_params.clear(); + m_typeParams.clear(); + m_childrenp.clear(); iterateChildrenConst(nodep); - - if (nodep->hierBlock()) { - m_planp->add(nodep, m_params); - for (const AstModule* modp : m_referred) m_planp->registerUsage(nodep, modp); - m_hierBlockp = prevHierBlockp; - m_referred = prevReferred; - } - m_params.swap(previousParams); + // Create the graph vertex for this hier block + V3HierBlock* const blockp = new V3HierBlock{m_graphp, nodep, m_params, m_typeParams}; + // Record it + m_mod2vtx[nodep] = blockp; + // Add an edge to each child block + for (V3HierBlock* const childp : m_childrenp) new V3GraphEdge{m_graphp, blockp, childp, 1}; } void visit(AstCell* nodep) override { - // Visit used module here to know that the module is hier_block or not. - // This visitor behaves almost depth first search - if (AstModule* const modp = VN_CAST(nodep->modp(), Module)) { - iterateConst(modp); - m_referred.insert(modp); - } - // Nothing to do for interface because hierarchical block does not exist - // beyond interface. + // Nothing to do for non AstModules because hierarchical block cannot exist under them. + AstModule* const modp = VN_CAST(nodep->modp(), Module); + if (!modp) return; + // Depth-first traversal of module hierechy + iterateConst(modp); + // If this is an instance of a hierarchical block, add to child array to link parent + if (modp->hierBlock()) m_childrenp.emplace_back(m_mod2vtx.at(modp)); } void visit(AstVar* nodep) override { if (m_modp && m_modp->hierBlock() && nodep->isIfaceRef() && !nodep->isIfaceParent()) { nodep->v3error("Modport cannot be used at the hierarchical block boundary"); } - if (nodep->isGParam() && nodep->overriddenParam()) m_params.add(nodep); + // Record overridden value parameter + if (nodep->isGParam() && nodep->overriddenParam()) { + UASSERT_OBJ(m_modp, nodep, "Value parameter not under module"); + m_params.push_back(nodep); + } + } + void visit(AstParamTypeDType* nodep) override { + // Record type parameter + UASSERT_OBJ(m_modp, nodep, "Type parameter not under module"); + m_typeParams.push_back(nodep); } - void visit(AstParamTypeDType* nodep) override { m_params.add(nodep); } - + void visit(AstNodeStmt*) override {} // Accelerate void visit(AstNodeExpr*) override {} // Accelerate void visit(AstConstPool*) override {} // Accelerate + void visit(AstTypeTable*) override {} // Accelerate void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } + // CONSTRUCTOR + HierBlockUsageCollectVisitor(AstNetlist* netlistp) { + iterateChildrenConst(netlistp); + if (dumpGraphLevel() >= 3) m_graphp->dumpDotFilePrefixed("hierblocks_initial"); + // Simplify dependencies + m_graphp->removeRedundantEdgesSum(&V3GraphEdge::followAlwaysTrue); + // Topologically sorder the graph + m_graphp->order(); // This is a bit heavy weight, but does produce a topological ordering + if (dumpGraphLevel() >= 3) m_graphp->dumpDotFilePrefixed("hierblocks"); + } + public: - HierBlockUsageCollectVisitor(V3HierBlockPlan* planp, AstNetlist* netlist) - : m_planp{planp} { - iterateChildrenConst(netlist); + static V3HierGraph* apply(AstNetlist* netlistp) { + return HierBlockUsageCollectVisitor{netlistp}.m_graphp; } }; -//###################################################################### +void V3HierGraph::writeCommandArgsFiles(bool forMkJson) const { -void V3HierBlockPlan::add(const AstNodeModule* modp, const V3HierBlockParams& params) { - const bool newEntry = m_blocks - .emplace(std::piecewise_construct, std::forward_as_tuple(modp), - std::forward_as_tuple(modp, params)) - .second; - if (newEntry) { - UINFO(3, "Add " << modp->prettyNameQ() << " with " << params.gparams().size() - << " parameters and " << params.gTypeParams().size() - << " type parameters"); - } -} - -void V3HierBlockPlan::registerUsage(const AstNodeModule* parentp, const AstNodeModule* childp) { - const iterator parent = m_blocks.find(parentp); - UASSERT_OBJ(parent != m_blocks.end(), parentp, "must be added"); - const iterator child = m_blocks.find(childp); - if (child != m_blocks.end()) { - UINFO(3, "Found usage relation " << parentp->prettyNameQ() << " uses " - << childp->prettyNameQ()); - parent->second.addChild(&child->second); - child->second.addParent(&parent->second); - } -} - -void V3HierBlockPlan::createPlan(AstNetlist* nodep) { - // When processing a hierarchical block, no need to create a plan anymore. - if (v3Global.opt.hierChild()) return; - - AstNodeModule* const modp = nodep->topModulep(); - if (modp->hierBlock()) { - modp->v3warn(HIERBLOCK, - "Top module illegally marked hierarchical block, ignoring marking\n" - + modp->warnMore() - + "... Suggest remove verilator hier_block on this module"); - modp->hierBlock(false); - } - - std::unique_ptr planp(new V3HierBlockPlan); - { HierBlockUsageCollectVisitor{planp.get(), nodep}; } - - V3Stats::addStat("HierBlock, Hierarchical blocks", planp->m_blocks.size()); - - // No hierarchical block is found, nothing to do. - if (planp->empty()) return; - - v3Global.hierPlanp(planp.release()); -} - -V3HierBlockPlan::HierVector V3HierBlockPlan::hierBlocksSorted() const { - using ChildrenMap - = std::unordered_map>; - ChildrenMap childrenOfHierBlock; - - HierVector sorted; - for (const_iterator it = begin(); it != end(); ++it) { - if (!it->second.hasChild()) { // No children, already leaf - sorted.push_back(&it->second); - } else { - ChildrenMap::value_type::second_type& childrenSet - = childrenOfHierBlock[&it->second]; // insert - const V3HierBlock::HierBlockSet& c = it->second.children(); - childrenSet.insert(c.begin(), c.end()); - } - } - - // Use index instead of iterator because new elements will be added in this loop - for (size_t i = 0; i < sorted.size(); ++i) { - // This hblockp is already leaf. - const V3HierBlock* hblockp = sorted[i]; - const V3HierBlock::HierBlockSet& p = hblockp->parents(); - for (V3HierBlock::HierBlockSet::const_iterator it = p.begin(); it != p.end(); ++it) { - // Delete hblockp from parents. If a parent does not have a child anymore, then it is - // a leaf too. - const auto parentIt = childrenOfHierBlock.find(*it); - UASSERT_OBJ(parentIt != childrenOfHierBlock.end(), (*it)->modp(), "must be included"); - const V3HierBlock::HierBlockSet::size_type erased = parentIt->second.erase(hblockp); - UASSERT_OBJ(erased == 1, hblockp->modp(), - " must be a child of " << parentIt->first->modp()); - if (parentIt->second.empty()) { // Now parentIt is leaf - sorted.push_back(parentIt->first); - childrenOfHierBlock.erase(parentIt); - } - } - } - return sorted; -} - -void V3HierBlockPlan::writeCommandArgsFiles(bool forMkJson) const { - for (const_iterator it = begin(); it != end(); ++it) { - it->second.writeCommandArgsFile(forMkJson); + for (const V3GraphVertex& vtx : vertices()) { + vtx.as()->writeCommandArgsFile(forMkJson); } // For the top module const std::unique_ptr of{ V3File::new_ofstream(topCommandArgsFilename(forMkJson))}; if (!forMkJson) { // Load wrappers first not to be overwritten by the original HDL - for (const_iterator it = begin(); it != end(); ++it) { - *of << it->second.hierWrapperFilename(true) << "\n"; + for (const V3GraphVertex& vtx : vertices()) { + *of << vtx.as()->hierWrapperFilename(true) << "\n"; } } V3HierWriteCommonInputs(nullptr, of.get(), forMkJson); @@ -478,8 +417,8 @@ void V3HierBlockPlan::writeCommandArgsFiles(bool forMkJson) const { *of << "-Mdir " << v3Global.opt.makeDir() << "\n"; *of << "--mod-prefix " << v3Global.opt.modPrefix() << "\n"; } - for (const_iterator it = begin(); it != end(); ++it) { - *of << it->second.hierBlockArgs().front() << "\n"; + for (const V3GraphVertex& vtx : vertices()) { + *of << vtx.as()->hierBlockArgs().front() << "\n"; } if (!v3Global.opt.libCreate().empty()) { @@ -493,10 +432,34 @@ void V3HierBlockPlan::writeCommandArgsFiles(bool forMkJson) const { *of << v3Global.opt.allArgsStringForHierBlock(true) << "\n"; } -string V3HierBlockPlan::topCommandArgsFilename(bool forMkJson) { +string V3HierGraph::topCommandArgsFilename(bool forMkJson) { return V3HierCommandArgsFilename(v3Global.opt.prefix(), forMkJson); } -void V3HierBlockPlan::writeParametersFiles() const { - for (const auto& block : *this) block.second.writeParametersFile(); +void V3HierGraph::writeParametersFiles() const { + for (const V3GraphVertex& vtx : vertices()) { vtx.as()->writeParametersFile(); } +} + +//###################################################################### + +void V3Hierarchical::createGraph(AstNetlist* netlistp) { + UASSERT(!v3Global.hierGraphp(), "Should only be called once"); + + AstNodeModule* const modp = netlistp->topModulep(); + if (modp->hierBlock()) { + modp->v3warn(HIERBLOCK, "Top module marked as hierarchical block, ignoring\n" + + modp->warnMore() + + "... Suggest remove verilator hier_block on this module"); + modp->hierBlock(false); + } + + V3HierGraph* const graphp = HierBlockUsageCollectVisitor::apply(netlistp); + V3Stats::addStat("HierBlock, Hierarchical blocks", graphp->vertices().size()); + // No hierarchical block is found, nothing to do. + if (graphp->empty()) { + VL_DO_DANGLING(delete graphp, graphp); + return; + } + // Hold on to the graph + v3Global.hierGraphp(graphp); } diff --git a/src/V3HierBlock.h b/src/V3HierBlock.h index 2666ce718..cdfeb1fb5 100644 --- a/src/V3HierBlock.h +++ b/src/V3HierBlock.h @@ -19,7 +19,8 @@ #include "verilatedos.h" -#include "V3Options.h" +#include "V3Ast.h" +#include "V3Graph.h" #include #include @@ -36,68 +37,51 @@ class AstVar; //###################################################################### -class V3HierBlockParams final { +class V3HierGraph final : public V3Graph { public: - using GParams = std::vector; - using GTypeParams = std::vector; + V3HierGraph() = default; + ~V3HierGraph() = default; + VL_UNCOPYABLE(V3HierGraph); + VL_UNMOVABLE(V3HierGraph); -private: - GParams m_params; - GTypeParams m_typeParams; - -public: - void add(AstVar* param) { m_params.push_back(param); } - void add(AstParamTypeDType* param) { m_typeParams.push_back(param); } - - const GParams& gparams() const { return m_params; }; - const GTypeParams& gTypeParams() const { return m_typeParams; }; - - void swap(V3HierBlockParams& other) { - m_params.swap(other.m_params); - m_typeParams.swap(other.m_typeParams); - } + // Write command line arguments to .f files for child Verilation run + void writeCommandArgsFiles(bool forMkJson) const VL_MT_DISABLED; + void writeParametersFiles() const VL_MT_DISABLED; + static string topCommandArgsFilename(bool forMkJson) VL_MT_DISABLED; }; -class V3HierBlock final { -public: - using HierBlockSet = std::unordered_set; +class V3HierBlock final : public V3GraphVertex { + VL_RTTI_IMPL(V3HierBlock, V3GraphVertex) -private: // TYPES // Parameter name, stringified value using StrGParam = std::pair; using StrGParams = std::vector; // MEMBERS - const AstNodeModule* const m_modp; // Hierarchical block module - // Hierarchical blocks that directly or indirectly instantiate this block - HierBlockSet m_parents; - // Hierarchical blocks that this block directly or indirectly instantiates - HierBlockSet m_children; - // Parameters that are overridden by #(.param(value)) syntax. - const V3HierBlockParams m_params; + const AstModule* const m_modp; // Hierarchical block module + // Value parameters that are overridden by #(.param(value)) syntax. + const std::vector m_params; + // Types parameters that are overridden by #(.param(value)) syntax. + const std::vector m_typeParams; // METHODS - VL_UNCOPYABLE(V3HierBlock); - VL_UNMOVABLE(V3HierBlock); - static StrGParams stringifyParams(const V3HierBlockParams::GParams& params, + static StrGParams stringifyParams(const std::vector& params, bool forGOption) VL_MT_DISABLED; public: - V3HierBlock(const AstNodeModule* modp, const V3HierBlockParams& params) - : m_modp{modp} - , m_params{params} {} - + // CONSTRUCTORs + V3HierBlock(V3HierGraph* graphp, const AstModule* modp, const std::vector& params, + const std::vector& typeParams) + : V3GraphVertex{graphp} + , m_modp{modp} + , m_params{params} + , m_typeParams{typeParams} {} ~V3HierBlock() VL_MT_DISABLED = default; + VL_UNCOPYABLE(V3HierBlock); + VL_UNMOVABLE(V3HierBlock); - void addParent(V3HierBlock* parentp) { m_parents.insert(parentp); } - bool hasParent() const { return !m_parents.empty(); } - void addChild(V3HierBlock* childp) { m_children.insert(childp); } - bool hasChild() const { return !m_children.empty(); } - const HierBlockSet& parents() const { return m_parents; } - const HierBlockSet& children() const { return m_children; } - const V3HierBlockParams& params() const { return m_params; } - const AstNodeModule* modp() const { return m_modp; } + const AstModule* modp() const { return m_modp; } // For emitting Makefile and build definition JSON VStringList commandArgs(bool forMkJson) const VL_MT_DISABLED; @@ -116,41 +100,18 @@ public: void writeParametersFile() const VL_MT_DISABLED; string commandArgsFilename(bool forMkJson) const VL_MT_DISABLED; string typeParametersFilename() const VL_MT_DISABLED; + + // For Graphviz dumps only + std::string name() const override { return m_modp->prettyNameQ(); } + std::string dotShape() const override { return "box"; } }; //###################################################################### +// Pass to create hierarcical block graph -// Holds relationship between AstNodeModule and V3HierBlock -class V3HierBlockPlan final { - // TODO: this map is non-deterministic - using HierMap = std::unordered_map; - HierMap m_blocks; - - V3HierBlockPlan() = default; - VL_UNCOPYABLE(V3HierBlockPlan); - +class V3Hierarchical final { public: - using iterator = HierMap::iterator; - using const_iterator = HierMap::const_iterator; - using HierVector = std::vector; - - void add(const AstNodeModule* modp, const V3HierBlockParams& params) VL_MT_DISABLED; - void registerUsage(const AstNodeModule* parentp, const AstNodeModule* childp) VL_MT_DISABLED; - - const_iterator begin() const { return m_blocks.begin(); } - const_iterator end() const { return m_blocks.end(); } - bool empty() const { return m_blocks.empty(); } - - // Returns all hierarchical blocks that sorted in leaf-first order. - // Latter block refers only already appeared hierarchical blocks. - HierVector hierBlocksSorted() const VL_MT_DISABLED; - - // Write command line arguments to .f files for child Verilation run - void writeCommandArgsFiles(bool forMkJson) const VL_MT_DISABLED; - void writeParametersFiles() const VL_MT_DISABLED; - static string topCommandArgsFilename(bool forMkJson) VL_MT_DISABLED; - - static void createPlan(AstNetlist* nodep) VL_MT_DISABLED; + static void createGraph(AstNetlist* nodep) VL_MT_DISABLED; }; #endif // guard diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 7b752c98a..d0afe8b64 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -193,11 +193,11 @@ static void process() { // Create a hierarchical Verilation plan if (!v3Global.opt.lintOnly() && !v3Global.opt.serializeOnly() - && v3Global.opt.hierarchical()) { - V3HierBlockPlan::createPlan(v3Global.rootp()); + && v3Global.opt.hierarchical() && !v3Global.opt.hierChild()) { + V3Hierarchical::createGraph(v3Global.rootp()); // If a plan is created, further analysis is not necessary. // The actual Verilation will be done based on this plan. - if (v3Global.hierPlanp()) { + if (v3Global.hierGraphp()) { reportStatsIfEnabled(); return; } @@ -744,23 +744,24 @@ static bool verilate(const string& argString) { V3Error::abortIfWarnings(); - if (v3Global.hierPlanp()) { // This run is for just write a makefile + if (V3HierGraph* const hierGraphp + = v3Global.hierGraphp()) { // This run is for just write a makefile UASSERT(v3Global.opt.hierarchical(), "hierarchical must be set"); UASSERT(!v3Global.opt.hierChild(), "This must not be a hierarchical-child run"); UASSERT(v3Global.opt.hierBlocks().empty(), "hierarchical-block must not be set"); if (v3Global.opt.gmake()) { - v3Global.hierPlanp()->writeCommandArgsFiles(false); - V3EmitMk::emitHierVerilation(v3Global.hierPlanp()); + hierGraphp->writeCommandArgsFiles(false); + V3EmitMk::emitHierVerilation(hierGraphp); } if (v3Global.opt.cmake()) { - v3Global.hierPlanp()->writeCommandArgsFiles(true); + hierGraphp->writeCommandArgsFiles(true); V3EmitCMake::emit(); } if (v3Global.opt.makeJson()) { - v3Global.hierPlanp()->writeCommandArgsFiles(true); + hierGraphp->writeCommandArgsFiles(true); V3EmitMkJson::emit(); } - v3Global.hierPlanp()->writeParametersFiles(); + hierGraphp->writeParametersFiles(); } if (v3Global.opt.makeDepend().isTrue()) { string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix(); @@ -835,7 +836,7 @@ static void execBuildJob() { } static void execHierVerilation() { - UASSERT(v3Global.hierPlanp(), "must be called only when plan exists"); + UASSERT(v3Global.hierGraphp(), "must be called only when plan exists"); const string makefile = v3Global.opt.prefix() + "_hier.mk "; const string target = v3Global.opt.build() ? " hier_build" : " hier_verilation"; const string cmdStr = buildMakeCmd(makefile, target); @@ -885,7 +886,7 @@ int main(int argc, char** argv) { UINFO(1, "Option --no-verilate: Skip Verilation"); } - if (v3Global.hierPlanp() && v3Global.opt.gmake()) { + if (v3Global.hierGraphp() && v3Global.opt.gmake()) { execHierVerilation(); // execHierVerilation() takes care of --build too } else if (v3Global.opt.build()) { execBuildJob(); diff --git a/test_regress/t/t_hier_block1_bad.out b/test_regress/t/t_hier_block1_bad.out index 43fc8dca9..be6024fb2 100644 --- a/test_regress/t/t_hier_block1_bad.out +++ b/test_regress/t/t_hier_block1_bad.out @@ -1,4 +1,4 @@ -%Warning-HIERBLOCK: t/t_hier_block1_bad.v:16:8: Top module illegally marked hierarchical block, ignoring marking +%Warning-HIERBLOCK: t/t_hier_block1_bad.v:16:8: Top module marked as hierarchical block, ignoring : ... note: In instance 't' : ... Suggest remove verilator hier_block on this module 16 | module t (