//************************************************************************* // DESCRIPTION: Verilator: Options parsing // // Code available from: http://www.veripool.org/verilator // // AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli // //************************************************************************* // // Copyright 2003-2010 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 #if !defined(__MINGW32__) #include #endif #include #include #include #include #include #include #include #include "V3Global.h" #include "V3Options.h" #include "V3Error.h" #include "V3File.h" #include "V3PreShell.h" #include "config_rev.h" //###################################################################### // V3 Internal state struct V3OptionsImp { // TYPES typedef std::map > DirMap; // Directory listing // STATE list m_allArgs; // List of every argument encountered list m_incDirs; // Include directories (ordered) set m_incDirSet; // Include directories (for removing duplicates) list m_libExts; // Library extensions (ordered) set m_libExtSet; // Library extensions (for removing duplicates) DirMap m_dirMap; // Directory listing // ACCESSOR METHODS void addIncDir(const string& incdir) { if (m_incDirSet.find(incdir) == m_incDirSet.end()) { m_incDirSet.insert(incdir); m_incDirs.push_back(incdir); } } void addLibExt(const string& libext) { if (m_libExtSet.find(libext) == m_libExtSet.end()) { m_libExtSet.insert(libext); m_libExts.push_back(libext); } } V3OptionsImp() {} }; void V3Options::addIncDir(const string& incdir) { m_impp->addIncDir(incdir); } void V3Options::addLibExt(const string& libext) { m_impp->addLibExt(libext); } void V3Options::addDefine(const string& defline) { // Split +define+foo=value into the appropriate parts and parse string def = defline; string value; string::size_type pos; if ( ((pos=defline.find("+")) != string::npos) || ((pos=defline.find("=")) != string::npos)) { value = def.substr(pos+1); def.erase(pos); } V3PreShell::defineCmdLine(def,value); } void V3Options::addCppFile(const string& filename) { if (m_cppFiles.find(filename) == m_cppFiles.end()) { m_cppFiles.insert(filename); } } void V3Options::addFuture(const string& flag) { if (m_futures.find(flag) == m_futures.end()) { m_futures.insert(flag); } } bool V3Options::isFuture(const string& flag) const { return m_futures.find(flag) != m_futures.end(); } void V3Options::addLibraryFile(const string& filename) { if (m_libraryFiles.find(filename) == m_libraryFiles.end()) { m_libraryFiles.insert(filename); } } void V3Options::addVFile(const string& filename) { // 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); } void V3Options::addArg(const string& arg) { m_impp->m_allArgs.push_back(arg); } string V3Options::allArgsString() { string out; for (list::iterator it=m_impp->m_allArgs.begin(); it!=m_impp->m_allArgs.end(); ++it) { if (out != "") out += " "; out += *it; } return out; } //###################################################################### // Language class V3LangCode::V3LangCode (const char* textp) { // Return code for given string, or ERROR, which is a bad code for (int codei=V3LangCode::ERROR; codeim_dirMap.find(dir); if (diriter == m_impp->m_dirMap.end()) { // Read the listing m_impp->m_dirMap.insert(make_pair(dir, set() )); diriter = m_impp->m_dirMap.find(dir); 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 set* filesetp = &(diriter->second); set::iterator fileiter = filesetp->find(basename); if (fileiter == filesetp->end()) { return ""; // Not found } // Check if it is a directory, ignore if so string filenameOut = filenameFromDirBase (dir, basename); if (!fileStatNormal(filenameOut)) return ""; // Directory return filenameOut; } string V3Options::filePath (FileLine* fl, const string& modname, const string& errmsg) { // Find a filename to read the specified module name, // using the incdir and libext's. // Return "" if not found. for (list::iterator dirIter=m_impp->m_incDirs.begin(); dirIter!=m_impp->m_incDirs.end(); ++dirIter) { for (list::iterator extIter=m_impp->m_libExts.begin(); extIter!=m_impp->m_libExts.end(); ++extIter) { string fn = filenameFromDirBase(*dirIter,modname+*extIter); string exists = fileExists(fn); if (exists!="") { // Strip ./, it just looks ugly if (exists.substr(0,2)=="./") exists.erase(0,2); return exists; } } } // Warn and return not found fl->v3error(errmsg+modname); static bool shown_notfound_msg = false; if (!shown_notfound_msg) { shown_notfound_msg = true; if (m_impp->m_incDirs.empty()) { fl->v3error("This may be because there's no search path specified with -I."<v3error("Looked in:"<::iterator dirIter=m_impp->m_incDirs.begin(); dirIter!=m_impp->m_incDirs.end(); ++dirIter) { for (list::iterator extIter=m_impp->m_libExts.begin(); extIter!=m_impp->m_libExts.end(); ++extIter) { string fn = filenameFromDirBase(*dirIter,modname+*extIter); fl->v3error(" "<d_name, regexp.c_str())) { string fullname = dir + "/" + string(direntp->d_name); unlink (fullname.c_str()); } } closedir(dirp); } } //###################################################################### // Environment string V3Options::getenvStr(const string& envvar, const string& defaultValue) { if (const char* envvalue = getenv(envvar.c_str())) { return envvalue; } else { return defaultValue; } } void V3Options::setenvStr(const string& envvar, const string& value, const string& why) { if (why != "") { UINFO(1,"export "<v3fatal ("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") ) { shift; m_traceDepth = atoi(argv[i]); } else if ( !strcmp (sw, "-unroll-count") ) { // Undocumented optimization tweak shift; m_unrollCount = atoi(argv[i]); } else if ( !strcmp (sw, "-unroll-stmts") ) { // Undocumented optimization tweak shift; m_unrollStmts = atoi(argv[i]); } else if ( !strcmp (sw, "-v") ) { shift; V3Options::addLibraryFile(filenameSubstitute(argv[i])); } else if ( !strcmp (sw, "-V") ) { showVersion(true); exit(0); } else if ( !strcmp (sw, "-version") ) { showVersion(false); exit(0); } // Single switches else if ( !strcmp (sw, "-E") ) { m_preprocOnly = true; } else if ( onoff (sw, "-MMD", flag/*ref*/) ) { m_makeDepend = flag; } else if ( onoff (sw, "-MP", flag/*ref*/) ) { m_makePhony = flag; } else if ( onoff (sw, "-assert", flag/*ref*/) ) { m_assert = flag; m_psl = flag; } else if ( onoff (sw, "-autoflush", flag/*ref*/) ) { m_autoflush = flag; } else if ( onoff (sw, "-bbox-sys", flag/*ref*/) ) { m_bboxSys = flag; } else if ( onoff (sw, "-bbox-unsup", flag/*ref*/) ) { m_bboxUnsup = flag; } else if ( !strcmp (sw, "-cc") ) { m_outFormatOk = true; m_systemC = false; m_systemPerl = false; } else if ( onoff (sw, "-cdc", flag/*ref*/) ) { m_cdc = flag; } else if ( onoff (sw, "-coverage", flag/*ref*/) ) { coverage(flag); } else if ( onoff (sw, "-coverage-line", flag/*ref*/) ){ m_coverageLine = flag; } else if ( onoff (sw, "-coverage-toggle", flag/*ref*/) ){ m_coverageToggle = flag; } else if ( onoff (sw, "-coverage-user", flag/*ref*/) ){ m_coverageUser = flag; } else if ( onoff (sw, "-covsp", flag/*ref*/) ) { } // TBD else if ( onoff (sw, "-debug-check", flag/*ref*/) ){ m_debugCheck = flag; } else if ( onoff (sw, "-dump-tree", flag/*ref*/) ) { m_dumpTree = flag; } else if ( onoff (sw, "-exe", flag/*ref*/) ) { m_exe = flag; } else if ( onoff (sw, "-ignc", flag/*ref*/) ) { m_ignc = flag; } else if ( onoff (sw, "-inhibit-sim", flag/*ref*/)){ m_inhibitSim = flag; } else if ( onoff (sw, "-l2name", flag/*ref*/) ) { m_l2Name = flag; } else if ( onoff (sw, "-lint-only", flag/*ref*/) ) { m_lintOnly = flag; } else if ( !strcmp (sw, "-no-pins64") ) { m_pinsBv = 33; } else if ( !strcmp (sw, "-pins64") ) { m_pinsBv = 65; } else if ( onoff (sw, "-pins-uint8", flag/*ref*/) ){ m_pinsUint8 = flag; } else if ( !strcmp (sw, "-private") ) { m_public = false; } else if ( onoff (sw, "-profile-cfuncs", flag/*ref*/) ) { m_profileCFuncs = flag; } else if ( onoff (sw, "-psl", flag/*ref*/) ) { m_psl = flag; } else if ( onoff (sw, "-public", flag/*ref*/) ) { m_public = flag; } else if ( !strcmp (sw, "-sc") ) { m_outFormatOk = true; m_systemC = true; m_systemPerl = false; } else if ( onoff (sw, "-skip-identical", flag/*ref*/) ) { m_skipIdentical = flag; } else if ( !strcmp (sw, "-sp") ) { m_outFormatOk = true; m_systemC = true; m_systemPerl = true; } else if ( onoff (sw, "-stats", flag/*ref*/) ) { m_stats = flag; } else if ( !strcmp (sw, "-sv") ) { m_language = V3LangCode::L1800_2005; } else if ( onoff (sw, "-trace", flag/*ref*/) ) { m_trace = flag; } else if ( onoff (sw, "-trace-dups", flag/*ref*/) ) { m_traceDups = flag; } else if ( onoff (sw, "-underline-zero", flag/*ref*/) ) { m_underlineZero = flag; } // Undocumented, old Verilator-2 // Optimization else if ( !strncmp (sw, "-O", 2) ) { for (const char* cp=sw+strlen("-O"); *cp; ++cp) { flag = isupper(*cp); switch (tolower(*cp)) { case '0': optimize(0); break; // 0=all off case '1': optimize(1); break; // 1=all on case '2': optimize(2); break; // 2=not used case '3': optimize(3); break; // 3=high case 'a': m_oTable = flag; break; case 'b': m_oCombine = flag; break; case 'c': m_oConst = flag; break; case 'e': m_oCase = flag; break; case 'f': m_oFlopGater = flag; break; case 'g': m_oGate = flag; break; case 'i': m_oInline = flag; break; case 'k': m_oSubstConst = flag; break; case 'l': m_oLife = flag; break; case 'p': m_public = !flag; break; //With -Op so flag=0, we want public on so few optimizations done case 'r': m_oReorder = flag; break; case 's': m_oSplit = flag; break; case 't': m_oLifePost = flag; break; case 'u': m_oSubst = flag; break; case 'x': m_oExpand = flag; break; case 'y': m_oAcycSimp = flag; break; case 'z': m_oLocalize = flag; break; default: break; // No error, just ignore } } } // Parameterized switches else if ( !strncmp (sw, "-D", 2)) { addDefine (string (sw+strlen("-D"))); } else if ( !strncmp (sw, "-I", 2)) { addIncDir (string (sw+strlen("-I"))); } else if ( !strcmp (sw, "-Mdir") && (i+1)v3fatal("Unknown warning specified: "<v3fatal("Unknown warning specified: "<v3fatal("Unknown warning specified: "<v3fatal("Unknown setting for --compiler: "<v3fatal("Unknown setting for --x-assign: "<v3fatal ("Invalid Option: "< ifp (V3File::new_ifstream(filename)); if (ifp->fail()) { fl->v3error("Cannot open -f command file: "+filename); return; } string whole_file; string::size_type pos; bool inCmt = false; while (!ifp->eof()) { string line; getline(*ifp, line); // Strip simple comments string oline; 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; ++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 ((pos=whole_file.find("\"")) != string::npos) { fl->v3error("Double quotes in -f files cause unspecified behavior."); } // Strip off arguments and parse into words 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; } // 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 = V3Options::filenameNonDirExt(srcfile_path); DebugSrcMap::iterator iter = m_debugSrcs.find(srcfile); if (iter!=m_debugSrcs.end()) { return iter->second; } else { return default_level; } } 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_oReorder = flag; m_oSplit = flag; m_oSubst = flag; m_oSubstConst = flag; m_oTable = flag; // And set specific optimization levels if (level >= 3) { m_inlineMult = -1; // Maximum inlining } }