//************************************************************************* // DESCRIPTION: Verilator: File stream wrapper that understands indentation // // Code available from: http://www.veripool.org/verilator // // AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli // //************************************************************************* // // Copyright 2003-2009 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 #include #include #include #include #include "V3Global.h" #include "V3File.h" #include "V3PreShell.h" //###################################################################### // V3File Internal state class V3FileDependImp { // TYPES class DependFile { // A single file bool m_target; // True if write, else read string m_filename; // Filename struct stat m_stat; // Stat information public: DependFile(const string& filename, bool target) : m_target(target), m_filename(filename) { m_stat.st_mtime = 0; } ~DependFile() {} const string& filename() const { return m_filename; } bool target() const { return m_target; } off_t size() const { return m_stat.st_size; } time_t mtime() const { return m_stat.st_mtime; } void loadStats() { if (!m_stat.st_mtime) { int err = stat(filename().c_str(), &m_stat); if (err!=0) { m_stat.st_mtime = 1; // Not a error... This can occur due to `line directives in the .vpp files UINFO(1,"-Info: File not statable: "< m_filenameSet; // Files generated (elim duplicates) set m_filenameList; // Files sourced/generated static string stripQuotes(const string& in) { string pretty = in; string::size_type pos; while ((pos=pretty.find("\"")) != string::npos) pretty.replace(pos, 1, "_"); while ((pos=pretty.find("\n")) != string::npos) pretty.replace(pos, 1, "_"); return pretty; } public: // ACCESSOR METHODS void addSrcDepend(const string& filename) { if (m_filenameSet.find(filename) == m_filenameSet.end()) { m_filenameSet.insert(filename); DependFile df (filename, false); df.loadStats(); // Get size now, in case changes during the run m_filenameList.insert(df); } } void addTgtDepend(const string& filename) { if (m_filenameSet.find(filename) == m_filenameSet.end()) { m_filenameSet.insert(filename); m_filenameList.insert(DependFile (filename, true)); } } void writeDepend(const string& filename); void writeTimes(const string& filename, const string& cmdline); bool checkTimes(const string& filename, const string& cmdline); }; V3FileDependImp dependImp; // Depend implementation class //###################################################################### // V3FileDependImp inline void V3FileDependImp::writeDepend(const string& filename) { const auto_ptr ofp (V3File::new_ofstream(filename)); if (ofp->fail()) v3fatalSrc("Can't write "<::iterator iter=m_filenameList.begin(); iter!=m_filenameList.end(); ++iter) { if (iter->target()) { *ofp<filename()<<" "; } } *ofp<<" : "; *ofp<::iterator iter=m_filenameList.begin(); iter!=m_filenameList.end(); ++iter) { if (!iter->target()) { *ofp<filename()<<" "; } } *ofp<::iterator iter=m_filenameList.begin(); iter!=m_filenameList.end(); ++iter) { if (!iter->target()) { *ofp<filename()<<":"< ofp (V3File::new_ofstream(filename)); if (ofp->fail()) v3fatalSrc("Can't write "<::iterator iter=m_filenameList.begin(); iter!=m_filenameList.end(); ++iter) { // Read stats of files we create after we're done making them (execpt for this file, of course) DependFile* dfp = (DependFile*)&(*iter); dfp->loadStats(); off_t showSize = iter->size(); if (dfp->filename() == filename) showSize=0; // We're writing it, so need to ignore it *ofp<<(iter->target()?"T":"S")<<" "; *ofp<<" "<mtime(); *ofp<<" \""<filename()<<"\""; *ofp< ifp (V3File::new_ifstream_nodepend(filename)); if (ifp->fail()) { UINFO(2," --check-times failed: no input "<>chkDir; char quote; *ifp>>quote; string chkCmdline; getline(*ifp, chkCmdline, '"'); string cmdline = stripQuotes(cmdlineIn); if (cmdline != chkCmdline) { UINFO(2," --check-times failed: different command line\n"); return false; } } while (!ifp->eof()) { char chkDir; *ifp>>chkDir; off_t chkSize; *ifp>>chkSize; if (ifp->eof()) break; // Needed to read final whitespace before found eof time_t chkMtime; *ifp>>chkMtime; char quote; *ifp>>quote; string chkFilename; getline(*ifp, chkFilename, '"'); //UINFO(9," got d="<= chkSize && chkStat.st_mtime >= chkMtime && chkStat.st_mtime <= (chkMtime + 20))) { UINFO(2," --check-times failed: out-of-date "<MAXSPACE) num=MAXSPACE; while (num>=8) { *cp++ = '\t'; num -= 8; } while (num>0) { *cp++ = ' '; num --; } *cp++ = '\0'; return (str); } const string V3OutFile::indentSpaces(int num) { // Indent the specified number of spaces. Use spaces. static char str[MAXSPACE+20]; char* cp = str; if (num>MAXSPACE) num=MAXSPACE; while (num>0) { *cp++ = ' '; num --; } *cp++ = '\0'; string st (str); return (st); } bool V3OutFile::tokenStart(const char* cp, const char* cmp) { while (*cmp == *cp) { cp++; cmp++; } if (*cmp) return false; if (*cp && !isspace(*cp)) return false; return true; } #define VERILOG_INDENTS 0 // No verilog output yet, speed things up bool V3OutFile::tokenEnd(const char* cp) { return (tokenStart(cp,"end") || tokenStart(cp,"endcase") || tokenStart(cp,"endmodule")); } int V3OutFile::endLevels (const char *strg) { int levels=m_indentLevel; const char* cp=strg; while (isspace(*cp)) cp++; switch (*cp) { case '\n': // Newlines.. No need for whitespace before it return (0); case '#': // Preproc directive return (0); } { // label/public/private: Deindent by 2 spaces const char* mp=cp; for (; isalnum(*mp); mp++) ; if (mp[0]==':' && mp[1]!=':') return (levels-INDBLK/2); } // We want "} else {" to be one level to the left of normal for (const char* cp=strg; *cp; cp++) { switch (*cp) { case '}': case ')': levels-=INDBLK; break; case 'e': if (VERILOG_INDENTS && tokenEnd(cp)) { levels-=INDBLK; } break; case '\t': case ' ': break; // Continue default: return (levels); // Letter } } return (levels); } void V3OutFile::puts (const char *strg) { if (m_prependIndent) { putsNoTracking(indentStr(endLevels(strg))); m_prependIndent = false; } bool wordstart = true; for (const char* cp=strg; *cp; cp++) { putcNoTracking (*cp); switch (*cp) { case '\n': m_lineno++; wordstart = true; if (cp[1]=='\0') { m_prependIndent = true; // Add the indent later, may be a indentInc/indentDec called between now and then } else { m_prependIndent = false; putsNoTracking(indentStr(endLevels(cp+1))); } break; case ' ': wordstart = true; break; case '\t': wordstart = true; break; case '{': indentInc(); break; case '(': indentInc(); m_parenVec.push(m_column); break; case '}': indentDec(); break; case ')': if (!m_parenVec.empty()) m_parenVec.pop(); indentDec(); break; case 'b': if (wordstart && VERILOG_INDENTS && tokenStart(cp,"begin")) { indentInc(); } wordstart = false; break; case 'c': if (wordstart && VERILOG_INDENTS && (tokenStart(cp,"case") || tokenStart(cp,"casex") || tokenStart(cp,"casez"))) { indentInc(); } wordstart = false; break; case 'e': if (wordstart && VERILOG_INDENTS && tokenEnd(cp)) { indentDec(); } wordstart = false; break; case 'm': if (wordstart && VERILOG_INDENTS && tokenStart(cp,"module")) { indentInc(); } wordstart = false; break; default: wordstart = false; break; } } } void V3OutFile::putBreakExpr () { if (!m_parenVec.empty()) putBreak(); } // Add a line break if too wide void V3OutFile::putBreak () { if (!m_nobreak) { //char s[1000]; sprintf(s,"{%d,%d}",m_column,m_parenVec.top()); putsNoTracking(s); if (exceededWidth()) { putcNoTracking('\n'); if (!m_parenVec.empty()) putsNoTracking(indentStr(m_parenVec.top())); } } } void V3OutFile::putsNoTracking (const char *strg) { // Don't track {}'s, probably because it's a $display format string for (const char* cp=strg; *cp; cp++) { putcNoTracking (*cp); } } void V3OutFile::putcNoTracking (char chr) { switch (chr) { case '\n': m_lineno++; m_column=0; m_nobreak=true; break; case '\t': m_column = ((m_column + 9)/8)*8; break; case ' ': case '(': case '|': case '&': m_column++; break; default: m_column++; m_nobreak=false; break; } fputc (chr, m_fp); } void V3OutFile::putAlign (bool/*AlignClass*/ isStatic, int align, int size, const char* prefix) { if (size==0) size=align; int alignSize = size; if (alignSize>8) alignSize=8; int& alignr = isStatic ? m_declSAlign : m_declNSAlign; int padsize = alignSize - (alignr % alignSize); if (padsize && padsize!=alignSize) { puts("char\t"); puts(prefix); puts("__VpadToAlign"+cvtToStr(alignr) +"["+cvtToStr(padsize)+"];\n"); alignr += padsize; m_declPadNum++; } alignr += size; } //---------------------------------------------------------------------- // Simple wrappers void V3OutFile::printf (const char *fmt...) { char sbuff[5000]; va_list ap; va_start(ap,fmt); vsprintf(sbuff,fmt,ap); va_end(ap); this->puts(sbuff); }