diff --git a/bin/verilator b/bin/verilator index 67650dcd1..62970eadd 100755 --- a/bin/verilator +++ b/bin/verilator @@ -419,6 +419,7 @@ detailed descriptions of these arguments. --l2-name Verilog scope name of the top module --language Default language standard to parse -LDFLAGS Linker pre-object arguments for makefile + -libmap Specify library mapping file --lib-create Create a DPI library +libext++[ext]... Extensions for finding modules +librescan Ignored for compatibility diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 7d73e896d..2da0fe466 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -989,6 +989,11 @@ Summary: "+libext+" is relatively standard across Verilog tools. Defaults to ".v+.sv". +.. option:: -libmap + + Specifies library mapping file to sort modules into libraries. This can be + overridden by :vltopt:`-work`. + .. option:: +librescan Ignored for compatibility with other simulators. diff --git a/docs/guide/verilating.rst b/docs/guide/verilating.rst index 99a9a9a3c..7e79d3641 100644 --- a/docs/guide/verilating.rst +++ b/docs/guide/verilating.rst @@ -80,7 +80,8 @@ 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`. +`b.v` to `libb`. In the absence of a `-work` mapping, each module is optionally +assigned to a library based on mappings provided by :vlopt:`-libmap`. 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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3bb4808c1..a08cb45c2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -290,6 +290,7 @@ set(COMMON_SOURCES V3Inst.cpp V3InstrCount.cpp V3Interface.cpp + V3LibMap.cpp V3Life.cpp V3LifePost.cpp V3LinkCells.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 78631a4fc..74696c1b1 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -287,6 +287,7 @@ RAW_OBJS_PCH_ASTNOMT = \ V3Inst.o \ V3InstrCount.o \ V3Interface.o \ + V3LibMap.o \ V3Life.o \ V3LifePost.o \ V3LinkCells.o \ diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 8ed3a6d00..96ae9ee88 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1125,6 +1125,23 @@ public: string name() const override VL_MT_STABLE { return m_name; } ASTGEN_MEMBERS_AstIntfRef; }; +class AstLibrary final : public AstNode { + // Parents: NETLIST + // Library declaration from lib.map file + // @astgen op1 := filesp : List[AstNodeExpr] + // @astgen op2 := incdirsp : List[AstNodeExpr] + std::string m_name; // Library name + +public: + AstLibrary(FileLine* fl, const std::string& name, AstNodeExpr* filesp, AstNodeExpr* incdirsp) + : ASTGEN_SUPER_Library(fl) + , m_name{name} { + addFilesp(filesp); + addIncdirsp(incdirsp); + } + ASTGEN_MEMBERS_AstLibrary; + std::string name() const override VL_MT_STABLE { return m_name; } +}; class AstModport final : public AstNode { // A modport in an interface // @astgen op1 := varsp : List[AstNode] diff --git a/src/V3Global.cpp b/src/V3Global.cpp index 56eda099b..b18e7ef33 100644 --- a/src/V3Global.cpp +++ b/src/V3Global.cpp @@ -22,8 +22,10 @@ #include "V3Error.h" #include "V3File.h" #include "V3HierBlock.h" +#include "V3LibMap.h" #include "V3LinkCells.h" #include "V3Parse.h" +#include "V3ParseImp.h" #include "V3PreShell.h" #include "V3Stats.h" #include "V3ThreadPool.h" @@ -51,12 +53,14 @@ extern "C" const char* __asan_default_options() { void V3Global::boot() { UASSERT(!m_rootp, "call once"); m_rootp = new AstNetlist; + m_libMapp = new V3LibMap; } void V3Global::shutdown() { V3PreShell::shutdown(); VL_DO_CLEAR(delete m_hierGraphp, m_hierGraphp = nullptr); // delete nullptr is safe VL_DO_CLEAR(delete m_threadPoolp, m_threadPoolp = nullptr); // delete nullptr is safe + VL_DO_CLEAR(delete m_libMapp, m_libMapp = nullptr); // delete nullptr is safe #ifdef VL_LEAK_CHECKS if (m_rootp) VL_DO_CLEAR(m_rootp->deleteTree(), m_rootp = nullptr); #endif @@ -84,26 +88,39 @@ void V3Global::readFiles() { if (v3Global.opt.stdWaiver()) { parser.parseFile( new FileLine{V3Options::getStdWaiverPath()}, V3Options::getStdWaiverPath(), false, - "work", "Cannot find verilated_std_waiver.vlt containing built-in lint waivers: "); + false, "work", + "Cannot find verilated_std_waiver.vlt containing built-in lint waivers: "); } // Read .vlt files for (const VFileLibName& filelib : v3Global.opt.vltFiles()) { parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filelib.filename(), - false, filelib.libname(), "Cannot find file containing .vlt file: "); + false, 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, "work", + false, false, "work", "Cannot find verilated_std.sv containing built-in std:: definitions: "); } + // Parse libmap files + for (const string& filename : v3Global.opt.libmapFiles()) { + parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filename, false, true, + "work", "Cannot find file containing libmap definitions: "); + } + // Create library mapping + V3LibMap::map(v3Global.rootp()); + // Read top module for (const auto& filelib : v3Global.opt.vFiles()) { + const string& libname = filelib.libname() == "work" + ? v3Global.libMapp()->matchMapping(filelib.filename()) + : filelib.libname(); parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filelib.filename(), - false, filelib.libname(), "Cannot find file containing module: "); + false, false, libname, "Cannot find file containing module: "); } // Read libraries @@ -111,14 +128,14 @@ void V3Global::readFiles() { // this needs to be done after the top file is read for (const auto& filelib : v3Global.opt.libraryFiles()) { parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filelib.filename(), - true, filelib.libname(), + true, false, filelib.libname(), "Cannot find file containing library module: "); } // Read hierarchical type parameter file for (const auto& filelib : v3Global.opt.hierParamFile()) { parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filelib.filename(), - false, filelib.libname(), + false, false, filelib.libname(), "Cannot open file containing hierarchical parameter declarations: "); } } diff --git a/src/V3Global.h b/src/V3Global.h index c6c478d91..613812779 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -38,6 +38,7 @@ class AstNetlist; class V3HierGraph; +class V3LibMap; class V3ThreadPool; //====================================================================== @@ -103,6 +104,8 @@ class V3Global final { V3HierGraph* m_hierGraphp = nullptr; // Thread Pool, nullptr unless 'verilatedJobs' is known, set via threadPoolp(V3ThreadPool*) V3ThreadPool* m_threadPoolp = nullptr; + // Library Mapping, nullptr unless --libmap is used + V3LibMap* m_libMapp = nullptr; VWidthMinUsage m_widthMinUsage = VWidthMinUsage::LINT_WIDTH; // What AstNode::widthMin() is used for @@ -152,6 +155,7 @@ public: // ACCESSORS (general) AstNetlist* rootp() const VL_MT_SAFE { return m_rootp; } + V3LibMap* libMapp() const VL_PURE { return m_libMapp; } V3ThreadPool* threadPoolp() const VL_PURE { return m_threadPoolp; } void threadPoolp(V3ThreadPool* threadPoolp) { UASSERT(!m_threadPoolp, "attempted to create multiple threadPool singletons"); diff --git a/src/V3LibMap.cpp b/src/V3LibMap.cpp new file mode 100644 index 000000000..b60c3e15e --- /dev/null +++ b/src/V3LibMap.cpp @@ -0,0 +1,107 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Resolve module/signal name references +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2025 by Wilson Snyder. This program is free software; you +// can redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +// Visitor to build library mapping patterns from --libmap files, which may +// be overridden on command line with --work. Patterns are relative to +// libmap file location. +//************************************************************************* + +#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT + +#include "V3LibMap.h" + +#include "V3Graph.h" +#include "V3Options.h" +#include "V3Os.h" +#include "V3Parse.h" +#include "V3SymTable.h" + +#include +#include + +VL_DEFINE_DEBUG_FUNCTIONS; + +class LibMapVisitor final : public VNVisitor { + // STATE + string m_lib; // Library currently being mapped + string m_rel; // Relative path of current libmap file + + // VISITORS + void visit(AstParseRef* nodep) override { + if (!m_lib.empty()) { + const string& pattern = nodep->name(); + v3Global.libMapp()->addPattern(pattern, m_lib, m_rel); + } + } + + void visit(AstLibrary* nodep) override { + VL_RESTORER(m_lib); + VL_RESTORER(m_rel); + m_lib = nodep->name(); + m_rel = V3Os::filenameDir(nodep->fileline()->filename()); + iterateChildren(nodep); + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + } + + void visit(AstNode* nodep) override { iterateChildren(nodep); } + +public: + // CONSTRUCTORS + LibMapVisitor(AstNetlist* nodep) { iterate(nodep); } +}; + +//###################################################################### +// LibMap class functions + +string V3LibMap::matchMapping(const string& filename) { + // Check explicit mappings first + for (const auto& mapping : m_explicitMappings) { + const string& filepath = V3Os::filenameRelativePath(filename, mapping.base()); + if (VString::wildmatch(filepath, mapping.pattern())) { return mapping.libname(); } + } + // Then check wildcard mappings + for (const auto& mapping : m_wildcardMappings) { + const string& filepath = V3Os::filenameRelativePath(filename, mapping.base()); + if (VString::wildmatch(filepath, mapping.pattern())) { return mapping.libname(); } + } + // Then check directory mappings + for (const auto& mapping : m_directoryMappings) { + const string& filepath = V3Os::filenameRelativePath(filename, mapping.base()); + const string& dirpart = V3Os::filenameDir(filepath); + if (VString::wildmatch(dirpart, mapping.pattern())) { return mapping.libname(); } + } + return "work"; +} + +void V3LibMap::addPattern(const string& pattern, const string& libname, const string& base) { + UINFO(4, __FUNCTION__ << ": pattern '" << pattern << "' => library '" << libname << "'"); + + bool isIncDir = pattern.back() == '/'; + const string& nondir = V3Os::filenameNonDir(pattern); + const string& cleanPattern = V3Os::filenameCleanup(pattern); + + if (isIncDir) { + m_directoryMappings.push_back({cleanPattern, libname, base}); + } else if (nondir.find('*') != string::npos || nondir.find('?') != string::npos) { + m_wildcardMappings.push_back({cleanPattern, libname, base}); + } else { + m_explicitMappings.push_back({cleanPattern, libname, base}); + } +} + +void V3LibMap::map(AstNetlist* nodep) { + UINFO(4, __FUNCTION__ << ": "); + { LibMapVisitor{nodep}; } +} diff --git a/src/V3LibMap.h b/src/V3LibMap.h new file mode 100644 index 000000000..28a2fec37 --- /dev/null +++ b/src/V3LibMap.h @@ -0,0 +1,67 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Link modules/signals together +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2025 by Wilson Snyder. This program is free software; you +// can redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#ifndef VERILATOR_V3LIBMAP_H_ +#define VERILATOR_V3LIBMAP_H_ + +#include "config_build.h" +#include "verilatedos.h" + +#include + +class AstNetlist; +class VInFilter; +class V3ParseSym; + +//============================================================================ + +// State to pass between config parsing and cell linking visitors. +class LibMapping final { + const string m_pattern; // Pattern to match + const string m_libname; // Library name + const string m_base; // Relative path of the libmap file + +public: + LibMapping(const string& pattern_, const string& libname_, const string& base_) + : m_pattern{pattern_} + , m_libname{libname_} + , m_base{base_} {} + string pattern() const { return m_pattern; } + string libname() const { return m_libname; } + string base() const { return m_base; } +}; + +class V3LibMap final { +private: + // STATE + // 33.3.1.1 File path resolution + // If a file name potentially matches multiple file path specifications, the path + // specifications shall be resolved in the following order: a) File path specifications that + // end with an explicit file name b) File path specifications that end with a wildcarded file + // name c) File path specifications that end with a directory name + std::vector m_explicitMappings; + std::vector m_wildcardMappings; + std::vector m_directoryMappings; + + // METHODS +public: + string matchMapping(const string& filename); + void addPattern(const string& pattern, const string& libname, const string& base); + + static void map(AstNetlist* nodep) VL_MT_DISABLED; +}; + +#endif // Guard diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 8126da202..102edcc7d 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -310,7 +310,7 @@ 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, m_modp->libname(), ""); + parser.parseFile(nodep->fileline(), prettyName, true, false, m_modp->libname(), ""); V3Error::abortIfErrors(); // We've read new modules, grab new pointers to their names readModNames(); diff --git a/src/V3Options.cpp b/src/V3Options.cpp index d186ac3b7..080b4a8c8 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -364,6 +364,7 @@ void V3Options::checkParameters() { void V3Options::addCppFile(const string& filename) { m_cppFiles.insert(filename); } void V3Options::addCFlags(const string& filename) { m_cFlags.push_back(filename); } +void V3Options::addLibMapFile(const string& filename) { m_libmapFiles.insert(filename); } void V3Options::addCompilerIncludes(const string& filename) { m_compilerIncludes.insert(filename); } @@ -1559,6 +1560,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, }; DECL_OPTION("-default-language", CbVal, setLang).notForRerun(); DECL_OPTION("-language", CbVal, setLang).notForRerun(); + DECL_OPTION("-libmap", CbVal, [this](const char* valp) { addLibMapFile(valp); }); DECL_OPTION("-lib-create", CbVal, [this, fl](const char* valp) { validateIdentifier(fl, valp, "--lib-create"); m_libCreate = valp; diff --git a/src/V3Options.h b/src/V3Options.h index 76780e1f8..1af02e49c 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -207,6 +207,7 @@ private: VFileLibSet m_libraryFiles; // argument: Verilog -v files VFileLibList m_vFiles; // argument: Verilog files to read VFileLibSet m_vltFiles; // argument: Verilator config files to read + VStringSet m_libmapFiles; // argument: --libmap files to read VStringList m_forceIncs; // argument: -FI DebugLevelMap m_debugLevel; // argument: --debugi- DebugLevelMap m_dumpLevel; // argument: --dumpi- @@ -469,6 +470,7 @@ public: void addCompilerIncludes(const string& filename); void addLdLibs(const string& filename); void addMakeFlags(const string& filename); + void addLibMapFile(const string& filename); void addLibraryFile(const string& filename, const string& libname); void addVFile(const string& filename, const string& libname); void addVltFile(const string& filename, const string& libname); @@ -679,6 +681,7 @@ public: const VStringList& ldLibs() const { return m_ldLibs; } const VStringList& makeFlags() const { return m_makeFlags; } const VFileLibSet& libraryFiles() const { return m_libraryFiles; } + const VStringSet& libmapFiles() const { return m_libmapFiles; } const VFileLibList& vFiles() const { return m_vFiles; } const VFileLibSet& vltFiles() const { return m_vltFiles; } const VStringList& forceIncs() const { return m_forceIncs; } diff --git a/src/V3Parse.h b/src/V3Parse.h index 6068988ed..a637d06c2 100644 --- a/src/V3Parse.h +++ b/src/V3Parse.h @@ -42,7 +42,7 @@ public: // METHODS // Preprocess and read the Verilog file specified into the netlist database - void parseFile(FileLine* fileline, const string& modname, bool inLibrary, + void parseFile(FileLine* fileline, const string& modname, bool inLibrary, bool inLibMap, const string& libname, const string& errmsg) VL_MT_DISABLED; // Push preprocessed text to the lexer diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index cba0ed5a1..f370aaff8 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -293,16 +293,18 @@ void V3ParseImp::preprocDumps(std::ostream& os, bool forInputs) { } void V3ParseImp::parseFile(FileLine* fileline, const string& modfilename, bool inLibrary, - const string& libname, + bool inLibMap, const string& libname, const string& errmsg) { // "" for no error, make fake node const string nondirname = V3Os::filenameNonDir(modfilename); const string modname = V3Os::filenameNonDirExt(modfilename); - UINFO(2, __FUNCTION__ << ": " << modname << (inLibrary ? " [LIB]" : "")); + UINFO(2, __FUNCTION__ << ": " << modname << (inLibrary ? " [LIB]" : "") + << (inLibMap ? " [LIBMAP]" : "")); m_lexFileline = new FileLine{fileline}; m_lexFileline->newContent(); m_bisonLastFileline = m_lexFileline; m_inLibrary = inLibrary; + m_inLibMap = inLibMap; m_libname = libname; // Preprocess into m_ppBuffer @@ -823,9 +825,9 @@ V3Parse::V3Parse(AstNetlist* rootp, VInFilter* filterp) { V3Parse::~V3Parse() { // VL_DO_CLEAR(delete m_impp, m_impp = nullptr); } -void V3Parse::parseFile(FileLine* fileline, const string& modname, bool inLibrary, +void V3Parse::parseFile(FileLine* fileline, const string& modname, bool inLibrary, bool inLibMap, const string& libname, const string& errmsg) { - m_impp->parseFile(fileline, modname, inLibrary, libname, errmsg); + m_impp->parseFile(fileline, modname, inLibrary, inLibMap, 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 3bbd111c1..380d84c1e 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -151,6 +151,7 @@ class V3ParseImp final { FileLine* m_bisonLastFileline = nullptr; // Filename/linenumber of last token bool m_inLibrary = false; // Currently reading a library vs. regular file + bool m_inLibMap = false; // Currently reading a libmap 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 @@ -264,6 +265,7 @@ public: // Return next token, for bison, since bison isn't class based, use a global THIS AstNetlist* rootp() const { return m_rootp; } + bool inLibMap() const { return m_inLibMap; } bool inLibrary() const { return m_inLibrary; } string libname() const { return m_libname; } VOptionBool unconnectedDrive() const { return m_unconnectedDrive; } @@ -299,7 +301,7 @@ public: // Preprocess and read the Verilog file specified into the netlist database int tokenToBison() VL_MT_DISABLED; // Pass token to bison - void parseFile(FileLine* fileline, const string& modfilename, bool inLibrary, + void parseFile(FileLine* fileline, const string& modfilename, bool inLibrary, bool inLibMap, const string& libname, const string& errmsg) VL_MT_DISABLED; void dumpInputsFile() VL_MT_DISABLED; void dumpTokensAhead(int line) VL_MT_DISABLED; diff --git a/src/V3PreLex.l b/src/V3PreLex.l index 4db29b811..1fc9f61e1 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -84,6 +84,7 @@ static void appendDefValue(const char* t, size_t l) { LEXP->appendDefValue(t, l) %x QQQMODE %x STRIFY %x STRMODE +%x LIBMODE /* drop: Drop Ctrl-Z - can't pass thru or may EOF the output too soon */ @@ -119,6 +120,8 @@ bom [\357\273\277] "`ifdef" { FL_FWDC; return VP_IFDEF; } "`ifndef" { FL_FWDC; return VP_IFNDEF; } "`include" { FL_FWDC; return VP_INCLUDE; } +"include" { FL_FWDC; yy_push_state(LIBMODE); return VP_TEXT; } +"library" { FL_FWDC; yy_push_state(LIBMODE); return VP_TEXT; } "`undef" { FL_FWDC; return VP_UNDEF; } "`undefineall" { FL_FWDC; return VP_UNDEFINEALL; } "`error" { FL_FWDC; if (!pedantic()) return VP_ERROR; else return VP_DEFREF; } @@ -319,6 +322,14 @@ bom [\357\273\277] [\\]. { yymore(); } [\>] { FL_FWDC; yy_pop_state(); return VP_STRING; } + /* Library/config file paths */ +{crnl} { FL_FWDC; linenoInc(); yytext=(char*)"\n"; yyleng=1; return VP_WHITE; } +{wsn}+ { FL_FWDC; return VP_WHITE; } +{quote} { yy_push_state(STRMODE); yymore(); } +"//" { yy_push_state(CMTONEM); yymore(); } +";" { FL_FWDC; yy_pop_state(); return VP_TEXT; } +. { FL_FWDC; return VP_TEXT; } + /* Reading definition formal parenthesis (or not) to begin formal arguments */ /* Note '(' must IMMEDIATELY follow definition name */ [(] { FL_FWDC; appendDefValue("(", 1); LEXP->m_formalLevel=1; diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 519096ee5..bfe406bd9 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -65,6 +65,7 @@ #include "V3Inline.h" #include "V3Inst.h" #include "V3Interface.h" +#include "V3LibMap.h" #include "V3Life.h" #include "V3LifePost.h" #include "V3LinkDot.h" diff --git a/src/verilog.l b/src/verilog.l index 426ed4e65..d7f62b0c3 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -54,6 +54,12 @@ yylval.strp = PARSEP->newString(AstNode::encodeName(str)); \ } while (false) +#define STR_FILEPATH \ + do { \ + const string str(yytext, yyleng); \ + yylval.strp = PARSEP->newString(str); \ + } while (false) + #define ERROR_RSVD_WORD(language) \ do { \ yylval.fl->v3warn(E_UNSUPPORTED, "Unsupported: " << language \ @@ -88,6 +94,7 @@ static double lexParseDouble(FileLine* fl, const char* textp, size_t length) { %s ATTRMODE QQQ STRING TABLE EDGEDESC %s VA5 SAX VLT %s SYSCHDR SYSCHDRP SYSCINT SYSCIMP SYSCIMPH SYSCCTOR SYSCDTOR +%x FILEPATH ws [ \t\f\r]+ wsnr [ \t\f]+ @@ -106,6 +113,17 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} .|\n { BEGIN STATE_VERILOG_RECENT; yyless(0); } + /************************************************************************/ + /* File paths */ +{ + {ws} { FL_FWD; FL_BRK; } + {crnl} { FL_FWD; FL_BRK; } + "-incdir" { FL; return yINCDIR; } + ";" { yy_pop_state(); return ';'; } + "," { FL; return ','; } + [^ \t\n\r;,"]+ { FL; STR_FILEPATH; return yaSTRING; } +} + /************************************************************************/ /* Verilator control files */ { @@ -461,15 +479,22 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "config" { FL; return yCONFIG; } "design" { FL; return yDESIGN; } "endconfig" { FL; return yENDCONFIG; } - "incdir" { FL; ERROR_RSVD_WORD("IEEE 1800-2001-config lib.map"); FL_BRK; } - "include" { FL; yylval.fl->v3warn(E_UNSUPPORTED, "Unsupported: IEEE 1800-2001-config lib.map" - " reserved word not implemented: 'include'\n" - << yylval.fl->warnMore() << "... Suggest unless in a lib.map file," - " want `include instead"); - FL_BRK; } + "incdir" { FL; yy_push_state(FILEPATH); return yINCDIR; } + "include" { FL; + if (PARSEP->inLibMap()) { + yy_push_state(FILEPATH); + return yINCLUDE; + } else { + yylval.fl->v3warn(E_UNSUPPORTED, "Unsupported: IEEE 1800-2001-config lib.map" + " reserved word not implemented: 'include'\n" + << yylval.fl->warnMore() << "... Suggest unless in a lib.map file," + " want `include instead"); + FL_BRK; + } + } "instance" { FL; return yINSTANCE; } "liblist" { FL; return yLIBLIST; } - "library" { FL; ERROR_RSVD_WORD("IEEE 1800-2001-config lib.map"); FL_BRK; } + "library" { FL; yy_push_state(FILEPATH); return yLIBRARY; } "use" { FL; return yUSE; } } diff --git a/src/verilog.y b/src/verilog.y index 59be4e9fb..4231b47fe 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -433,6 +433,8 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yIMPLEMENTS "implements" %token yIMPLIES "implies" %token yIMPORT "import" +%token yINCDIR "incdir" +%token yINCLUDE "include" %token yINITIAL "initial" %token yINOUT "inout" %token yINPUT "input" @@ -448,6 +450,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yJOIN_NONE "join_none" %token yLET "let" %token yLIBLIST "liblist" +%token yLIBRARY "library" %token yLOCALPARAM "localparam" %token yLOCAL__COLONCOLON "local-then-::" %token yLOCAL__ETC "local" @@ -1028,6 +1031,9 @@ descriptionList: // IEEE: part of source_text description: // ==IEEE: description module_declaration { } // // udp_declaration moved into module_declaration + // // library_declaration and include_statement moved from library_description + | library_declaration { PARSEP->rootp()->addMiscsp($1); } + | include_statement { } | interface_declaration { } | program_declaration { } | package_declaration { } @@ -7999,33 +8005,26 @@ colonConfigE: // IEEE: [ ':' yCONFIG] //********************************************************************** // Config - lib.map // -// TODO when implement this support, add -libmap option which takes multiple files. -//UNSUP library_text: // == IEEE: library_text (note is top-level entry point) -//UNSUP library_description { } -//UNSUP | library_text library_description { } -//UNSUP ; - -//UNSUP library_description: // == IEEE: library_description -//UNSUP // // IEEE: library_declaration -//UNSUP yLIBRARY idAny/*library_identifier*/ file_path_specList ';' -//UNSUP { BBUNSUP($1, "Unsupported: config lib.map library"); } -//UNSUP yLIBRARY idAny/*library_identifier*/ file_path_specList '-' yINCDIR file_path_specList ';' -//UNSUP { BBUNSUP($1, "Unsupported: config lib.map library"); } -//UNSUP // // IEEE: include_statement -//UNSUP | yINCLUDE file_path_spec ';' { BBUNSUP($1, "Unsupported: config include"); } -//UNSUP | config_declaration { } -//UNSUP | ';' { } -//UNSUP ; - -//UNSUP file_path_specList: // IEEE: file_path_spec { ',' file_path_spec } -//UNSUP file_path_spec { } -//UNSUP | file_path_specList ',' file_path_spec { } -//UNSUP ; - -//UNSUP file_path_spec: // IEEE: file_path_spec -//UNSUP Needs to be lexer rule, Note '/' '*' must not be a comment. -//UNSUP ; +library_declaration: // IEEE: library_declaration + yLIBRARY yaSTRING file_path_specList incdirE ';' + { $$ = new AstLibrary{$1, *$2, $3, $4}; } + ; +incdirE: // IEEE: [ '-' yINCDIR file_path_specList ';'] + /* empty */ { $$ = nullptr; } + // https://accellera.mantishub.io/view.php?id=1166 + | yINCDIR file_path_specList { $$ = nullptr; BBUNSUP($1, "Unsupported: config incdir"); } + ; +include_statement: // IEEE: include_statement + yINCLUDE file_path_spec ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: config include"); } + ; +file_path_specList: // IEEE: file_path_spec { ',' file_path_spec } + file_path_spec { $$ = $1; } + | file_path_specList ',' file_path_spec { $$ = addNextNull($1, $3); } + ; +file_path_spec: // IEEE: file_path_spec + yaSTRING { $$ = new AstParseRef{$1, *$1}; } + ; //********************************************************************** // VLT Files diff --git a/test_regress/t/t_config_libmap.out b/test_regress/t/t_config_libmap.out index 8b2993499..5fe7bb800 100644 --- a/test_regress/t/t_config_libmap.out +++ b/test_regress/t/t_config_libmap.out @@ -1,31 +1,7 @@ -%Error-UNSUPPORTED: t/t_config_libmap.map:9:1: Unsupported: IEEE 1800-2001-config lib.map reserved word not implemented: 'include' - : ... Suggest unless in a lib.map file, want `include instead - 9 | include ./t_config_libmap_inc.map - | ^~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: t/t_config_libmap.map:9:9: syntax error, unexpected '.' - 9 | include ./t_config_libmap_inc.map - | ^ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error-UNSUPPORTED: t/t_config_libmap.map:11:1: Unsupported: IEEE 1800-2001-config lib.map reserved word not implemented: 'library' - 11 | library rtllib *.v; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_config_libmap.map:12:1: Unsupported: IEEE 1800-2001-config lib.map reserved word not implemented: 'library' - 12 | library rtllib2 *.v, *.sv; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_config_libmap.map:13:1: Unsupported: IEEE 1800-2001-config lib.map reserved word not implemented: 'library' - 13 | library rtllib3 *.v -incdir *.vh; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_config_libmap.map:13:22: Unsupported: IEEE 1800-2001-config lib.map reserved word not implemented: 'incdir' - 13 | library rtllib3 *.v -incdir *.vh; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_config_libmap.map:14:1: Unsupported: IEEE 1800-2001-config lib.map reserved word not implemented: 'library' - 14 | library rtllib4 *.v -incdir *.vh, *.svh; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_config_libmap.map:14:22: Unsupported: IEEE 1800-2001-config lib.map reserved word not implemented: 'incdir' - 14 | library rtllib4 *.v -incdir *.vh, *.svh; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_config_libmap.map:17:1: Unsupported: IEEE 1800-2001-config lib.map reserved word not implemented: 'library' - 17 | library gatelib . - | ^~~~~~~ -%Error: Exiting due to +*-* All Finished *-* +m1 %m=top.t.u_1 %l=rtllib.m1 +m2 %m=top.t.u_2 %l=rtllib.m2 +m3 %m=top.t.u_3 %l=gatelib.m3 +m5 %m=top.t.u_5 %l=rtllib5.m5 +other %m=top.t.u_o %l=rtllib2.other +x4 %m=top.t.u_4 %l=work.x4 diff --git a/test_regress/t/t_config_libmap.py b/test_regress/t/t_config_libmap.py index 4438b7436..cd5705948 100755 --- a/test_regress/t/t_config_libmap.py +++ b/test_regress/t/t_config_libmap.py @@ -9,10 +9,17 @@ import vltest_bootstrap -test.scenarios('linter') +test.scenarios('simulator') -test.lint(verilator_flags2=["t/" + test.name + ".map"], - fails=test.vlt_all, - expect_filename=test.golden_filename) +test.compile(verilator_flags2=[ + "-libmap t/t_config_libmap/lib.map", "t/t_config_libmap/m1.v", "t/t_config_libmap/m2.sv", + "t/t_config_libmap/m3.vg", "t/t_config_libmap/m5.v", "t/t_config_libmap/x4.v", + "t/t_config_libmap/sub/other.sv", "--top-module cfg" +]) + +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_libmap.v b/test_regress/t/t_config_libmap.v index 7bb33e757..3d5df6376 100644 --- a/test_regress/t/t_config_libmap.v +++ b/test_regress/t/t_config_libmap.v @@ -5,8 +5,15 @@ // SPDX-License-Identifier: CC0-1.0 module t; - initial begin - $write("*-* All Finished *-*\n"); - $finish; - end + m1 u_1(); + m2 u_2(); + m3 u_3(); + x4 u_4(); + m5 u_5(); + other u_o(); + + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end endmodule diff --git a/test_regress/t/t_config_libmap.map b/test_regress/t/t_config_libmap/lib.map similarity index 69% rename from test_regress/t/t_config_libmap.map rename to test_regress/t/t_config_libmap/lib.map index e2d7e6e46..1393eb2d3 100644 --- a/test_regress/t/t_config_libmap.map +++ b/test_regress/t/t_config_libmap/lib.map @@ -6,12 +6,10 @@ // SPDX-License-Identifier: CC0-1.0 // lib.map file: -include ./t_config_libmap_inc.map -library rtllib *.v; -library rtllib2 *.v, *.sv; -library rtllib3 *.v -incdir *.vh; -library rtllib4 *.v -incdir *.vh, *.svh; +library rtllib m?.v, m*.sv; // wildcard match (?, *) +library rtllib2 sub/; // include directory match +library rtllib5 m5.v; // explicit match // Note this does not start a comment library gatelib ./*.vg; diff --git a/test_regress/t/t_config_libmap/m1.v b/test_regress/t/t_config_libmap/m1.v new file mode 100644 index 000000000..7322ff636 --- /dev/null +++ b/test_regress/t/t_config_libmap/m1.v @@ -0,0 +1,9 @@ +// 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; + initial $display("m1 %%m=%m %%l=%l"); +endmodule diff --git a/test_regress/t/t_config_libmap/m2.sv b/test_regress/t/t_config_libmap/m2.sv new file mode 100644 index 000000000..debd8ce2b --- /dev/null +++ b/test_regress/t/t_config_libmap/m2.sv @@ -0,0 +1,9 @@ +// 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; + initial $display("m2 %%m=%m %%l=%l"); +endmodule diff --git a/test_regress/t/t_config_libmap/m3.vg b/test_regress/t/t_config_libmap/m3.vg new file mode 100644 index 000000000..4e28607c5 --- /dev/null +++ b/test_regress/t/t_config_libmap/m3.vg @@ -0,0 +1,9 @@ +// 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 m3; + initial $display("m3 %%m=%m %%l=%l"); +endmodule diff --git a/test_regress/t/t_config_libmap/m5.v b/test_regress/t/t_config_libmap/m5.v new file mode 100644 index 000000000..45465c839 --- /dev/null +++ b/test_regress/t/t_config_libmap/m5.v @@ -0,0 +1,9 @@ +// 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 m5; + initial $display("m5 %%m=%m %%l=%l"); +endmodule diff --git a/test_regress/t/t_config_libmap/sub/other.sv b/test_regress/t/t_config_libmap/sub/other.sv new file mode 100644 index 000000000..e95618256 --- /dev/null +++ b/test_regress/t/t_config_libmap/sub/other.sv @@ -0,0 +1,9 @@ +// 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 other; + initial $display("other %%m=%m %%l=%l"); +endmodule diff --git a/test_regress/t/t_config_libmap/x4.v b/test_regress/t/t_config_libmap/x4.v new file mode 100644 index 000000000..d7b4f8e9f --- /dev/null +++ b/test_regress/t/t_config_libmap/x4.v @@ -0,0 +1,9 @@ +// 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 x4; + initial $display("x4 %%m=%m %%l=%l"); +endmodule diff --git a/test_regress/t/t_config_libmap_inc.map b/test_regress/t/t_config_libmap_inc.map index 44a8994ce..1d01af834 100644 --- a/test_regress/t/t_config_libmap_inc.map +++ b/test_regress/t/t_config_libmap_inc.map @@ -6,13 +6,8 @@ // SPDX-License-Identifier: CC0-1.0 // lib.map file: -include ./t_config_libmap_inc.map +include ./t_config_libmap/lib.map; -library rtllib *.v; -library rtllib2 *.v, *.sv; -library rtllib3 *.v -incdir *.vh; -library rtllib4 *.v -incdir *.vh, *.svh; - -config cfg; - design t; -endconfig +library rtllib1 somepath -incdir somedir; +library rtllib2 somewild*; +library rtllib3 someexplicit; diff --git a/test_regress/t/t_config_libmap_inc.out b/test_regress/t/t_config_libmap_inc.out new file mode 100644 index 000000000..835e77594 --- /dev/null +++ b/test_regress/t/t_config_libmap_inc.out @@ -0,0 +1,8 @@ +%Error-UNSUPPORTED: t/t_config_libmap_inc.map:9:1: Unsupported: config include + 9 | include ./t_config_libmap/lib.map; + | ^~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_config_libmap_inc.map:11:26: Unsupported: config incdir + 11 | library rtllib1 somepath -incdir somedir; + | ^~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_config_libmap_inc.py b/test_regress/t/t_config_libmap_inc.py new file mode 100755 index 000000000..6dc55a9b4 --- /dev/null +++ b/test_regress/t/t_config_libmap_inc.py @@ -0,0 +1,19 @@ +#!/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.scenarios('linter') + +test.lint(verilator_flags2=["--lint-only", "-libmap t/t_config_libmap_inc.map"], + fails=test.vlt_all, + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_config_libmap_inc.v b/test_regress/t/t_config_libmap_inc.v new file mode 100644 index 000000000..c24a75a62 --- /dev/null +++ b/test_regress/t/t_config_libmap_inc.v @@ -0,0 +1,12 @@ +// 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; + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_config_work.map b/test_regress/t/t_config_work.map new file mode 100644 index 000000000..2e50445e4 --- /dev/null +++ b/test_regress/t/t_config_work.map @@ -0,0 +1,10 @@ +// -*- Verilog -*- +// 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 + +// lib.map file: + +library nonelib none.sv, none*.sv, none/; // no matches diff --git a/test_regress/t/t_config_work.py b/test_regress/t/t_config_work.py index 45a18e197..c39443cc5 100755 --- a/test_regress/t/t_config_work.py +++ b/test_regress/t/t_config_work.py @@ -12,7 +12,8 @@ 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' + '--binary', '--work liba', 't/t_config_work__liba.v', '--work libb', 't/t_config_work__libb.v', + '-libmap t_config_work.map' ]) test.execute()