Add `--work` library-selection option (#5891 partial).

This commit is contained in:
Wilson Snyder 2025-06-29 20:17:27 -04:00
parent c3d86626ee
commit 916a89761e
32 changed files with 350 additions and 139 deletions

View File

@ -26,6 +26,7 @@ Verilator 5.037 devel
* Add BADVLTPRAGMA on unknown Verilator pragmas (#5945). [Shou-Li Hsu]
* Add ternary operator into branch coverage (#5880). [Ryszard Rozak, Antmicro Ltd.]
* Add aggregate type error checks (#5570) (#5950). [Shou-Li Hsu]
* Add `--work` library-selection option (#5891 partial).
* Add `--filter-type` to verilator_coverage (#6030). [Ryszard Rozak, Antmicro Ltd.]
* Add `--hierarchical-threads` (#6037). [Bartłomiej Chmiel]
* Add `MODMISSING` error, in place of unnamed error (#6054). [Paul Swirhun]

View File

@ -513,6 +513,7 @@ detailed descriptions of these arguments.
-Wno-fatal Disable fatal exit on warnings
-Wno-lint Disable all lint warnings
-Wno-style Disable all style warnings
-work <libname> Set config library for following files
-Wpedantic Warn on compliance-test issues
-Wwarn-<message> Enable specified warning message
-Wwarn-lint Enable lint warning message

View File

@ -1596,8 +1596,8 @@ Summary:
When the input Verilog contains more than one top-level module,
it specifies the name of the module to become the top-level module,
and sets the default for :vlopt:`--prefix` if not explicitly specified.
This is not needed with standard designs with only one top. See also
:option:`MULTITOP` warning.
This is not needed with standard designs with only one top.
See :ref:`Finding and Binding Modules`.
.. option:: --trace
@ -1881,6 +1881,19 @@ Summary:
``-Wno-UNUSEDGENVAR`` ``-Wno-UNUSEDPARAM`` ``-Wno-UNUSEDSIGNAL``
``-Wno-VARHIDDEN``.
.. option:: -work <libname>
Use the specified Verilog config library name for all cells read after
this argument. May be specified multiple times, it will apply to cells
read between the given arguments. E.g. `-work liba a.v -work libb b.v`
will use `liba` for modules inside `a.v` or in cells resolved
hierarchically under those modules, and will use `libb` for modules
inside `b.v` or hierarchically under.
Defaults to "work" (IEEE 1800-2023 3.3.1).
See :ref:`Finding and Binding Modules`.
.. option:: -Wpedantic
Warn on any construct demanded by IEEE, and disable all Verilator

View File

@ -478,7 +478,8 @@ shortreal
other simulators either do not support float, or convert likewise.
specify specparam
All specify blocks and timing checks are ignored.
All timing checks and specify blocks (except specparam, which is
supported) are ignored.
uwire
Verilator does not perform warning checking on uwires; it treats the

View File

@ -66,6 +66,40 @@ Once a model is built, the next step is typically for the user to run it,
see :ref:`Simulating`.
.. _Finding and Binding Modules:
Finding and Binding Modules
===========================
Verilator provides several mechanisms to find the source code containing a
module, primitive, interface, or program ("module" in this section) and
bind them to an instantiation. These capabilities are similar to the
"Precompiling in a single-pass" use model described in IEEE 1800-2023
33.5.1, although `config` is not yet supported.
Verilator first reads all files provided on the command line and
:vlopt:`-f` files, and parses all modules within. Each module is assigned
to the most recent library specified with :vlopt:`-work`, thus `-work liba
a.v -work libb b.v` will assign modules in `a.v` to `liba` and modules in
`b.v` to `libb`.
If a module is not defined from a file on the command-line, Verilator
attempts to find a filename constructed from the module name using
:vlopt:`-y` and `+libext`.
Binding begins with the :vlopt:`--top` module, if provided. If not provided
Verilator attempts to figure out the top module itself, and if multiple
tops result a :option:`MULTITOP` warning is issued which may be suppressed
(see details in :option:`MULTITOP`).
Verilator will attempt to bind lower unresolved instances first in the same
library name as the parent's instantiation library, and if not found search
globally across all libraries in the order modules were declared. This
allows otherwise conflicting duplicate module names between libraries to
coexist uniquely within each library name. When IEEE `config use` is
supported, more complicated selections will be able to be specified.
.. _Hierarchical Verilation:
Hierarchical Verilation

View File

@ -332,6 +332,7 @@ Prabhat
Prabhu
Prateek
Pre
Precompiling
Preprocess
Pretet
Pretl

View File

@ -193,6 +193,11 @@ string AstNode::prettyName(const string& namein) VL_PURE {
pos += 7;
continue;
}
if (0 == std::strncmp(pos, "__LIB__", 7)) {
pretty = ""; // Trim library name before module name
pos += 7;
continue;
}
if (0 == std::strncmp(pos, "__PVT__", 7)) {
pretty += "";
pos += 7;

View File

@ -226,6 +226,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
const string m_origName; // Name of the module, ignoring name() changes, for dot lookup
string m_someInstanceName; // Hierarchical name of some arbitrary instance of this module.
// Used for user messages only.
string m_libname; // Work library
int m_level = 0; // 1=top module, 2=cell off top module, ...
VLifetime m_lifetime; // Lifetime
VTimescale m_timeunit; // Global time unit
@ -243,10 +244,11 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
bool m_recursive : 1; // Recursive module
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
protected:
AstNodeModule(VNType t, FileLine* fl, const string& name)
AstNodeModule(VNType t, FileLine* fl, const string& name, const string& libname)
: AstNode{t, fl}
, m_name{name}
, m_origName{name}
, m_libname{libname}
, m_modPublic{false}
, m_modTrace{false}
, m_inLibrary{false}
@ -275,6 +277,8 @@ public:
void inLibrary(bool flag) { m_inLibrary = flag; }
void level(int level) { m_level = level; }
int level() const VL_MT_SAFE { return m_level; }
string libname() const { return m_libname; }
string prettyLibnameQ() const { return "'" + prettyName(libname()) + "'"; }
bool isTop() const VL_MT_SAFE { return level() == 1; }
bool modPublic() const { return m_modPublic; }
void modPublic(bool flag) { m_modPublic = flag; }
@ -2507,8 +2511,8 @@ class AstClass final : public AstNodeModule {
bool m_virtual = false; // Virtual class
public:
AstClass(FileLine* fl, const string& name)
: ASTGEN_SUPER_Class(fl, name) {}
AstClass(FileLine* fl, const string& name, const string& libname)
: ASTGEN_SUPER_Class(fl, name, libname) {}
ASTGEN_MEMBERS_AstClass;
string verilogKwd() const override { return "class"; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
@ -2581,8 +2585,8 @@ class AstClassPackage final : public AstNodeModule {
// @astgen ptr := m_classp : Optional[AstClass] // Class package this is under
// // (weak pointer, hard link is other way)
public:
AstClassPackage(FileLine* fl, const string& name)
: ASTGEN_SUPER_ClassPackage(fl, name) {}
AstClassPackage(FileLine* fl, const string& name, const string& libname)
: ASTGEN_SUPER_ClassPackage(fl, name, libname) {}
ASTGEN_MEMBERS_AstClassPackage;
string verilogKwd() const override { return "classpackage"; }
bool timescaleMatters() const override { return false; }
@ -2592,8 +2596,8 @@ public:
class AstIface final : public AstNodeModule {
// A module declaration
public:
AstIface(FileLine* fl, const string& name)
: ASTGEN_SUPER_Iface(fl, name) {}
AstIface(FileLine* fl, const string& name, const string& libname)
: ASTGEN_SUPER_Iface(fl, name, libname) {}
ASTGEN_MEMBERS_AstIface;
// Interfaces have `timescale applicability but lots of code seems to
// get false warnings if we enable this
@ -2607,13 +2611,13 @@ class AstModule final : public AstNodeModule {
public:
class Checker {}; // for constructor type-overload selection
class Program {}; // for constructor type-overload selection
AstModule(FileLine* fl, const string& name)
: ASTGEN_SUPER_Module(fl, name) {}
AstModule(FileLine* fl, const string& name, Checker)
: ASTGEN_SUPER_Module(fl, name)
AstModule(FileLine* fl, const string& name, const string& libname)
: ASTGEN_SUPER_Module(fl, name, libname) {}
AstModule(FileLine* fl, const string& name, const string& libname, Checker)
: ASTGEN_SUPER_Module(fl, name, libname)
, m_isChecker{true} {}
AstModule(FileLine* fl, const string& name, Program)
: ASTGEN_SUPER_Module(fl, name)
AstModule(FileLine* fl, const string& name, const string& libname, Program)
: ASTGEN_SUPER_Module(fl, name, libname)
, m_isProgram{true} {}
ASTGEN_MEMBERS_AstModule;
string verilogKwd() const override {
@ -2628,8 +2632,8 @@ public:
class AstNotFoundModule final : public AstNodeModule {
// A missing module declaration
public:
AstNotFoundModule(FileLine* fl, const string& name)
: ASTGEN_SUPER_NotFoundModule(fl, name) {}
AstNotFoundModule(FileLine* fl, const string& name, const string& libname)
: ASTGEN_SUPER_NotFoundModule(fl, name, libname) {}
ASTGEN_MEMBERS_AstNotFoundModule;
string verilogKwd() const override { return "/*not-found-*/ module"; }
bool timescaleMatters() const override { return false; }
@ -2637,8 +2641,8 @@ public:
class AstPackage final : public AstNodeModule {
// A package declaration
public:
AstPackage(FileLine* fl, const string& name)
: ASTGEN_SUPER_Package(fl, name) {}
AstPackage(FileLine* fl, const string& name, const string& libname)
: ASTGEN_SUPER_Package(fl, name, libname) {}
ASTGEN_MEMBERS_AstPackage;
string verilogKwd() const override { return "package"; }
bool timescaleMatters() const override { return !isDollarUnit(); }
@ -2648,8 +2652,8 @@ public:
class AstPrimitive final : public AstNodeModule {
// A primitive declaration
public:
AstPrimitive(FileLine* fl, const string& name)
: ASTGEN_SUPER_Primitive(fl, name) {}
AstPrimitive(FileLine* fl, const string& name, const string& libname)
: ASTGEN_SUPER_Primitive(fl, name, libname) {}
ASTGEN_MEMBERS_AstPrimitive;
string verilogKwd() const override { return "primitive"; }
bool timescaleMatters() const override { return false; }

View File

@ -1353,7 +1353,7 @@ AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
AstConstPool::AstConstPool(FileLine* fl)
: ASTGEN_SUPER_ConstPool(fl)
, m_modp{new AstModule{fl, "@CONST-POOL@"}}
, m_modp{new AstModule{fl, "@CONST-POOL@", "work"}}
, m_scopep{new AstScope{fl, m_modp, "@CONST-POOL@", nullptr, nullptr}} {
this->modulep(m_modp);
m_modp->addStmtsp(m_scopep);
@ -2298,7 +2298,7 @@ void AstNetlist::dumpJson(std::ostream& str) const {
}
AstPackage* AstNetlist::dollarUnitPkgAddp() {
if (!m_dollarUnitPkgp) {
m_dollarUnitPkgp = new AstPackage{fileline(), AstPackage::dollarUnitName()};
m_dollarUnitPkgp = new AstPackage{fileline(), AstPackage::dollarUnitName(), "work"};
// packages are always libraries; don't want to make them a "top"
m_dollarUnitPkgp->inLibrary(true);
m_dollarUnitPkgp->modTrace(false); // may reconsider later
@ -2325,6 +2325,7 @@ void AstNodeModule::dump(std::ostream& str) const {
str << " [RECURSIVE]";
}
str << " [" << timeunit() << "]";
if (libname() != "work") str << " libname=" << libname();
}
void AstNodeModule::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, origName);
@ -2335,6 +2336,7 @@ void AstNodeModule::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, recursiveClone);
dumpJsonBoolFunc(str, recursive);
dumpJsonStr(str, "timeunit", timeunit().ascii());
if (libname() != "work") dumpJsonStr(str, "libname=", libname());
dumpJsonGen(str);
}
void AstPackageExport::dump(std::ostream& str) const {

View File

@ -93,7 +93,7 @@ class ClassVisitor final : public VNVisitor {
// Make containing package
// Note origName is the same as the class origName so errors look correct
AstClassPackage* const packagep
= new AstClassPackage{nodep->fileline(), nodep->origName()};
= new AstClassPackage{nodep->fileline(), nodep->origName(), nodep->libname()};
packagep->name(nodep->name() + "__Vclpkg");
nodep->editCountInc();
nodep->classOrPackagep(packagep);
@ -138,6 +138,7 @@ class ClassVisitor final : public VNVisitor {
}
void visit(AstNodeModule* nodep) override {
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
// Classes are always under a Package (perhaps $unit) or a module
VL_RESTORER(m_prefix);
VL_RESTORER(m_modp);
m_modp = nodep;

View File

@ -39,11 +39,21 @@ class CMakeEmitter final {
template <typename T_List>
static string cmake_list(const T_List& strs) {
string s;
for (auto it = strs.begin(); it != strs.end(); ++it) {
for (auto& itr : strs) {
if (!s.empty()) s += ' ';
s += '"';
s += V3OutFormatter::quoteNameControls(*it);
s += V3OutFormatter::quoteNameControls(itr);
s += '"';
}
return s;
}
static string cmake_list(const VFileLibList& strs) {
string s;
for (auto& itr : strs) {
if (!s.empty()) s += ' ';
s += '"';
s += V3OutFormatter::quoteNameControls(itr.filename());
s += '"';
if (it != strs.end()) s += ' ';
}
return s;
}
@ -193,8 +203,8 @@ class CMakeEmitter final {
*of << " ";
const string vFile = hblockp->vFileIfNecessary();
if (!vFile.empty()) *of << vFile << " ";
const V3StringList& vFiles = v3Global.opt.vFiles();
for (const string& i : vFiles) *of << V3Os::filenameRealPath(i) << " ";
for (const auto& i : v3Global.opt.vFiles())
*of << V3Os::filenameRealPath(i.filename()) << " ";
*of << " VERILATOR_ARGS ";
*of << "-f " << hblockp->commandArgsFilename(true)
<< " -CFLAGS -fPIC" // hierarchical block will be static, but may be linked

View File

@ -814,13 +814,12 @@ class EmitMkHierVerilation final {
const string verilator_wrapper = V3Os::filenameDir(fullpath_bin) + "/verilator";
of.puts("VM_HIER_VERILATOR := " + verilator_wrapper + "\n");
of.puts("VM_HIER_INPUT_FILES := \\\n");
const V3StringList& vFiles = v3Global.opt.vFiles();
for (const string& i : vFiles) of.puts(" " + V3Os::filenameRealPath(i) + " \\\n");
for (const auto& i : v3Global.opt.vFiles())
of.puts(" " + V3Os::filenameRealPath(i.filename()) + " \\\n");
of.puts("\n");
const V3StringSet& libraryFiles = v3Global.opt.libraryFiles();
of.puts("VM_HIER_VERILOG_LIBS := \\\n");
for (const string& i : libraryFiles) {
of.puts(" " + V3Os::filenameRealPath(i) + " \\\n");
for (const auto& i : v3Global.opt.libraryFiles()) {
of.puts(" " + V3Os::filenameRealPath(i.filename()) + " \\\n");
}
of.puts("\n");
}

View File

@ -133,9 +133,9 @@ class V3EmitMkJsonEmitter final {
const string vFile = hblockp->vFileIfNecessary();
if (!vFile.empty()) sources.emplace_back(vFile);
const V3StringList& vFiles = v3Global.opt.vFiles();
for (const string& i : vFiles)
sources.emplace_back(V3Os::filenameSlashPath(V3Os::filenameRealPath(i)));
for (const auto& i : v3Global.opt.vFiles())
sources.emplace_back(
V3Os::filenameSlashPath(V3Os::filenameRealPath(i.filename())));
std::vector<std::string> cflags;
cflags.emplace_back("-fPIC");
@ -156,9 +156,9 @@ class V3EmitMkJsonEmitter final {
for (const auto& itr : *planp)
sources.emplace_back(makeDir + "/" + itr.second->hierWrapperFilename(true));
const V3StringList& vFiles = v3Global.opt.vFiles();
for (const string& i : vFiles)
sources.emplace_back(V3Os::filenameSlashPath(V3Os::filenameRealPath(i)));
for (const auto& itr : v3Global.opt.vFiles())
sources.emplace_back(
V3Os::filenameSlashPath(V3Os::filenameRealPath(itr.filename())));
of.begin()
.put("prefix", v3Global.opt.prefix())

View File

@ -81,7 +81,8 @@ public:
ForkDynScopeInstance& createInstancePrototype() {
UASSERT_OBJ(!m_instance.initialized(), m_procp, "Dynamic scope already instantiated.");
m_instance.m_classp = new AstClass{m_procp->fileline(), generateDynScopeClassName()};
m_instance.m_classp
= new AstClass{m_procp->fileline(), generateDynScopeClassName(), m_modp->libname()};
UINFO(9, "new dynscope class " << m_instance.m_classp);
m_instance.m_refDTypep
= new AstClassRefDType{m_procp->fileline(), m_instance.m_classp, nullptr};

View File

@ -60,42 +60,39 @@ void V3Global::readFiles() {
if (v3Global.opt.stdWaiver()) {
parser.parseFile(
new FileLine{V3Options::getStdWaiverPath()}, V3Options::getStdWaiverPath(), false,
"Cannot find verilated_std_waiver.vlt containing built-in lint waivers: ");
"work", "Cannot find verilated_std_waiver.vlt containing built-in lint waivers: ");
}
// Read .vlt files
const V3StringSet& vltFiles = v3Global.opt.vltFiles();
for (const string& filename : vltFiles) {
parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filename, false,
"Cannot find file containing .vlt file: ");
for (auto& filelib : v3Global.opt.vltFiles()) {
parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filelib.filename(), false,
filelib.libname(), "Cannot find file containing .vlt file: ");
}
// Parse the std package
if (v3Global.opt.stdPackage()) {
parser.parseFile(new FileLine{V3Options::getStdPackagePath()},
V3Options::getStdPackagePath(), false,
V3Options::getStdPackagePath(), false, "work",
"Cannot find verilated_std.sv containing built-in std:: definitions: ");
}
// Read top module
const V3StringList& vFiles = v3Global.opt.vFiles();
for (const string& filename : vFiles) {
parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filename, false,
"Cannot find file containing module: ");
for (const auto& filelib : v3Global.opt.vFiles()) {
parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filelib.filename(), false,
filelib.libname(), "Cannot find file containing module: ");
}
// Read libraries
// To be compatible with other simulators,
// this needs to be done after the top file is read
const V3StringSet& libraryFiles = v3Global.opt.libraryFiles();
for (const string& filename : libraryFiles) {
parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filename, true,
"Cannot find file containing library module: ");
for (const auto& filelib : v3Global.opt.libraryFiles()) {
parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filelib.filename(), true,
filelib.libname(), "Cannot find file containing library module: ");
}
// Read hierarchical type parameter file
const string filename = v3Global.opt.hierParamFile();
if (!filename.empty()) {
parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filename, false,
for (const auto& filelib : v3Global.opt.hierParamFile()) {
parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filelib.filename(), false,
filelib.libname(),
"Cannot open file containing hierarchical parameter declarations: ");
}

View File

@ -116,12 +116,11 @@ static void V3HierWriteCommonInputs(const V3HierBlock* hblockp, std::ostream* of
if (hblockp) topModuleFile = hblockp->vFileIfNecessary();
if (!forCMake) {
if (!topModuleFile.empty()) *of << topModuleFile << "\n";
const V3StringList& vFiles = v3Global.opt.vFiles();
for (const string& i : vFiles) *of << i << "\n";
for (const auto& i : v3Global.opt.vFiles()) *of << i.filename() << "\n";
}
const V3StringSet& libraryFiles = v3Global.opt.libraryFiles();
for (const string& i : libraryFiles) {
if (V3Os::filenameRealPath(i) != topModuleFile) *of << "-v " << i << "\n";
for (const auto& i : v3Global.opt.libraryFiles()) {
if (V3Os::filenameRealPath(i.filename()) != topModuleFile)
*of << "-v " << i.filename() << "\n";
}
}
@ -251,9 +250,9 @@ string V3HierBlock::hierGeneratedFilenames(bool withDir) const {
string V3HierBlock::vFileIfNecessary() const {
string filename = V3Os::filenameRealPath(m_modp->fileline()->filename());
for (const string& v : v3Global.opt.vFiles()) {
for (const auto& v : v3Global.opt.vFiles()) {
// Already listed in vFiles, so no need to add the file.
if (filename == V3Os::filenameRealPath(v)) return "";
if (filename == V3Os::filenameRealPath(v.filename())) return "";
}
return filename;
}

View File

@ -127,14 +127,34 @@ class LinkCellsVisitor final : public VNVisitor {
const V3GraphEdge* const edgep = new V3GraphEdge{&m_graph, fromp, top, weight, cuttable};
UINFO(9, " newEdge " << edgep << " " << fromp->name() << " -> " << top->name());
}
AstNodeModule* findModuleSym(const string& modName) {
const VSymEnt* const foundp = m_mods.rootp()->findIdFallback(modName);
return foundp ? VN_AS(foundp->nodep(), NodeModule) : nullptr;
void insertModInLib(const string& name, const string& libname, AstNodeModule* nodep) {
// Be able to find the module under it's library using the name it was given
VSymEnt* libSymp = m_mods.rootp()->findIdFlat(libname);
if (!libSymp)
libSymp = m_mods.rootp()->insert(libname, new VSymEnt{&m_mods, v3Global.rootp()});
libSymp->insert(name, new VSymEnt{&m_mods, nodep});
}
AstNodeModule* resolveModule(AstNode* nodep, const string& modName) {
AstNodeModule* modp = findModuleSym(modName);
AstNodeModule* findModuleLibSym(const string& modName, const string& libname) {
// Given module name and library name, find within that exact library
const VSymEnt* const libSymp = m_mods.rootp()->findIdFallback(libname);
if (!libSymp) return nullptr;
const VSymEnt* const foundp = libSymp->findIdFallback(modName);
return foundp ? VN_AS(foundp->nodep(), NodeModule) : nullptr;
}
AstNodeModule* findModuleSym(const string& modName, const string& libname) {
// Given module and library to start search in, resolve using config library choices
// TODO support IEEE config library search order
// First search local library
AstNodeModule* foundp = findModuleLibSym(modName, libname);
if (foundp) return foundp;
// THen search global
foundp = findModuleLibSym(modName, "__GLOBAL");
return foundp;
}
AstNodeModule* resolveModule(AstNode* nodep, const string& modName, const string& libname) {
AstNodeModule* modp = findModuleSym(modName, libname);
if (!modp) {
// Read-subfile
// If file not found, make AstNotFoundModule, rather than error out.
@ -142,12 +162,12 @@ class LinkCellsVisitor final : public VNVisitor {
const string prettyName = AstNode::prettyName(modName);
V3Parse parser{v3Global.rootp(), m_filterp};
// true below -> other simulators treat modules in link-found files as library cells
parser.parseFile(nodep->fileline(), prettyName, true, "");
parser.parseFile(nodep->fileline(), prettyName, true, m_modp->libname(), "");
V3Error::abortIfErrors();
// We've read new modules, grab new pointers to their names
readModNames();
// Check again
modp = findModuleSym(modName);
modp = findModuleSym(modName, libname);
if (!modp) {
// This shouldn't throw a message as parseFile will create
// a AstNotFoundModule for us
@ -188,7 +208,7 @@ class LinkCellsVisitor final : public VNVisitor {
m_modp = nodep;
UINFO(4, "Link Module: " << nodep);
if (nodep->fileline()->filebasenameNoExt() != nodep->prettyName()
&& !v3Global.opt.isLibraryFile(nodep->fileline()->filename())
&& !v3Global.opt.isLibraryFile(nodep->fileline()->filename(), nodep->libname())
&& !VN_IS(nodep, NotFoundModule) && !nodep->recursiveClone()
&& !nodep->internal()) {
// We only complain once per file, otherwise library-like files
@ -228,7 +248,7 @@ class LinkCellsVisitor final : public VNVisitor {
UINFO(4, "Link IfaceRef: " << nodep);
// Use findIdUpward instead of findIdFlat; it doesn't matter for now
// but we might support modules-under-modules someday.
AstNodeModule* const modp = resolveModule(nodep, nodep->ifaceName());
AstNodeModule* const modp = resolveModule(nodep, nodep->ifaceName(), m_modp->libname());
if (modp) {
if (VN_IS(modp, Iface)) {
// Track module depths, so can sort list from parent down to children
@ -259,7 +279,7 @@ class LinkCellsVisitor final : public VNVisitor {
// Package Import: We need to do the package before the use of a package
iterateChildren(nodep);
if (!nodep->packagep()) {
AstNodeModule* const modp = resolveModule(nodep, nodep->pkgName());
AstNodeModule* const modp = resolveModule(nodep, nodep->pkgName(), m_modp->libname());
if (AstPackage* const pkgp = VN_CAST(modp, Package)) nodep->packagep(pkgp);
if (!nodep->packagep()) {
nodep->v3error("Export package not found: " << nodep->prettyPkgNameQ());
@ -271,7 +291,7 @@ class LinkCellsVisitor final : public VNVisitor {
// Package Import: We need to do the package before the use of a package
iterateChildren(nodep);
if (!nodep->packagep()) {
AstNodeModule* const modp = resolveModule(nodep, nodep->pkgName());
AstNodeModule* const modp = resolveModule(nodep, nodep->pkgName(), m_modp->libname());
if (AstPackage* const pkgp = VN_CAST(modp, Package)) nodep->packagep(pkgp);
// If not found, V3LinkDot will report errors
if (!nodep->packagep()) {
@ -289,7 +309,7 @@ class LinkCellsVisitor final : public VNVisitor {
// this move to post param, which would mean we do not auto-read modules
// and means we cannot compute module levels until later.
UINFO(4, "Link Bind: " << nodep);
AstNodeModule* const modp = resolveModule(nodep, nodep->name());
AstNodeModule* const modp = resolveModule(nodep, nodep->name(), m_modp->libname());
if (modp) {
AstNode* const cellsp = nodep->cellsp()->unlinkFrBackWithNext();
// Module may have already linked, so need to pick up these new cells
@ -325,7 +345,7 @@ class LinkCellsVisitor final : public VNVisitor {
UINFO(4, "Link Cell: " << nodep);
// Use findIdFallback instead of findIdFlat; it doesn't matter for now
// but we might support modules-under-modules someday.
AstNodeModule* cellmodp = resolveModule(nodep, nodep->modName());
AstNodeModule* cellmodp = resolveModule(nodep, nodep->modName(), m_modp->libname());
if (cellmodp) {
if (cellmodp == m_modp || cellmodp->user2p() == m_modp) {
UINFO(1, "Self-recursive module " << cellmodp);
@ -529,7 +549,7 @@ class LinkCellsVisitor final : public VNVisitor {
if (pinp->name() == "") pinp->name("__paramNumber" + cvtToStr(pinp->pinNum()));
}
if (m_varp) { // Parser didn't know what was interface, resolve now
AstNodeModule* const varModp = findModuleSym(nodep->name());
AstNodeModule* const varModp = findModuleSym(nodep->name(), m_modp->libname());
if (AstIface* const ifacep = VN_CAST(varModp, Iface)) {
// Might be an interface, but might also not really be due to interface being
// hidden by another declaration. Assume it is relevant and order as-if.
@ -591,21 +611,26 @@ class LinkCellsVisitor final : public VNVisitor {
// Look at all modules, and store pointers to all module names
for (AstNodeModule *nextp, *nodep = v3Global.rootp()->modulesp(); nodep; nodep = nextp) {
nextp = VN_AS(nodep->nextp(), NodeModule);
if (v3Global.opt.hierChild() && nodep->name() == hierIt->second.origName()) {
if (v3Global.opt.hierChild() && nodep->origName() == hierIt->second.origName()) {
nodep->name(hierIt->first); // Change name of this module to be mangled name
// considering parameter
}
const AstNodeModule* const foundp = findModuleSym(nodep->name());
if (foundp && foundp != nodep) {
if (!(foundp->fileline()->warnIsOff(V3ErrorCode::MODDUP)
const AstNodeModule* const libFoundp = findModuleLibSym(nodep->origName(), nodep->libname());
const AstNodeModule* const globalFoundp = findModuleLibSym(nodep->name(), "__GLOBAL");
if (libFoundp && libFoundp == nodep) {
// Ok
} else if (libFoundp && !globalFoundp) {
nodep->v3fatalSrc("Module should be found globally if inserted in lib");
} else if (libFoundp) {
if (!(libFoundp->fileline()->warnIsOff(V3ErrorCode::MODDUP)
|| nodep->fileline()->warnIsOff(V3ErrorCode::MODDUP)
|| hierBlocks.find(nodep->name()) != hierBlocks.end())) {
nodep->v3warn(MODDUP, "Duplicate declaration of module: "
<< nodep->prettyNameQ() << '\n'
<< nodep->warnContextPrimary() << '\n'
<< foundp->warnOther()
<< libFoundp->warnOther()
<< "... Location of original declaration\n"
<< foundp->warnContextSecondary());
<< libFoundp->warnContextSecondary());
}
if (VN_IS(nodep, Package)) {
// Packages may be imported, we instead rename to be unique
@ -614,8 +639,16 @@ class LinkCellsVisitor final : public VNVisitor {
nodep->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
} else if (!foundp) {
m_mods.rootp()->insert(nodep->name(), new VSymEnt{&m_mods, nodep});
} else if (!libFoundp && globalFoundp && globalFoundp != nodep) {
// ...__LIB__ stripped by prettyName
const string newName = nodep->libname() + "__LIB__" + nodep->origName();
UINFO(9, "Module rename as in multiple libraries " << newName << " <- " << nodep);
insertModInLib(nodep->origName(), nodep->libname(), nodep); // Original name
nodep->name(newName);
insertModInLib(nodep->name(), "__GLOBAL", nodep);
} else if (!libFoundp) {
insertModInLib(nodep->origName(), nodep->libname(), nodep);
insertModInLib(nodep->name(), "__GLOBAL", nodep);
}
}
// if (debug() >= 9) m_mods.dump(cout, "-syms: ");
@ -641,7 +674,9 @@ public:
iterate(nodep);
}
~LinkCellsVisitor() override {
if (debug() >= 5 || dumpGraphLevel() >= 5) { m_mods.dumpFilePrefixed("linkcells"); }
if (debug() >= 5 || dumpGraphLevel() >= 5) {
m_mods.dumpFilePrefixed("linkcells");
}
}
};

View File

@ -164,7 +164,7 @@ void V3LinkLevel::wrapTop(AstNetlist* rootp) {
return;
}
AstNodeModule* const newmodp = new AstModule{oldmodp->fileline(), "$root"};
AstNodeModule* const newmodp = new AstModule{oldmodp->fileline(), "$root", oldmodp->libname()};
newmodp->name(AstNode::encodeName(newmodp->name())); // so origName is nice
// Make the new module first in the list
oldmodp->unlinkFrBackWithNext();

View File

@ -313,7 +313,8 @@ class LinkResolveVisitor final : public VNVisitor {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: %l in $fscanf");
fmt = "";
}
if (m_modp) fmt = VString::quotePercent(m_modp->prettyName());
if (m_modp)
fmt = AstNode::prettyName(m_modp->libname()) + "." + m_modp->prettyName();
break;
default: // Most operators, just move to next argument
if (!V3Number::displayedFmtLegal(ch, isScan)) {

View File

@ -380,10 +380,12 @@ bool V3Options::isFuture0(const string& flag) const {
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();
bool V3Options::isLibraryFile(const string& filename, const string& libname) const {
return m_libraryFiles.find({filename, libname}) != m_libraryFiles.end();
}
void V3Options::addLibraryFile(const string& filename, const string& libname) {
m_libraryFiles.insert({filename, libname});
}
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();
}
@ -392,12 +394,14 @@ 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) {
void V3Options::addVFile(const string& filename, const string& libname) {
// 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);
m_vFiles.push_back({filename, libname});
}
void V3Options::addVltFile(const string& filename, const string& libname) {
m_vltFiles.insert({filename, libname});
}
void V3Options::addVltFile(const string& filename) { m_vltFiles.insert(filename); }
void V3Options::addForceInc(const string& filename) { m_forceIncs.push_back(filename); }
void V3Options::addLineArg(const string& arg) { m_impp->m_lineArgs.push_back(arg); }
@ -416,7 +420,7 @@ string V3Options::allArgsString() const VL_MT_SAFE {
// 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);
for (const auto& vFile : m_vFiles) vFiles.insert(vFile.filename());
string out;
bool stripArg = false;
bool stripArgIfNum = false;
@ -1058,16 +1062,16 @@ void V3Options::parseOpts(FileLine* fl, int argc, char** argv) VL_MT_DISABLED {
// 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()) {
if (vFiles().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 = "V"s + AstNode::encodeName(topModule());
if (prefix() == "" && vFilesList.size() >= 1)
m_prefix = "V"s + AstNode::encodeName(V3Os::filenameNonDirExt(*(vFilesList.begin())));
if (prefix() == "" && vFiles().size() >= 1)
m_prefix
= "V"s + AstNode::encodeName(V3Os::filenameNonDirExt(vFiles().begin()->filename()));
if (modPrefix() == "") m_modPrefix = prefix();
// Find files in makedir
@ -1386,8 +1390,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
m_hierBlocks.emplace(opt.mangledName(), opt);
});
DECL_OPTION("-hierarchical-child", Set, &m_hierChild);
DECL_OPTION("-hierarchical-params-file", CbVal,
[this](const char* optp) { m_hierParamsFile = optp; });
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)); });
@ -1699,7 +1704,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
std::exit(0);
});
DECL_OPTION("-v", CbVal, [this, &optdir](const char* valp) {
V3Options::addLibraryFile(parseFileArg(optdir, valp));
V3Options::addLibraryFile(parseFileArg(optdir, valp), work());
});
DECL_OPTION("-valgrind", CbCall, []() {}); // Processed only in bin/verilator shell
DECL_OPTION("-verilate", OnOff, &m_verilate);
@ -1762,6 +1767,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
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("-work", Set, &m_work);
DECL_OPTION("-Wpedantic", CbCall, [this]() {
m_pedantic = true;
V3Error::pretendError(V3ErrorCode::ASSIGNIN, false);
@ -1888,9 +1894,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|| suffixed(filename, ".so")) {
V3Options::addLdLibs(filename);
} else if (suffixed(filename, ".vlt")) {
V3Options::addVltFile(filename);
V3Options::addVltFile(filename, work());
} else {
V3Options::addVFile(filename);
V3Options::addVFile(filename, work());
}
++i;
}

View File

@ -59,6 +59,34 @@ constexpr bool operator==(const VOptionBool& lhs, const VOptionBool& rhs) {
constexpr bool operator==(const VOptionBool& lhs, VOptionBool::en rhs) { return lhs.m_e == rhs; }
constexpr bool operator==(VOptionBool::en lhs, const VOptionBool& rhs) { return lhs == rhs.m_e; }
//######################################################################
class VFileLibName final {
// Filename and libname pair
const string m_filename; // Filename
const string m_libname; // Libname
public:
VFileLibName(const string& filename, const string& libname)
: m_filename{filename}
, m_libname{libname} {}
VFileLibName(const VFileLibName& rhs)
: m_filename{rhs.m_filename}
, m_libname{rhs.m_libname} {}
string filename() const { return m_filename; }
string libname() const { return m_libname; }
bool operator==(const VFileLibName& rhs) const {
return m_filename == rhs.m_filename && m_libname == rhs.m_libname;
}
bool operator<(const VFileLibName& rhs) const {
if (m_filename < rhs.m_filename) return true;
if (m_filename > rhs.m_filename) return false;
return m_libname < rhs.m_libname;
}
};
using VFileLibList = std::vector<VFileLibName>;
using VFileLibSet = std::set<VFileLibName>;
// ######################################################################
class VTimescale final {
@ -207,11 +235,11 @@ private:
V3StringSet m_futures; // argument: -Wfuture- list
V3StringSet m_future0s; // argument: -future list
V3StringSet m_future1s; // argument: -future1 list
V3StringSet m_libraryFiles; // argument: Verilog -v files
VFileLibSet m_libraryFiles; // argument: Verilog -v files
V3StringSet m_clockers; // argument: Verilog -clk signals
V3StringSet m_noClockers; // argument: Verilog -noclk signals
V3StringList m_vFiles; // argument: Verilog files to read
V3StringSet m_vltFiles; // argument: Verilator config files to read
VFileLibList m_vFiles; // argument: Verilog files to read
VFileLibSet m_vltFiles; // argument: Verilator config files to read
V3StringList m_forceIncs; // argument: -FI
DebugLevelMap m_debugLevel; // argument: --debugi-<srcfile/tag> <level>
DebugLevelMap m_dumpLevel; // argument: --dumpi-<srcfile/tag> <level>
@ -359,7 +387,7 @@ private:
string m_diagnosticsSarifOutput; // main switch: --diagnostics-sarif-output
string m_exeName; // main switch: -o {name}
string m_flags; // main switch: -f {name}
string m_hierParamsFile; // main switch: --hierarchical-params-file
VFileLibList m_hierParamsFile; // main switch: --hierarchical-params-file
string m_jsonOnlyOutput; // main switch: --json-only-output
string m_jsonOnlyMetaOutput; // main switch: --json-only-meta-output
string m_l2Name; // main switch: --l2name; "" for top-module's name
@ -373,6 +401,7 @@ private:
string m_topModule; // main switch: --top-module
string m_unusedRegexp; // main switch: --unused-regexp
string m_waiverOutput; // main switch: --waiver-output {filename}
string m_work = "work"; // main switch: --work {libname}
string m_xAssign; // main switch: --x-assign
string m_xInitial; // main switch: --x-initial
string m_xmlOutput; // main switch: --xml-output
@ -463,11 +492,11 @@ public:
void addCompilerIncludes(const string& filename);
void addLdLibs(const string& filename);
void addMakeFlags(const string& filename);
void addLibraryFile(const string& filename);
void addLibraryFile(const string& filename, const string& libname);
void addClocker(const string& signame);
void addNoClocker(const string& signame);
void addVFile(const string& filename);
void addVltFile(const string& filename);
void addVFile(const string& filename, const string& libname);
void addVltFile(const string& filename, const string& libname);
void addForceInc(const string& filename);
bool available() const VL_MT_SAFE { return m_available; }
void ccSet();
@ -642,7 +671,7 @@ public:
: m_diagnosticsSarifOutput;
}
string exeName() const { return m_exeName != "" ? m_exeName : prefix(); }
string hierParamFile() const { return m_hierParamsFile; }
VFileLibList hierParamFile() const { return m_hierParamsFile; }
string jsonOnlyOutput() const { return m_jsonOnlyOutput; }
string jsonOnlyMetaOutput() const { return m_jsonOnlyMetaOutput; }
string l2Name() const { return m_l2Name; }
@ -668,6 +697,7 @@ public:
bool noTraceTop() const { return m_noTraceTop; }
string unusedRegexp() const { return m_unusedRegexp; }
string waiverOutput() const { return m_waiverOutput; }
string work() const { return m_work; }
bool isWaiverOutput() const { return !m_waiverOutput.empty(); }
string xAssign() const { return m_xAssign; }
string xInitial() const { return m_xInitial; }
@ -678,9 +708,9 @@ public:
const V3StringSet& compilerIncludes() const { return m_compilerIncludes; }
const V3StringList& ldLibs() const { return m_ldLibs; }
const V3StringList& makeFlags() const { return m_makeFlags; }
const V3StringSet& libraryFiles() const { return m_libraryFiles; }
const V3StringList& vFiles() const { return m_vFiles; }
const V3StringSet& vltFiles() const { return m_vltFiles; }
const VFileLibSet& libraryFiles() const { return m_libraryFiles; }
const VFileLibList& vFiles() const { return m_vFiles; }
const VFileLibSet& vltFiles() const { return m_vltFiles; }
const V3StringList& forceIncs() const { return m_forceIncs; }
bool hasParameter(const string& name);
@ -690,7 +720,7 @@ public:
bool isFuture(const string& flag) const;
bool isFuture0(const string& flag) const;
bool isFuture1(const string& flag) const;
bool isLibraryFile(const string& filename) const;
bool isLibraryFile(const string& filename, const string& libname) const;
bool isClocker(const string& signame) const;
bool isNoClocker(const string& signame) const;

View File

@ -43,7 +43,7 @@ public:
// METHODS
// Preprocess and read the Verilog file specified into the netlist database
void parseFile(FileLine* fileline, const string& modname, bool inLibrary,
const string& errmsg) VL_MT_DISABLED;
const string& libname, const string& errmsg) VL_MT_DISABLED;
// Push preprocessed text to the lexer
static void ppPushText(V3ParseImp* impp, const string& text) VL_MT_DISABLED;

View File

@ -294,6 +294,7 @@ void V3ParseImp::preprocDumps(std::ostream& os, bool forInputs) {
}
void V3ParseImp::parseFile(FileLine* fileline, const string& modfilename, bool inLibrary,
const string& libname,
const string& errmsg) { // "" for no error, make fake node
const string nondirname = V3Os::filenameNonDir(modfilename);
const string modname = V3Os::filenameNonDirExt(modfilename);
@ -303,13 +304,14 @@ void V3ParseImp::parseFile(FileLine* fileline, const string& modfilename, bool i
m_lexFileline->newContent();
m_bisonLastFileline = m_lexFileline;
m_inLibrary = inLibrary;
m_libname = libname;
// Preprocess into m_ppBuffer
const bool ok = V3PreShell::preproc(fileline, modfilename, m_filterp, this, errmsg);
if (!ok) {
if (errmsg != "") return; // Threw error already
// Create fake node for later error reporting
AstNodeModule* const nodep = new AstNotFoundModule{fileline, modname};
AstNodeModule* const nodep = new AstNotFoundModule{fileline, modname, libname};
v3Global.rootp()->addModulesp(nodep);
return;
}
@ -745,8 +747,8 @@ V3Parse::~V3Parse() { //
VL_DO_CLEAR(delete m_impp, m_impp = nullptr);
}
void V3Parse::parseFile(FileLine* fileline, const string& modname, bool inLibrary,
const string& errmsg) {
m_impp->parseFile(fileline, modname, inLibrary, errmsg);
const string& libname, const string& errmsg) {
m_impp->parseFile(fileline, modname, inLibrary, libname, errmsg);
}
void V3Parse::ppPushText(V3ParseImp* impp, const string& text) {
if (text != "") impp->ppPushText(text);

View File

@ -150,6 +150,7 @@ class V3ParseImp final {
FileLine* m_bisonLastFileline = nullptr; // Filename/linenumber of last token
bool m_inLibrary = false; // Currently reading a library vs. regular file
string m_libname; // Config library name (or --work)
int m_lexKwdDepth = 0; // Inside a `begin_keywords
int m_lexKwdLast; // Last LEX state in `begin_keywords
VOptionBool m_unconnectedDrive; // Last unconnected drive
@ -254,6 +255,7 @@ public:
// Return next token, for bison, since bison isn't class based, use a global THIS
AstNetlist* rootp() const { return m_rootp; }
bool inLibrary() const { return m_inLibrary; }
string libname() const { return m_libname; }
VOptionBool unconnectedDrive() const { return m_unconnectedDrive; }
void unconnectedDrive(const VOptionBool flag) { m_unconnectedDrive = flag; }
@ -287,7 +289,7 @@ public:
int tokenToBison() VL_MT_DISABLED; // Pass token to bison
void parseFile(FileLine* fileline, const string& modfilename, bool inLibrary,
const string& errmsg) VL_MT_DISABLED;
const string& libname, const string& errmsg) VL_MT_DISABLED;
void dumpInputsFile() VL_MT_DISABLED;
static void candidatePli(VSpellCheck* spellerp) VL_MT_DISABLED;

View File

@ -1250,7 +1250,7 @@ package_declaration: // ==IEEE: package_declaration
packageFront<nodeModulep>:
yPACKAGE lifetimeE idAny ';'
{ $$ = new AstPackage{$<fl>3, *$3};
{ $$ = new AstPackage{$<fl>3, *$3, PARSEP->libname()};
if ($$->name() == "std") {
if ($$->fileline()->filename() != V3Options::getStdPackagePath()) {
$$->v3error("Redeclaring the 'std' package is not allowed");
@ -1394,7 +1394,7 @@ modFront<nodeModulep>:
// // General note: all *Front functions must call symPushNew before
// // any formal arguments, as the arguments must land in the new scope.
yMODULE lifetimeE idAny
{ $$ = new AstModule{$<fl>3, *$3};
{ $$ = new AstModule{$<fl>3, *$3, PARSEP->libname()};
$$->lifetime($2);
$$->inLibrary(PARSEP->inLibrary() || $$->fileline()->celldefineOn());
$$->modTrace(GRAMMARP->allTracingOn($$->fileline()));
@ -1415,7 +1415,7 @@ importsAndParametersE<nodep>: // IEEE: common part of module_declaration, inte
udpFront<nodeModulep>:
yPRIMITIVE lifetimeE idAny
{ $$ = new AstPrimitive{$<fl>3, *$3};
{ $$ = new AstPrimitive{$<fl>3, *$3, PARSEP->libname()};
$$->inLibrary(true);
$$->lifetime($2);
$$->modTrace(false);
@ -1685,7 +1685,7 @@ interface_declaration: // IEEE: interface_declaration + interface_nonan
intFront<nodeModulep>:
yINTERFACE lifetimeE idAny/*new_interface*/
{ $$ = new AstIface{$<fl>3, *$3};
{ $$ = new AstIface{$<fl>3, *$3, PARSEP->libname()};
$$->inLibrary(true);
$$->lifetime($2);
PARSEP->rootp()->addModulesp($$); }
@ -1772,7 +1772,7 @@ program_declaration: // IEEE: program_declaration + program_nonansi_h
pgmFront<nodeModulep>:
yPROGRAM lifetimeE idAny/*new_program*/
{ $$ = new AstModule{$<fl>3, *$3, AstModule::Program{}};
{ $$ = new AstModule{$<fl>3, *$3, PARSEP->libname(), AstModule::Program{}};
$$->lifetime($2);
$$->inLibrary(PARSEP->inLibrary() || $$->fileline()->celldefineOn());
$$->modTrace(GRAMMARP->allTracingOn($$->fileline()));
@ -6792,7 +6792,7 @@ covergroup_declaration<nodep>: // ==IEEE: covergroup_declaration
covergroup_declarationFront<classp>: // IEEE: part of covergroup_declaration
yCOVERGROUP idAny
{ $$ = new AstClass{$<fl>2, *$2};
{ $$ = new AstClass{$<fl>2, *$2, PARSEP->libname()};
BBCOVERIGN($<fl>1, "Ignoring unsupported: covergroup"); }
;
@ -7196,7 +7196,7 @@ checker_declaration<nodeModulep>: // ==IEEE: part of checker_declaration
checkerFront<nodeModulep>: // IEEE: part of checker_declaration
yCHECKER idAny/*checker_identifier*/
{ $$ = new AstModule{$<fl>2, *$2, AstModule::Checker{}};
{ $$ = new AstModule{$<fl>2, *$2, PARSEP->libname(), AstModule::Checker{}};
$$->modTrace(GRAMMARP->allTracingOn($$->fileline()));
$$->timeunit(PARSEP->timeLastUnit());
$$->unconnectedDrive(PARSEP->unconnectedDrive()); }
@ -7327,14 +7327,14 @@ class_declaration<nodep>: // ==IEEE: part of class_declaration
classFront<classp>: // IEEE: part of class_declaration
// // IEEE 1800-2023: lifetimeE replaced with final_specifierE
classVirtualE yCLASS final_specifierE lifetimeE idAny/*class_identifier*/
{ $$ = new AstClass{$2, *$5};
{ $$ = new AstClass{$2, *$5, PARSEP->libname()};
$$->baseOverride($3);
$$->isVirtual($1);
v3Global.setHasClasses(); }
// // IEEE: part of interface_class_declaration
// // IEEE 1800-2023: lifetimeE removed
| yINTERFACE yCLASS idAny/*class_identifier*/
{ $$ = new AstClass{$2, *$3};
{ $$ = new AstClass{$2, *$3, PARSEP->libname()};
$$->isInterfaceClass(true);
v3Global.setHasClasses(); }
;

View File

@ -0,0 +1,5 @@
*-* All Finished *-*
liba:m1 %m=t.u_1 %l=liba.m1
liba:m3 %m=t.u_1.u_13 %l=liba.m3
libb:m2 %m=t.u_2 %l=libb.m2
libb:m3 %m=t.u_2.u_23 %l=libb.m3

22
test_regress/t/t_config_work.py Executable file
View File

@ -0,0 +1,22 @@
#!/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('simulator')
test.compile(
verilator_flags2=['--binary', '--work liba', 't/t_config_work__liba.v', '--work libb', 't/t_config_work__libb.v'])
test.execute()
# Sort so that 'initial' scheduling order is not relevant
test.files_identical_sorted(test.run_log_filename, test.golden_filename, is_logfile=True)
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
module t;
m1 u_1();
m2 u_2();
final $write("*-* All Finished *-*\n");
endmodule

View File

@ -0,0 +1,14 @@
// 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
module m1;
m3 u_13();
initial $display("liba:m1 %%m=%m %%l=%l");
endmodule
module m3; // Module name duplicated between libraries
initial $display("liba:m3 %%m=%m %%l=%l");
endmodule

View File

@ -0,0 +1,14 @@
// 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
module m2;
m3 u_23();
initial $display("libb:m2 %%m=%m %%l=%l");
endmodule
module m3; // Module name duplicated between libraries
initial $display("libb:m3 %%m=%m %%l=%l");
endmodule

View File

@ -1,8 +1,8 @@
[0] In top.t: Hi
[0] In top.t.sub.write_m (sub)
[0] In top.t.sub.write_m.subblock (sub)
[0] In top.t.sub2.write_m (sub2)
[0] In top.t.sub2.write_m.subblock2 (sub2)
[0] In top.t.sub.write_m (work.sub)
[0] In top.t.sub.write_m.subblock (work.sub)
[0] In top.t.sub2.write_m (work.sub2)
[0] In top.t.sub2.write_m.subblock2 (work.sub2)
a: -0.4=> 0.4 0 0 0
[0] Back \ Quote "
[0] %b=000001100 %0b=1100 %b=00000101010111011101110111100110011001100 %0b=101010111011101110111100110011001100 %b=000001010101111000001001000110100010101100111100000010010001101000101011001111000 %0b=1010101111000001001000110100010101100111100000010010001101000101011001111000

View File

@ -58,7 +58,7 @@ module t;
$swrite(str2, "lib=%l");
`ifdef TEST_VERBOSE $display("chkl %0s",str2); `endif
if (str2 !== "lib=t") $stop;
if (str2 !== "lib=work.t") $stop;
str3 = $sformatf("u=%u", {"a","b","c","d"}); // Value selected so is printable
`ifdef TEST_VERBOSE $display("chku %s", str3); `endif