Improve reusability of --dump-inputs output (#6812)

This commit is contained in:
Geza Lore 2025-12-16 11:08:19 +00:00 committed by GitHub
parent 25f72e4305
commit 47a4f7fb9b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 685 additions and 361 deletions

View File

@ -285,6 +285,15 @@ or "`ifdef`"'s may break other tools.
(if appropriate :vlopt:`--coverage` flags are passed) after being
disabled earlier with :option:`/*verilator&32;coverage_off*/`.
.. option:: /*verilator&32;fargs <arguments>*/
For Verilator developers only. When a source file containing :option:`fargs`
metacomments is passed to Verilator on the command line via the :option:`-f`
or :option:`-F` option, the provided arguments will be added as if specified
on the command line. The source file is not preprocessed before parsing for
arguments, and all :option:`fargs` comment will take effect, even if placed
undef an inactive :option:`\`ifdef`.
.. option:: /*verilator&32;forceable*/
Specifies that the signal (net or variable) should be made forceable from

View File

@ -1769,10 +1769,20 @@ placed into the obj_dir, .vpp, .tree and .dot files.
Verilator creates a *{mod_prefix}*\ __inputs.vpp file containing all the
files that were read, filtered by preprocessing. This file can be fed back
into Verilator, replacing on the command line all of the previous input
files, to enable simplification of test cases.
files, to enable simplification of test cases. This file also contains most
command line arguments Verilator was invoked as `// verilator fargs``
metacomments, with and can be parsed by ``-f`. So to reproduce the run that
created the file, run:
::
verilator -f <prefix>__inputs.vpp <prefix>__inputs.vpp
The first `-f <prefix>__inputs.vpp` reads the command line arguments from
the file. The second `<prefix>__inputsl.vpp` adds the file as a normal
source file.
Verilator also creates .vpp files for each individual file passed on the
command line.
command line. These do not contain the command line argument metacomments.
.dot Output

View File

@ -39,14 +39,23 @@ struct V3OptionParser::Impl final {
// Base class of actual action classes
template <en N_Mode, bool N_Allow_Partial_Match = false>
class ActionBase VL_NOT_FINAL : public ActionIfs {
bool m_notForRerun = false; // This option is not needed for rerun with --dump-inputs
bool m_undocumented = false; // This option is not documented
public:
bool isValueNeeded() const override final { return N_Mode == en::VALUE; }
bool isFOnOffAllowed() const override final { return N_Mode == en::FONOFF; }
bool isNotForRerun() const override final { return m_notForRerun; }
bool isOnOffAllowed() const override final { return N_Mode == en::ONOFF; }
bool isPartialMatchAllowed() const override final { return N_Allow_Partial_Match; }
bool isUndocumented() const override { return m_undocumented; }
void undocumented() override { m_undocumented = true; }
ActionIfs& undocumented() override {
m_undocumented = true;
return *this;
}
ActionIfs& notForRerun() override {
m_notForRerun = true;
return *this;
}
};
// Actual action classes
@ -195,20 +204,20 @@ bool V3OptionParser::hasPrefixNo(const char* strp) {
return VString::startsWith(strp, "-no");
}
int V3OptionParser::parse(int idx, int argc, char* argv[]) {
std::pair<int, bool> V3OptionParser::parse(int idx, int argc, char* argv[]) {
UASSERT(m_pimpl->m_isFinalized, "finalize() must be called before parse()");
const char* optp = argv[idx];
if (optp[0] == '-' && optp[1] == '-') ++optp;
ActionIfs* const actp = find(optp);
if (!actp) return 0;
if (!actp) return {0, false};
if (!actp->isValueNeeded()) {
actp->exec(optp, nullptr);
return 1;
return {1, !actp->isNotForRerun()};
} else if (idx + 1 < argc) {
actp->exec(optp, argv[idx + 1]);
return 2;
return {2, !actp->isNotForRerun()};
}
return 0;
return {0, false};
}
string V3OptionParser::getSuggestion(const char* str) const {

View File

@ -74,8 +74,9 @@ private:
public:
// METHODS
// Returns how many args are consumed. 0 means not match
int parse(int idx, int argc, char* argv[]) VL_MT_DISABLED;
// Returns how many args are consumed. 0 means not match.
// Also bool, true iff option is needed for rerun with --dump-inputs
std::pair<int, bool> parse(int idx, int argc, char* argv[]) VL_MT_DISABLED;
// Find the most similar option
string getSuggestion(const char* str) const VL_MT_DISABLED;
void addSuggestionCandidate(const string& s) VL_MT_DISABLED;
@ -93,13 +94,16 @@ public:
virtual ~ActionIfs() = default;
virtual bool isValueNeeded() const = 0; // Need val of "-opt val"
virtual bool isFOnOffAllowed() const = 0; // true if "-fno-opt" is allowed
virtual bool isNotForRerun() const = 0; // Will not be dumped with --dump-inputs
virtual bool isOnOffAllowed() const = 0; // true if "-no-opt" is allowed
virtual bool isPartialMatchAllowed() const = 0; // true if "-Wno-" matches "-Wno-fatal"
virtual bool isUndocumented() const = 0; // Will not be suggested in typo
// Set a value or run callback
virtual void exec(const char* optp, const char* valp) = 0;
// Mark this option undocumented. (Exclude this option from suggestion list).
virtual void undocumented() = 0;
virtual ActionIfs& undocumented() = 0;
// Mark this as not needed for rerunning with preprocessed sources
virtual ActionIfs& notForRerun() = 0;
};
// A helper class to register options

View File

@ -68,7 +68,8 @@ public:
// STATE
std::list<string> m_lineArgs; // List of command line argument encountered
std::list<string> m_allArgs; // List of every argument encountered
// List of arguments encounterd, and a bool in needed for rerunning --dump-inputs
std::list<std::pair<std::list<std::string>, bool>> m_allArgs;
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)
@ -398,13 +399,31 @@ void V3Options::addForceInc(const string& filename) { m_forceIncs.push_back(file
void V3Options::addLineArg(const string& arg) { m_impp->m_lineArgs.push_back(arg); }
void V3Options::addArg(const string& arg) { m_impp->m_allArgs.push_back(arg); }
void V3Options::addArg(char** argv, size_t count, bool isForRerun) {
m_impp->m_allArgs.emplace_back();
std::pair<std::list<std::string>, bool>& pair = m_impp->m_allArgs.back();
pair.second = isForRerun;
for (size_t n = 0; n < count; ++n) pair.first.emplace_back(argv[n]);
}
void V3Options::addArg(const std::string& arg, bool isForRerun) {
m_impp->m_allArgs.emplace_back();
std::pair<std::list<std::string>, bool>& pair = m_impp->m_allArgs.back();
pair.second = isForRerun;
pair.first.emplace_back(arg);
}
const std::list<std::pair<std::list<std::string>, bool>>& V3Options::allArgs() const {
return m_impp->m_allArgs;
}
string V3Options::allArgsString() const VL_MT_SAFE {
string result;
for (const string& i : m_impp->m_allArgs) {
if (result != "") result += " ";
result += i;
for (const auto& pair : m_impp->m_allArgs) {
for (const string& arg : pair.first) {
if (!result.empty()) result += " ";
result += arg;
}
}
return result;
}
@ -1165,9 +1184,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
// 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};
@ -1209,8 +1225,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
// (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) VL_MT_DISABLED { addDefine(optp, true); });
DECL_OPTION("+define+", CbPartialMatch, [this](const char* optp) VL_MT_DISABLED {
addDefine(optp, true);
}).notForRerun();
DECL_OPTION("+incdir+", CbPartialMatch, [this, &optdir](const char* optp) {
string dirs = optp;
string::size_type pos;
@ -1219,7 +1236,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
dirs = dirs.substr(pos + 1);
}
addIncDirUser(parseFileArg(optdir, dirs));
});
}).notForRerun();
DECL_OPTION("+libext+", CbPartialMatch, [this](const char* optp) {
string exts = optp;
string::size_type pos;
@ -1228,31 +1245,42 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
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); });
DECL_OPTION("+1800-2023ext+", CbPartialMatch,
[this](const char* optp) { addLangExt(optp, V3LangCode::L1800_2023); });
}).notForRerun();
DECL_OPTION("+librescan", CbCall, []() {}).notForRerun(); // NOP
DECL_OPTION("+notimingchecks", CbCall, []() {}).notForRerun(); // NOP
DECL_OPTION("+systemverilogext+", CbPartialMatch, [this](const char* optp) {
addLangExt(optp, V3LangCode::L1800_2017);
}).notForRerun();
DECL_OPTION("+verilog1995ext+", CbPartialMatch, [this](const char* optp) {
addLangExt(optp, V3LangCode::L1364_1995);
}).notForRerun();
DECL_OPTION("+verilog2001ext+", CbPartialMatch, [this](const char* optp) {
addLangExt(optp, V3LangCode::L1364_2001);
}).notForRerun();
DECL_OPTION("+1364-1995ext+", CbPartialMatch, [this](const char* optp) {
addLangExt(optp, V3LangCode::L1364_1995);
}).notForRerun();
DECL_OPTION("+1364-2001ext+", CbPartialMatch, [this](const char* optp) {
addLangExt(optp, V3LangCode::L1364_2001);
}).notForRerun();
DECL_OPTION("+1364-2005ext+", CbPartialMatch, [this](const char* optp) {
addLangExt(optp, V3LangCode::L1364_2005);
}).notForRerun();
DECL_OPTION("+1800-2005ext+", CbPartialMatch, [this](const char* optp) {
addLangExt(optp, V3LangCode::L1800_2005);
}).notForRerun();
DECL_OPTION("+1800-2009ext+", CbPartialMatch, [this](const char* optp) {
addLangExt(optp, V3LangCode::L1800_2009);
}).notForRerun();
DECL_OPTION("+1800-2012ext+", CbPartialMatch, [this](const char* optp) {
addLangExt(optp, V3LangCode::L1800_2012);
}).notForRerun();
DECL_OPTION("+1800-2017ext+", CbPartialMatch, [this](const char* optp) {
addLangExt(optp, V3LangCode::L1800_2017);
}).notForRerun();
DECL_OPTION("+1800-2023ext+", CbPartialMatch, [this](const char* optp) {
addLangExt(optp, V3LangCode::L1800_2023);
}).notForRerun();
// Minus options
DECL_OPTION("-aslr", CbOnOff, [](bool) {}); // Processed only in bin/verilator shell
@ -1333,10 +1361,11 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-coverage-underscore", OnOff, &m_coverageUnderscore);
DECL_OPTION("-coverage-user", OnOff, &m_coverageUser);
DECL_OPTION("-D", CbPartialMatch,
[this](const char* valp) VL_MT_DISABLED { addDefine(valp, false); });
DECL_OPTION("-debug", CbCall, [this]() { setDebugMode(3); });
DECL_OPTION("-debugi", CbVal, [this](int v) { setDebugMode(v); });
DECL_OPTION("-D", CbPartialMatch, [this](const char* valp) VL_MT_DISABLED {
addDefine(valp, false);
}).notForRerun();
DECL_OPTION("-debug", CbCall, [this]() { setDebugMode(3); }).notForRerun();
DECL_OPTION("-debugi", CbVal, [this](int v) { setDebugMode(v); }).notForRerun();
DECL_OPTION("-debugi-", CbPartialMatchVal, [this](const char* optp, const char* valp) {
m_debugLevel[optp] = std::atoi(valp);
});
@ -1370,11 +1399,15 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
m_diagnosticsSarif = true;
});
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("-dump-", CbPartialMatch, [this](const char* optp) {
m_dumpLevel[optp] = 3;
}).notForRerun();
DECL_OPTION("-no-dump-", CbPartialMatch, [this](const char* optp) {
m_dumpLevel[optp] = 0;
}).notForRerun();
DECL_OPTION("-dumpi-", CbPartialMatchVal, [this](const char* optp, const char* valp) {
m_dumpLevel[optp] = std::atoi(valp);
});
}).notForRerun();
DECL_OPTION("-E", CbOnOff, [this](bool flag) {
if (flag) {
@ -1391,12 +1424,12 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-F", CbVal, [this, fl, &optdir](const char* valp) VL_MT_DISABLED {
parseOptsFile(fl, parseFileArg(optdir, valp), true);
});
}).notForRerun();
DECL_OPTION("-FI", CbVal,
[this, &optdir](const char* valp) { addForceInc(parseFileArg(optdir, valp)); });
DECL_OPTION("-f", CbVal, [this, fl, &optdir](const char* valp) VL_MT_DISABLED {
parseOptsFile(fl, parseFileArg(optdir, valp), false);
});
}).notForRerun();
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); });
@ -1485,8 +1518,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-hierarchical-params-file", CbVal,
[this](const char* optp) { m_hierParamsFile.push_back({optp, work()}); });
DECL_OPTION("-I", CbPartialMatch,
[this, &optdir](const char* optp) { addIncDirUser(parseFileArg(optdir, optp)); });
DECL_OPTION("-I", CbPartialMatch, [this, &optdir](const char* optp) {
addIncDirUser(parseFileArg(optdir, optp));
}).notForRerun();
DECL_OPTION("-if-depth", Set, &m_ifDepth);
DECL_OPTION("-ignc", OnOff, &m_ignc).undocumented();
DECL_OPTION("-inline-mult", Set, &m_inlineMult);
@ -1523,8 +1557,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
fl->v3error("Unknown language specified: " << valp << spell.bestCandidateMsg(valp));
}
};
DECL_OPTION("-default-language", CbVal, setLang);
DECL_OPTION("-language", CbVal, setLang);
DECL_OPTION("-default-language", CbVal, setLang).notForRerun();
DECL_OPTION("-language", CbVal, setLang).notForRerun();
DECL_OPTION("-lib-create", CbVal, [this, fl](const char* valp) {
validateIdentifier(fl, valp, "--lib-create");
m_libCreate = valp;
@ -1538,7 +1572,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-Mdir", CbVal, [this](const char* valp) {
m_makeDir = valp;
addIncDirFallback(m_makeDir); // Need to find generated files there too
});
}).notForRerun();
DECL_OPTION("-main", OnOff, &m_main);
DECL_OPTION("-main-top-name", Set, &m_mainTopName);
DECL_OPTION("-make", CbVal, [this, fl](const char* valp) {
@ -1943,39 +1977,58 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-y", CbVal, [this, &optdir](const char* valp) {
addIncDirUser(parseFileArg(optdir, string{valp}));
});
}).notForRerun();
parser.finalize();
const std::string cwd = V3Os::filenameRealPath(".");
for (int i = 0; i < argc;) {
UINFO(9, " Option: " << argv[i]);
if (!std::strcmp(argv[i], "-j")
|| !std::strcmp(argv[i], "--j")) { // Allow gnu -- switches
++i;
// Option
if (argv[i][0] == '-' || argv[i][0] == '+') {
const std::string argName = argv[i] + (argv[i][0] == argv[i][1] ? 2 : 1);
int consumed = 0;
bool isForRerun = true;
// Special case for -j which has an optional argument
if (argv[i][0] == '-' && argName == "j") {
consumed = 1;
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 (i + 1 < argc && std::isdigit(argv[i + 1][0])) {
val = std::atoi(argv[i + 1]); // Can't be negative due to isdigit above
if (val == 0) val = VlOs::getProcessDefaultParallelism();
++i;
consumed = 2;
}
if (m_buildJobs == -1) m_buildJobs = val;
if (m_verilateJobs == -1) m_verilateJobs = val;
if (m_outputGroups == -1) m_outputGroups = 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 {
const auto pair = parser.parse(i, argc, argv);
consumed = pair.first;
isForRerun = pair.second;
if (consumed) {
// Already processed
} else if (isFuture0(argName)) {
consumed = 1;
} else if (isFuture1(argName)) {
consumed = 2;
} else {
fl->v3fatal("Invalid option: " << argv[i] << parser.getSuggestion(argv[i]));
++i; // LCOV_EXCL_LINE
consumed = 1;
}
} else {
}
UASSERT(consumed, "Failed to consume argument: " << argv[i]);
// Record all arguments except for -f and -F (their contents will be recorded instead)
if (argName != "f" && argName != "F") addArg(argv + i, consumed, isForRerun);
i += consumed;
continue;
}
// Filename
const string filename = parseFileArg(optdir, argv[i]);
const std::string filename = parseFileArg(optdir, argv[i]);
bool isForRerun = true;
if (suffixed(filename, ".cpp") //
|| suffixed(filename, ".cxx") //
|| suffixed(filename, ".cc") //
@ -1988,12 +2041,14 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
V3Options::addLdLibs(filename);
} else if (suffixed(filename, ".vlt")) {
V3Options::addVltFile(filename, work());
isForRerun = false;
} else {
V3Options::addVFile(filename, work());
isForRerun = false;
}
addArg(filename, isForRerun);
++i;
}
}
if (m_debugOptions) {
parser.dumpOptions();
@ -2013,38 +2068,73 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) VL
return;
}
string whole_file;
// If we find any '// verilator fargs' line in the file, we will only use these,
// otherwise we will use the file contents. This enables rerunning the output
// produced by --dump-inptus with:
// verilator -f <prefix>__inputs.vpp <prefix>__inputs.vpp
bool has_fargs = false;
std::string whole_file;
// Parses a '// verilator fargs' or '/* verilator fargs */' comment
const auto parse_fargs_cmt = [&](const char* p) -> void {
const bool blockCmt = p[1] == '*';
p += 2;
while (std::isspace(*p)) ++p;
if (VString::startsWith(p, "verilator")) {
p += std::strlen("verilator");
if (std::isspace(*p)) {
while (std::isspace(*p)) ++p;
if (VString::startsWith(p, "fargs")) {
p += std::strlen("fargs");
if (std::isspace(*p)) {
while (std::isspace(*p)) ++p;
if (!has_fargs) {
has_fargs = true;
whole_file.clear();
}
whole_file += p;
if (blockCmt) {
const std::string::size_type pos = whole_file.find("*/");
if (pos == std::string::npos) {
fl->v3fatal("/* verilator fargs */ must be on single line");
}
whole_file.resize(pos);
}
whole_file += ' ';
}
}
}
}
};
bool inCmt = false;
while (!ifp->eof()) {
const string line = V3Os::getline(*ifp);
const std::string line = V3Os::getline(*ifp);
// Strip simple comments
string oline;
// cppcheck-suppress StlMissingComparison
char lastch = ' ';
char lastch = ' '; // 'std::isspace(lastch)' is true at the start of the line
bool space_begin = true; // At beginning or leading spaces only
for (string::const_iterator pos = line.begin(); pos != line.end(); lastch = *pos++) {
for (const char* p = line.c_str(); *p; lastch = *p++) {
if (inCmt) {
if (*pos == '*' && *(pos + 1) == '/') {
if (p[0] == '*' && p[1] == '/') {
inCmt = false;
++pos;
++p;
}
} else if (*pos == '/' && *(pos + 1) == '/'
&& (pos == line.begin()
|| std::isspace(lastch))) { // But allow /file//path
} else if (p[0] == '/' && p[1] == '/' && std::isspace(lastch)) { // Allow /file//path
parse_fargs_cmt(p);
break; // Ignore to EOL
} else if (*pos == '#' && space_begin) { // Only # at [spaced] begin of line
} else if (p[0] == '#' && space_begin) { // Only # at [spaced] begin of line
break; // Ignore to EOL
} else if (*pos == '/' && *(pos + 1) == '*') {
} else if (p[0] == '/' && p[1] == '*') {
parse_fargs_cmt(p);
inCmt = true;
space_begin = false;
// cppcheck-suppress StlMissingComparison
++pos;
++p;
} else {
if (!std::isspace(*pos)) space_begin = false;
oline += *pos;
if (!std::isspace(*p)) space_begin = false;
if (!has_fargs) whole_file += *p;
}
}
whole_file += oline + " ";
if (!has_fargs) whole_file += " ";
}
whole_file += "\n"; // So string match below is simplified
if (inCmt) fl->v3error("Unterminated /* comment inside -f file.");

View File

@ -23,6 +23,7 @@
#include "V3Error.h"
#include "V3LangCode.h"
#include <list>
#include <map>
#include <set>
#include <string>
@ -427,8 +428,9 @@ private:
private:
// METHODS
void addArg(char** argv, size_t count, bool isForRerun);
void addArg(const std::string& arg, bool isForRerun);
void addLineArg(const string& arg);
void addArg(const string& arg);
void addDefine(const string& defline, bool allowPlus) VL_MT_DISABLED;
void addFuture(const string& flag);
void addFuture0(const string& flag);
@ -758,6 +760,7 @@ public:
static string version() VL_PURE;
static string argString(int argc, char** argv); ///< Return list of arguments as simple string
string allArgsString() const VL_MT_SAFE; ///< Return all passed arguments as simple string
const std::list<std::pair<std::list<std::string>, bool>>& allArgs() const;
// Return options for child hierarchical blocks when forTop==false, otherwise returns args for
// the top module.
string allArgsStringForHierBlock(bool forTop) const;

View File

@ -373,8 +373,29 @@ void V3ParseImp::dumpInputsFile() {
*ofp << "// Blank lines and `line directives have been removed\n";
*ofp << "//\n";
V3Stats::infoHeader(*ofp, "// ");
*ofp << '\n';
for (const auto& pair : v3Global.opt.allArgs()) {
if (!pair.second) continue;
*ofp << "// verilator fargs";
for (const std::string& arg : pair.first) {
// Apply some quoting, pretty basic, update as needed ...
std::string quoted;
bool quoteIt = false;
for (const char c : arg) {
if (c == '"' || c == '\\') {
quoteIt = true;
quoted += '\\';
} else if (std::isspace(c)) {
quoteIt = true;
}
quoted += c;
}
*ofp << " " << (quoteIt ? '"' + quoted + '"' : arg);
}
*ofp << "\n";
}
*ofp << "\n";
}
preprocDumps(*ofp, true);
ofp->close();
VL_DO_DANGLING(delete ofp, ofp);

View File

@ -528,7 +528,11 @@ void V3PreProcImp::comment(const string& text) {
//}
// else ignore the comment we don't recognize
} // else no assertions
} else if (vlcomment && !(v3Global.opt.publicOff() && VString::startsWith(cmd, "public"))) {
} else if (vlcomment
// Drop 'public' if disabled on the command line
&& !(v3Global.opt.publicOff() && VString::startsWith(cmd, "public"))
// Drop 'fargs', they are handled separately during option parsing
&& !VString::startsWith(cmd, "fargs")) {
if (VString::startsWith(cmd, "public_flat_rw")) {
// "/*verilator public_flat_rw @(foo) */" -> "/*verilator public_flat_rw*/ @(foo)"
string::size_type endOfCmd = std::strlen("public_flat_rw");
@ -1062,12 +1066,12 @@ int V3PreProcImp::getStateToken() {
string rtn;
rtn.assign(yyourtext(), yyourleng());
comment(rtn);
// Need to ensure "foo/**/bar" becomes two tokens
insertUnreadback(" ");
// Need to ensure "foo/**/bar" becomes two tokens, not one 'foobar'
if (m_lineCmt.empty()) insertUnreadback(" ");
} else if (m_lexp->m_keepComments) {
return tok;
} else {
// Need to ensure "foo/**/bar" becomes two tokens
// Need to ensure "foo/**/bar" becomes two tokens, not one 'foobar'
insertUnreadback(" ");
}
}

View File

@ -89,7 +89,7 @@ void VlcOptions::parseOptsList(int argc, char** argv) {
for (int i = 0; i < argc;) {
UINFO(9, " Option: " << argv[i]);
if (argv[i][0] == '-') {
if (const int consumed = parser.parse(i, argc, argv)) {
if (const int consumed = parser.parse(i, argc, argv).first) {
i += consumed;
} else {
v3fatal("Invalid option: " << argv[i] << parser.getSuggestion(argv[i]));

View File

@ -1,4 +1,4 @@
%Error: t/t_dpi_display_bad.v:17:69: /*verilator sformat*/ can only be applied to last argument of a function
%Error: t/t_dpi_display_bad.v:17:68: /*verilator sformat*/ can only be applied to last argument of a function
17 | (input string formatted /*verilator sformat*/, input string other_bad );
| ^~~~~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.

View File

@ -0,0 +1,98 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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
import vltest_bootstrap
test.scenarios('vlt')
def fixup(path):
lines = []
removeBeginKeywords = True
with open(path, "r", encoding="utf-8") as rfd:
for line in rfd:
# Remove "all arguments" line, which can differ
if line.startswith("// Arguments:"):
lines.append("// Arguments: <REDACTED>")
# Remove first `begin_keywords, which will be re-inserted on reruns
elif removeBeginKeywords and line.startswith("`begin_keywords"):
removeBeginKeywords = False
lines.append("// " + line)
else:
lines.append(line)
with open(path, "w", encoding="utf-8") as wfd:
for line in lines:
wfd.write(line)
obj_dir_1 = test.obj_dir + "/obj_dir_1"
test.mkdir_ok(obj_dir_1)
dump_1 = obj_dir_1 + "/Vprefix__inputs.vpp"
test.run(
logfile=obj_dir_1 + "/vlt_compile.log",
cmd=[
"perl",
os.environ["VERILATOR_ROOT"] + "/bin/verilator",
"-cc",
"-Mdir",
obj_dir_1,
"--prefix",
"Vprefix",
"--top-module",
"lpm_divide",
"--no-timing",
"-CFLAGS",
"'-O3 --foo'", # Argument with whitespace
"-CFLAGS",
"'-DDQUOTE=\"'", # Argument with quote
"--dump-inputs",
"t/t_altera_lpm.v",
"t/t_math_cond_huge.v"
])
fixup(dump_1)
obj_dir_2 = test.obj_dir + "/obj_dir_2"
test.mkdir_ok(obj_dir_2)
dump_2 = obj_dir_2 + "/Vprefix__inputs.vpp"
test.run(
logfile=obj_dir_2 + "/vlt_compile.log",
cmd=[
"perl",
os.environ["VERILATOR_ROOT"] + "/bin/verilator",
"-Mdir",
obj_dir_2,
"-f",
dump_1,
dump_1,
"--debug", # --debug also dumps the same way
"--debugi",
"1"
])
fixup(dump_2)
obj_dir_3 = test.obj_dir + "/obj_dir_3"
test.mkdir_ok(obj_dir_3)
dump_3 = obj_dir_3 + "/Vprefix__inputs.vpp"
test.run(logfile=obj_dir_3 + "/vlt_compile.log",
cmd=[
"perl",
os.environ["VERILATOR_ROOT"] + "/bin/verilator",
"-Mdir",
obj_dir_3,
"-f",
dump_2,
dump_2,
"--dump-inputs",
])
fixup(dump_3)
test.files_identical(dump_1, dump_2)
test.files_identical(dump_1, dump_3)
test.passes()

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["-f", test.top_filename], make_main=False)
test.file_grep(test.stats, r'Tracing, Traced signals\s+(\d+)', 2)
test.execute()
test.passes()

View File

@ -0,0 +1,25 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
// verilator fargs --binary -Wno-WIDTHEXPAND
/* verilator fargs -Wno-WIDTHTRUNC *//* verilator fargs --trace-vcd --stats */
module top;
bit clk = 0;
always #5 clk = ~clk;
reg [3:0] cyc = 0;
always @(posedge clk) begin
cyc <= cyc + 10'd1; // Intentional width warning
$display("%8t %1d", $time, cyc);
if (cyc == 3'd7) begin // Intentional width warning
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,2 @@
%Error: /* verilator fargs */ must be on single line
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["-f", test.top_filename],
fails=True,
expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,11 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
/* verilator fargs --cc
*/
module t;
endmodule

View File

@ -4,7 +4,7 @@
| ^
... For warning description see https://verilator.org/warn/ASCRANGE?v=latest
... Use "/* verilator lint_off ASCRANGE */" and lint_on around source to disable this message.
%Warning-ASCRANGE: t/t_metacmt_onoff.v:8:107: Ascending bit range vector: left < right of bit range: [0:3]
%Warning-ASCRANGE: t/t_metacmt_onoff.v:8:105: Ascending bit range vector: left < right of bit range: [0:3]
: ... note: In instance 't'
8 | reg [0:1] show1; /*verilator lint_off ASCRANGE*/ reg [0:2] ign2; /*verilator lint_on ASCRANGE*/ reg [0:3] show3;
| ^

View File

@ -26,7 +26,7 @@
},
"invocations": [
{
"commandLine": "--prefix Vt_sarif --lint-only -Mdir obj_vlt/t_sarif --debug-check --x-assign unique -Wno-fatal --diagnostics-sarif --no-skip-identical -f input.vc +define+TEST_OBJ_DIR=obj_vlt/t_sarif +define+TEST_DUMPFILE=obj_vlt/t_sarif/simx.vcd t/t_sarif.v +librescan +notimingchecks +libext+.v -y t +incdir+t",
"commandLine": "--prefix Vt_sarif --lint-only -Mdir obj_vlt/t_sarif --debug-check --x-assign unique -Wno-fatal --diagnostics-sarif --no-skip-identical +librescan +notimingchecks +libext+.v -y t +incdir+t +define+TEST_OBJ_DIR=obj_vlt/t_sarif +define+TEST_DUMPFILE=obj_vlt/t_sarif/simx.vcd t/t_sarif.v",
"executionSuccessful": true
}
],

View File

@ -72,7 +72,7 @@
: ... note: In instance 't.i_sub1'
62 | genvar cannot_split_genvar /*verilator split_var*/;
| ^~~~~~~~~~~~~~~~~~~
%Warning-SPLITVAR: t/t_split_var_1_bad.v:65:72: 'cannot_split' has split_var metacomment but will not be split because its bit range cannot be determined statically.
%Warning-SPLITVAR: t/t_split_var_1_bad.v:65:71: 'cannot_split' has split_var metacomment but will not be split because its bit range cannot be determined statically.
: ... note: In instance 't.i_sub1'
65 | static logic [8:0] rd_tmp /*verilator split_var*/ = cannot_split[addr];
| ^