// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Emit CMake file list // // Code available from: https://verilator.org // //************************************************************************* // // Copyright 2004-2025 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 // //************************************************************************* #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT #include "V3EmitCMake.h" #include "V3EmitCBase.h" #include "V3HierBlock.h" #include "V3Os.h" #include VL_DEFINE_DEBUG_FUNCTIONS; // TODO: this file is completely uncovered by tests // ###################################################################### // Emit statements class CMakeEmitter final { // METHODS // STATIC FUNCTIONS // Concatenate all strings in 'strs' with ' ' between them. template static string cmake_list(const T_List& strs) { string s; for (const std::string& itr : strs) { if (!s.empty()) s += ' '; s += '"'; s += V3OutFormatter::quoteNameControls(itr); s += '"'; } return s; } static string cmake_list(const VFileLibList& strs) { string s; for (const VFileLibName& itr : strs) { if (!s.empty()) s += ' '; s += '"'; s += V3OutFormatter::quoteNameControls(itr.filename()); s += '"'; } return s; } // Print CMake variable set command: output raw_value unmodified // cache_type should be empty for a normal variable // "BOOL", "FILEPATH", "PATH", "STRING" or "INTERNAL" for a CACHE variable // See https://cmake.org/cmake/help/latest/command/set.html static void cmake_set_raw(std::ofstream& of, const string& name, const string& raw_value, const string& cache_type = "", const string& docstring = "") { of << "set(" << name << " " << raw_value; if (!cache_type.empty()) of << " CACHE " << cache_type << " \"" << docstring << '"'; of << ")\n"; } static void cmake_set(std::ofstream& of, const string& name, const string& value, const string& cache_type = "", const string& docstring = "") { const string raw_value = '"' + value + '"'; cmake_set_raw(of, name, raw_value, cache_type, docstring); } static void emitOverallCMake() { const std::unique_ptr of{ V3File::new_ofstream(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".cmake")}; const string name = v3Global.opt.prefix(); *of << "# Verilated -*- CMake -*-\n"; *of << "# DESCR" "IPTION: Verilator output: CMake include script with class lists\n"; *of << "#\n"; *of << "# This CMake script lists generated Verilated files, for " "including in higher level CMake scripts.\n"; *of << "# This file is meant to be consumed by the verilate() function,\n"; *of << "# which becomes available after executing `find_package(verilator).\n"; *of << "\n### Constants...\n"; cmake_set(*of, "PERL", V3OutFormatter::quoteNameControls(V3Options::getenvPERL()), "FILEPATH", "Perl executable (from $PERL, defaults to 'perl' if not set)"); cmake_set(*of, "PYTHON3", V3OutFormatter::quoteNameControls(V3Options::getenvPYTHON3()), "FILEPATH", "Python3 executable (from $PYTHON3, defaults to 'python3' if not set)"); cmake_set(*of, "VERILATOR_ROOT", V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_ROOT()), "PATH", "Path to Verilator kit (from $VERILATOR_ROOT)"); cmake_set(*of, "VERILATOR_SOLVER", V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_SOLVER()), "STRING", "Default SMT solver for constrained randomization (from $VERILATOR_SOLVER)"); *of << "\n### Compiler flags...\n"; *of << "# User CFLAGS (from -CFLAGS on Verilator command line)\n"; cmake_set_raw(*of, name + "_USER_CFLAGS", cmake_list(v3Global.opt.cFlags())); *of << "# User LDLIBS (from -LDFLAGS on Verilator command line)\n"; cmake_set_raw(*of, name + "_USER_LDLIBS", cmake_list(v3Global.opt.ldLibs())); *of << "\n### Switches...\n"; *of << "# SystemC output mode? 0/1 (from --sc)\n"; cmake_set_raw(*of, name + "_SC", v3Global.opt.systemC() ? "1" : "0"); *of << "# Coverage output mode? 0/1 (from --coverage)\n"; cmake_set_raw(*of, name + "_COVERAGE", v3Global.opt.coverage() ? "1" : "0"); *of << "# Timing mode? 0/1\n"; cmake_set_raw(*of, name + "_TIMING", v3Global.usesTiming() ? "1" : "0"); *of << "# Threaded output mode? 1/N threads (from --threads)\n"; cmake_set_raw(*of, name + "_THREADS", cvtToStr(v3Global.opt.threads())); *of << "# FST Tracing output mode? 0/1 (from --trace-fst)\n"; cmake_set_raw(*of, name + "_TRACE_FST", (v3Global.opt.traceEnabledFst()) ? "1" : "0"); *of << "# SAIF Tracing output mode? 0/1 (from --trace-saif)\n"; cmake_set_raw(*of, name + "_TRACE_SAIF", (v3Global.opt.traceEnabledSaif()) ? "1" : "0"); *of << "# VCD Tracing output mode? 0/1 (from --trace-vcd)\n"; cmake_set_raw(*of, name + "_TRACE_VCD", (v3Global.opt.traceEnabledVcd()) ? "1" : "0"); *of << "\n### Sources...\n"; std::vector classes_fast; std::vector classes_slow; std::vector support_fast; std::vector support_slow; std::vector global; for (AstNodeFile* nodep = v3Global.rootp()->filesp(); nodep; nodep = VN_AS(nodep->nextp(), NodeFile)) { const AstCFile* const cfilep = VN_CAST(nodep, CFile); if (cfilep && cfilep->source()) { if (cfilep->support()) { if (cfilep->slow()) { support_slow.push_back(cfilep->name()); } else { support_fast.push_back(cfilep->name()); } } else { if (cfilep->slow()) { classes_slow.push_back(cfilep->name()); } else { classes_fast.push_back(cfilep->name()); } } } } for (const string& cpp : v3Global.verilatedCppFiles()) global.emplace_back("${VERILATOR_ROOT}/include/"s + cpp); if (!v3Global.opt.libCreate().empty()) { global.emplace_back(v3Global.opt.makeDir() + "/" + v3Global.opt.libCreate() + ".cpp"); } *of << "# Global classes, need linked once per executable\n"; cmake_set_raw(*of, name + "_GLOBAL", cmake_list(global)); *of << "# Generated module classes, non-fast-path, compile with low/medium optimization\n"; cmake_set_raw(*of, name + "_CLASSES_SLOW", cmake_list(classes_slow)); *of << "# Generated module classes, fast-path, compile with highest optimization\n"; cmake_set_raw(*of, name + "_CLASSES_FAST", cmake_list(classes_fast)); *of << "# Generated support classes, non-fast-path, compile with " "low/medium optimization\n"; cmake_set_raw(*of, name + "_SUPPORT_SLOW", cmake_list(support_slow)); *of << "# Generated support classes, fast-path, compile with highest optimization\n"; cmake_set_raw(*of, name + "_SUPPORT_FAST", cmake_list(support_fast)); *of << "# All dependencies\n"; cmake_set_raw(*of, name + "_DEPS", cmake_list(V3File::getAllDeps())); *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 V3HierGraph* const graphp = v3Global.hierGraphp()) { *of << "# Verilate hierarchical blocks\n"; // Sorted hierarchical blocks in order of leaf-first. *of << "get_target_property(TOP_TARGET_NAME \"${TARGET}\" NAME)\n"; 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 (!hblockp->outEmpty()) { *of << "target_link_libraries(" << prefix << " INTERFACE"; 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 V3GraphEdge& edge : hblockp->outEdges()) { const V3HierBlock* const dependencyp = edge.top()->as(); *of << " " << v3Global.opt.makeDir() + "/" + dependencyp->hierWrapperFilename(true); } *of << " "; const string vFile = hblockp->vFileIfNecessary(); if (!vFile.empty()) *of << vFile << " "; for (const auto& i : v3Global.opt.vFiles()) *of << V3Os::filenameRealPath(i.filename()) << " "; *of << " VERILATOR_ARGS "; *of << "-f " << hblockp->commandArgsFilename(true) << " -CFLAGS -fPIC" // hierarchical block will be static, but may be linked // with .so << ")\n"; } *of << "\n# Verilate the top module that refers to lib-create wrappers of above\n"; *of << "verilate(${TOP_TARGET_NAME} PREFIX " << v3Global.opt.prefix() << " TOP_MODULE " << v3Global.rootp()->topModulep()->name() << " DIRECTORY " << v3Global.opt.makeDir() << " SOURCES "; 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 " << graphp->topCommandArgsFilename(true); *of << ")\n"; } } public: explicit CMakeEmitter() { emitOverallCMake(); } virtual ~CMakeEmitter() = default; }; void V3EmitCMake::emit() { UINFO(2, __FUNCTION__ << ":"); const CMakeEmitter emitter; }