forked from github/verilator
The baked in DEFENV paths might end up with extra NULL characters at the end if the binaries are installed by something that patches them for relocatable installs (e.g. conda). Avoid this issue by immediately passing them through std::string::c_str() method to stop at the first NULL
2004 lines
80 KiB
C++
2004 lines
80 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Verilator: Options parsing
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// Copyright 2003-2023 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 "config_build.h"
|
|
#include "verilatedos.h"
|
|
|
|
#include "V3Options.h"
|
|
|
|
#include "V3Ast.h"
|
|
#include "V3Error.h"
|
|
#include "V3File.h"
|
|
#include "V3Global.h"
|
|
#include "V3OptionParser.h"
|
|
#include "V3Os.h"
|
|
#include "V3PreShell.h"
|
|
#include "V3String.h"
|
|
|
|
// clang-format off
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#ifndef _WIN32
|
|
# include <sys/utsname.h>
|
|
#endif
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#ifdef _MSC_VER
|
|
# include <filesystem> // C++17
|
|
# define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR)
|
|
#else
|
|
# include <dirent.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include <list>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <thread>
|
|
#include <set>
|
|
#include <string>
|
|
|
|
#include "config_rev.h"
|
|
|
|
#if defined(_WIN32) || defined(__MINGW32__)
|
|
# include <io.h> // open, close
|
|
#endif
|
|
// clang-format on
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
//######################################################################
|
|
// V3 Internal state
|
|
|
|
class V3OptionsImp final {
|
|
public:
|
|
// TYPES
|
|
using DirMap = std::map<const string, std::set<std::string>>; // Directory listing
|
|
|
|
// STATE
|
|
std::list<string> m_allArgs; // List of every argument encountered
|
|
std::list<string> m_incDirUsers; // Include directories (ordered)
|
|
std::set<string> m_incDirUserSet; // Include directories (for removing duplicates)
|
|
std::list<string> m_incDirFallbacks; // Include directories (ordered)
|
|
std::set<string> m_incDirFallbackSet; // Include directories (for removing duplicates)
|
|
std::map<const string, V3LangCode> m_langExts; // Language extension map
|
|
std::list<string> m_libExtVs; // Library extensions (ordered)
|
|
std::set<string> m_libExtVSet; // Library extensions (for removing duplicates)
|
|
DirMap m_dirMap; // Directory listing
|
|
|
|
// ACCESSOR METHODS
|
|
void addIncDirUser(const string& incdir) {
|
|
const auto itFoundPair = m_incDirUserSet.insert(incdir);
|
|
if (itFoundPair.second) {
|
|
// cppcheck-suppress stlFindInsert // cppcheck 1.90 bug
|
|
m_incDirUsers.push_back(incdir);
|
|
m_incDirFallbacks.remove(incdir); // User has priority over Fallback
|
|
m_incDirFallbackSet.erase(incdir); // User has priority over Fallback
|
|
}
|
|
}
|
|
void addIncDirFallback(const string& incdir) {
|
|
if (m_incDirUserSet.find(incdir)
|
|
== m_incDirUserSet.end()) { // User has priority over Fallback
|
|
const auto itFoundPair = m_incDirFallbackSet.insert(incdir);
|
|
if (itFoundPair.second) m_incDirFallbacks.push_back(incdir);
|
|
}
|
|
}
|
|
void addLangExt(const string& langext, const V3LangCode& lc) {
|
|
// New language extension replaces any pre-existing one.
|
|
(void)m_langExts.erase(langext);
|
|
m_langExts[langext] = lc;
|
|
}
|
|
|
|
void addLibExtV(const string& libext) {
|
|
const auto itFoundPair = m_libExtVSet.insert(libext);
|
|
if (itFoundPair.second) m_libExtVs.push_back(libext);
|
|
}
|
|
V3OptionsImp() = default;
|
|
~V3OptionsImp() = default;
|
|
};
|
|
|
|
//######################################################################
|
|
// V3LangCode class functions
|
|
|
|
V3LangCode::V3LangCode(const char* textp) {
|
|
// Return code for given string, or ERROR, which is a bad code
|
|
for (int codei = V3LangCode::L_ERROR; codei < V3LangCode::_ENUM_END; ++codei) {
|
|
const V3LangCode code{codei};
|
|
if (0 == VL_STRCASECMP(textp, code.ascii())) {
|
|
m_e = code;
|
|
return;
|
|
}
|
|
}
|
|
m_e = V3LangCode::L_ERROR;
|
|
}
|
|
|
|
//######################################################################
|
|
// VTimescale class functions
|
|
|
|
VTimescale::VTimescale(const string& value, bool& badr)
|
|
: m_e{VTimescale::NONE} {
|
|
badr = true;
|
|
const string spaceless = VString::removeWhitespace(value);
|
|
for (int i = TS_100S; i < _ENUM_END; ++i) {
|
|
const VTimescale ts{i};
|
|
if (spaceless == ts.ascii()) {
|
|
badr = false;
|
|
m_e = ts.m_e;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//######################################################################
|
|
// V3HierarchicalBlockOption class functions
|
|
|
|
// Parse "--hierarchical-block orig_name,mangled_name,param0_name,param0_value,... " option.
|
|
// The format of value is as same as -G option. (can be string literal surrounded by ")
|
|
V3HierarchicalBlockOption::V3HierarchicalBlockOption(const string& opts) {
|
|
V3StringList vals;
|
|
bool inStr = false;
|
|
string cur;
|
|
static const string hierBlock("--hierarchical-block");
|
|
FileLine cmdfl(FileLine::commandLineFilename());
|
|
// Split by ','. If ',' appears between "", that is not a separator.
|
|
for (string::const_iterator it = opts.begin(); it != opts.end();) {
|
|
if (inStr) {
|
|
if (*it == '\\') {
|
|
++it;
|
|
if (it == opts.end()) {
|
|
cmdfl.v3error(hierBlock + " must not end with \\");
|
|
break;
|
|
}
|
|
if (*it != '"' && *it != '\\') {
|
|
cmdfl.v3error(hierBlock + " does not allow '" + *it + "' after \\");
|
|
break;
|
|
}
|
|
cur.push_back(*it);
|
|
++it;
|
|
} else if (*it == '"') { // end of string
|
|
cur.push_back(*it);
|
|
vals.push_back(cur);
|
|
cur.clear();
|
|
++it;
|
|
if (it != opts.end()) {
|
|
if (*it != ',') {
|
|
cmdfl.v3error(hierBlock + " expects ',', but '" + *it + "' is passed");
|
|
break;
|
|
}
|
|
++it;
|
|
if (it == opts.end()) {
|
|
cmdfl.v3error(hierBlock + " must not end with ','");
|
|
break;
|
|
}
|
|
inStr = *it == '"';
|
|
cur.push_back(*it);
|
|
++it;
|
|
}
|
|
} else {
|
|
cur.push_back(*it);
|
|
++it;
|
|
}
|
|
} else {
|
|
if (*it == '"') {
|
|
cmdfl.v3error(hierBlock + " does not allow '\"' in the middle of literal");
|
|
break;
|
|
}
|
|
if (*it == ',') { // end of this parameter
|
|
vals.push_back(cur);
|
|
cur.clear();
|
|
++it;
|
|
if (it == opts.end()) {
|
|
cmdfl.v3error(hierBlock + " must not end with ','");
|
|
break;
|
|
}
|
|
inStr = *it == '"';
|
|
}
|
|
cur.push_back(*it);
|
|
++it;
|
|
}
|
|
}
|
|
if (!cur.empty()) vals.push_back(cur);
|
|
if (vals.size() >= 2) {
|
|
if (vals.size() % 2) {
|
|
cmdfl.v3error(hierBlock + " requires the number of entries to be even");
|
|
}
|
|
m_origName = vals[0];
|
|
m_mangledName = vals[1];
|
|
} else {
|
|
cmdfl.v3error(hierBlock + " requires at least two comma-separated values");
|
|
}
|
|
for (size_t i = 2; i + 1 < vals.size(); i += 2) {
|
|
const bool inserted = m_parameters.insert(std::make_pair(vals[i], vals[i + 1])).second;
|
|
if (!inserted) {
|
|
cmdfl.v3error("Module name '" + vals[i] + "' is duplicated in " + hierBlock);
|
|
}
|
|
}
|
|
}
|
|
|
|
//######################################################################
|
|
// V3Options class functions
|
|
|
|
void VTimescale::parseSlashed(FileLine* fl, const char* textp, VTimescale& unitr,
|
|
VTimescale& precr, bool allowEmpty) {
|
|
// Parse `timescale of <number><units> / <number><units>
|
|
unitr = VTimescale::NONE;
|
|
precr = VTimescale::NONE;
|
|
|
|
const char* cp = textp;
|
|
for (; std::isspace(*cp); ++cp) {}
|
|
const char* const unitp = cp;
|
|
for (; *cp && *cp != '/'; ++cp) {}
|
|
const string unitStr(unitp, cp - unitp);
|
|
for (; std::isspace(*cp); ++cp) {}
|
|
string precStr;
|
|
if (*cp == '/') {
|
|
++cp;
|
|
for (; std::isspace(*cp); ++cp) {}
|
|
const char* const precp = cp;
|
|
for (; *cp && *cp != '/'; ++cp) {}
|
|
precStr = string(precp, cp - precp);
|
|
}
|
|
for (; std::isspace(*cp); ++cp) {}
|
|
if (*cp) {
|
|
fl->v3error("`timescale syntax error: '" << textp << "'");
|
|
return;
|
|
}
|
|
|
|
bool unitbad = false;
|
|
const VTimescale unit{unitStr, unitbad /*ref*/};
|
|
if (unitbad && !(unitStr.empty() && allowEmpty)) {
|
|
fl->v3error("`timescale timeunit syntax error: '" << unitStr << "'");
|
|
return;
|
|
}
|
|
unitr = unit;
|
|
|
|
if (!precStr.empty()) {
|
|
VTimescale prec{VTimescale::NONE};
|
|
bool precbad;
|
|
prec = VTimescale{precStr, precbad /*ref*/};
|
|
if (precbad) {
|
|
fl->v3error("`timescale timeprecision syntax error: '" << precStr << "'");
|
|
return;
|
|
}
|
|
if (!unit.isNone() && !prec.isNone() && unit < prec) {
|
|
fl->v3error("`timescale timeunit '"
|
|
<< unitStr << "' must be greater than or equal to timeprecision '"
|
|
<< precStr << "'");
|
|
return;
|
|
}
|
|
precr = prec;
|
|
}
|
|
}
|
|
|
|
//######################################################################
|
|
// V3Options class functions
|
|
|
|
void V3Options::addIncDirUser(const string& incdir) { m_impp->addIncDirUser(incdir); }
|
|
void V3Options::addIncDirFallback(const string& incdir) { m_impp->addIncDirFallback(incdir); }
|
|
void V3Options::addLangExt(const string& langext, const V3LangCode& lc) {
|
|
m_impp->addLangExt(langext, lc);
|
|
}
|
|
void V3Options::addLibExtV(const string& libext) { m_impp->addLibExtV(libext); }
|
|
void V3Options::addDefine(const string& defline, bool allowPlus) {
|
|
// Split +define+foo=value into the appropriate parts and parse
|
|
// Optional + says to allow multiple defines on the line
|
|
// + is not quotable, as other simulators do not allow that
|
|
string left = defline;
|
|
while (left != "") {
|
|
string def = left;
|
|
string::size_type pos;
|
|
if (allowPlus && ((pos = left.find('+')) != string::npos)) {
|
|
left = left.substr(pos + 1);
|
|
def.erase(pos);
|
|
} else {
|
|
left = "";
|
|
}
|
|
string value;
|
|
if ((pos = def.find('=')) != string::npos) {
|
|
value = def.substr(pos + 1);
|
|
def.erase(pos);
|
|
}
|
|
V3PreShell::defineCmdLine(def, value);
|
|
}
|
|
}
|
|
void V3Options::addParameter(const string& paramline, bool allowPlus) {
|
|
// Split +define+foo=value into the appropriate parts and parse
|
|
// Optional + says to allow multiple defines on the line
|
|
// + is not quotable, as other simulators do not allow that
|
|
string left = paramline;
|
|
while (left != "") {
|
|
string param = left;
|
|
string::size_type pos;
|
|
if (allowPlus && ((pos = left.find('+')) != string::npos)) {
|
|
left = left.substr(pos + 1);
|
|
param.erase(pos);
|
|
} else {
|
|
left = "";
|
|
}
|
|
string value;
|
|
if ((pos = param.find('=')) != string::npos) {
|
|
value = param.substr(pos + 1);
|
|
param.erase(pos);
|
|
}
|
|
UINFO(4, "Add parameter" << param << "=" << value << endl);
|
|
(void)m_parameters.erase(param);
|
|
m_parameters[param] = value;
|
|
}
|
|
}
|
|
|
|
bool V3Options::hasParameter(const string& name) {
|
|
return m_parameters.find(name) != m_parameters.end();
|
|
}
|
|
|
|
string V3Options::parameter(const string& name) {
|
|
string value = m_parameters.find(name)->second;
|
|
m_parameters.erase(m_parameters.find(name));
|
|
return value;
|
|
}
|
|
|
|
void V3Options::checkParameters() {
|
|
if (!m_parameters.empty()) {
|
|
std::stringstream msg;
|
|
msg << "Parameters from the command line were not found in the design:";
|
|
for (const auto& i : m_parameters) msg << " " << i.first;
|
|
v3error(msg.str());
|
|
}
|
|
}
|
|
|
|
void V3Options::addCppFile(const string& filename) { m_cppFiles.insert(filename); }
|
|
void V3Options::addCFlags(const string& filename) { m_cFlags.push_back(filename); }
|
|
void V3Options::addLdLibs(const string& filename) { m_ldLibs.push_back(filename); }
|
|
void V3Options::addMakeFlags(const string& filename) { m_makeFlags.push_back(filename); }
|
|
void V3Options::addFuture(const string& flag) { m_futures.insert(flag); }
|
|
void V3Options::addFuture0(const string& flag) { m_future0s.insert(flag); }
|
|
void V3Options::addFuture1(const string& flag) { m_future1s.insert(flag); }
|
|
bool V3Options::isFuture(const string& flag) const {
|
|
return m_futures.find(flag) != m_futures.end();
|
|
}
|
|
bool V3Options::isFuture0(const string& flag) const {
|
|
return m_future0s.find(flag) != m_future0s.end();
|
|
}
|
|
bool V3Options::isFuture1(const string& flag) const {
|
|
return m_future1s.find(flag) != m_future1s.end();
|
|
}
|
|
bool V3Options::isLibraryFile(const string& filename) const {
|
|
return m_libraryFiles.find(filename) != m_libraryFiles.end();
|
|
}
|
|
void V3Options::addLibraryFile(const string& filename) { m_libraryFiles.insert(filename); }
|
|
bool V3Options::isClocker(const string& signame) const {
|
|
return m_clockers.find(signame) != m_clockers.end();
|
|
}
|
|
void V3Options::addClocker(const string& signame) { m_clockers.insert(signame); }
|
|
bool V3Options::isNoClocker(const string& signame) const {
|
|
return m_noClockers.find(signame) != m_noClockers.end();
|
|
}
|
|
void V3Options::addNoClocker(const string& signame) { m_noClockers.insert(signame); }
|
|
void V3Options::addVFile(const string& filename) {
|
|
// We use a list for v files, because it's legal to have includes
|
|
// in a specific order and multiple of them.
|
|
m_vFiles.push_back(filename);
|
|
}
|
|
void V3Options::addForceInc(const string& filename) { m_forceIncs.push_back(filename); }
|
|
|
|
void V3Options::addArg(const string& arg) { m_impp->m_allArgs.push_back(arg); }
|
|
|
|
string V3Options::allArgsString() const VL_MT_SAFE {
|
|
string out;
|
|
for (const string& i : m_impp->m_allArgs) {
|
|
if (out != "") out += " ";
|
|
out += i;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// Delete some options for Verilation of the hierarchical blocks.
|
|
string V3Options::allArgsStringForHierBlock(bool forTop) const {
|
|
std::set<string> vFiles;
|
|
for (const auto& vFile : m_vFiles) vFiles.insert(vFile);
|
|
string out;
|
|
for (std::list<string>::const_iterator it = m_impp->m_allArgs.begin();
|
|
it != m_impp->m_allArgs.end(); ++it) {
|
|
int skip = 0;
|
|
if (it->length() >= 2 && (*it)[0] == '-' && (*it)[1] == '-') {
|
|
skip = 2;
|
|
} else if (it->length() >= 1 && (*it)[0] == '-') {
|
|
skip = 1;
|
|
}
|
|
if (skip > 0) { // *it is an option
|
|
const string opt = it->substr(skip); // Remove '-' in the beginning
|
|
const int numStrip = stripOptionsForChildRun(opt, forTop);
|
|
if (numStrip) {
|
|
UASSERT(0 <= numStrip && numStrip <= 2, "should be one of 0, 1, 2");
|
|
if (numStrip == 2) ++it;
|
|
continue;
|
|
}
|
|
} else { // Not an option
|
|
if (vFiles.find(*it) != vFiles.end() // Remove HDL
|
|
|| m_cppFiles.find(*it) != m_cppFiles.end()) { // Remove C++
|
|
continue;
|
|
}
|
|
}
|
|
if (out != "") out += " ";
|
|
// Don't use opt here because '-' is removed in it
|
|
// Use double quote because *it may contain whitespaces
|
|
out += '"' + VString::quoteAny(*it, '"', '\\') + '"';
|
|
}
|
|
return out;
|
|
}
|
|
|
|
void V3Options::ccSet() { // --cc
|
|
m_outFormatOk = true;
|
|
m_systemC = false;
|
|
}
|
|
|
|
//######################################################################
|
|
// File searching
|
|
|
|
bool V3Options::fileStatNormal(const string& filename) {
|
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
|
struct stat sstat; // Stat information
|
|
const int err = stat(filename.c_str(), &sstat);
|
|
if (err != 0) return false;
|
|
if (S_ISDIR(sstat.st_mode)) return false;
|
|
return true;
|
|
}
|
|
|
|
void V3Options::fileNfsFlush(const string& filename) {
|
|
// NFS caches stat() calls so to get up-to-date information must
|
|
// do a open or opendir on the filename.
|
|
// Faster to just try both rather than check if a file is a dir.
|
|
#ifdef _MSC_VER
|
|
if (int fd = ::open(filename.c_str(), O_RDONLY)) { // LCOV_EXCL_BR_LINE
|
|
if (fd > 0) ::close(fd);
|
|
}
|
|
#else
|
|
if (DIR* const dirp = opendir(filename.c_str())) { // LCOV_EXCL_BR_LINE
|
|
closedir(dirp); // LCOV_EXCL_LINE
|
|
} else if (int fd = ::open(filename.c_str(), O_RDONLY)) { // LCOV_EXCL_BR_LINE
|
|
if (fd > 0) ::close(fd);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
string V3Options::fileExists(const string& filename) {
|
|
// Surprisingly, for VCS and other simulators, this process
|
|
// is quite slow; presumably because of re-reading each directory
|
|
// many times. So we read a whole dir at once and cache it
|
|
|
|
const string dir = V3Os::filenameDir(filename);
|
|
const string basename = V3Os::filenameNonDir(filename);
|
|
|
|
auto diriter = m_impp->m_dirMap.find(dir);
|
|
if (diriter == m_impp->m_dirMap.end()) {
|
|
// Read the listing
|
|
m_impp->m_dirMap.emplace(dir, std::set<string>());
|
|
diriter = m_impp->m_dirMap.find(dir);
|
|
|
|
std::set<string>* setp = &(diriter->second);
|
|
|
|
#ifdef _MSC_VER
|
|
try {
|
|
for (const auto& dirEntry : std::filesystem::directory_iterator(dir.c_str()))
|
|
setp->insert(dirEntry.path().filename().string());
|
|
} catch (std::filesystem::filesystem_error const& ex) { return ""; }
|
|
#else
|
|
if (DIR* const dirp = opendir(dir.c_str())) {
|
|
while (struct dirent* direntp = readdir(dirp)) setp->insert(direntp->d_name);
|
|
closedir(dirp);
|
|
}
|
|
#endif
|
|
}
|
|
// Find it
|
|
const std::set<string>* filesetp = &(diriter->second);
|
|
const auto fileiter = filesetp->find(basename);
|
|
if (fileiter == filesetp->end()) {
|
|
return ""; // Not found
|
|
}
|
|
// Check if it is a directory, ignore if so
|
|
string filenameOut = V3Os::filenameFromDirBase(dir, basename);
|
|
if (!fileStatNormal(filenameOut)) return ""; // Directory
|
|
return filenameOut;
|
|
}
|
|
|
|
string V3Options::filePathCheckOneDir(const string& modname, const string& dirname) {
|
|
for (const string& i : m_impp->m_libExtVs) {
|
|
const string fn = V3Os::filenameFromDirBase(dirname, modname + i);
|
|
string exists = fileExists(fn);
|
|
if (exists != "") {
|
|
// Strip ./, it just looks ugly
|
|
if (exists.substr(0, 2) == "./") exists.erase(0, 2);
|
|
return exists;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
// Checks if a option needs to be stripped for child run of hierarchical Verilation.
|
|
// 0: Keep the option including its argument
|
|
// 1: Delete the option which has no argument
|
|
// 2: Delete the option and its argument
|
|
int V3Options::stripOptionsForChildRun(const string& opt, bool forTop) {
|
|
if (opt == "Mdir" || opt == "clk" || opt == "lib-create" || opt == "f" || opt == "j"
|
|
|| opt == "l2-name" || opt == "mod-prefix" || opt == "prefix" || opt == "protect-lib"
|
|
|| opt == "protect-key" || opt == "threads" || opt == "top-module" || opt == "v") {
|
|
return 2;
|
|
}
|
|
if (opt == "build" || (!forTop && (opt == "cc" || opt == "exe" || opt == "sc"))
|
|
|| opt == "hierarchical" || (opt.length() > 2 && opt.substr(0, 2) == "G=")) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
string V3Options::filePath(FileLine* fl, const string& modname, const string& lastpath,
|
|
const string& errmsg) { // Error prefix or "" to suppress error
|
|
// Find a filename to read the specified module name,
|
|
// using the incdir and libext's.
|
|
// Return "" if not found.
|
|
for (const string& dir : m_impp->m_incDirUsers) {
|
|
string exists = filePathCheckOneDir(modname, dir);
|
|
if (exists != "") return exists;
|
|
}
|
|
for (const string& dir : m_impp->m_incDirFallbacks) {
|
|
string exists = filePathCheckOneDir(modname, dir);
|
|
if (exists != "") return exists;
|
|
}
|
|
|
|
if (m_relativeIncludes) {
|
|
const string exists = filePathCheckOneDir(modname, lastpath);
|
|
if (exists != "") return V3Os::filenameRealPath(exists);
|
|
}
|
|
|
|
// Warn and return not found
|
|
if (errmsg != "") {
|
|
fl->v3error(errmsg + modname);
|
|
filePathLookedMsg(fl, modname);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
void V3Options::filePathLookedMsg(FileLine* fl, const string& modname) {
|
|
static bool shown_notfound_msg = false;
|
|
if (modname.find("__Vhsh") != string::npos) {
|
|
std::cerr << V3Error::warnMoreStandalone()
|
|
<< "... Unsupported: Name is longer than 127 characters;"
|
|
<< " automatic file lookup not supported.\n";
|
|
std::cerr << V3Error::warnMoreStandalone()
|
|
<< "... Suggest putting filename with this module/package"
|
|
<< " onto command line instead.\n";
|
|
} else if (!shown_notfound_msg) {
|
|
shown_notfound_msg = true;
|
|
if (m_impp->m_incDirUsers.empty()) {
|
|
fl->v3error("This may be because there's no search path specified with -I<dir>.");
|
|
}
|
|
std::cerr << V3Error::warnMoreStandalone() << "... Looked in:" << endl;
|
|
for (const string& dir : m_impp->m_incDirUsers) {
|
|
for (const string& ext : m_impp->m_libExtVs) {
|
|
const string fn = V3Os::filenameFromDirBase(dir, modname + ext);
|
|
std::cerr << V3Error::warnMoreStandalone() << " " << fn << endl;
|
|
}
|
|
}
|
|
for (const string& dir : m_impp->m_incDirFallbacks) {
|
|
for (const string& ext : m_impp->m_libExtVs) {
|
|
const string fn = V3Os::filenameFromDirBase(dir, modname + ext);
|
|
std::cerr << V3Error::warnMoreStandalone() << " " << fn << endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//! Determine what language is associated with a filename
|
|
|
|
//! If we recognize the extension, use its language, otherwise, use the
|
|
//! default language.
|
|
V3LangCode V3Options::fileLanguage(const string& filename) {
|
|
string ext = V3Os::filenameNonDir(filename);
|
|
string::size_type pos;
|
|
if (filename == V3Options::getStdPackagePath()) {
|
|
return V3LangCode::mostRecent();
|
|
} else if ((pos = ext.rfind('.')) != string::npos) {
|
|
ext.erase(0, pos + 1);
|
|
const auto it = m_impp->m_langExts.find(ext);
|
|
if (it != m_impp->m_langExts.end()) return it->second;
|
|
}
|
|
return m_defaultLanguage;
|
|
}
|
|
|
|
//######################################################################
|
|
// Environment
|
|
|
|
string V3Options::getenvBuiltins(const string& var) {
|
|
// If update below, also update V3Options::showVersion()
|
|
if (var == "MAKE") {
|
|
return getenvMAKE();
|
|
} else if (var == "PERL") {
|
|
return getenvPERL();
|
|
} else if (var == "SYSTEMC") {
|
|
return getenvSYSTEMC();
|
|
} else if (var == "SYSTEMC_ARCH") {
|
|
return getenvSYSTEMC_ARCH();
|
|
} else if (var == "SYSTEMC_INCLUDE") {
|
|
return getenvSYSTEMC_INCLUDE();
|
|
} else if (var == "SYSTEMC_LIBDIR") {
|
|
return getenvSYSTEMC_LIBDIR();
|
|
} else if (var == "VERILATOR_ROOT") {
|
|
return getenvVERILATOR_ROOT();
|
|
} else {
|
|
return V3Os::getenvStr(var, "");
|
|
}
|
|
}
|
|
|
|
#ifdef __FreeBSD__
|
|
string V3Options::getenvMAKE() { return V3Os::getenvStr("MAKE", "gmake"); }
|
|
#else
|
|
string V3Options::getenvMAKE() { return V3Os::getenvStr("MAKE", "make"); }
|
|
#endif
|
|
|
|
string V3Options::getenvMAKEFLAGS() { //
|
|
return V3Os::getenvStr("MAKEFLAGS", "");
|
|
}
|
|
|
|
string V3Options::getenvPERL() { //
|
|
return V3Os::getenvStr("PERL", "perl");
|
|
}
|
|
|
|
string V3Options::getenvSYSTEMC() {
|
|
string var = V3Os::getenvStr("SYSTEMC", "");
|
|
// Treat compiled-in DEFENV string literals as C-strings to enable
|
|
// binary patching for relocatable installs (e.g. conda)
|
|
string defenv = string{DEFENV_SYSTEMC}.c_str();
|
|
if (var == "" && defenv != "") {
|
|
var = defenv;
|
|
V3Os::setenvStr("SYSTEMC", var, "Hardcoded at build time");
|
|
}
|
|
return var;
|
|
}
|
|
|
|
string V3Options::getenvSYSTEMC_ARCH() {
|
|
string var = V3Os::getenvStr("SYSTEMC_ARCH", "");
|
|
// Treat compiled-in DEFENV string literals as C-strings to enable
|
|
// binary patching for relocatable installs (e.g. conda)
|
|
string defenv = string{DEFENV_SYSTEMC_ARCH}.c_str();
|
|
if (var == "" && defenv != "") {
|
|
var = defenv;
|
|
V3Os::setenvStr("SYSTEMC_ARCH", var, "Hardcoded at build time");
|
|
}
|
|
if (var == "") {
|
|
#if defined(__MINGW32__)
|
|
// Hardcoded with MINGW current version. Would like a better way.
|
|
const string sysname = "MINGW32_NT-5.0";
|
|
var = "mingw32";
|
|
#elif defined(_WIN32)
|
|
const string sysname = "WIN32";
|
|
var = "win32";
|
|
#else
|
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
|
struct utsname uts;
|
|
uname(&uts);
|
|
const string sysname = VString::downcase(uts.sysname); // aka 'uname -s'
|
|
if (VL_UNCOVERABLE(VString::wildmatch(sysname.c_str(), "*solaris*"))) {
|
|
var = "gccsparcOS5";
|
|
} else if (VL_UNCOVERABLE(VString::wildmatch(sysname.c_str(), "*cygwin*"))) {
|
|
var = "cygwin";
|
|
} else {
|
|
var = "linux";
|
|
}
|
|
#endif
|
|
V3Os::setenvStr("SYSTEMC_ARCH", var, "From sysname '" + sysname + "'");
|
|
}
|
|
return var;
|
|
}
|
|
|
|
string V3Options::getenvSYSTEMC_INCLUDE() {
|
|
string var = V3Os::getenvStr("SYSTEMC_INCLUDE", "");
|
|
// Treat compiled-in DEFENV string literals as C-strings to enable
|
|
// binary patching for relocatable installs (e.g. conda)
|
|
string defenv = string{DEFENV_SYSTEMC_INCLUDE}.c_str();
|
|
if (var == "" && defenv != "") {
|
|
var = defenv;
|
|
V3Os::setenvStr("SYSTEMC_INCLUDE", var, "Hardcoded at build time");
|
|
}
|
|
if (var == "") {
|
|
const string sc = getenvSYSTEMC();
|
|
if (sc != "") var = sc + "/include";
|
|
}
|
|
return var;
|
|
}
|
|
|
|
string V3Options::getenvSYSTEMC_LIBDIR() {
|
|
string var = V3Os::getenvStr("SYSTEMC_LIBDIR", "");
|
|
// Treat compiled-in DEFENV string literals as C-strings to enable
|
|
// binary patching for relocatable installs (e.g. conda)
|
|
string defenv = string{DEFENV_SYSTEMC_LIBDIR}.c_str();
|
|
if (var == "" && defenv != "") {
|
|
var = defenv;
|
|
V3Os::setenvStr("SYSTEMC_LIBDIR", var, "Hardcoded at build time");
|
|
}
|
|
if (var == "") {
|
|
const string sc = getenvSYSTEMC();
|
|
const string arch = getenvSYSTEMC_ARCH();
|
|
if (sc != "" && arch != "") var = sc + "/lib-" + arch;
|
|
}
|
|
return var;
|
|
}
|
|
|
|
string V3Options::getenvVERILATOR_ROOT() {
|
|
string var = V3Os::getenvStr("VERILATOR_ROOT", "");
|
|
// Treat compiled-in DEFENV string literals as C-strings to enable
|
|
// binary patching for relocatable installs (e.g. conda)
|
|
string defenv = string{DEFENV_VERILATOR_ROOT}.c_str();
|
|
if (var == "" && defenv != "") {
|
|
var = defenv;
|
|
V3Os::setenvStr("VERILATOR_ROOT", var, "Hardcoded at build time");
|
|
}
|
|
if (var == "") v3fatal("$VERILATOR_ROOT needs to be in environment\n");
|
|
return var;
|
|
}
|
|
|
|
string V3Options::getStdPackagePath() {
|
|
return getenvVERILATOR_ROOT() + "/include/verilated_std.sv";
|
|
}
|
|
|
|
string V3Options::getSupported(const string& var) {
|
|
// If update below, also update V3Options::showVersion()
|
|
if (var == "COROUTINES" && coroutineSupport()) {
|
|
return "1";
|
|
} else if (var == "SYSTEMC" && systemCFound()) {
|
|
return "1";
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
bool V3Options::systemCSystemWide() {
|
|
#ifdef HAVE_SYSTEMC
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool V3Options::systemCFound() {
|
|
return (systemCSystemWide()
|
|
|| (!getenvSYSTEMC_INCLUDE().empty() && !getenvSYSTEMC_LIBDIR().empty()));
|
|
}
|
|
|
|
bool V3Options::coroutineSupport() {
|
|
#ifdef HAVE_COROUTINES
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
//######################################################################
|
|
// V3 Options notification methods
|
|
|
|
void V3Options::notify() {
|
|
// Notify that all arguments have been passed and final modification can be made.
|
|
FileLine* const cmdfl = new FileLine{FileLine::commandLineFilename()};
|
|
|
|
if (!outFormatOk() && v3Global.opt.main()) ccSet(); // --main implies --cc if not provided
|
|
if (!outFormatOk() && !dpiHdrOnly() && !lintOnly() && !preprocOnly() && !xmlOnly()) {
|
|
v3fatal("verilator: Need --binary, --cc, --sc, --dpi-hdr-only, --lint-only, "
|
|
"--xml-only or --E option");
|
|
}
|
|
|
|
if (m_build && (m_gmake || m_cmake)) {
|
|
cmdfl->v3error("--make cannot be used together with --build. Suggest see manual");
|
|
}
|
|
|
|
if (m_exe && !v3Global.opt.libCreate().empty()) {
|
|
cmdfl->v3error("--exe cannot be used together with --lib-create. Suggest see manual");
|
|
}
|
|
|
|
// Make sure at least one make system is enabled
|
|
if (!m_gmake && !m_cmake) m_gmake = true;
|
|
|
|
if (m_hierarchical && (m_hierChild || !m_hierBlocks.empty())) {
|
|
cmdfl->v3error(
|
|
"--hierarchical must not be set with --hierarchical-child or --hierarchical-block");
|
|
}
|
|
if (m_hierChild && m_hierBlocks.empty()) {
|
|
cmdfl->v3error("--hierarchical-block must be set when --hierarchical-child is set");
|
|
}
|
|
|
|
if (protectIds()) {
|
|
if (allPublic()) {
|
|
// We always call protect() on names, we don't check if public or not
|
|
// Hence any external references wouldn't be able to find the refed public object.
|
|
cmdfl->v3warn(E_UNSUPPORTED, "Unsupported: Using --protect-ids with --public\n" //
|
|
+ cmdfl->warnMore() + "... Suggest remove --public.");
|
|
}
|
|
if (trace()) {
|
|
cmdfl->v3warn(INSECURE,
|
|
"Using --protect-ids with --trace may expose private design details\n"
|
|
+ cmdfl->warnMore() + "... Suggest remove --trace.");
|
|
}
|
|
if (vpi()) {
|
|
cmdfl->v3warn(INSECURE,
|
|
"Using --protect-ids with --vpi may expose private design details\n"
|
|
+ cmdfl->warnMore() + "... Suggest remove --vpi.");
|
|
}
|
|
}
|
|
|
|
// Default some options if not turned on or off
|
|
if (v3Global.opt.skipIdentical().isDefault()) {
|
|
v3Global.opt.m_skipIdentical.setTrueOrFalse( //
|
|
!v3Global.opt.dpiHdrOnly() //
|
|
&& !v3Global.opt.lintOnly() //
|
|
&& !v3Global.opt.preprocOnly() //
|
|
&& !v3Global.opt.xmlOnly());
|
|
}
|
|
if (v3Global.opt.makeDepend().isDefault()) {
|
|
v3Global.opt.m_makeDepend.setTrueOrFalse( //
|
|
!v3Global.opt.dpiHdrOnly() //
|
|
&& !v3Global.opt.lintOnly() //
|
|
&& !v3Global.opt.preprocOnly() //
|
|
&& !v3Global.opt.xmlOnly());
|
|
}
|
|
|
|
if (trace()) {
|
|
// With --trace-fst, --trace-threads implies --threads 1 unless explicitly specified
|
|
if (traceFormat().fst() && traceThreads() && !threads()) m_threads = 1;
|
|
|
|
// With --trace, --trace-threads is ignored
|
|
if (traceFormat().vcd()) m_traceThreads = threads() ? 1 : 0;
|
|
}
|
|
|
|
UASSERT(!(useTraceParallel() && useTraceOffload()),
|
|
"Cannot use both parallel and offloaded tracing");
|
|
|
|
// Default split limits if not specified
|
|
if (m_outputSplitCFuncs < 0) m_outputSplitCFuncs = m_outputSplit;
|
|
if (m_outputSplitCTrace < 0) m_outputSplitCTrace = m_outputSplit;
|
|
|
|
if (v3Global.opt.main() && v3Global.opt.systemC()) {
|
|
cmdfl->v3warn(E_UNSUPPORTED,
|
|
"--main not usable with SystemC. Suggest see examples for sc_main().");
|
|
}
|
|
|
|
if (coverage() && savable()) {
|
|
cmdfl->v3error("Unsupported: --coverage and --savable not supported together");
|
|
}
|
|
|
|
// Mark options as available
|
|
m_available = true;
|
|
|
|
// --dump-tree-dot will turn on tree dumping.
|
|
if (!m_dumpLevel.count("tree") && m_dumpLevel.count("tree-dot")) {
|
|
m_dumpLevel["tree"] = m_dumpLevel["tree-dot"];
|
|
}
|
|
|
|
// Preprocessor defines based on options used
|
|
if (timing().isSetTrue()) V3PreShell::defineCmdLine("VERILATOR_TIMING", "1");
|
|
}
|
|
|
|
//######################################################################
|
|
// V3 Options accessors
|
|
|
|
string V3Options::version() VL_PURE {
|
|
string ver = DTVERSION;
|
|
ver += " rev " + cvtToStr(DTVERSION_rev);
|
|
return ver;
|
|
}
|
|
|
|
string V3Options::protectKeyDefaulted() VL_MT_SAFE {
|
|
static VerilatedMutex mutex;
|
|
const VerilatedLockGuard lock{mutex};
|
|
if (m_protectKey.empty()) {
|
|
// Create a key with a human-readable symbol-like name.
|
|
// This conversion drops ~2 bits of entropy out of 256, shouldn't matter.
|
|
VHashSha256 digest{V3Os::trueRandom(32)};
|
|
m_protectKey = "VL-KEY-" + digest.digestSymbol();
|
|
}
|
|
return m_protectKey;
|
|
}
|
|
|
|
void V3Options::throwSigsegv() { // LCOV_EXCL_START
|
|
#if !(defined(VL_CPPCHECK) || defined(__clang_analyzer__))
|
|
// clang-format off
|
|
*static_cast<volatile char*>(nullptr) = 0; // Intentional core dump, ignore warnings here
|
|
// clang-format on
|
|
#endif
|
|
} // LCOV_EXCL_STOP
|
|
|
|
VTimescale V3Options::timeComputePrec(const VTimescale& flag) const {
|
|
if (!timeOverridePrec().isNone()) {
|
|
return timeOverridePrec();
|
|
} else if (flag.isNone()) {
|
|
return timeDefaultPrec();
|
|
} else {
|
|
return flag;
|
|
}
|
|
}
|
|
|
|
VTimescale V3Options::timeComputeUnit(const VTimescale& flag) const {
|
|
if (!timeOverrideUnit().isNone()) {
|
|
return timeOverrideUnit();
|
|
} else if (flag.isNone()) {
|
|
return timeDefaultUnit();
|
|
} else {
|
|
return flag;
|
|
}
|
|
}
|
|
|
|
//######################################################################
|
|
// V3 Options utilities
|
|
|
|
string V3Options::argString(int argc, char** argv) {
|
|
// Return list of arguments as simple string
|
|
string opts;
|
|
for (int i = 0; i < argc; ++i) {
|
|
if (i != 0) opts += " ";
|
|
opts += string(argv[i]);
|
|
}
|
|
return opts;
|
|
}
|
|
|
|
//######################################################################
|
|
// V3 Options Parsing
|
|
|
|
void V3Options::parseOpts(FileLine* fl, int argc, char** argv) {
|
|
// Parse all options
|
|
// Initial entry point from Verilator.cpp
|
|
parseOptsList(fl, ".", argc, argv);
|
|
|
|
// Default certain options and error check
|
|
// Detailed error, since this is what we often get when run with minimal arguments
|
|
const V3StringList& vFilesList = vFiles();
|
|
if (vFilesList.empty()) {
|
|
v3fatal("verilator: No Input Verilog file specified on command line, "
|
|
"see verilator --help for more information\n");
|
|
}
|
|
|
|
// Default prefix to the filename
|
|
if (prefix() == "" && topModule() != "")
|
|
m_prefix = string("V") + AstNode::encodeName(topModule());
|
|
if (prefix() == "" && vFilesList.size() >= 1)
|
|
m_prefix = string("V") + AstNode::encodeName(V3Os::filenameNonExt(*(vFilesList.begin())));
|
|
if (modPrefix() == "") m_modPrefix = prefix();
|
|
|
|
// Find files in makedir
|
|
addIncDirFallback(makeDir());
|
|
}
|
|
|
|
//======================================================================
|
|
|
|
bool V3Options::suffixed(const string& sw, const char* arg) {
|
|
if (std::strlen(arg) > sw.length()) return false;
|
|
return (0 == std::strcmp(sw.c_str() + sw.length() - std::strlen(arg), arg));
|
|
}
|
|
|
|
void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char** argv) {
|
|
// Parse parameters
|
|
// Note argc and argv DO NOT INCLUDE the filename in [0]!!!
|
|
// May be called recursively when there are -f files.
|
|
for (int i = 0; i < argc; ++i) {
|
|
addArg(argv[i]); // -f's really should be inserted in the middle, but this is for debug
|
|
}
|
|
|
|
V3OptionParser parser;
|
|
const V3OptionParser::AppendHelper DECL_OPTION{parser};
|
|
V3OPTION_PARSER_DECL_TAGS;
|
|
|
|
const auto callStrSetter = [this](void (V3Options::*cbStr)(const string&)) {
|
|
return [this, cbStr](const string& v) { (this->*cbStr)(v); };
|
|
};
|
|
// Usage
|
|
// DECL_OPTION("-option", action, pointer_or_lambda);
|
|
// action: one of Set, OnOff, CbCall, CbOnOff, CbVal, CbPartialMatch, and CbPartialMatchVal
|
|
// Set : Set value to a variable, pointer_or_lambda must be a pointer to the
|
|
// variable.
|
|
// true is set to bool-ish variable when '-opt' is passed to verilator.
|
|
// val is set to int and string variable when '-opt val' is passed.
|
|
// OnOff : Set value to a bool-ish variable, pointer_or_lambda must be a pointer
|
|
// to bool or VOptionBool.
|
|
// true is set if "-opt" is passed to verilator while false is set if
|
|
// "-no-opt" is given.
|
|
// CbCall : Call lambda or function that does not take argument.
|
|
// CbOnOff : Call lambda or function that takes bool argument.
|
|
// Supports "-opt" and "-no-opt" style options.
|
|
// CbVal : Call lambda or function that takes int or const char*.
|
|
// "-opt val" is passed to verilator, val is passed to the lambda.
|
|
// If a function to be called is a member of V3Options that only takes
|
|
// const string&, callStrSetter(&V3Options::memberFunc) can be passed
|
|
// instead of lambda as a syntax sugar.
|
|
// CbPartialMatch : Call lambda or function that takes remaining string.
|
|
// e.g. DECL_OPTION("-opt-", CbPartialMatch, [](const char*optp) { cout <<
|
|
// optp << endl; }); and "-opt-ABC" is passed, "ABC" will be emit to
|
|
// stdout.
|
|
// CbPartialMatchVal: Call lambda or function that takes remaining string and value.
|
|
// e.g. DECL_OPTION("-opt-", CbPartialMatchVal, [](const char*optp, const
|
|
// char*valp) {
|
|
// cout << optp << ":" << valp << endl; });
|
|
// and "-opt-ABC VAL" is passed, "ABC:VAL" will be emit to stdout.
|
|
//
|
|
// DECL_OPTION is not C-macro to get correct line coverage even when lambda is passed.
|
|
// (If DECL_OPTION is a macro, then lambda would be collapsed into a single line).
|
|
|
|
// Plus options
|
|
DECL_OPTION("+define+", CbPartialMatch, [this](const char* optp) { addDefine(optp, true); });
|
|
DECL_OPTION("+incdir+", CbPartialMatch,
|
|
[this, &optdir](const char* optp) { addIncDirUser(parseFileArg(optdir, optp)); });
|
|
DECL_OPTION("+libext+", CbPartialMatch, [this](const char* optp) {
|
|
string exts = optp;
|
|
string::size_type pos;
|
|
while ((pos = exts.find('+')) != string::npos) {
|
|
addLibExtV(exts.substr(0, pos));
|
|
exts = exts.substr(pos + 1);
|
|
}
|
|
addLibExtV(exts);
|
|
});
|
|
DECL_OPTION("+librescan", CbCall, []() {}); // NOP
|
|
DECL_OPTION("+notimingchecks", CbCall, []() {}); // NOP
|
|
DECL_OPTION("+systemverilogext+", CbPartialMatch,
|
|
[this](const char* optp) { addLangExt(optp, V3LangCode::L1800_2017); });
|
|
DECL_OPTION("+verilog1995ext+", CbPartialMatch,
|
|
[this](const char* optp) { addLangExt(optp, V3LangCode::L1364_1995); });
|
|
DECL_OPTION("+verilog2001ext+", CbPartialMatch,
|
|
[this](const char* optp) { addLangExt(optp, V3LangCode::L1364_2001); });
|
|
DECL_OPTION("+1364-1995ext+", CbPartialMatch,
|
|
[this](const char* optp) { addLangExt(optp, V3LangCode::L1364_1995); });
|
|
DECL_OPTION("+1364-2001ext+", CbPartialMatch,
|
|
[this](const char* optp) { addLangExt(optp, V3LangCode::L1364_2001); });
|
|
DECL_OPTION("+1364-2005ext+", CbPartialMatch,
|
|
[this](const char* optp) { addLangExt(optp, V3LangCode::L1364_2005); });
|
|
DECL_OPTION("+1800-2005ext+", CbPartialMatch,
|
|
[this](const char* optp) { addLangExt(optp, V3LangCode::L1800_2005); });
|
|
DECL_OPTION("+1800-2009ext+", CbPartialMatch,
|
|
[this](const char* optp) { addLangExt(optp, V3LangCode::L1800_2009); });
|
|
DECL_OPTION("+1800-2012ext+", CbPartialMatch,
|
|
[this](const char* optp) { addLangExt(optp, V3LangCode::L1800_2012); });
|
|
DECL_OPTION("+1800-2017ext+", CbPartialMatch,
|
|
[this](const char* optp) { addLangExt(optp, V3LangCode::L1800_2017); });
|
|
|
|
// Minus options
|
|
DECL_OPTION("-assert", OnOff, &m_assert);
|
|
DECL_OPTION("-autoflush", OnOff, &m_autoflush);
|
|
|
|
DECL_OPTION("-bbox-sys", OnOff, &m_bboxSys);
|
|
DECL_OPTION("-bbox-unsup", CbOnOff, [this](bool flag) {
|
|
m_bboxUnsup = flag;
|
|
FileLine::globalWarnOff(V3ErrorCode::E_UNSUPPORTED, true);
|
|
});
|
|
DECL_OPTION("-binary", CbCall, [this]() {
|
|
m_build = true;
|
|
m_exe = true;
|
|
m_main = true;
|
|
if (m_timing.isDefault()) m_timing = VOptionBool::OPT_TRUE;
|
|
});
|
|
DECL_OPTION("-build", Set, &m_build);
|
|
DECL_OPTION("-build-dep-bin", Set, &m_buildDepBin);
|
|
DECL_OPTION("-build-jobs", CbVal, [this, fl](const char* valp) {
|
|
int val = std::atoi(valp);
|
|
if (val < 0) {
|
|
fl->v3fatal("--build-jobs requires a non-negative integer, but '" << valp
|
|
<< "' was passed");
|
|
val = 1;
|
|
} else if (val == 0) {
|
|
val = std::thread::hardware_concurrency();
|
|
}
|
|
m_buildJobs = val;
|
|
});
|
|
|
|
DECL_OPTION("-CFLAGS", CbVal, callStrSetter(&V3Options::addCFlags));
|
|
DECL_OPTION("-cc", CbCall, [this]() { ccSet(); });
|
|
DECL_OPTION("-clk", CbVal, callStrSetter(&V3Options::addClocker));
|
|
DECL_OPTION("-no-clk", CbVal, callStrSetter(&V3Options::addNoClocker));
|
|
DECL_OPTION("-comp-limit-blocks", Set, &m_compLimitBlocks).undocumented();
|
|
DECL_OPTION("-comp-limit-members", Set,
|
|
&m_compLimitMembers)
|
|
.undocumented(); // Ideally power-of-two so structs stay aligned
|
|
DECL_OPTION("-comp-limit-parens", Set, &m_compLimitParens).undocumented();
|
|
DECL_OPTION("-comp-limit-syms", CbVal, [](int val) { VName::maxLength(val); }).undocumented();
|
|
DECL_OPTION("-compiler", CbVal, [this, fl](const char* valp) {
|
|
if (!std::strcmp(valp, "clang")) {
|
|
m_compLimitBlocks = 80; // limit unknown
|
|
m_compLimitMembers = 64; // soft limit, has slowdown bug as of clang++ 3.8
|
|
m_compLimitParens = 240; // controlled by -fbracket-depth, which defaults to 256
|
|
} else if (!std::strcmp(valp, "gcc")) {
|
|
m_compLimitBlocks = 0; // Bug free
|
|
m_compLimitMembers = 64; // soft limit, has slowdown bug as of g++ 7.1
|
|
m_compLimitParens = 240; // Unlimited, but generate same code as for clang
|
|
} else if (!std::strcmp(valp, "msvc")) {
|
|
m_compLimitBlocks = 80; // 128, but allow some room
|
|
m_compLimitMembers = 0; // probably ok, and AFAIK doesn't support anon structs
|
|
m_compLimitParens = 80; // 128, but allow some room
|
|
} else {
|
|
fl->v3fatal("Unknown setting for --compiler: '"
|
|
<< valp << "'\n"
|
|
<< fl->warnMore() << "... Suggest 'clang', 'gcc', or 'msvc'");
|
|
}
|
|
});
|
|
DECL_OPTION("-coverage", CbOnOff, [this](bool flag) { coverage(flag); });
|
|
DECL_OPTION("-converge-limit", Set, &m_convergeLimit);
|
|
DECL_OPTION("-coverage-line", OnOff, &m_coverageLine);
|
|
DECL_OPTION("-coverage-max-width", Set, &m_coverageMaxWidth);
|
|
DECL_OPTION("-coverage-toggle", OnOff, &m_coverageToggle);
|
|
DECL_OPTION("-coverage-underscore", OnOff, &m_coverageUnderscore);
|
|
DECL_OPTION("-coverage-user", OnOff, &m_coverageUser);
|
|
|
|
DECL_OPTION("-D", CbPartialMatch, [this](const char* valp) { addDefine(valp, false); });
|
|
DECL_OPTION("-debug", CbCall, [this]() { setDebugMode(3); });
|
|
DECL_OPTION("-debugi", CbVal, [this](int v) { setDebugMode(v); });
|
|
DECL_OPTION("-debugi-", CbPartialMatchVal, [this](const char* optp, const char* valp) {
|
|
m_debugLevel[optp] = std::atoi(valp);
|
|
});
|
|
DECL_OPTION("-debug-abort", CbCall,
|
|
V3Error::vlAbort)
|
|
.undocumented(); // See also --debug-sigsegv
|
|
DECL_OPTION("-debug-check", OnOff, &m_debugCheck);
|
|
DECL_OPTION("-debug-collision", OnOff, &m_debugCollision).undocumented();
|
|
DECL_OPTION("-debug-emitv", OnOff, &m_debugEmitV).undocumented();
|
|
DECL_OPTION("-debug-exit-parse", OnOff, &m_debugExitParse).undocumented();
|
|
DECL_OPTION("-debug-exit-uvm", OnOff, &m_debugExitUvm).undocumented();
|
|
DECL_OPTION("-debug-fatalsrc", CbCall, []() {
|
|
v3fatalSrc("--debug-fatal-src");
|
|
}).undocumented(); // See also --debug-abort
|
|
DECL_OPTION("-debug-leak", OnOff, &m_debugLeak);
|
|
DECL_OPTION("-debug-nondeterminism", OnOff, &m_debugNondeterminism);
|
|
DECL_OPTION("-debug-partition", OnOff, &m_debugPartition).undocumented();
|
|
DECL_OPTION("-debug-protect", OnOff, &m_debugProtect).undocumented();
|
|
DECL_OPTION("-debug-self-test", OnOff, &m_debugSelfTest).undocumented();
|
|
DECL_OPTION("-debug-sigsegv", CbCall, throwSigsegv).undocumented(); // See also --debug-abort
|
|
DECL_OPTION("-decoration", OnOff, &m_decoration);
|
|
DECL_OPTION("-dpi-hdr-only", OnOff, &m_dpiHdrOnly);
|
|
DECL_OPTION("-dump-", CbPartialMatch, [this](const char* optp) { m_dumpLevel[optp] = 3; });
|
|
DECL_OPTION("-no-dump-", CbPartialMatch, [this](const char* optp) { m_dumpLevel[optp] = 0; });
|
|
DECL_OPTION("-dumpi-", CbPartialMatchVal, [this](const char* optp, const char* valp) {
|
|
m_dumpLevel[optp] = std::atoi(valp);
|
|
});
|
|
DECL_OPTION("-E", CbOnOff, [this](bool flag) {
|
|
if (flag) m_std = false;
|
|
m_preprocOnly = flag;
|
|
});
|
|
DECL_OPTION("-error-limit", CbVal, static_cast<void (*)(int)>(&V3Error::errorLimit));
|
|
DECL_OPTION("-exe", OnOff, &m_exe);
|
|
DECL_OPTION("-expand-limit", CbVal,
|
|
[this](const char* valp) { m_expandLimit = std::atoi(valp); });
|
|
|
|
DECL_OPTION("-F", CbVal, [this, fl, &optdir](const char* valp) {
|
|
parseOptsFile(fl, parseFileArg(optdir, valp), true);
|
|
});
|
|
DECL_OPTION("-FI", CbVal,
|
|
[this, &optdir](const char* valp) { addForceInc(parseFileArg(optdir, valp)); });
|
|
DECL_OPTION("-f", CbVal, [this, fl, &optdir](const char* valp) {
|
|
parseOptsFile(fl, parseFileArg(optdir, valp), false);
|
|
});
|
|
DECL_OPTION("-flatten", OnOff, &m_flatten);
|
|
DECL_OPTION("-future0", CbVal, [this](const char* valp) { addFuture0(valp); });
|
|
DECL_OPTION("-future1", CbVal, [this](const char* valp) { addFuture1(valp); });
|
|
|
|
DECL_OPTION("-facyc-simp", FOnOff, &m_fAcycSimp);
|
|
DECL_OPTION("-fassemble", FOnOff, &m_fAssemble);
|
|
DECL_OPTION("-fcase", FOnOff, &m_fCase);
|
|
DECL_OPTION("-fcombine", FOnOff, &m_fCombine);
|
|
DECL_OPTION("-fconst", FOnOff, &m_fConst);
|
|
DECL_OPTION("-fconst-before-dfg", FOnOff, &m_fConstBeforeDfg);
|
|
DECL_OPTION("-fconst-bit-op-tree", FOnOff, &m_fConstBitOpTree);
|
|
DECL_OPTION("-fdedup", FOnOff, &m_fDedupe);
|
|
DECL_OPTION("-fdfg", CbFOnOff, [this](bool flag) {
|
|
m_fDfgPreInline = flag;
|
|
m_fDfgPostInline = flag;
|
|
});
|
|
DECL_OPTION("-fdfg-peephole", FOnOff, &m_fDfgPeephole);
|
|
DECL_OPTION("-fdfg-peephole-", CbPartialMatch, [this](const char* optp) { //
|
|
m_fDfgPeepholeDisabled.erase(optp);
|
|
});
|
|
DECL_OPTION("-fno-dfg-peephole-", CbPartialMatch, [this](const char* optp) { //
|
|
m_fDfgPeepholeDisabled.emplace(optp);
|
|
});
|
|
DECL_OPTION("-fdfg-pre-inline", FOnOff, &m_fDfgPreInline);
|
|
DECL_OPTION("-fdfg-post-inline", FOnOff, &m_fDfgPostInline);
|
|
DECL_OPTION("-fexpand", FOnOff, &m_fExpand);
|
|
DECL_OPTION("-fgate", FOnOff, &m_fGate);
|
|
DECL_OPTION("-finline", FOnOff, &m_fInline);
|
|
DECL_OPTION("-flife", FOnOff, &m_fLife);
|
|
DECL_OPTION("-flife-post", FOnOff, &m_fLifePost);
|
|
DECL_OPTION("-flocalize", FOnOff, &m_fLocalize);
|
|
DECL_OPTION("-fmerge-cond", FOnOff, &m_fMergeCond);
|
|
DECL_OPTION("-fmerge-cond-motion", FOnOff, &m_fMergeCondMotion);
|
|
DECL_OPTION("-fmerge-const-pool", FOnOff, &m_fMergeConstPool);
|
|
DECL_OPTION("-freloop", FOnOff, &m_fReloop);
|
|
DECL_OPTION("-freorder", FOnOff, &m_fReorder);
|
|
DECL_OPTION("-fsplit", FOnOff, &m_fSplit);
|
|
DECL_OPTION("-fsubst", FOnOff, &m_fSubst);
|
|
DECL_OPTION("-fsubst-const", FOnOff, &m_fSubstConst);
|
|
DECL_OPTION("-ftable", FOnOff, &m_fTable);
|
|
|
|
DECL_OPTION("-G", CbPartialMatch, [this](const char* optp) { addParameter(optp, false); });
|
|
DECL_OPTION("-gate-stmts", Set, &m_gateStmts);
|
|
DECL_OPTION("-gdb", CbCall, []() {}); // Processed only in bin/verilator shell
|
|
DECL_OPTION("-gdbbt", CbCall, []() {}); // Processed only in bin/verilator shell
|
|
DECL_OPTION("-generate-key", CbCall, [this]() {
|
|
cout << protectKeyDefaulted() << endl;
|
|
std::exit(0);
|
|
});
|
|
DECL_OPTION("-getenv", CbVal, [](const char* valp) {
|
|
cout << V3Options::getenvBuiltins(valp) << endl;
|
|
std::exit(0);
|
|
});
|
|
DECL_OPTION("-get-supported", CbVal, [](const char* valp) {
|
|
cout << V3Options::getSupported(valp) << endl;
|
|
std::exit(0);
|
|
});
|
|
|
|
DECL_OPTION("-hierarchical", OnOff, &m_hierarchical);
|
|
DECL_OPTION("-hierarchical-block", CbVal, [this](const char* valp) {
|
|
const V3HierarchicalBlockOption opt{valp};
|
|
m_hierBlocks.emplace(opt.mangledName(), opt);
|
|
});
|
|
DECL_OPTION("-hierarchical-child", Set, &m_hierChild);
|
|
|
|
DECL_OPTION("-I", CbPartialMatch,
|
|
[this, &optdir](const char* optp) { addIncDirUser(parseFileArg(optdir, optp)); });
|
|
DECL_OPTION("-if-depth", Set, &m_ifDepth);
|
|
DECL_OPTION("-ignc", OnOff, &m_ignc);
|
|
DECL_OPTION("-inline-mult", Set, &m_inlineMult);
|
|
DECL_OPTION("-instr-count-dpi", CbVal, [this, fl](int val) {
|
|
m_instrCountDpi = val;
|
|
if (m_instrCountDpi < 0) fl->v3fatal("--instr-count-dpi must be non-negative: " << val);
|
|
});
|
|
|
|
DECL_OPTION("-LDFLAGS", CbVal, callStrSetter(&V3Options::addLdLibs));
|
|
const auto setLang = [this, fl](const char* valp) {
|
|
const V3LangCode optval{valp};
|
|
if (optval.legal()) {
|
|
m_defaultLanguage = optval;
|
|
} else {
|
|
VSpellCheck spell;
|
|
for (int i = V3LangCode::L_ERROR + 1; i < V3LangCode::_ENUM_END; ++i) {
|
|
spell.pushCandidate(V3LangCode{i}.ascii());
|
|
}
|
|
fl->v3fatal("Unknown language specified: " << valp << spell.bestCandidateMsg(valp));
|
|
}
|
|
};
|
|
DECL_OPTION("-default-language", CbVal, setLang);
|
|
DECL_OPTION("-language", CbVal, setLang);
|
|
DECL_OPTION("-lib-create", Set, &m_libCreate);
|
|
DECL_OPTION("-lint-only", OnOff, &m_lintOnly);
|
|
DECL_OPTION("-l2-name", Set, &m_l2Name);
|
|
DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical
|
|
DECL_OPTION("-l2name", CbCall, [this]() { m_l2Name = "v"; }).undocumented(); // Historical
|
|
|
|
DECL_OPTION("-MAKEFLAGS", CbVal, callStrSetter(&V3Options::addMakeFlags));
|
|
DECL_OPTION("-MMD", OnOff, &m_makeDepend);
|
|
DECL_OPTION("-MP", OnOff, &m_makePhony);
|
|
DECL_OPTION("-Mdir", CbVal, [this](const char* valp) {
|
|
m_makeDir = valp;
|
|
addIncDirFallback(m_makeDir); // Need to find generated files there too
|
|
});
|
|
DECL_OPTION("-main", OnOff, &m_main);
|
|
DECL_OPTION("-make", CbVal, [this, fl](const char* valp) {
|
|
if (!std::strcmp(valp, "cmake")) {
|
|
m_cmake = true;
|
|
} else if (!std::strcmp(valp, "gmake")) {
|
|
m_gmake = true;
|
|
} else {
|
|
fl->v3fatal("Unknown --make system specified: '" << valp << "'");
|
|
}
|
|
});
|
|
DECL_OPTION("-max-num-width", Set, &m_maxNumWidth);
|
|
DECL_OPTION("-mod-prefix", Set, &m_modPrefix);
|
|
|
|
DECL_OPTION("-O0", CbCall, [this]() { optimize(0); });
|
|
DECL_OPTION("-O1", CbCall, [this]() { optimize(1); });
|
|
DECL_OPTION("-O2", CbCall, [this]() { optimize(2); });
|
|
DECL_OPTION("-O3", CbCall, [this]() { optimize(3); });
|
|
|
|
DECL_OPTION("-O", CbPartialMatch, [this, fl](const char* optp) {
|
|
// Optimization, e.g. -O1rX
|
|
// LCOV_EXCL_START
|
|
fl->v3warn(DEPRECATED, "Option -O<letter> is deprecated. "
|
|
"Use -f<optimization> or -fno-<optimization> instead.");
|
|
for (const char* cp = optp; *cp; ++cp) {
|
|
const bool flag = std::isupper(*cp);
|
|
switch (std::tolower(*cp)) {
|
|
case '0': optimize(0); break;
|
|
case '1': optimize(1); break;
|
|
case '2': optimize(2); break;
|
|
case '3': optimize(3); break;
|
|
case 'a': m_fTable = flag; break; // == -fno-table
|
|
case 'b': m_fCombine = flag; break; // == -fno-combine
|
|
case 'c': m_fConst = flag; break; // == -fno-const
|
|
case 'd': m_fDedupe = flag; break; // == -fno-dedup
|
|
case 'e': m_fCase = flag; break; // == -fno-case
|
|
case 'g': m_fGate = flag; break; // == -fno-gate
|
|
case 'i': m_fInline = flag; break; // == -fno-inline
|
|
case 'k': m_fSubstConst = flag; break; // == -fno-subst-const
|
|
case 'l': m_fLife = flag; break; // == -fno-life
|
|
case 'm': m_fAssemble = flag; break; // == -fno-assemble
|
|
case 'o': m_fConstBitOpTree = flag; break; // == -fno-const-bit-op-tree
|
|
case 'p':
|
|
m_public = !flag;
|
|
break; // With -Op so flag=0, we want public on so few optimizations done
|
|
case 'r': m_fReorder = flag; break; // == -fno-reorder
|
|
case 's': m_fSplit = flag; break; // == -fno-split
|
|
case 't': m_fLifePost = flag; break; // == -fno-life-post
|
|
case 'u': m_fSubst = flag; break; // == -fno-subst
|
|
case 'v': m_fReloop = flag; break; // == -fno-reloop
|
|
case 'w': m_fMergeCond = flag; break; // == -fno-merge-cond
|
|
case 'x': m_fExpand = flag; break; // == -fno-expand
|
|
case 'y': m_fAcycSimp = flag; break; // == -fno-acyc-simp
|
|
case 'z': m_fLocalize = flag; break; // == -fno-localize
|
|
default:
|
|
break; // No error, just ignore
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
}
|
|
});
|
|
DECL_OPTION("-o", Set, &m_exeName);
|
|
DECL_OPTION("-order-clock-delay", CbOnOff, [fl](bool /*flag*/) {
|
|
fl->v3warn(DEPRECATED, "Option order-clock-delay is deprecated and has no effect.");
|
|
});
|
|
DECL_OPTION("-output-split", Set, &m_outputSplit);
|
|
DECL_OPTION("-output-split-cfuncs", CbVal, [this, fl](const char* valp) {
|
|
m_outputSplitCFuncs = std::atoi(valp);
|
|
if (m_outputSplitCFuncs < 0) {
|
|
fl->v3error("--output-split-cfuncs must be >= 0: " << valp);
|
|
}
|
|
});
|
|
DECL_OPTION("-output-split-ctrace", CbVal, [this, fl](const char* valp) {
|
|
m_outputSplitCTrace = std::atoi(valp);
|
|
if (m_outputSplitCTrace < 0) {
|
|
fl->v3error("--output-split-ctrace must be >= 0: " << valp);
|
|
}
|
|
});
|
|
|
|
DECL_OPTION("-P", Set, &m_preprocNoLine);
|
|
DECL_OPTION("-pvalue+", CbPartialMatch,
|
|
[this](const char* varp) { addParameter(varp, false); });
|
|
DECL_OPTION("-pins64", CbCall, [this]() { m_pinsBv = 65; });
|
|
DECL_OPTION("-no-pins64", CbCall, [this]() { m_pinsBv = 33; });
|
|
DECL_OPTION("-pins-bv", CbVal, [this, fl](const char* valp) {
|
|
m_pinsBv = std::atoi(valp);
|
|
if (m_pinsBv > 65) fl->v3fatal("--pins-bv maximum is 65: " << valp);
|
|
});
|
|
DECL_OPTION("-pins-sc-uint", CbOnOff, [this](bool flag) {
|
|
m_pinsScUint = flag;
|
|
if (!m_pinsScBigUint) m_pinsBv = 65;
|
|
});
|
|
DECL_OPTION("-pins-sc-biguint", CbOnOff, [this](bool flag) {
|
|
m_pinsScBigUint = flag;
|
|
m_pinsBv = 513;
|
|
});
|
|
DECL_OPTION("-pins-uint8", OnOff, &m_pinsUint8);
|
|
DECL_OPTION("-pipe-filter", Set, &m_pipeFilter);
|
|
DECL_OPTION("-pp-comments", OnOff, &m_ppComments);
|
|
DECL_OPTION("-prefix", Set, &m_prefix);
|
|
DECL_OPTION("-private", CbCall, [this]() { m_public = false; });
|
|
DECL_OPTION("-prof-c", OnOff, &m_profC);
|
|
DECL_OPTION("-prof-cfuncs", CbCall, [this]() { m_profC = m_profCFuncs = true; });
|
|
DECL_OPTION("-profile-cfuncs", CbCall,
|
|
[this]() { m_profC = m_profCFuncs = true; }); // Renamed
|
|
DECL_OPTION("-prof-exec", OnOff, &m_profExec);
|
|
DECL_OPTION("-prof-pgo", OnOff, &m_profPgo);
|
|
DECL_OPTION("-prof-threads", CbOnOff, [this, fl](bool flag) {
|
|
fl->v3warn(DEPRECATED, "Option --prof-threads is deprecated. "
|
|
"Use --prof-exec and --prof-pgo instead.");
|
|
m_profExec = m_profPgo = flag;
|
|
});
|
|
DECL_OPTION("-protect-ids", OnOff, &m_protectIds);
|
|
DECL_OPTION("-protect-key", Set, &m_protectKey);
|
|
DECL_OPTION("-protect-lib", CbVal, [this](const char* valp) {
|
|
m_libCreate = valp;
|
|
m_protectIds = true;
|
|
});
|
|
DECL_OPTION("-public", OnOff, &m_public);
|
|
DECL_OPTION("-public-depth", Set, &m_publicDepth);
|
|
DECL_OPTION("-public-flat-rw", CbOnOff, [this](bool flag) {
|
|
m_publicFlatRW = flag;
|
|
v3Global.dpi(true);
|
|
});
|
|
DECL_OPTION("-public-params", CbOnOff, [this](bool flag) {
|
|
m_public_params = flag;
|
|
v3Global.dpi(true);
|
|
});
|
|
DECL_OPTION("-quiet-exit", OnOff, &m_quietExit);
|
|
|
|
DECL_OPTION("-relative-includes", OnOff, &m_relativeIncludes);
|
|
DECL_OPTION("-reloop-limit", CbVal, [this, fl](const char* valp) {
|
|
m_reloopLimit = std::atoi(valp);
|
|
if (m_reloopLimit < 2) { fl->v3error("--reloop-limit must be >= 2: " << valp); }
|
|
});
|
|
DECL_OPTION("-report-unoptflat", OnOff, &m_reportUnoptflat);
|
|
DECL_OPTION("-rr", CbCall, []() {}); // Processed only in bin/verilator shell
|
|
|
|
DECL_OPTION("-savable", OnOff, &m_savable);
|
|
DECL_OPTION("-sc", CbCall, [this]() {
|
|
m_outFormatOk = true;
|
|
m_systemC = true;
|
|
});
|
|
DECL_OPTION("-skip-identical", OnOff, &m_skipIdentical);
|
|
DECL_OPTION("-stats", OnOff, &m_stats);
|
|
DECL_OPTION("-stats-vars", CbOnOff, [this](bool flag) {
|
|
m_statsVars = flag;
|
|
m_stats |= flag;
|
|
});
|
|
DECL_OPTION("-std", OnOff, &m_std);
|
|
DECL_OPTION("-structs-packed", OnOff, &m_structsPacked);
|
|
DECL_OPTION("-sv", CbCall, [this]() { m_defaultLanguage = V3LangCode::L1800_2017; });
|
|
|
|
DECL_OPTION("-threads-coarsen", OnOff, &m_threadsCoarsen).undocumented(); // Debug
|
|
DECL_OPTION("-no-threads", CbCall, [this, fl]() {
|
|
fl->v3warn(DEPRECATED, "Option --no-threads is deprecated, use '--threads 1' instead");
|
|
m_threads = 1;
|
|
});
|
|
DECL_OPTION("-threads", CbVal, [this, fl](const char* valp) {
|
|
m_threads = std::atoi(valp);
|
|
if (m_threads < 0) fl->v3fatal("--threads must be >= 0: " << valp);
|
|
if (m_threads == 0) {
|
|
fl->v3warn(DEPRECATED, "Option --threads 0 is deprecated, use '--threads 1' instead");
|
|
m_threads = 1;
|
|
}
|
|
});
|
|
DECL_OPTION("-threads-dpi", CbVal, [this, fl](const char* valp) {
|
|
if (!std::strcmp(valp, "all")) {
|
|
m_threadsDpiPure = true;
|
|
m_threadsDpiUnpure = true;
|
|
} else if (!std::strcmp(valp, "none")) {
|
|
m_threadsDpiPure = false;
|
|
m_threadsDpiUnpure = false;
|
|
} else if (!std::strcmp(valp, "pure")) {
|
|
m_threadsDpiPure = true;
|
|
m_threadsDpiUnpure = false;
|
|
} else {
|
|
fl->v3fatal("Unknown setting for --threads-dpi: '"
|
|
<< valp << "'\n"
|
|
<< fl->warnMore() << "... Suggest 'all', 'none', or 'pure'");
|
|
}
|
|
});
|
|
DECL_OPTION("-threads-max-mtasks", CbVal, [this, fl](const char* valp) {
|
|
m_threadsMaxMTasks = std::atoi(valp);
|
|
if (m_threadsMaxMTasks < 1) fl->v3fatal("--threads-max-mtasks must be >= 1: " << valp);
|
|
});
|
|
DECL_OPTION("-timescale", CbVal, [this, fl](const char* valp) {
|
|
VTimescale unit;
|
|
VTimescale prec;
|
|
VTimescale::parseSlashed(fl, valp, unit /*ref*/, prec /*ref*/);
|
|
if (!unit.isNone() && timeOverrideUnit().isNone()) m_timeDefaultUnit = unit;
|
|
if (!prec.isNone() && timeOverridePrec().isNone()) m_timeDefaultPrec = prec;
|
|
});
|
|
DECL_OPTION("-timescale-override", CbVal, [this, fl](const char* valp) {
|
|
VTimescale unit;
|
|
VTimescale prec;
|
|
VTimescale::parseSlashed(fl, valp, unit /*ref*/, prec /*ref*/, true);
|
|
if (!unit.isNone()) {
|
|
m_timeDefaultUnit = unit;
|
|
m_timeOverrideUnit = unit;
|
|
}
|
|
if (!prec.isNone()) {
|
|
m_timeDefaultPrec = prec;
|
|
m_timeOverridePrec = prec;
|
|
}
|
|
});
|
|
DECL_OPTION("-timing", OnOff, &m_timing);
|
|
DECL_OPTION("-top-module", Set, &m_topModule);
|
|
DECL_OPTION("-top", Set, &m_topModule);
|
|
DECL_OPTION("-trace", OnOff, &m_trace);
|
|
DECL_OPTION("-trace-coverage", OnOff, &m_traceCoverage);
|
|
DECL_OPTION("-trace-depth", Set, &m_traceDepth);
|
|
DECL_OPTION("-trace-fst", CbCall, [this]() {
|
|
m_trace = true;
|
|
m_traceFormat = TraceFormat::FST;
|
|
addLdLibs("-lz");
|
|
});
|
|
DECL_OPTION("-trace-fst-thread", CbCall, [this, fl]() {
|
|
m_trace = true;
|
|
m_traceFormat = TraceFormat::FST;
|
|
addLdLibs("-lz");
|
|
fl->v3warn(DEPRECATED, "Option --trace-fst-thread is deprecated. "
|
|
"Use --trace-fst with --trace-threads > 0.");
|
|
if (m_traceThreads == 0) m_traceThreads = 1;
|
|
});
|
|
DECL_OPTION("-trace-max-array", Set, &m_traceMaxArray);
|
|
DECL_OPTION("-trace-max-width", Set, &m_traceMaxWidth);
|
|
DECL_OPTION("-trace-params", OnOff, &m_traceParams);
|
|
DECL_OPTION("-trace-structs", OnOff, &m_traceStructs);
|
|
DECL_OPTION("-trace-threads", CbVal, [this, fl](const char* valp) {
|
|
m_trace = true;
|
|
m_traceThreads = std::atoi(valp);
|
|
if (m_traceThreads < 1) fl->v3fatal("--trace-threads must be >= 1: " << valp);
|
|
});
|
|
DECL_OPTION("-trace-underscore", OnOff, &m_traceUnderscore);
|
|
|
|
DECL_OPTION("-U", CbPartialMatch, &V3PreShell::undef);
|
|
DECL_OPTION("-underline-zero", OnOff, &m_underlineZero); // Deprecated
|
|
DECL_OPTION("-no-unlimited-stack", CbCall, []() {}); // Processed only in bin/verilator shell
|
|
DECL_OPTION("-unroll-count", Set, &m_unrollCount).undocumented(); // Optimization tweak
|
|
DECL_OPTION("-unroll-stmts", Set, &m_unrollStmts).undocumented(); // Optimization tweak
|
|
DECL_OPTION("-unused-regexp", Set, &m_unusedRegexp);
|
|
|
|
DECL_OPTION("-V", CbCall, [this]() {
|
|
showVersion(true);
|
|
std::exit(0);
|
|
});
|
|
DECL_OPTION("-v", CbVal, [this, &optdir](const char* valp) {
|
|
V3Options::addLibraryFile(parseFileArg(optdir, valp));
|
|
});
|
|
DECL_OPTION("-verilate-jobs", CbVal, [this, fl](const char* valp) {
|
|
int val = std::atoi(valp);
|
|
if (val < 0) {
|
|
fl->v3error("--verilate-jobs requires a non-negative integer, but '"
|
|
<< valp << "' was passed");
|
|
val = 1;
|
|
} else if (val == 0) {
|
|
val = std::thread::hardware_concurrency();
|
|
}
|
|
m_verilateJobs = val;
|
|
});
|
|
DECL_OPTION("-verilate", OnOff, &m_verilate);
|
|
DECL_OPTION("-version", CbCall, [this]() {
|
|
showVersion(false);
|
|
std::exit(0);
|
|
});
|
|
DECL_OPTION("-vpi", OnOff, &m_vpi);
|
|
|
|
DECL_OPTION("-Wpedantic", CbCall, [this]() {
|
|
m_pedantic = true;
|
|
V3Error::pretendError(V3ErrorCode::ASSIGNIN, false);
|
|
});
|
|
DECL_OPTION("-Wall", CbCall, []() {
|
|
FileLine::globalWarnLintOff(false);
|
|
FileLine::globalWarnStyleOff(false);
|
|
});
|
|
DECL_OPTION("-Werror-UNUSED", CbCall, []() {
|
|
V3Error::pretendError(V3ErrorCode::UNUSEDGENVAR, true);
|
|
V3Error::pretendError(V3ErrorCode::UNUSEDPARAM, true);
|
|
V3Error::pretendError(V3ErrorCode::UNUSEDSIGNAL, true);
|
|
});
|
|
DECL_OPTION("-Werror-", CbPartialMatch, [this, fl](const char* optp) {
|
|
const V3ErrorCode code{optp};
|
|
if (code == V3ErrorCode::EC_ERROR) {
|
|
if (!isFuture(optp)) fl->v3fatal("Unknown warning specified: -Werror-" << optp);
|
|
} else {
|
|
V3Error::pretendError(code, true);
|
|
}
|
|
});
|
|
DECL_OPTION("-Wfuture-", CbPartialMatch, [this](const char* optp) {
|
|
// Note it may not be a future option, but one that is currently implemented.
|
|
addFuture(optp);
|
|
});
|
|
DECL_OPTION("-Wno-", CbPartialMatch, [fl, &parser](const char* optp) {
|
|
if (!FileLine::globalWarnOff(optp, true)) {
|
|
const string fullopt = std::string{"-Wno-"} + optp;
|
|
fl->v3fatal("Unknown warning specified: " << fullopt
|
|
<< parser.getSuggestion(fullopt.c_str()));
|
|
}
|
|
});
|
|
for (int i = V3ErrorCode::EC_FIRST_WARN; i < V3ErrorCode::_ENUM_MAX; ++i) {
|
|
for (const string prefix : {"-Wno-", "-Wwarn-"})
|
|
parser.addSuggestionCandidate(prefix + V3ErrorCode{i}.ascii());
|
|
}
|
|
DECL_OPTION("-Wno-context", CbCall, [this]() { m_context = false; });
|
|
DECL_OPTION("-Wno-fatal", CbCall, []() { V3Error::warnFatal(false); });
|
|
DECL_OPTION("-Wno-lint", CbCall, []() {
|
|
FileLine::globalWarnLintOff(true);
|
|
FileLine::globalWarnStyleOff(true);
|
|
});
|
|
DECL_OPTION("-Wno-style", CbCall, []() { FileLine::globalWarnStyleOff(true); });
|
|
DECL_OPTION("-Wno-UNUSED", CbCall, []() { FileLine::globalWarnUnusedOff(true); });
|
|
DECL_OPTION("-Wno-WIDTH", CbCall, []() { FileLine::globalWarnOff(V3ErrorCode::WIDTH, true); });
|
|
DECL_OPTION("-Wwarn-", CbPartialMatch, [this, fl, &parser](const char* optp) {
|
|
const V3ErrorCode code{optp};
|
|
if (code == V3ErrorCode::EC_ERROR) {
|
|
if (!isFuture(optp)) {
|
|
const string fullopt = std::string{"-Wwarn-"} + optp;
|
|
fl->v3fatal("Unknown warning specified: "
|
|
<< fullopt << parser.getSuggestion(fullopt.c_str()));
|
|
}
|
|
} else {
|
|
FileLine::globalWarnOff(code, false);
|
|
V3Error::pretendError(code, false);
|
|
}
|
|
});
|
|
DECL_OPTION("-Wwarn-lint", CbCall, []() { FileLine::globalWarnLintOff(false); });
|
|
DECL_OPTION("-Wwarn-style", CbCall, []() { FileLine::globalWarnStyleOff(false); });
|
|
DECL_OPTION("-Wwarn-UNUSED", CbCall, []() {
|
|
FileLine::globalWarnUnusedOff(false);
|
|
V3Error::pretendError(V3ErrorCode::UNUSEDGENVAR, false);
|
|
V3Error::pretendError(V3ErrorCode::UNUSEDSIGNAL, false);
|
|
V3Error::pretendError(V3ErrorCode::UNUSEDPARAM, false);
|
|
});
|
|
DECL_OPTION("-Wwarn-WIDTH", CbCall, []() {
|
|
FileLine::globalWarnOff(V3ErrorCode::WIDTH, false);
|
|
V3Error::pretendError(V3ErrorCode::WIDTH, false);
|
|
});
|
|
DECL_OPTION("-waiver-output", Set, &m_waiverOutput);
|
|
|
|
DECL_OPTION("-x-assign", CbVal, [this, fl](const char* valp) {
|
|
if (!std::strcmp(valp, "0")) {
|
|
m_xAssign = "0";
|
|
} else if (!std::strcmp(valp, "1")) {
|
|
m_xAssign = "1";
|
|
} else if (!std::strcmp(valp, "fast")) {
|
|
m_xAssign = "fast";
|
|
} else if (!std::strcmp(valp, "unique")) {
|
|
m_xAssign = "unique";
|
|
} else {
|
|
fl->v3fatal("Unknown setting for --x-assign: '"
|
|
<< valp << "'\n"
|
|
<< fl->warnMore() << "... Suggest '0', '1', 'fast', or 'unique'");
|
|
}
|
|
});
|
|
DECL_OPTION("-x-initial", CbVal, [this, fl](const char* valp) {
|
|
if (!std::strcmp(valp, "0")) {
|
|
m_xInitial = "0";
|
|
} else if (!std::strcmp(valp, "fast")) {
|
|
m_xInitial = "fast";
|
|
} else if (!std::strcmp(valp, "unique")) {
|
|
m_xInitial = "unique";
|
|
} else {
|
|
fl->v3fatal("Unknown setting for --x-initial: '"
|
|
<< valp << "'\n"
|
|
<< fl->warnMore() << "... Suggest '0', 'fast', or 'unique'");
|
|
}
|
|
});
|
|
DECL_OPTION("-x-initial-edge", OnOff, &m_xInitialEdge);
|
|
DECL_OPTION("-xml-only", OnOff, &m_xmlOnly);
|
|
DECL_OPTION("-xml-output", CbVal, [this](const char* valp) {
|
|
m_xmlOutput = valp;
|
|
m_xmlOnly = true;
|
|
});
|
|
|
|
DECL_OPTION("-y", CbVal, [this, &optdir](const char* valp) {
|
|
addIncDirUser(parseFileArg(optdir, string(valp)));
|
|
});
|
|
parser.finalize();
|
|
|
|
for (int i = 0; i < argc;) {
|
|
UINFO(9, " Option: " << argv[i] << endl);
|
|
if (!std::strcmp(argv[i], "-j")
|
|
|| !std::strcmp(argv[i], "--j")) { // Allow gnu -- switches
|
|
++i;
|
|
int val = 0;
|
|
if (i < argc && std::isdigit(argv[i][0])) {
|
|
val = std::atoi(argv[i]); // Can't be negative due to isdigit above
|
|
if (val == 0) val = std::thread::hardware_concurrency();
|
|
++i;
|
|
}
|
|
if (m_buildJobs == -1) m_buildJobs = val;
|
|
if (m_verilateJobs == -1) m_verilateJobs = val;
|
|
} else if (argv[i][0] == '-' || argv[i][0] == '+') {
|
|
const char* argvNoDashp = (argv[i][1] == '-') ? (argv[i] + 2) : (argv[i] + 1);
|
|
if (const int consumed = parser.parse(i, argc, argv)) {
|
|
i += consumed;
|
|
} else if (isFuture0(argvNoDashp)) {
|
|
++i;
|
|
} else if (isFuture1(argvNoDashp)) {
|
|
i += 2;
|
|
} else {
|
|
fl->v3fatal("Invalid option: " << argv[i] << parser.getSuggestion(argv[i]));
|
|
++i; // LCOV_EXCL_LINE
|
|
}
|
|
} else {
|
|
// Filename
|
|
const string filename = parseFileArg(optdir, argv[i]);
|
|
if (suffixed(filename, ".cpp") //
|
|
|| suffixed(filename, ".cxx") //
|
|
|| suffixed(filename, ".cc") //
|
|
|| suffixed(filename, ".c") //
|
|
|| suffixed(filename, ".sp")) {
|
|
V3Options::addCppFile(filename);
|
|
} else if (suffixed(filename, ".a") //
|
|
|| suffixed(filename, ".o") //
|
|
|| suffixed(filename, ".so")) {
|
|
V3Options::addLdLibs(filename);
|
|
} else {
|
|
V3Options::addVFile(filename);
|
|
}
|
|
++i;
|
|
}
|
|
}
|
|
if (m_buildJobs == -1) m_buildJobs = 1;
|
|
if (m_verilateJobs == -1) m_verilateJobs = 1;
|
|
}
|
|
|
|
//======================================================================
|
|
|
|
void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) {
|
|
// Read the specified -f filename and process as arguments
|
|
UINFO(1, "Reading Options File " << filename << endl);
|
|
|
|
const std::unique_ptr<std::ifstream> ifp{V3File::new_ifstream(filename)};
|
|
if (ifp->fail()) {
|
|
fl->v3error("Cannot open -f command file: " + filename);
|
|
return;
|
|
}
|
|
|
|
string whole_file;
|
|
bool inCmt = false;
|
|
while (!ifp->eof()) {
|
|
const string line = V3Os::getline(*ifp);
|
|
// Strip simple comments
|
|
string oline;
|
|
// cppcheck-suppress StlMissingComparison
|
|
char lastch = ' ';
|
|
bool space_begin = true; // At beginning or leading spaces only
|
|
for (string::const_iterator pos = line.begin(); pos != line.end(); lastch = *pos++) {
|
|
if (inCmt) {
|
|
if (*pos == '*' && *(pos + 1) == '/') {
|
|
inCmt = false;
|
|
++pos;
|
|
}
|
|
} else if (*pos == '/' && *(pos + 1) == '/'
|
|
&& (pos == line.begin()
|
|
|| std::isspace(lastch))) { // But allow /file//path
|
|
break; // Ignore to EOL
|
|
} else if (*pos == '#' && space_begin) { // Only # at [spaced] begin of line
|
|
break; // Ignore to EOL
|
|
} else if (*pos == '/' && *(pos + 1) == '*') {
|
|
inCmt = true;
|
|
space_begin = false;
|
|
// cppcheck-suppress StlMissingComparison
|
|
++pos;
|
|
} else {
|
|
if (!std::isspace(*pos)) space_begin = false;
|
|
oline += *pos;
|
|
}
|
|
}
|
|
whole_file += oline + " ";
|
|
}
|
|
whole_file += "\n"; // So string match below is simplified
|
|
if (inCmt) fl->v3error("Unterminated /* comment inside -f file.");
|
|
|
|
fl = new FileLine{filename};
|
|
|
|
// Split into argument list and process
|
|
// Note we try to respect escaped char, double/simple quoted strings
|
|
// Other simulators don't respect a common syntax...
|
|
|
|
// Strip off arguments and parse into words
|
|
std::vector<string> args;
|
|
|
|
// Parse file using a state machine, taking into account quoted strings and escaped chars
|
|
enum state : uint8_t {
|
|
ST_IN_OPTION,
|
|
ST_ESCAPED_CHAR,
|
|
ST_IN_QUOTED_STR,
|
|
ST_IN_DOUBLE_QUOTED_STR
|
|
};
|
|
|
|
state st = ST_IN_OPTION;
|
|
state last_st = ST_IN_OPTION;
|
|
string arg;
|
|
for (string::size_type pos = 0; pos < whole_file.length(); ++pos) {
|
|
char curr_char = whole_file[pos];
|
|
switch (st) {
|
|
case ST_IN_OPTION: // Get all chars up to a white space or a "="
|
|
if (std::isspace(curr_char)) { // End of option
|
|
if (!arg.empty()) { // End of word
|
|
args.push_back(arg);
|
|
}
|
|
arg = "";
|
|
break;
|
|
}
|
|
if (curr_char == '\\') { // Escape char, we wait for next char
|
|
last_st = st; // Memorize current state
|
|
st = ST_ESCAPED_CHAR;
|
|
break;
|
|
}
|
|
if (curr_char == '\'') { // Find begin of quoted string
|
|
// Examine next char in order to decide between
|
|
// a string or a base specifier for integer literal
|
|
++pos;
|
|
if (pos < whole_file.length()) curr_char = whole_file[pos];
|
|
if (curr_char == '"') { // String
|
|
st = ST_IN_QUOTED_STR;
|
|
} else { // Base specifier
|
|
arg += '\'';
|
|
}
|
|
arg += curr_char;
|
|
break;
|
|
}
|
|
if (curr_char == '"') { // Find begin of double quoted string
|
|
// Doesn't insert the quote
|
|
st = ST_IN_DOUBLE_QUOTED_STR;
|
|
break;
|
|
}
|
|
arg += curr_char;
|
|
break;
|
|
case ST_IN_QUOTED_STR: // Just store all chars inside string
|
|
if (curr_char != '\'') {
|
|
arg += curr_char;
|
|
} else { // End of quoted string
|
|
st = ST_IN_OPTION;
|
|
}
|
|
break;
|
|
case ST_IN_DOUBLE_QUOTED_STR: // Take into account escaped chars
|
|
if (curr_char != '"') {
|
|
if (curr_char == '\\') {
|
|
last_st = st;
|
|
st = ST_ESCAPED_CHAR;
|
|
} else {
|
|
arg += curr_char;
|
|
}
|
|
} else { // End of double quoted string
|
|
st = ST_IN_OPTION;
|
|
}
|
|
break;
|
|
case ST_ESCAPED_CHAR: // Just add the escaped char
|
|
arg += curr_char;
|
|
st = last_st;
|
|
break;
|
|
}
|
|
}
|
|
if (!arg.empty()) { // Add last word
|
|
args.push_back(arg);
|
|
}
|
|
|
|
// Path
|
|
const string optdir = (rel ? V3Os::filenameDir(filename) : ".");
|
|
|
|
// Convert to argv style arg list and parse them
|
|
std::vector<char*> argv;
|
|
argv.reserve(args.size() + 1);
|
|
for (const string& i : args) argv.push_back(const_cast<char*>(i.c_str()));
|
|
argv.push_back(nullptr); // argv is nullptr-terminated
|
|
parseOptsList(fl, optdir, static_cast<int>(argv.size() - 1), argv.data());
|
|
}
|
|
|
|
//======================================================================
|
|
|
|
string V3Options::parseFileArg(const string& optdir, const string& relfilename) {
|
|
string filename = V3Os::filenameSubstitute(relfilename);
|
|
if (optdir != "." && V3Os::filenameIsRel(filename)) filename = optdir + "/" + filename;
|
|
return filename;
|
|
}
|
|
|
|
//======================================================================
|
|
|
|
void V3Options::showVersion(bool verbose) {
|
|
cout << version();
|
|
cout << endl;
|
|
if (!verbose) return;
|
|
|
|
cout << endl;
|
|
cout << "Copyright 2003-2023 by Wilson Snyder. Verilator is free software; you can\n";
|
|
cout << "redistribute it and/or modify the Verilator internals under the terms of\n";
|
|
cout << "either the GNU Lesser General Public License Version 3 or the Perl Artistic\n";
|
|
cout << "License Version 2.0.\n";
|
|
|
|
cout << endl;
|
|
cout << "See https://verilator.org for documentation\n";
|
|
|
|
cout << endl;
|
|
cout << "Summary of configuration:\n";
|
|
cout << " Compiled in defaults if not in environment:\n";
|
|
cout << " SYSTEMC = " << DEFENV_SYSTEMC << endl;
|
|
cout << " SYSTEMC_ARCH = " << DEFENV_SYSTEMC_ARCH << endl;
|
|
cout << " SYSTEMC_INCLUDE = " << DEFENV_SYSTEMC_INCLUDE << endl;
|
|
cout << " SYSTEMC_LIBDIR = " << DEFENV_SYSTEMC_LIBDIR << endl;
|
|
cout << " VERILATOR_ROOT = " << DEFENV_VERILATOR_ROOT << endl;
|
|
cout << " SystemC system-wide = " << cvtToStr(systemCSystemWide()) << endl;
|
|
|
|
// If update below, also update V3Options::getenvBuiltins()
|
|
cout << endl;
|
|
cout << "Environment:\n";
|
|
cout << " MAKE = " << V3Os::getenvStr("MAKE", "") << endl;
|
|
cout << " PERL = " << V3Os::getenvStr("PERL", "") << endl;
|
|
cout << " SYSTEMC = " << V3Os::getenvStr("SYSTEMC", "") << endl;
|
|
cout << " SYSTEMC_ARCH = " << V3Os::getenvStr("SYSTEMC_ARCH", "") << endl;
|
|
cout << " SYSTEMC_INCLUDE = " << V3Os::getenvStr("SYSTEMC_INCLUDE", "") << endl;
|
|
cout << " SYSTEMC_LIBDIR = " << V3Os::getenvStr("SYSTEMC_LIBDIR", "") << endl;
|
|
// wrapper uses VERILATOR_BIN
|
|
cout << " VERILATOR_BIN = " << V3Os::getenvStr("VERILATOR_BIN", "") << endl;
|
|
cout << " VERILATOR_ROOT = " << V3Os::getenvStr("VERILATOR_ROOT", "") << endl;
|
|
|
|
// If update below, also update V3Options::getSupported()
|
|
cout << endl;
|
|
cout << "Supported features (compiled-in or forced by environment):\n";
|
|
cout << " COROUTINES = " << getSupported("COROUTINES") << endl;
|
|
cout << " SYSTEMC = " << getSupported("SYSTEMC") << endl;
|
|
}
|
|
|
|
//======================================================================
|
|
|
|
V3Options::V3Options() {
|
|
m_impp = new V3OptionsImp;
|
|
|
|
m_traceFormat = TraceFormat::VCD;
|
|
|
|
m_makeDir = "obj_dir";
|
|
m_unusedRegexp = "*unused*";
|
|
m_xAssign = "fast";
|
|
|
|
m_defaultLanguage = V3LangCode::mostRecent();
|
|
|
|
VName::maxLength(128); // Linux filename limits 256; leave half for prefix
|
|
|
|
optimize(1);
|
|
// Default +libext+
|
|
addLibExtV(""); // So include "filename.v" will find the same file
|
|
addLibExtV(".v");
|
|
addLibExtV(".sv");
|
|
// Default -I
|
|
addIncDirFallback("."); // Looks better than {long_cwd_path}/...
|
|
}
|
|
|
|
V3Options::~V3Options() { VL_DO_CLEAR(delete m_impp, m_impp = nullptr); }
|
|
|
|
void V3Options::setDebugMode(int level) {
|
|
V3Error::debugDefault(level);
|
|
if (!m_dumpLevel.count("tree")) m_dumpLevel["tree"] = 3; // Don't override if already set.
|
|
m_stats = true;
|
|
m_debugCheck = true;
|
|
cout << "Starting " << version() << endl;
|
|
}
|
|
|
|
unsigned V3Options::debugLevel(const string& tag) const VL_MT_SAFE {
|
|
const auto iter = m_debugLevel.find(tag);
|
|
return iter != m_debugLevel.end() ? iter->second : V3Error::debugDefault();
|
|
}
|
|
|
|
unsigned V3Options::debugSrcLevel(const string& srcfile_path) const VL_MT_SAFE {
|
|
// For simplicity, calling functions can just use __FILE__ for srcfile.
|
|
// That means we need to strip the filenames: ../Foo.cpp -> Foo
|
|
return debugLevel(V3Os::filenameNonDirExt(srcfile_path));
|
|
}
|
|
|
|
unsigned V3Options::dumpLevel(const string& tag) const VL_MT_SAFE {
|
|
const auto iter = m_dumpLevel.find(tag);
|
|
return iter != m_dumpLevel.end() ? iter->second : 0;
|
|
}
|
|
|
|
unsigned V3Options::dumpSrcLevel(const string& srcfile_path) const {
|
|
// For simplicity, calling functions can just use __FILE__ for srcfile.
|
|
// That means we need to strip the filenames: ../Foo.cpp -> Foo
|
|
return dumpLevel(V3Os::filenameNonDirExt(srcfile_path));
|
|
}
|
|
|
|
bool V3Options::dumpTreeAddrids() const VL_MT_SAFE {
|
|
static int level = -1;
|
|
if (VL_UNLIKELY(level < 0)) {
|
|
const unsigned value = dumpLevel("tree-addrids");
|
|
if (!available()) return value > 0;
|
|
level = static_cast<unsigned>(value);
|
|
}
|
|
return level > 0;
|
|
}
|
|
|
|
void V3Options::optimize(int level) {
|
|
// Set all optimizations to on/off
|
|
const bool flag = level > 0;
|
|
m_fAcycSimp = flag;
|
|
m_fAssemble = flag;
|
|
m_fCase = flag;
|
|
m_fCombine = flag;
|
|
m_fConst = flag;
|
|
m_fConstBitOpTree = flag;
|
|
m_fDedupe = flag;
|
|
m_fDfgPreInline = flag;
|
|
m_fDfgPostInline = flag;
|
|
m_fExpand = flag;
|
|
m_fGate = flag;
|
|
m_fInline = flag;
|
|
m_fLife = flag;
|
|
m_fLifePost = flag;
|
|
m_fLocalize = flag;
|
|
m_fMergeCond = flag;
|
|
m_fReloop = flag;
|
|
m_fReorder = flag;
|
|
m_fSplit = flag;
|
|
m_fSubst = flag;
|
|
m_fSubstConst = flag;
|
|
m_fTable = flag;
|
|
// And set specific optimization levels
|
|
if (level >= 3) {
|
|
m_inlineMult = -1; // Maximum inlining
|
|
}
|
|
}
|