From 916a89761eb4194aac77bcea777c19b04e25a1b1 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 29 Jun 2025 20:17:27 -0400 Subject: [PATCH] Add `--work` library-selection option (#5891 partial). --- Changes | 1 + bin/verilator | 1 + docs/guide/exe_verilator.rst | 17 +++++- docs/guide/languages.rst | 3 +- docs/guide/verilating.rst | 34 ++++++++++++ docs/spelling.txt | 1 + src/V3Ast.cpp | 5 ++ src/V3AstNodeOther.h | 42 +++++++------- src/V3AstNodes.cpp | 6 +- src/V3Class.cpp | 3 +- src/V3EmitCMake.cpp | 20 +++++-- src/V3EmitMk.cpp | 9 ++- src/V3EmitMkJson.cpp | 12 ++-- src/V3Fork.cpp | 3 +- src/V3Global.cpp | 31 +++++------ src/V3HierBlock.cpp | 13 ++--- src/V3LinkCells.cpp | 83 ++++++++++++++++++++-------- src/V3LinkLevel.cpp | 2 +- src/V3LinkResolve.cpp | 3 +- src/V3Options.cpp | 38 +++++++------ src/V3Options.h | 54 ++++++++++++++---- src/V3Parse.h | 2 +- src/V3ParseImp.cpp | 8 ++- src/V3ParseImp.h | 4 +- src/verilog.y | 18 +++--- test_regress/t/t_config_work.out | 5 ++ test_regress/t/t_config_work.py | 22 ++++++++ test_regress/t/t_config_work.v | 11 ++++ test_regress/t/t_config_work__liba.v | 14 +++++ test_regress/t/t_config_work__libb.v | 14 +++++ test_regress/t/t_display.out | 8 +-- test_regress/t/t_sys_sformat.v | 2 +- 32 files changed, 350 insertions(+), 139 deletions(-) create mode 100644 test_regress/t/t_config_work.out create mode 100755 test_regress/t/t_config_work.py create mode 100644 test_regress/t/t_config_work.v create mode 100644 test_regress/t/t_config_work__liba.v create mode 100644 test_regress/t/t_config_work__libb.v diff --git a/Changes b/Changes index a3bf6a698..a2ba53512 100644 --- a/Changes +++ b/Changes @@ -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] diff --git a/bin/verilator b/bin/verilator index 3ab656be9..2709e6cd0 100755 --- a/bin/verilator +++ b/bin/verilator @@ -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 Set config library for following files -Wpedantic Warn on compliance-test issues -Wwarn- Enable specified warning message -Wwarn-lint Enable lint warning message diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 281e390aa..78c54f463 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -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 + + 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 diff --git a/docs/guide/languages.rst b/docs/guide/languages.rst index beb66229c..24f2e0fbd 100644 --- a/docs/guide/languages.rst +++ b/docs/guide/languages.rst @@ -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 diff --git a/docs/guide/verilating.rst b/docs/guide/verilating.rst index dd760e8ea..ab0d0b7d8 100644 --- a/docs/guide/verilating.rst +++ b/docs/guide/verilating.rst @@ -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 diff --git a/docs/spelling.txt b/docs/spelling.txt index 4a8a63c3b..192d4ea7d 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -332,6 +332,7 @@ Prabhat Prabhu Prateek Pre +Precompiling Preprocess Pretet Pretl diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 2514a53bd..4d7bcd99e 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -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; diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 694b5057e..2a4304ccf 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -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; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index aabf0d51f..02c5a498e 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -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 { diff --git a/src/V3Class.cpp b/src/V3Class.cpp index af9bfddde..a3a1a3aaf 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -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; diff --git a/src/V3EmitCMake.cpp b/src/V3EmitCMake.cpp index c76e33c4d..0949ce3fa 100644 --- a/src/V3EmitCMake.cpp +++ b/src/V3EmitCMake.cpp @@ -39,11 +39,21 @@ class CMakeEmitter final { template 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 diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index 9b8a61c58..6b96ebcca 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -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"); } diff --git a/src/V3EmitMkJson.cpp b/src/V3EmitMkJson.cpp index 09bfc6271..843e7fe87 100644 --- a/src/V3EmitMkJson.cpp +++ b/src/V3EmitMkJson.cpp @@ -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 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()) diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 4948ed955..c5faa2110 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -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}; diff --git a/src/V3Global.cpp b/src/V3Global.cpp index 56ce737f9..bd1c5e30a 100644 --- a/src/V3Global.cpp +++ b/src/V3Global.cpp @@ -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: "); } diff --git a/src/V3HierBlock.cpp b/src/V3HierBlock.cpp index b248a6f21..77ba1992f 100644 --- a/src/V3HierBlock.cpp +++ b/src/V3HierBlock.cpp @@ -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; } diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 1c464b8d1..ed3d5872f 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -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"); + } } }; diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index 8f068d741..e5b834eb9 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -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(); diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 85eb490f9..9694dc6c5 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -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)) { diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 257b1c7fe..e0916c629 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -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 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; } diff --git a/src/V3Options.h b/src/V3Options.h index 8d5a2de1d..3ab444cee 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -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; +using VFileLibSet = std::set; + // ###################################################################### 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- DebugLevelMap m_dumpLevel; // argument: --dumpi- @@ -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; diff --git a/src/V3Parse.h b/src/V3Parse.h index f471f0136..6068988ed 100644 --- a/src/V3Parse.h +++ b/src/V3Parse.h @@ -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; diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index f1ba202b6..9150910f4 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -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); diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index 9dededfdb..c87e182a5 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -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; diff --git a/src/verilog.y b/src/verilog.y index d627010ca..468036835 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1250,7 +1250,7 @@ package_declaration: // ==IEEE: package_declaration packageFront: yPACKAGE lifetimeE idAny ';' - { $$ = new AstPackage{$3, *$3}; + { $$ = new AstPackage{$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: // // 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{$3, *$3}; + { $$ = new AstModule{$3, *$3, PARSEP->libname()}; $$->lifetime($2); $$->inLibrary(PARSEP->inLibrary() || $$->fileline()->celldefineOn()); $$->modTrace(GRAMMARP->allTracingOn($$->fileline())); @@ -1415,7 +1415,7 @@ importsAndParametersE: // IEEE: common part of module_declaration, inte udpFront: yPRIMITIVE lifetimeE idAny - { $$ = new AstPrimitive{$3, *$3}; + { $$ = new AstPrimitive{$3, *$3, PARSEP->libname()}; $$->inLibrary(true); $$->lifetime($2); $$->modTrace(false); @@ -1685,7 +1685,7 @@ interface_declaration: // IEEE: interface_declaration + interface_nonan intFront: yINTERFACE lifetimeE idAny/*new_interface*/ - { $$ = new AstIface{$3, *$3}; + { $$ = new AstIface{$3, *$3, PARSEP->libname()}; $$->inLibrary(true); $$->lifetime($2); PARSEP->rootp()->addModulesp($$); } @@ -1772,7 +1772,7 @@ program_declaration: // IEEE: program_declaration + program_nonansi_h pgmFront: yPROGRAM lifetimeE idAny/*new_program*/ - { $$ = new AstModule{$3, *$3, AstModule::Program{}}; + { $$ = new AstModule{$3, *$3, PARSEP->libname(), AstModule::Program{}}; $$->lifetime($2); $$->inLibrary(PARSEP->inLibrary() || $$->fileline()->celldefineOn()); $$->modTrace(GRAMMARP->allTracingOn($$->fileline())); @@ -6792,7 +6792,7 @@ covergroup_declaration: // ==IEEE: covergroup_declaration covergroup_declarationFront: // IEEE: part of covergroup_declaration yCOVERGROUP idAny - { $$ = new AstClass{$2, *$2}; + { $$ = new AstClass{$2, *$2, PARSEP->libname()}; BBCOVERIGN($1, "Ignoring unsupported: covergroup"); } ; @@ -7196,7 +7196,7 @@ checker_declaration: // ==IEEE: part of checker_declaration checkerFront: // IEEE: part of checker_declaration yCHECKER idAny/*checker_identifier*/ - { $$ = new AstModule{$2, *$2, AstModule::Checker{}}; + { $$ = new AstModule{$2, *$2, PARSEP->libname(), AstModule::Checker{}}; $$->modTrace(GRAMMARP->allTracingOn($$->fileline())); $$->timeunit(PARSEP->timeLastUnit()); $$->unconnectedDrive(PARSEP->unconnectedDrive()); } @@ -7327,14 +7327,14 @@ class_declaration: // ==IEEE: part of class_declaration classFront: // 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(); } ; diff --git a/test_regress/t/t_config_work.out b/test_regress/t/t_config_work.out new file mode 100644 index 000000000..0e99db9dd --- /dev/null +++ b/test_regress/t/t_config_work.out @@ -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 diff --git a/test_regress/t/t_config_work.py b/test_regress/t/t_config_work.py new file mode 100755 index 000000000..04abe3314 --- /dev/null +++ b/test_regress/t/t_config_work.py @@ -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() diff --git a/test_regress/t/t_config_work.v b/test_regress/t/t_config_work.v new file mode 100644 index 000000000..6f3705a76 --- /dev/null +++ b/test_regress/t/t_config_work.v @@ -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 diff --git a/test_regress/t/t_config_work__liba.v b/test_regress/t/t_config_work__liba.v new file mode 100644 index 000000000..539d98c20 --- /dev/null +++ b/test_regress/t/t_config_work__liba.v @@ -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 diff --git a/test_regress/t/t_config_work__libb.v b/test_regress/t/t_config_work__libb.v new file mode 100644 index 000000000..b756b5fa5 --- /dev/null +++ b/test_regress/t/t_config_work__libb.v @@ -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 diff --git a/test_regress/t/t_display.out b/test_regress/t/t_display.out index dee744f90..79cf4f30a 100644 --- a/test_regress/t/t_display.out +++ b/test_regress/t/t_display.out @@ -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 diff --git a/test_regress/t/t_sys_sformat.v b/test_regress/t/t_sys_sformat.v index ae1af86aa..564d66ec8 100644 --- a/test_regress/t/t_sys_sformat.v +++ b/test_regress/t/t_sys_sformat.v @@ -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