// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Options parsing // // Code available from: http://www.veripool.org/verilator // //************************************************************************* // // Copyright 2003-2018 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. // // Verilator is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include #include #ifndef _WIN32 # include #endif #include #include #include #include #include #include #include #include #include "V3Global.h" #include "V3String.h" #include "V3Os.h" #include "V3Options.h" #include "V3Error.h" #include "V3File.h" #include "V3PreShell.h" #include "config_rev.h" //###################################################################### // V3 Internal state class V3OptionsImp { public: // TYPES typedef std::map > DirMap; // Directory listing // STATE std::list m_allArgs; // List of every argument encountered std::list m_incDirUsers; // Include directories (ordered) std::set m_incDirUserSet; // Include directories (for removing duplicates) std::list m_incDirFallbacks; // Include directories (ordered) std::set m_incDirFallbackSet; // Include directories (for removing duplicates) std::map m_langExts; // Language extension map std::list m_libExtVs; // Library extensions (ordered) std::set m_libExtVSet; // Library extensions (for removing duplicates) DirMap m_dirMap; // Directory listing // ACCESSOR METHODS void addIncDirUser(const string& incdir) { if (m_incDirUserSet.find(incdir) == m_incDirUserSet.end()) { m_incDirUserSet.insert(incdir); m_incDirUsers.push_back(incdir); m_incDirFallbacks.remove(incdir); // User has priority over Fallback m_incDirFallbackSet.erase(incdir); // User has priority over Fallback } } void addIncDirFallback(const string& incdir) { if (m_incDirUserSet.find(incdir) == m_incDirUserSet.end()) { // User has priority over Fallback if (m_incDirFallbackSet.find(incdir) == m_incDirFallbackSet.end()) { m_incDirFallbackSet.insert(incdir); m_incDirFallbacks.push_back(incdir); } } } void addLangExt(const string& langext, const V3LangCode& lc) { // New language extension replaces any pre-existing one. (void)m_langExts.erase(langext); m_langExts[langext] = lc; } void addLibExtV(const string& libext) { if (m_libExtVSet.find(libext) == m_libExtVSet.end()) { m_libExtVSet.insert(libext); m_libExtVs.push_back(libext); } } V3OptionsImp() {} ~V3OptionsImp() {} }; void V3Options::addIncDirUser(const string& incdir) { m_impp->addIncDirUser(incdir); } void V3Options::addIncDirFallback(const string& incdir) { m_impp->addIncDirFallback(incdir); } void V3Options::addLangExt(const string& langext, const V3LangCode& lc) { m_impp->addLangExt(langext, lc); } void V3Options::addLibExtV(const string& libext) { m_impp->addLibExtV(libext); } void V3Options::addDefine(const string& defline, bool allowPlus) { // Split +define+foo=value into the appropriate parts and parse // Optional + says to allow multiple defines on the line // + is not quotable, as other simulators do not allow that string left = defline; while (left != "") { string def = left; string::size_type pos; if (allowPlus && ((pos=left.find("+")) != string::npos)) { left = left.substr(pos+1); def.erase(pos); } else { left = ""; } string value; if ((pos=def.find("=")) != string::npos) { value = def.substr(pos+1); def.erase(pos); } V3PreShell::defineCmdLine(def,value); } } void V3Options::addParameter(const string& paramline, bool allowPlus) { // Split +define+foo=value into the appropriate parts and parse // Optional + says to allow multiple defines on the line // + is not quotable, as other simulators do not allow that string left = paramline; while (left != "") { string param = left; string::size_type pos; if (allowPlus && ((pos=left.find("+")) != string::npos)) { left = left.substr(pos+1); param.erase(pos); } else { left = ""; } string value; if ((pos=param.find("=")) != string::npos) { value = param.substr(pos+1); param.erase(pos); } UINFO(4,"Add parameter"<second; m_parameters.erase(m_parameters.find(name)); return value; } void V3Options::checkParameters() { if (!m_parameters.empty()) { std::stringstream msg; msg << "Parameters from the command line were not found in the design:"; for (std::map::iterator it = m_parameters.begin(); it != m_parameters.end(); ++it) { msg << " " << it->first; } v3fatal(msg.str()<m_allArgs.push_back(arg); } string V3Options::allArgsString() { string out; for (std::list::iterator it=m_impp->m_allArgs.begin(); it!=m_impp->m_allArgs.end(); ++it) { if (out != "") out += " "; out += *it; } return out; } //###################################################################### // V3LangCode class functions V3LangCode::V3LangCode(const char* textp) { // Return code for given string, or ERROR, which is a bad code for (int codei=V3LangCode::L_ERROR; codei0) ::close(fd); } } string V3Options::fileExists(const string& filename) { // Surprisingly, for VCS and other simulators, this process // is quite slow; presumably because of re-reading each directory // many times. So we read a whole dir at once and cache it string dir = V3Os::filenameDir(filename); string basename = V3Os::filenameNonDir(filename); V3OptionsImp::DirMap::iterator diriter = m_impp->m_dirMap.find(dir); if (diriter == m_impp->m_dirMap.end()) { // Read the listing m_impp->m_dirMap.insert(std::make_pair(dir, std::set() )); diriter = m_impp->m_dirMap.find(dir); std::set* setp = &(diriter->second); if (DIR* dirp = opendir(dir.c_str())) { while (struct dirent* direntp = readdir(dirp)) { setp->insert(direntp->d_name); } closedir(dirp); } } // Find it std::set* filesetp = &(diriter->second); std::set::iterator fileiter = filesetp->find(basename); if (fileiter == filesetp->end()) { return ""; // Not found } // Check if it is a directory, ignore if so string filenameOut = V3Os::filenameFromDirBase(dir, basename); if (!fileStatNormal(filenameOut)) return ""; // Directory return filenameOut; } string V3Options::filePathCheckOneDir(const string& modname, const string& dirname) { for (std::list::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) { string fn = V3Os::filenameFromDirBase(dirname, modname+*extIter); string exists = fileExists(fn); if (exists!="") { // Strip ./, it just looks ugly if (exists.substr(0,2)=="./") exists.erase(0,2); return exists; } } return ""; } string V3Options::filePath(FileLine* fl, const string& modname, const string& lastpath, const string& errmsg) { // Error prefix or "" to suppress error // Find a filename to read the specified module name, // using the incdir and libext's. // Return "" if not found. for (std::list::iterator dirIter=m_impp->m_incDirUsers.begin(); dirIter!=m_impp->m_incDirUsers.end(); ++dirIter) { string exists = filePathCheckOneDir(modname, *dirIter); if (exists!="") return exists; } for (std::list::iterator dirIter=m_impp->m_incDirFallbacks.begin(); dirIter!=m_impp->m_incDirFallbacks.end(); ++dirIter) { string exists = filePathCheckOneDir(modname, *dirIter); if (exists!="") return exists; } if (m_relativeIncludes) { string exists = filePathCheckOneDir(modname, lastpath); if (exists!="") return V3Os::filenameRealPath(exists); } // Warn and return not found if (errmsg != "") { fl->v3error(errmsg+modname); filePathLookedMsg(fl, modname); } return ""; } void V3Options::filePathLookedMsg(FileLine* fl, const string& modname) { static bool shown_notfound_msg = false; if (!shown_notfound_msg) { shown_notfound_msg = true; if (m_impp->m_incDirUsers.empty()) { fl->v3error("This may be because there's no search path specified with -I."<v3error("Looked in:"<::iterator dirIter=m_impp->m_incDirUsers.begin(); dirIter!=m_impp->m_incDirUsers.end(); ++dirIter) { for (std::list::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) { string fn = V3Os::filenameFromDirBase(*dirIter,modname+*extIter); fl->v3error(" "<::iterator dirIter=m_impp->m_incDirFallbacks.begin(); dirIter!=m_impp->m_incDirFallbacks.end(); ++dirIter) { for (std::list::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) { string fn = V3Os::filenameFromDirBase(*dirIter,modname+*extIter); fl->v3error(" "<::iterator it = m_impp->m_langExts.find(ext); if (it != m_impp->m_langExts.end()) { return it->second; } } return m_defaultLanguage; } //###################################################################### // Environment string V3Options::getenvBuiltins(const string& var) { if (var == "PERL") return getenvPERL(); else if (var == "SYSTEMC") return getenvSYSTEMC(); else if (var == "SYSTEMC_ARCH") return getenvSYSTEMC_ARCH(); else if (var == "SYSTEMC_INCLUDE") return getenvSYSTEMC_INCLUDE(); else if (var == "SYSTEMC_LIBDIR") return getenvSYSTEMC_LIBDIR(); else if (var == "VERILATOR_ROOT") return getenvVERILATOR_ROOT(); else { return V3Os::getenvStr(var,""); } } string V3Options::getenvPERL() { return V3Os::getenvStr("PERL","perl"); } string V3Options::getenvSYSTEMC() { string var = V3Os::getenvStr("SYSTEMC",""); if (var == "" && string(DEFENV_SYSTEMC) != "") { var = DEFENV_SYSTEMC; V3Os::setenvStr("SYSTEMC", var, "Hardcoded at build time"); } return var; } string V3Options::getenvSYSTEMC_ARCH() { string var = V3Os::getenvStr("SYSTEMC_ARCH",""); if (var == "" && string(DEFENV_SYSTEMC_ARCH) != "") { var = DEFENV_SYSTEMC_ARCH; V3Os::setenvStr("SYSTEMC_ARCH", var, "Hardcoded at build time"); } if (var == "") { #if defined (__MINGW32__) // Hardcoded with MINGW current version. Would like a better way. string sysname = "MINGW32_NT-5.0"; var = "mingw32"; #elif defined (_WIN32) string sysname = "WIN32"; var = "win32"; #else struct utsname uts; uname(&uts); string sysname = VString::downcase(uts.sysname); // aka 'uname -s' if (VString::wildmatch(sysname.c_str(), "*solaris*")) { var = "gccsparcOS5"; } else if (VString::wildmatch(sysname.c_str(), "*cygwin*")) { var ="cygwin"; } else { var = "linux"; } #endif V3Os::setenvStr("SYSTEMC_ARCH", var,"From sysname '"+sysname+"'"); } return var; } string V3Options::getenvSYSTEMC_INCLUDE() { string var = V3Os::getenvStr("SYSTEMC_INCLUDE",""); if (var == "" && string(DEFENV_SYSTEMC_INCLUDE) != "") { var = DEFENV_SYSTEMC_INCLUDE; V3Os::setenvStr("SYSTEMC_INCLUDE", var, "Hardcoded at build time"); } if (var == "") { string sc = getenvSYSTEMC(); if (sc != "") var = sc+"/include"; } // Only correct or check it if we really need the value if (v3Global.opt.usingSystemCLibs()) { if (var == "") { v3fatal("Need $SYSTEMC_INCLUDE in environment or when Verilator configured\n" "Probably System-C isn't installed, see http://www.systemc.org\n"); } } return var; } string V3Options::getenvSYSTEMC_LIBDIR() { string var = V3Os::getenvStr("SYSTEMC_LIBDIR",""); if (var == "" && string(DEFENV_SYSTEMC_LIBDIR) != "") { var = DEFENV_SYSTEMC_LIBDIR; V3Os::setenvStr("SYSTEMC_LIBDIR", var, "Hardcoded at build time"); } if (var == "") { string sc = getenvSYSTEMC(); string arch = getenvSYSTEMC_ARCH(); if (sc != "" && arch != "") var = sc+"/lib-"+arch; } // Only correct or check it if we really need the value if (v3Global.opt.usingSystemCLibs()) { if (var == "") { v3fatal("Need $SYSTEMC_LIBDIR in environment or when Verilator configured\n" "Probably System-C isn't installed, see http://www.systemc.org\n"); } } return var; } string V3Options::getenvVERILATOR_ROOT() { string var = V3Os::getenvStr("VERILATOR_ROOT",""); if (var == "" && string(DEFENV_VERILATOR_ROOT) != "") { var = DEFENV_VERILATOR_ROOT; V3Os::setenvStr("VERILATOR_ROOT", var, "Hardcoded at build time"); } if (var == "") { v3fatal("$VERILATOR_ROOT needs to be in environment\n"); } return var; } //###################################################################### // V3 Options accessors string V3Options::version() { string ver = DTVERSION; ver += " rev "+cvtToStr(DTVERSION_rev); return ver; } void V3Options::throwSigsegv() { #if !(defined(VL_CPPCHECK) || defined(__clang_analyzer__)) char* zp=NULL; *zp=0; // Intentional core dump, ignore warnings here #endif } //###################################################################### // V3 Options utilities string V3Options::argString(int argc, char** argv) { // Return list of arguments as simple string string opts; for (int i=0; i=1) m_prefix = string("V")+V3Os::filenameNonExt(*(vFilesList.begin())); if (modPrefix()=="") m_modPrefix = prefix(); // Find files in makedir addIncDirFallback(makeDir()); } //====================================================================== bool V3Options::onoff(const char* sw, const char* arg, bool& flag) { // if sw==arg, then return true (found it), and flag=true // if sw=="-no-arg", then return true (found it), and flag=false // if sw=="-noarg", then return true (found it), and flag=false // else return false if (arg[0]!='-') v3fatalSrc("OnOff switches must have leading dash"); if (0==strcmp(sw,arg)) { flag=true; return true; } else if (0==strncmp(sw,"-no",3) && (0==strcmp(sw+3,arg+1))) { flag=false; return true; } else if (0==strncmp(sw,"-no-",4) && (0==strcmp(sw+4,arg+1))) { flag=false; return true; } return false; } bool V3Options::suffixed(const string& sw, const char* arg) { if (strlen(arg) > sw.length()) return false; return (0==strcmp(sw.c_str()+sw.length()-strlen(arg), arg)); } void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char** argv) { // Parse parameters // Note argc and argv DO NOT INCLUDE the filename in [0]!!! // May be called recursively when there are -f files. for (int i=0; iv3fatal("Invalid Option: "<v3fatal("Unknown language specified: "<m_outputSplitCFuncs)) { m_outputSplitCTrace = m_outputSplitCFuncs; } } else if ( !strcmp (sw, "-output-split-ctrace") ) { // Undocumented optimization tweak shift; m_outputSplitCTrace = atoi(argv[i]); } else if ( !strcmp (sw, "-trace-depth") && (i+1)v3fatal("Unknown warning specified: "<v3fatal("Unknown warning specified: "<v3fatal("Unknown warning specified: "<v3fatal("Unknown setting for --compiler: "< 65) fl->v3fatal("--pins-bv maximum is 65: "<v3fatal("--threads must be >= 0: "<v3fatal("Unknown setting for --threads-dpi: "<v3fatal("--threads-max-mtasks must be >= 1: "<v3fatal("Unknown setting for --x-assign: "<v3fatal("Unknown setting for --x-initial: "<v3fatal("Invalid Option: "< ifp (V3File::new_ifstream(filename)); if (ifp->fail()) { fl->v3error("Cannot open -f command file: "+filename); return; } string whole_file; bool inCmt = false; while (!ifp->eof()) { string line; getline(*ifp, line); // Strip simple comments string oline; // cppcheck-suppress StlMissingComparison for (string::const_iterator pos = line.begin(); pos != line.end(); ++pos) { if (inCmt) { if (*pos=='*' && *(pos+1)=='/') { inCmt = false; ++pos; } } else if (*pos=='/' && *(pos+1)=='/') { break; // Ignore to EOL } else if (*pos=='/' && *(pos+1)=='*') { inCmt = true; // cppcheck-suppress StlMissingComparison ++pos; } else { oline += *pos; } } whole_file += oline + " "; } whole_file += "\n"; // So string match below is simplified if (inCmt) fl->v3error("Unterminated /* comment inside -f file."); fl = new FileLine(filename, 0); // Split into argument list and process // Note we don't respect quotes. It seems most simulators dont. // Woez those that expect it; we'll at least complain. if (whole_file.find("\"") != string::npos) { fl->v3error("Double quotes in -f files cause unspecified behavior."); } // Strip off arguments and parse into words std::vector args; string::size_type startpos = 0; while (startpos < whole_file.length()) { while (isspace(whole_file[startpos])) ++startpos; string::size_type endpos = startpos; while (endpos < whole_file.length() && !isspace(whole_file[endpos])) ++endpos; if (startpos != endpos) { string arg (whole_file, startpos, endpos-startpos); args.reserve(args.size()+1); args.push_back(arg); } startpos = endpos; } // Path string optdir = (rel ? V3Os::filenameDir(filename) : "."); // Convert to argv style arg list and parse them char* argv [args.size()+1]; for (unsigned i=0; isecond = level; } else { m_debugSrcs.insert(make_pair(srcfile,level)); } } int V3Options::debugSrcLevel(const string& srcfile_path, int default_level) { // For simplicity, calling functions can just use __FILE__ for srcfile. // That means though we need to cleanup the filename from ../Foo.cpp -> Foo string srcfile = V3Os::filenameNonDirExt(srcfile_path); DebugSrcMap::iterator iter = m_debugSrcs.find(srcfile); if (iter!=m_debugSrcs.end()) { return iter->second; } else { return default_level; } } void V3Options::setDumpTreeLevel(const string& srcfile, int level) { DebugSrcMap::iterator iter = m_dumpTrees.find(srcfile); if (iter!=m_dumpTrees.end()) { iter->second = level; } else { m_dumpTrees.insert(make_pair(srcfile,level)); } } int V3Options::dumpTreeLevel(const string& srcfile_path) { // For simplicity, calling functions can just use __FILE__ for srcfile. // That means though we need to cleanup the filename from ../Foo.cpp -> Foo string srcfile = V3Os::filenameNonDirExt(srcfile_path); DebugSrcMap::iterator iter = m_dumpTrees.find(srcfile); if (iter!=m_dumpTrees.end()) { return iter->second; } else { return m_dumpTree; } } void V3Options::optimize(int level) { // Set all optimizations to on/off bool flag = level > 0; m_oAcycSimp = flag; m_oCase = flag; m_oCombine = flag; m_oConst = flag; m_oExpand = flag; m_oFlopGater = flag; m_oGate = flag; m_oInline = flag; m_oLife = flag; m_oLifePost = flag; m_oLocalize = flag; m_oReloop = flag; m_oReorder = flag; m_oSplit = flag; m_oSubst = flag; m_oSubstConst = flag; m_oTable = flag; m_oDedupe = flag; m_oAssemble = flag; // And set specific optimization levels if (level >= 3) { m_inlineMult = -1; // Maximum inlining } }