Add `--make json` to enable integration with non-make/cmake build systems (#5799)
This commit is contained in:
parent
53151d7c5f
commit
6a48d3bb83
|
@ -19,6 +19,7 @@ Andreas Kuster
|
|||
Andrei Kostovski
|
||||
Andrew Miloradovsky
|
||||
Andrew Nolte
|
||||
Andrew Voznytsa
|
||||
Anthony Donlon
|
||||
Anthony Moore
|
||||
Arkadiusz Kozdra
|
||||
|
|
|
@ -85,6 +85,7 @@ set(HEADERS
|
|||
V3EmitCMain.h
|
||||
V3EmitCMake.h
|
||||
V3EmitMk.h
|
||||
V3EmitMkJson.h
|
||||
V3EmitV.h
|
||||
V3EmitXml.h
|
||||
V3Error.h
|
||||
|
@ -246,6 +247,7 @@ set(COMMON_SOURCES
|
|||
V3EmitCPch.cpp
|
||||
V3EmitCSyms.cpp
|
||||
V3EmitMk.cpp
|
||||
V3EmitMkJson.cpp
|
||||
V3EmitV.cpp
|
||||
V3EmitXml.cpp
|
||||
V3Error.cpp
|
||||
|
|
|
@ -253,6 +253,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3EmitCModel.o \
|
||||
V3EmitCSyms.o \
|
||||
V3EmitMk.o \
|
||||
V3EmitMkJson.o \
|
||||
V3EmitXml.o \
|
||||
V3ExecGraph.o \
|
||||
V3Expand.o \
|
||||
|
|
|
@ -0,0 +1,317 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Emit JSON manifest file
|
||||
//
|
||||
// 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 "V3EmitMkJson.h"
|
||||
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3HierBlock.h"
|
||||
#include "V3Os.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
// ######################################################################
|
||||
// Emit statements
|
||||
|
||||
class V3EmitMkJsonEmitter final {
|
||||
class Printer final {
|
||||
// MEMBERS
|
||||
private:
|
||||
const std::unique_ptr<std::ofstream>& m_of;
|
||||
std::stack<char>
|
||||
m_scope; // Stack of ']' and '}' to be used to close currently open scopes
|
||||
std::string m_prefix; // Prefix emitted before each line in the current scope (indent *
|
||||
// scope depth)
|
||||
std::string m_indent; // Single indent
|
||||
bool m_empty = true; // Indicates that the current scope is empty
|
||||
|
||||
// METHODS
|
||||
public:
|
||||
Printer(const std::unique_ptr<std::ofstream>& of, const std::string& indent = " ")
|
||||
: m_of(of)
|
||||
, m_indent(indent) {
|
||||
begin();
|
||||
}
|
||||
|
||||
~Printer() { end(); }
|
||||
|
||||
Printer& begin(const std::string& name, char type = '{') {
|
||||
if (!m_empty) *m_of << ",\n";
|
||||
*m_of << m_prefix << "\"" << name << "\": " << type << "\n";
|
||||
m_prefix += m_indent;
|
||||
m_scope.push(type == '{' ? '}' : ']');
|
||||
m_empty = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Printer& put(const std::string& name, const std::string& value) {
|
||||
if (!m_empty) *m_of << ",\n";
|
||||
*m_of << m_prefix << "\"" << name << "\": \"" << value << "\"";
|
||||
m_empty = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Printer& put(const std::string& name, bool value) {
|
||||
if (!m_empty) *m_of << ",\n";
|
||||
*m_of << m_prefix << "\"" << name << "\": " << (value ? "true" : "false");
|
||||
m_empty = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Printer& put(const std::string& name, int value) {
|
||||
if (!m_empty) *m_of << ",\n";
|
||||
*m_of << m_prefix << "\"" << name << "\": " << value;
|
||||
m_empty = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Printer& begin(char type = '{') {
|
||||
if (!m_empty) *m_of << ",\n";
|
||||
*m_of << m_prefix << type << "\n";
|
||||
m_prefix += m_indent;
|
||||
m_scope.push(type == '{' ? '}' : ']');
|
||||
m_empty = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Printer& put(const std::string& value) {
|
||||
if (!m_empty) *m_of << ",\n";
|
||||
*m_of << m_prefix << "\"" << value << "\"";
|
||||
m_empty = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Printer& put(bool value) {
|
||||
if (!m_empty) *m_of << ",\n";
|
||||
*m_of << m_prefix << (value ? "true" : "false");
|
||||
m_empty = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Printer& put(int value) {
|
||||
if (!m_empty) *m_of << ",\n";
|
||||
*m_of << m_prefix << value;
|
||||
m_empty = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Printer& putList(const std::string& name, const T& list) {
|
||||
if (list.empty()) return *this;
|
||||
begin(name, '[');
|
||||
for (auto it = list.begin(); it != list.end(); ++it) { put(*it); }
|
||||
return end();
|
||||
}
|
||||
|
||||
Printer& end() {
|
||||
assert(m_prefix.length() >= m_indent.length());
|
||||
m_prefix.erase(m_prefix.end() - m_indent.length(), m_prefix.end());
|
||||
assert(!m_scope.empty());
|
||||
*m_of << "\n" << m_prefix << m_scope.top();
|
||||
m_scope.pop();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Printer& operator+=(Printer& cursor) {
|
||||
// Meaningless syntax sugar, at least for now
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// METHODS
|
||||
|
||||
// STATIC FUNCTIONS
|
||||
static void emitManifest() {
|
||||
const std::string makeDir
|
||||
= V3Os::filenameSlashPath(V3Os::filenameRealPath(v3Global.opt.makeDir()));
|
||||
|
||||
const std::unique_ptr<std::ofstream> of{
|
||||
V3File::new_ofstream(makeDir + "/" + v3Global.opt.prefix() + ".json")};
|
||||
|
||||
const string name = v3Global.opt.prefix();
|
||||
const std::string trace
|
||||
= v3Global.opt.trace() ? (v3Global.opt.traceFormat().vcd() ? "vcd" : "fst") : "off";
|
||||
|
||||
std::vector<string> classesFast;
|
||||
std::vector<string> classesSlow;
|
||||
std::vector<string> supportFast;
|
||||
std::vector<string> supportSlow;
|
||||
std::vector<string> global;
|
||||
std::vector<string> deps;
|
||||
std::vector<string> cppFiles;
|
||||
|
||||
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()) {
|
||||
const std::string filename
|
||||
= V3Os::filenameSlashPath(V3Os::filenameRealPath(cfilep->name()));
|
||||
if (cfilep->support()) {
|
||||
if (cfilep->slow()) {
|
||||
supportSlow.emplace_back(filename);
|
||||
} else {
|
||||
supportFast.emplace_back(filename);
|
||||
}
|
||||
} else {
|
||||
if (cfilep->slow()) {
|
||||
classesSlow.emplace_back(filename);
|
||||
} else {
|
||||
classesFast.emplace_back(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::string verilatorRoot
|
||||
= V3Os::filenameSlashPath(V3Os::filenameRealPath(V3Options::getenvVERILATOR_ROOT()));
|
||||
global.emplace_back(verilatorRoot + "/include/verilated.cpp");
|
||||
if (v3Global.dpi()) { global.emplace_back(verilatorRoot + "/include/verilated_dpi.cpp"); }
|
||||
if (v3Global.opt.vpi()) {
|
||||
global.emplace_back(verilatorRoot + "/include/verilated_vpi.cpp");
|
||||
}
|
||||
if (v3Global.opt.savable()) {
|
||||
global.emplace_back(verilatorRoot + "/include/verilated_save.cpp");
|
||||
}
|
||||
if (v3Global.opt.coverage()) {
|
||||
global.emplace_back(verilatorRoot + "/include/verilated_cov.cpp");
|
||||
}
|
||||
if (v3Global.opt.trace()) {
|
||||
global.emplace_back(verilatorRoot + "/include/" + v3Global.opt.traceSourceBase()
|
||||
+ "_c.cpp");
|
||||
}
|
||||
if (v3Global.usesProbDist()) {
|
||||
global.emplace_back(verilatorRoot + "/include/verilated_probdist.cpp");
|
||||
}
|
||||
if (v3Global.usesTiming()) {
|
||||
global.emplace_back(verilatorRoot + "/include/verilated_timing.cpp");
|
||||
}
|
||||
if (v3Global.useRandomizeMethods()) {
|
||||
global.emplace_back(verilatorRoot + "/include/verilated_random.cpp");
|
||||
}
|
||||
global.emplace_back(verilatorRoot + "/include/verilated_threads.cpp");
|
||||
if (v3Global.opt.usesProfiler()) {
|
||||
global.emplace_back(verilatorRoot + "/include/verilated_profiler.cpp");
|
||||
}
|
||||
if (!v3Global.opt.libCreate().empty()) {
|
||||
global.emplace_back(makeDir + "/" + v3Global.opt.libCreate() + ".cpp");
|
||||
}
|
||||
|
||||
for (const auto& dep : V3File::getAllDeps())
|
||||
deps.emplace_back(V3Os::filenameSlashPath(V3Os::filenameRealPath(dep)));
|
||||
for (const auto& cppFile : v3Global.opt.cppFiles())
|
||||
cppFiles.emplace_back(V3Os::filenameSlashPath(V3Os::filenameRealPath(cppFile)));
|
||||
|
||||
Printer manifest(of);
|
||||
Printer& cursor = manifest.put("version", 1)
|
||||
.begin("system")
|
||||
.put("perl", V3Options::getenvPERL())
|
||||
.put("python3", V3Options::getenvPYTHON3())
|
||||
.put("verilator_root", verilatorRoot)
|
||||
.put("verilator_solver", V3Options::getenvVERILATOR_SOLVER())
|
||||
.end()
|
||||
.begin("options")
|
||||
.putList("cflags", v3Global.opt.cFlags())
|
||||
.putList("ldflags", v3Global.opt.ldLibs())
|
||||
.put("system_c", v3Global.opt.systemC())
|
||||
.put("coverage", v3Global.opt.coverage())
|
||||
.put("use_timing", v3Global.usesTiming())
|
||||
.put("threads", v3Global.opt.threads())
|
||||
.put("trace", trace)
|
||||
.end()
|
||||
.begin("sources")
|
||||
.putList("global", global)
|
||||
.putList("classes_slow", classesSlow)
|
||||
.putList("classes_fast", classesFast)
|
||||
.putList("support_slow", supportSlow)
|
||||
.putList("support_fast", supportFast)
|
||||
.putList("deps", deps)
|
||||
.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();
|
||||
|
||||
cursor += cursor.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<std::string> deps;
|
||||
std::vector<std::string> sources;
|
||||
|
||||
for (const auto& childr : children) {
|
||||
deps.emplace_back((childr)->hierPrefix());
|
||||
sources.emplace_back(makeDir + "/" + childr->hierWrapperFilename(true));
|
||||
}
|
||||
|
||||
const string vFile = hblockp->vFileIfNecessary();
|
||||
if (!vFile.empty()) { sources.emplace_back(vFile); }
|
||||
|
||||
const V3StringList& vFiles = v3Global.opt.vFiles();
|
||||
for (const string& i : vFiles)
|
||||
sources.emplace_back(V3Os::filenameSlashPath(V3Os::filenameRealPath(i)));
|
||||
|
||||
std::vector<std::string> cflags;
|
||||
cflags.emplace_back("-fPIC");
|
||||
|
||||
cursor += cursor.begin()
|
||||
.put("prefix", hblockp->hierPrefix())
|
||||
.put("top", hblockp->modp()->name())
|
||||
.putList("deps", deps)
|
||||
.put("directory", makeDir + "/" + hblockp->hierPrefix())
|
||||
.putList("sources", sources)
|
||||
.putList("cflags", cflags)
|
||||
.put("verilator_args",
|
||||
V3Os::filenameSlashPath(
|
||||
V3Os::filenameRealPath(hblockp->commandArgsFilename(true))))
|
||||
.end();
|
||||
}
|
||||
|
||||
std::vector<std::string> sources;
|
||||
for (const auto& itr : *planp)
|
||||
sources.emplace_back(makeDir + "/" + itr.second->hierWrapperFilename(true));
|
||||
|
||||
const V3StringList& vFiles = v3Global.opt.vFiles();
|
||||
for (const string& i : vFiles)
|
||||
sources.emplace_back(V3Os::filenameSlashPath(V3Os::filenameRealPath(i)));
|
||||
|
||||
cursor += cursor.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();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
explicit V3EmitMkJsonEmitter() { emitManifest(); }
|
||||
virtual ~V3EmitMkJsonEmitter() = default;
|
||||
};
|
||||
|
||||
void V3EmitMkJson::emit() {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
const V3EmitMkJsonEmitter emitter;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Emit JSON manifest file
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-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
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3EMITMKJSON_H_
|
||||
#define VERILATOR_V3EMITMKJSON_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3EmitMkJson final {
|
||||
public:
|
||||
static void emit() VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
|
@ -849,7 +849,7 @@ void V3Options::notify() VL_MT_DISABLED {
|
|||
"--xml-only, --json-only or --E option");
|
||||
}
|
||||
|
||||
if (m_build && (m_gmake || m_cmake)) {
|
||||
if (m_build && (m_gmake || m_cmake || m_makeJson)) {
|
||||
cmdfl->v3error("--make cannot be used together with --build. Suggest see manual");
|
||||
}
|
||||
|
||||
|
@ -881,7 +881,7 @@ void V3Options::notify() VL_MT_DISABLED {
|
|||
}
|
||||
|
||||
// Make sure at least one make system is enabled
|
||||
if (!m_gmake && !m_cmake) m_gmake = true;
|
||||
if (!m_gmake && !m_cmake && !m_makeJson) m_gmake = true;
|
||||
|
||||
if (m_hierarchical && (m_hierChild || !m_hierBlocks.empty())) {
|
||||
cmdfl->v3error(
|
||||
|
@ -1443,6 +1443,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
|||
m_cmake = true;
|
||||
} else if (!std::strcmp(valp, "gmake")) {
|
||||
m_gmake = true;
|
||||
} else if (!std::strcmp(valp, "json")) {
|
||||
m_makeJson = true;
|
||||
} else {
|
||||
fl->v3fatal("Unknown --make system specified: '" << valp << "'");
|
||||
}
|
||||
|
|
|
@ -261,6 +261,7 @@ private:
|
|||
bool m_jsonOnly = false; // main switch: --json-only
|
||||
bool m_lintOnly = false; // main switch: --lint-only
|
||||
bool m_gmake = false; // main switch: --make gmake
|
||||
bool m_makeJson = false; // main switch: --make json
|
||||
bool m_main = false; // main switch: --main
|
||||
bool m_outFormatOk = false; // main switch: --cc, --sc or --sp was specified
|
||||
bool m_pedantic = false; // main switch: --Wpedantic
|
||||
|
@ -527,6 +528,7 @@ public:
|
|||
bool exe() const { return m_exe; }
|
||||
bool flatten() const { return m_flatten; }
|
||||
bool gmake() const { return m_gmake; }
|
||||
bool makeJson() const { return m_makeJson; }
|
||||
bool threadsDpiPure() const { return m_threadsDpiPure; }
|
||||
bool threadsDpiUnpure() const { return m_threadsDpiUnpure; }
|
||||
bool threadsCoarsen() const { return m_threadsCoarsen; }
|
||||
|
|
10
src/V3Os.cpp
10
src/V3Os.cpp
|
@ -310,6 +310,16 @@ bool V3Os::filenameIsRel(const string& filename) VL_PURE {
|
|||
#endif
|
||||
}
|
||||
|
||||
string V3Os::filenameSlashPath(const string& path) VL_PURE {
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
string slashedPath = path;
|
||||
std::replace(slashedPath.begin(), slashedPath.end(), '\\', '/');
|
||||
return slashedPath;
|
||||
#else
|
||||
return path;
|
||||
#endif
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// File utilities
|
||||
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
static string filenameRelativePath(const string& filename, const string& base) VL_PURE;
|
||||
///< @return filename is relative
|
||||
static bool filenameIsRel(const string& filename) VL_PURE;
|
||||
///< @return path slashed with '/'
|
||||
static string filenameSlashPath(const string& filename) VL_PURE;
|
||||
|
||||
// METHODS (file utilities)
|
||||
static string getline(std::istream& is, char delim = '\n');
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "V3EmitCMain.h"
|
||||
#include "V3EmitCMake.h"
|
||||
#include "V3EmitMk.h"
|
||||
#include "V3EmitMkJson.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3EmitXml.h"
|
||||
#include "V3ExecGraph.h"
|
||||
|
@ -625,7 +626,7 @@ static void process() {
|
|||
if (!v3Global.opt.lintOnly() && !v3Global.opt.serializeOnly() && !v3Global.opt.dpiHdrOnly()) {
|
||||
if (v3Global.opt.main()) V3EmitCMain::emit();
|
||||
|
||||
// V3EmitMk/V3EmitCMake must be after all other emitters,
|
||||
// V3EmitMk/V3EmitCMake/V3EmitMkJson must be after all other emitters,
|
||||
// as they and below code visits AstCFiles added earlier
|
||||
size_t src_f_cnt = 0;
|
||||
for (AstNode* nodep = v3Global.rootp()->filesp(); nodep; nodep = nodep->nextp()) {
|
||||
|
@ -634,6 +635,7 @@ static void process() {
|
|||
}
|
||||
if (src_f_cnt >= V3EmitMk::PARALLEL_FILE_CNT_THRESHOLD) v3Global.useParallelBuild(true);
|
||||
if (v3Global.opt.cmake()) V3EmitCMake::emit();
|
||||
if (v3Global.opt.makeJson()) V3EmitMkJson::emit();
|
||||
if (v3Global.opt.gmake()) V3EmitMk::emitmk();
|
||||
}
|
||||
|
||||
|
@ -734,6 +736,10 @@ static void verilate(const string& argString) {
|
|||
v3Global.hierPlanp()->writeCommandArgsFiles(true);
|
||||
V3EmitCMake::emit();
|
||||
}
|
||||
if (v3Global.opt.makeJson()) {
|
||||
v3Global.hierPlanp()->writeCommandArgsFiles(true);
|
||||
V3EmitMkJson::emit();
|
||||
}
|
||||
v3Global.hierPlanp()->writeParametersFiles();
|
||||
}
|
||||
if (v3Global.opt.makeDepend().isTrue()) {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 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
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
test.top_filename = "t/t_flag_make_cmake.v"
|
||||
|
||||
test.compile(verilator_flags2=['--make json'],
|
||||
verilator_make_gmake=False, verilator_make_cmake=False)
|
||||
|
||||
nout = test.run_capture("jq --version", check=False)
|
||||
version_match = re.search(r'jq-([0-9.]+)', nout, re.IGNORECASE)
|
||||
if not version_match:
|
||||
test.skip("jq is not installed")
|
||||
|
||||
json_filename = test.obj_dir + "/" + test.vm_prefix + ".json"
|
||||
if not os.path.exists(json_filename):
|
||||
test.error(json_filename + " does not exist")
|
||||
|
||||
test.run(cmd=['cat "' + json_filename + '" | jq ".version"'])
|
||||
|
||||
test.passes()
|
|
@ -19,7 +19,7 @@
|
|||
#
|
||||
######################################################################
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
# Prefer VERILATOR_ROOT from environment
|
||||
if(DEFINED ENV{VERILATOR_ROOT})
|
||||
|
@ -163,6 +163,81 @@ define_property(
|
|||
FULL_DOCS "Verilator trace structs enabled"
|
||||
)
|
||||
|
||||
function(json_get_string RET JSON SECTION VARIABLE)
|
||||
string(JSON JV ERROR_VARIABLE STATUS GET "${JSON}" ${SECTION} ${VARIABLE})
|
||||
if (NOT ${STATUS} STREQUAL "NOTFOUND")
|
||||
set(JV "")
|
||||
endif()
|
||||
set(${RET} ${JV} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(json_get_bool RET JSON SECTION VARIABLE)
|
||||
string(JSON JV GET "${JSON}" ${SECTION} ${VARIABLE})
|
||||
if(JV)
|
||||
set(${RET} 1 PARENT_SCOPE)
|
||||
else()
|
||||
set(${RET} 0 PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(json_get_int RET JSON SECTION VARIABLE)
|
||||
string(JSON JV GET "${JSON}" ${SECTION} ${VARIABLE})
|
||||
set(${RET} ${JV} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(json_get_trace RET_VCD RET_FST JSON SECTION VARIABLE)
|
||||
string(JSON JV GET "${JSON}" ${SECTION} ${VARIABLE})
|
||||
if(JV STREQUAL "vcd")
|
||||
set(${RET_VCD} 1 PARENT_SCOPE)
|
||||
set(${RET_FST} 0 PARENT_SCOPE)
|
||||
elseif(JV STREQUAL "fst")
|
||||
set(${RET_VCD} 0 PARENT_SCOPE)
|
||||
set(${RET_FST} 1 PARENT_SCOPE)
|
||||
else()
|
||||
set(${RET_VCD} 0 PARENT_SCOPE)
|
||||
set(${RET_FST} 0 PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(json_get_submodules SUBMODULES NSUBMODULES JSON)
|
||||
string(JSON JV ERROR_VARIABLE STATUS GET "${JSON}" submodules)
|
||||
|
||||
if (NOT ${STATUS} STREQUAL "NOTFOUND")
|
||||
set(${SUBMODULES} "" PARENT_SCOPE)
|
||||
set(${NSUBMODULES} 0 PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
string(JSON L ERROR_VARIABLE STATUS LENGTH "${JSON}" submodules)
|
||||
|
||||
math(EXPR L "${L}-1")
|
||||
|
||||
set(${SUBMODULES} ${JV} PARENT_SCOPE)
|
||||
set(${NSUBMODULES} ${L} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(json_get_list RET JSON SECTION VARIABLE)
|
||||
string(JSON L ERROR_VARIABLE STATUS LENGTH "${JSON}" ${SECTION} ${VARIABLE})
|
||||
|
||||
if (NOT ${STATUS} STREQUAL "NOTFOUND" OR NOT ${L})
|
||||
set(${RET} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
math(EXPR L "${L}-1")
|
||||
|
||||
foreach(I RANGE ${L})
|
||||
string(JSON JV GET "${JSON}" ${SECTION} ${VARIABLE} ${I})
|
||||
if(NOT JL)
|
||||
string(APPEND JL "${JV}")
|
||||
else()
|
||||
string(APPEND JL " ${JV}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(${RET} ${JL} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(verilate TARGET)
|
||||
cmake_parse_arguments(
|
||||
VERILATE
|
||||
|
@ -294,7 +369,7 @@ function(verilate TARGET)
|
|||
--Mdir
|
||||
${VDIR}
|
||||
--make
|
||||
cmake
|
||||
json
|
||||
${VERILATOR_ARGS}
|
||||
${VERILATE_VERILATOR_ARGS}
|
||||
${VERILATE_SOURCES}
|
||||
|
@ -337,6 +412,127 @@ function(verilate TARGET)
|
|||
"Verilator command failed (return code=${_VERILATOR_RC})"
|
||||
)
|
||||
endif()
|
||||
|
||||
file(READ ${VDIR}/${VERILATE_PREFIX}.json MANIFEST)
|
||||
|
||||
json_get_string(JSYSTEM_PERL "${MANIFEST}" system perl)
|
||||
json_get_string(JSYSTEM_PYTHON3 "${MANIFEST}" system python3)
|
||||
json_get_string(JSYSTEM_VERILATOR_ROOT "${MANIFEST}" system verilator_root)
|
||||
json_get_string(JSYSTEM_VERILATOR_SOLVER "${MANIFEST}" system verilator_solver)
|
||||
|
||||
json_get_list(JOPTIONS_CFLAGS "${MANIFEST}" options cflags)
|
||||
json_get_list(JOPTIONS_LDFLAGS "${MANIFEST}" options ldflags)
|
||||
json_get_bool(JOPTIONS_SYSTEM_C "${MANIFEST}" options system_c)
|
||||
json_get_bool(JOPTIONS_COVERAGE "${MANIFEST}" options coverage)
|
||||
json_get_bool(JOPTIONS_USE_TIMING "${MANIFEST}" options use_timing)
|
||||
json_get_int(JOPTIONS_THREADS "${MANIFEST}" options threads)
|
||||
json_get_trace(JOPTIONS_TRACE_VCD JOPTIONS_TRACE_FST "${MANIFEST}" options trace)
|
||||
|
||||
json_get_list(JSOURCES_GLOBAL "${MANIFEST}" sources global)
|
||||
json_get_list(JSOURCES_CLASSES_SLOW "${MANIFEST}" sources classes_slow)
|
||||
json_get_list(JSOURCES_CLASSES_FAST "${MANIFEST}" sources classes_fast)
|
||||
json_get_list(JSOURCES_SUPPORT_SLOW "${MANIFEST}" sources support_slow)
|
||||
json_get_list(JSOURCES_SUPPORT_FAST "${MANIFEST}" sources support_fast)
|
||||
json_get_list(JSOURCES_USER_CLASSES "${MANIFEST}" sources user_classes)
|
||||
|
||||
file(WRITE ${VDIR}/${VERILATE_PREFIX}.cmake
|
||||
"# Verilated -*- CMake -*-\n"
|
||||
"# DESCRIPTION: Verilator output: CMake include script with class lists\n"
|
||||
"#\n"
|
||||
"# This CMake script lists generated Verilated files, for including in higher level CMake scripts.\n"
|
||||
"# This file is meant to be consumed by the verilate() function,\n"
|
||||
"# which becomes available after executing `find_package(verilator).\n\n"
|
||||
"### Constants...\n"
|
||||
"set(PERL \"${JSYSTEM_PERL}\" CACHE FILEPATH \"Perl executable (from \$PERL, defaults to 'perl' if not set)\")\n"
|
||||
"set(PYTHON3 \"${JSYSTEM_PYTHON3}\" CACHE FILEPATH \"Python3 executable (from \$PYTHON3, defaults to 'python3' if not set)\")\n"
|
||||
"set(VERILATOR_ROOT \"${JSYSTEM_VERILATOR_ROOT}\" CACHE PATH \"Path to Verilator kit (from $VERILATOR_ROOT)\")\n"
|
||||
"set(VERILATOR_SOLVER \"${JSYSTEM_VERILATOR_SOLVER}\" CACHE STRING \"Default SMT solver for constrained randomization (from \$VERILATOR_SOLVER)\")\n\n"
|
||||
"### Compiler flags...\n"
|
||||
"# User CFLAGS (from -CFLAGS on Verilator command line)\n"
|
||||
"set(${VERILATE_PREFIX}_USER_CFLAGS ${JOPTIONS_CFLAGS})\n"
|
||||
"# User LDLIBS (from -LDFLAGS on Verilator command line)\n"
|
||||
"set(${VERILATE_PREFIX}_USER_LDLIBS ${JOPTIONS_LDFLAGS})\n\n"
|
||||
"### Switches...\n"
|
||||
"# SystemC output mode? 0/1 (from --sc)\n"
|
||||
"set(${VERILATE_PREFIX}_SC ${JOPTIONS_SYSTEM_C})\n"
|
||||
"# Coverage output mode? 0/1 (from --coverage)\n"
|
||||
"set(${VERILATE_PREFIX}_COVERAGE ${JOPTIONS_COVERAGE})\n"
|
||||
"# Timing mode? 0/1\n"
|
||||
"set(${VERILATE_PREFIX}_TIMING ${JOPTIONS_USE_TIMING})\n"
|
||||
"# Threaded output mode? 1/N threads (from --threads)\n"
|
||||
"set(${VERILATE_PREFIX}_THREADS ${JOPTIONS_THREADS})\n"
|
||||
"# VCD Tracing output mode? 0/1 (from --trace)\n"
|
||||
"set(${VERILATE_PREFIX}_TRACE_VCD ${JOPTIONS_TRACE_VCD})\n"
|
||||
"# FST Tracing output mode? 0/1 (from --trace-fst)\n"
|
||||
"set(${VERILATE_PREFIX}_TRACE_FST ${JOPTIONS_TRACE_FST})\n\n"
|
||||
"### Sources...\n"
|
||||
"# Global classes, need linked once per executable\n"
|
||||
"set(${VERILATE_PREFIX}_GLOBAL ${JSOURCES_GLOBAL})\n"
|
||||
"# Generated module classes, non-fast-path, compile with low/medium optimization\n"
|
||||
"set(${VERILATE_PREFIX}_CLASSES_SLOW ${JSOURCES_CLASSES_SLOW})\n"
|
||||
"# Generated module classes, fast-path, compile with highest optimization\n"
|
||||
"set(${VERILATE_PREFIX}_CLASSES_FAST ${JSOURCES_CLASSES_FAST})\n"
|
||||
"# Generated support classes, non-fast-path, compile with low/medium optimization\n"
|
||||
"set(${VERILATE_PREFIX}_SUPPORT_SLOW ${JSOURCES_SUPPORT_SLOW})\n"
|
||||
"# Generated support classes, fast-path, compile with highest optimization\n"
|
||||
"set(${VERILATE_PREFIX}_SUPPORT_FAST ${JSOURCES_SUPPORT_FAST})\n"
|
||||
"# All dependencies\n"
|
||||
"set(${VERILATE_PREFIX}_DEPS ${JSOURCES_DEPS})\n"
|
||||
"# User .cpp files (from .cpp's on Verilator command line)\n"
|
||||
"set(${VERILATE_PREFIX}_USER_CLASSES ${JSOURCES_USER_CLASSES})\n"
|
||||
)
|
||||
|
||||
json_get_submodules(JSUBMODULES JNSUBMODULES "${MANIFEST}")
|
||||
|
||||
if (JNSUBMODULES)
|
||||
file(APPEND ${VDIR}/${VERILATE_PREFIX}.cmake
|
||||
"# Verilate hierarchical blocks\n"
|
||||
"get_target_property(TOP_TARGET_NAME \"\${TARGET}\" NAME)\n"
|
||||
)
|
||||
|
||||
foreach(I RANGE ${JNSUBMODULES})
|
||||
json_get_string(JSUBMODULE_PREFIX "${JSUBMODULES}" ${I} prefix)
|
||||
json_get_string(JSUBMODULE_TOP "${JSUBMODULES}" ${I} top)
|
||||
json_get_list(JSUBMODULE_DEPS "${JSUBMODULES}" ${I} deps)
|
||||
json_get_string(JSUBMODULE_DIRECTORY "${JSUBMODULES}" ${I} directory)
|
||||
json_get_list(JSUBMODULE_SOURCES "${JSUBMODULES}" ${I} sources)
|
||||
json_get_list(JSUBMODULE_CFLAGS "${JSUBMODULES}" ${I} cflags)
|
||||
json_get_string(JSUBMODILE_VERILATOR_ARGS "${JSUBMODULES}" ${I} verilator_args)
|
||||
|
||||
set(SUBMODULE_CMAKE "")
|
||||
set(SUBMODULE_VERILATE_ARGS "")
|
||||
|
||||
if (NOT ${I} STREQUAL ${JNSUBMODULES})
|
||||
string(APPEND SUBMODULE_CMAKE
|
||||
"add_library(${JSUBMODULE_PREFIX} STATIC)\n"
|
||||
"target_link_libraries(\${TOP_TARGET_NAME} PRIVATE ${JSUBMODULE_PREFIX})\n"
|
||||
)
|
||||
|
||||
if (JSUBMODULE_DEPS)
|
||||
string(APPEND SUBMODULE_CMAKE "target_link_libraries(${JSUBMODULE_PREFIX} INTERFACE ${JSUBMODULE_DEPS})\n")
|
||||
endif()
|
||||
|
||||
string(APPEND SUBMODULE_VERILATE_ARGS "${JSUBMODULE_PREFIX} PREFIX ${JSUBMODULE_PREFIX} TOP_MODULE ${JSUBMODULE_TOP} DIRECTORY ${JSUBMODULE_DIRECTORY} SOURCES ${JSUBMODULE_SOURCES}")
|
||||
else()
|
||||
string(APPEND SUBMODULE_CMAKE "# Verilate the top module that refers to lib-create wrappers of above\n")
|
||||
string(APPEND SUBMODULE_VERILATE_ARGS "\${TOP_TARGET_NAME} PREFIX ${JSUBMODULE_PREFIX} TOP_MODULE ${JSUBMODULE_TOP} DIRECTORY ${JSUBMODULE_DIRECTORY} SOURCES ${JSUBMODULE_SOURCES}")
|
||||
endif()
|
||||
|
||||
if (JSUBMODILE_VERILATOR_ARGS)
|
||||
string(APPEND SUBMODULE_VERILATE_ARGS " VERILATOR_ARGS -f ${JSUBMODILE_VERILATOR_ARGS}")
|
||||
endif()
|
||||
|
||||
if (JSUBMODULE_CFLAGS)
|
||||
string(APPEND SUBMODULE_VERILATE_ARGS " -CFLAGS ${JSUBMODULE_CFLAGS}")
|
||||
endif()
|
||||
|
||||
file(APPEND ${VDIR}/${VERILATE_PREFIX}.cmake
|
||||
"${SUBMODULE_CMAKE}"
|
||||
"verilate(${SUBMODULE_VERILATE_ARGS})\n"
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy "${VCMAKE}" "${VCMAKE_COPY}"
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue