From 6196cf09ff3fb9ee3aa112ba27016f9a64b72afe Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 20 Jan 2010 07:15:51 -0500 Subject: [PATCH] Add experimental --pipe-filter to filter all Verilog input. --- Changes | 2 + Makefile.in | 1 + bin/verilator | 18 ++ src/V3File.cpp | 257 +++++++++++++++++++++++++++++ src/V3File.h | 17 ++ src/V3LinkCells.cpp | 11 +- src/V3LinkCells.h | 4 +- src/V3Options.cpp | 3 + src/V3Options.h | 6 +- src/V3Parse.h | 3 +- src/V3ParseImp.cpp | 6 +- src/V3ParseImp.h | 10 +- src/V3PreLex.h | 7 +- src/V3PreProc.cpp | 25 +-- src/V3PreProc.h | 4 +- src/V3PreShell.cpp | 18 +- src/V3PreShell.h | 3 +- src/Verilator.cpp | 6 +- test_regress/t/t_pipe_exit_bad.pf | 9 + test_regress/t/t_pipe_exit_bad.pl | 28 ++++ test_regress/t/t_pipe_filter.out | 52 ++++++ test_regress/t/t_pipe_filter.pf | 56 +++++++ test_regress/t/t_pipe_filter.pl | 24 +++ test_regress/t/t_pipe_filter.v | 18 ++ test_regress/t/t_pipe_filter_inc.v | 9 + 25 files changed, 555 insertions(+), 42 deletions(-) create mode 100644 test_regress/t/t_pipe_exit_bad.pf create mode 100755 test_regress/t/t_pipe_exit_bad.pl create mode 100644 test_regress/t/t_pipe_filter.out create mode 100644 test_regress/t/t_pipe_filter.pf create mode 100755 test_regress/t/t_pipe_filter.pl create mode 100644 test_regress/t/t_pipe_filter.v create mode 100644 test_regress/t/t_pipe_filter_inc.v diff --git a/Changes b/Changes index 2b7082619..c16f57290 100644 --- a/Changes +++ b/Changes @@ -33,6 +33,8 @@ indicates the contributor was also the author of the fix; Thanks! *** Add experimental clock domain crossing checks. +*** Add experimental --pipe-filter to filter all Verilog input. + *** Speed compiles by avoiding including the STL iostream header. Application programs may need to include it themselves to avoid errors. diff --git a/Makefile.in b/Makefile.in index 44d4756d3..77618dce6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -125,6 +125,7 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \ test_regress/t/*.mem \ test_regress/t/*.out \ test_regress/t/*.pl \ + test_regress/t/*.pf \ test_regress/t/*.v* \ verilator.* \ diff --git a/bin/verilator b/bin/verilator index c73027190..8fcbffc79 100755 --- a/bin/verilator +++ b/bin/verilator @@ -231,6 +231,7 @@ descriptions in the next sections for more information. --output-split-cfuncs Split .ccp functions --pins-bv Specify types for top level ports --pins-uint8 Specify types for top level ports + --pipe-filter Filter all input through a script --prefix Name of top level class --profile-cfuncs Name functions for profiling --private Debugging; see docs @@ -573,6 +574,23 @@ Specifies SystemC inputs/outputs that are smaller than the --pins-bv setting and 8 bits or less should use uint8_t instead of uint32_t. Likewise pins of width 9-16 will use uint16_t instead of uint32_t. +=item --pipe-filter I + +Rarely needed and experimental. Verilator will spawn the specified command +as a subprocess pipe, to allow the command to perform custom edits on the +Verilog code before it reaches Verilator. + +Before reading each Verilog file, Verilator will pass the file name to the +subprocess' stdin with 'read_verilog ""'. The filter may then +read the file and perform any filtering it desires, and feeds the new file +contents back to Verilator on stdout with 'Content-Length'. Output to +stderr from the filter feeds through to Verilator's stdout and if the +filter exits with non-zero status Verilator terminates. See the +t/t_pipe_filter test for an example. + +To debug the output of the filter, try using the -E option to see +preprocessed output. + =item --prefix I Specifies the name of the top level class and makefile. Defaults to V diff --git a/src/V3File.cpp b/src/V3File.cpp index 657e52fd5..9da26d5c7 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -25,14 +25,28 @@ #include #include #include +#include +#include #include #include +#include + +#if defined(__unix__) +# define INFILTER_PIPE // Allow pipe filtering. Needs fork() +#endif + +#ifdef INFILTER_PIPE +# include +#endif #include "V3Global.h" #include "V3File.h" #include "V3PreShell.h" #include "V3Ast.h" +#define INFILTER_IPC_BUFSIZ 64*1024 // For debug, try this as a small number +#define INFILTER_CACHE_MAX 64*1024 // Maximum bytes to cache if same file read twice + //###################################################################### // V3File Internal state @@ -252,6 +266,249 @@ void V3File::createMakeDir() { } } +//###################################################################### +// V3InFilterImp + +class V3InFilterImp { + typedef map FileContentsMap; + + FileContentsMap m_contentsMap; // Cache of file contents + bool m_readEof; // Received EOF on read +#ifdef INFILTER_PIPE + pid_t m_pid; // fork() process id +#else + int m_pid; // fork() process id - always zero as disabled +#endif + bool m_pidExited; + int m_pidStatus; + int m_writeFd; // File descriptor TO filter + int m_readFd; // File descriptor FROM filter + +private: + // METHODS + static int debug() { + static int level = -1; + if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); + return level; + } + + bool readContents(const string& filename, string& out) { + if (m_pid) return readContentsFilter(filename,out); + else return readContentsFile(filename,out); + } + bool readContentsFile(const string& filename, string& out) { + int fd = open (filename.c_str(), O_RDONLY); + if (!fd) return false; + m_readEof = false; + out = readBlocks(fd, -1); + close(fd); + return true; + } + bool readContentsFilter(const string& filename, string& out) { + if (filename!="" || out!="") {} // Prevent unused +#ifdef INFILTER_PIPE + writeFilter("read \""+filename+"\"\n"); + string line = readFilterLine(); + if (line.find("Content-Length") != string::npos) { + int len = 0; + sscanf(line.c_str(), "Content-Length: %d\n", &len); + out = readBlocks(m_readFd, len); + return true; + } else { + if (line!="") v3error("--pipe-filter protocol error, unexpected: "<(int)out.length())) { + int todo = INFILTER_IPC_BUFSIZ; + if (size>0 && size0) out.append(buf, got); + else if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { + checkFilter(false); usleep(1000); continue; + } else { m_readEof = true; break; } + } + return out; + } + string readFilterLine() { + // Slow, but we don't need it much + UINFO(9,"readFilterLine\n"); + string line; + while (!m_readEof) { + string c = readBlocks(m_readFd, 1); + line += c; + if (c == "\n") { + if (line == "\n") { line=""; continue; } + else break; + } + } + UINFO(6,"filter-line-in: "<=6) { UINFO(6,"filter-out: "<offset) { + int got = write (m_writeFd, (out.c_str())+offset, out.length()-offset); + //UINFO(9,"WR GOT g "<< got<<" e "<0) offset += got; + else if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { + checkFilter(false); usleep(1000); continue; + } + else break; + } + } + + // Start the filter + void start(const string& command) { + if (command=="") { + m_pid = 0; // Disabled + } else { + startFilter(command); + } + } + void startFilter(const string& command) { + if (command=="") {} // Prevent Unused +#ifdef INFILTER_PIPE + int fd_stdin[2], fd_stdout[2]; + static const int P_RD = 0; + static const int P_WR = 1; + + if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) { + v3fatal("--pipe-filter: Can't pipe: "<"<"<second; + return true; + } + if (!readContents(filename, out)) return false; + if (out.length() < INFILTER_CACHE_MAX) { + // Cache small files (only to save space) + // It's quite common to `include "timescale" thousands of times + // This isn't so important if it's just a open(), but filtering can be slow + m_contentsMap.insert(make_pair(filename,out)); + } + return true; + } + // CONSTRUCTORS + V3InFilterImp(const string& command) { + m_readEof = false; + m_pid = 0; + m_pidExited = false; + m_pidStatus = 0; + m_writeFd = 0; + m_readFd = 0; + start(command); + } + ~V3InFilterImp() { stop(); } +}; + +//###################################################################### +// V3InFilter +// Just dispatch to the implementation + +V3InFilter::V3InFilter(const string& command) { m_impp = new V3InFilterImp(command); } +V3InFilter::~V3InFilter() { if (m_impp) delete m_impp; m_impp=NULL; } + +bool V3InFilter::readWholefile(const string& filename, string& out) { + if (!m_impp) v3fatalSrc("readWholefile on invalid filter"); + return m_impp->readWholefile(filename, out); +} + //###################################################################### // V3OutFormatter: A class for printing to a file, with automatic indentation of C++ code. diff --git a/src/V3File.h b/src/V3File.h index e4bf6f2e4..169ecf020 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -71,6 +71,23 @@ public: static void createMakeDir(); }; +//============================================================================ +// V3InFilter: Read a input file, possibly filtering it, and caching contents + +class V3InFilterImp; + +class V3InFilter { + V3InFilterImp* m_impp; +public: + // METHODS + // Read file contents and return it. Return true on success. + bool readWholefile(const string& filename, string& out); + + // CONSTRUCTORS + V3InFilter(const string& command); + ~V3InFilter(); +}; + //============================================================================ // V3OutFormatter: A class for automatic indentation of C++ or Verilog code. diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index c20e90c76..5101767b5 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -96,6 +96,8 @@ private: AstUser1InUse m_inuser1; // STATE + V3InFilter* m_filterp; // Parser filter + // Below state needs to be preserved between each module call. AstNodeModule* m_modp; // Current module V3SymTable m_mods; // Symbol table of all module names @@ -174,7 +176,7 @@ private: AstNodeModule* modp = m_mods.findIdUpward(nodep->modName())->castNodeModule(); if (!modp) { // Read-subfile - V3Parse parser (v3Global.rootp()); + V3Parse parser (v3Global.rootp(), m_filterp); parser.parseFile(nodep->fileline(), nodep->modName(), false); V3Error::abortIfErrors(); // We've read new modules, grab new pointers to their names @@ -269,7 +271,8 @@ private: public: // CONSTUCTORS - LinkCellsVisitor(AstNetlist* rootp) { + LinkCellsVisitor(AstNetlist* rootp, V3InFilter* filterp) { + m_filterp = filterp; m_modp = NULL; m_libVertexp = NULL; m_topVertexp = NULL; @@ -281,7 +284,7 @@ public: //###################################################################### // Link class functions -void V3LinkCells::link(AstNetlist* rootp) { +void V3LinkCells::link(AstNetlist* rootp, V3InFilter* filterp) { UINFO(4,__FUNCTION__<<": "< m_bufferStack; // Stack of inserted text above current point // State to lexer @@ -123,19 +122,15 @@ class V3PreLex { string m_defValue; // Definition value being built. // CONSTRUCTORS - V3PreLex(FILE* fp) { - m_fp = fp; + V3PreLex() { m_keepComments = 0; m_pedantic = false; m_formalLevel = 0; m_parenLevel = 0; m_pslParenLevel = 0; m_pslMoreNeeded = false; - m_bufferStack.push(yy_create_buffer (fp, YY_BUF_SIZE)); - yy_switch_to_buffer(m_bufferStack.top()); } ~V3PreLex() { - fclose(m_fp); while (!m_bufferStack.empty()) { yy_delete_buffer(m_bufferStack.top()); m_bufferStack.pop(); } } diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index 3b9c00aa5..1047d26ff 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -162,7 +162,7 @@ private: bool commentTokenMatch(string& cmdr, const char* strg); string trimWhitespace(const string& strg, bool trailing); - void unputString(const string& strg); + void unputString(const string& strg, bool first=false); void parsingOn() { m_off--; @@ -173,7 +173,7 @@ private: public: // METHODS, called from upper level shell - virtual void openFile(FileLine* fl, const string& filename); + virtual void openFile(FileLine* fl, V3InFilter* filterp, const string& filename); virtual bool isEof() const { return (m_lexp==NULL); } virtual string getline(); virtual void insertUnreadback(const string& text) { m_lineCmt += text; } @@ -402,14 +402,16 @@ const char* V3PreProcImp::tokenName(int tok) { } } -void V3PreProcImp::unputString(const string& strg) { +void V3PreProcImp::unputString(const string& strg, bool first) { // We used to just m_lexp->unputString(strg.c_str()); // However this can lead to "flex scanner push-back overflow" // so instead we scan from a temporary buffer, then on EOF return. // This is also faster than the old scheme, amazingly. - if (m_lexp->m_bufferStack.empty() || m_lexp->m_bufferStack.top()!=m_lexp->currentBuffer()) { - fileline()->v3fatalSrc("bufferStack missing current buffer; will return incorrectly"); - // Hard to debug lost text as won't know till much later + if (!first) { // Else the initial creation + if (m_lexp->m_bufferStack.empty() || m_lexp->m_bufferStack.top()!=m_lexp->currentBuffer()) { + fileline()->v3fatalSrc("bufferStack missing current buffer; will return incorrectly"); + // Hard to debug lost text as won't know till much later + } } m_lexp->scanBytes(strg); } @@ -576,15 +578,17 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) { //********************************************************************** // Parser routines -void V3PreProcImp::openFile(FileLine* fl, const string& filename) { +void V3PreProcImp::openFile(FileLine* fl, V3InFilter* filterp, const string& filename) { // Open a new file, possibly overriding the current one which is active. if (fl) { m_fileline = new FileLine(fl); } V3File::addSrcDepend(filename); - FILE* fp = fopen (filename.c_str(), "r"); - if (!fp) { + + string wholefile; + bool ok = filterp->readWholefile(filename, wholefile/*ref*/); + if (!ok) { fileline()->v3error("File not found: "+filename+"\n"); return; } @@ -601,7 +605,7 @@ void V3PreProcImp::openFile(FileLine* fl, const string& filename) { addLineComment(0); } - m_lexp = new V3PreLex (fp); + m_lexp = new V3PreLex(); m_lexp->m_keepComments = keepComments(); m_lexp->m_pedantic = pedantic(); m_lexp->m_curFilelinep = new FileLine(filename, 1); @@ -609,6 +613,7 @@ void V3PreProcImp::openFile(FileLine* fl, const string& filename) { addLineComment(1); // Enter yy_flex_debug = (debug()>4)?1:0; + unputString(wholefile,true); } void V3PreProcImp::insertUnreadbackAtBol(const string& text) { diff --git a/src/V3PreProc.h b/src/V3PreProc.h index a4968521c..042c050e0 100644 --- a/src/V3PreProc.h +++ b/src/V3PreProc.h @@ -30,6 +30,8 @@ #include #include +class V3InFilter; + class V3PreProc { // This defines a preprocessor. Functions are virtual so implementation can be hidden. // After creating, call open(), then getline() in a loop. The class will to the rest... @@ -48,7 +50,7 @@ public: // ACCESSORS // Insert given file into this point in input stream - virtual void openFile(FileLine* fileline, const string& filename)=0; + virtual void openFile(FileLine* fileline, V3InFilter* filterp, const string& filename)=0; virtual string getline()=0; // Return next line/lines. (Null if done.) virtual bool isEof() const =0; // Return true on EOF. virtual void insertUnreadback(const string& text) = 0; diff --git a/src/V3PreShell.cpp b/src/V3PreShell.cpp index 0fe3ffe1d..a75911280 100644 --- a/src/V3PreShell.cpp +++ b/src/V3PreShell.cpp @@ -43,6 +43,7 @@ protected: static V3PreShellImp s_preImp; static V3PreProc* s_preprocp; + static V3InFilter* s_filterp; //--------------------------------------- // METHODS @@ -70,12 +71,13 @@ protected: } } - void preproc (FileLine* fl, const string& modname, V3ParseImp* parsep) { + void preproc (FileLine* fl, const string& modname, V3InFilter* filterp, V3ParseImp* parsep) { // Preprocess the given module, putting output in vppFilename UINFONL(1," Preprocessing "<isEof()) { string line = s_preprocp->getline(); V3Parse::ppPushText(parsep, line); @@ -83,10 +85,10 @@ protected: } void preprocInclude (FileLine* fl, const string& modname) { - preprocOpen(fl, modname, "Cannot find include file: "); + preprocOpen(fl, s_filterp, modname, "Cannot find include file: "); } - void preprocOpen (FileLine* fl, const string& modname, const string& errmsg) { + void preprocOpen (FileLine* fl, V3InFilter* filterp, const string& modname, const string& errmsg) { // Allow user to put `defined names on the command line instead of filenames, // then convert them properly. string ppmodname = s_preprocp->removeDefines (modname); @@ -96,15 +98,17 @@ protected: if (filename=="") return; // Not found UINFO(2," Reading "<openFile(fl, filename); + s_preprocp->openFile(fl, filterp, filename); } // CONSTRUCTORS V3PreShellImp() {} + ~V3PreShellImp() {} }; V3PreShellImp V3PreShellImp::s_preImp; V3PreProc* V3PreShellImp::s_preprocp = NULL; +V3InFilter* V3PreShellImp::s_filterp = NULL; //###################################################################### // Perl class functions @@ -112,8 +116,8 @@ V3PreProc* V3PreShellImp::s_preprocp = NULL; void V3PreShell::boot(char** env) { V3PreShellImp::s_preImp.boot(env); } -void V3PreShell::preproc(FileLine* fl, const string& modname, V3ParseImp* parsep) { - V3PreShellImp::s_preImp.preproc(fl, modname, parsep); +void V3PreShell::preproc(FileLine* fl, const string& modname, V3InFilter* filterp, V3ParseImp* parsep) { + V3PreShellImp::s_preImp.preproc(fl, modname, filterp, parsep); } void V3PreShell::preprocInclude(FileLine* fl, const string& modname) { V3PreShellImp::s_preImp.preprocInclude(fl, modname); diff --git a/src/V3PreShell.h b/src/V3PreShell.h index 389082686..8478146f4 100644 --- a/src/V3PreShell.h +++ b/src/V3PreShell.h @@ -28,6 +28,7 @@ #include "V3Error.h" class V3ParseImp; +class V3InFilter; //============================================================================ @@ -35,7 +36,7 @@ class V3PreShell { // Static class for calling preprocessor public: static void boot(char** env); - static void preproc(FileLine* fileline, const string& module, V3ParseImp* parsep); + static void preproc(FileLine* fileline, const string& module, V3InFilter* filterp, V3ParseImp* parsep); static void preprocInclude(FileLine* fileline, const string& module); static string dependFiles() { return ""; } // Perl only static void defineCmdLine(const string& name, const string& value); diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 4cfd18e3a..4706f2182 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -98,7 +98,9 @@ void V3Global::readFiles() { // AstNode::user4p() // V3SymTable* Package and typedef symbol names AstUser4InUse inuser4; - V3Parse parser (v3Global.rootp()); + V3InFilter filter (v3Global.opt.pipeFilter()); + + V3Parse parser (v3Global.rootp(), &filter); // Read top module for (V3StringList::const_iterator it = v3Global.opt.vFiles().begin(); it != v3Global.opt.vFiles().end(); ++it) { @@ -119,7 +121,7 @@ void V3Global::readFiles() { if (!v3Global.opt.preprocOnly()) { // Resolve all modules cells refer to - V3LinkCells::link(v3Global.rootp()); + V3LinkCells::link(v3Global.rootp(), &filter); } } diff --git a/test_regress/t/t_pipe_exit_bad.pf b/test_regress/t/t_pipe_exit_bad.pf new file mode 100644 index 000000000..ff75390a7 --- /dev/null +++ b/test_regress/t/t_pipe_exit_bad.pf @@ -0,0 +1,9 @@ +#!/usr/bin/perl -w +# DESCRIPTION: Verilator: Verilog Test example --pipe-filter script +# +# Copyright 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. + +die "%Error: t_pipe_exit_bad.pf: Intentional bad exit status...\n"; diff --git a/test_regress/t/t_pipe_exit_bad.pl b/test_regress/t/t_pipe_exit_bad.pl new file mode 100755 index 000000000..1ac6881b7 --- /dev/null +++ b/test_regress/t/t_pipe_exit_bad.pl @@ -0,0 +1,28 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2010-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. + +top_filename("t/t_pipe_filter.v"); + +if (!$Self->{v3}) { + ok(1); +} else { + compile ( + v_flags2 => ['-E --pipe-filter \'perl t/t_pipe_exit_bad.pf\' '], + verilator_make_gcc=>0, + stdout_filename => $stdout_filename, + fails=>1, + expect=> +'%Error: t_pipe_exit_bad.pf: Intentional bad exit status... +%Error: File not found: t/t_pipe_filter.v +%Error: Exiting due to.*', + ); + ok(1); +} + +1; diff --git a/test_regress/t/t_pipe_filter.out b/test_regress/t/t_pipe_filter.out new file mode 100644 index 000000000..a7fc3b734 --- /dev/null +++ b/test_regress/t/t_pipe_filter.out @@ -0,0 +1,52 @@ +`line 1 "t/t_pipe_filter.v" 1 + + + + + + + + + +example line 10; +example line 11; + + +`line 13 "t/t_pipe_filter.v" 0 +`line 1 "t/t_pipe_filter_inc.v" 1 +int lint_off_line_7 = 1; +int lint_off_line_8 = 1; + + + + + +inc line 6; +inc line 7; +inc line 8; +inc line 9; +`line 12 "t/t_pipe_filter_inc.v" 2 +`line 13 "t/t_pipe_filter.v" 0 + + + +`line 15 "t/t_pipe_filter.v" 0 +`line 1 "t/t_pipe_filter_inc.v" 1 +int lint_off_line_7 = 1; +int lint_off_line_8 = 1; + + + + + +inc line 6; +inc line 7; +inc line 8; +inc line 9; +`line 12 "t/t_pipe_filter_inc.v" 2 +`line 15 "t/t_pipe_filter.v" 0 + + +example line 15; +example line 16; +`line 19 "t/t_pipe_filter.v" 2 diff --git a/test_regress/t/t_pipe_filter.pf b/test_regress/t/t_pipe_filter.pf new file mode 100644 index 000000000..25b078a72 --- /dev/null +++ b/test_regress/t/t_pipe_filter.pf @@ -0,0 +1,56 @@ +#!/usr/bin/perl -w +# DESCRIPTION: Verilator: Verilog Test example --pipe-filter script +# +# Copyright 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. + +use IO::File; +use strict; + +our $Debug = 0; + +autoflush STDOUT; + +print STDERR "t_pipe_filter.pf: Hello in Perl\n" if $Debug; +while (defined(my $cmd = )) { + print STDERR "t_pipe_filter.pf: gotcmd: $cmd" if $Debug; + if ($cmd =~ /^read "(.*)"/) { + my $filename = $1; + open(my $fh, "<$filename") + or die "t_pipe_filter.pf: %Error: $! $filename\n"; + + my $wholefile=""; + # It's faster to slurp the whole file then scan (if needed), + # then to read it into an array with getlines + { local $/; undef $/; $wholefile = <$fh>; } + close $fh; + + if ($wholefile =~ /example_lint/) { # else short circuit + my $lineno = 1; my $pos=0; + my @prefixes; + while (1) { + my $newpos=index($wholefile,"\n",$pos); + last if $newpos<$pos; + my $line = substr($wholefile,$pos,$newpos-$pos); + if ($line =~ /example_lint/) { + # We don't have a way to specify this yet, so just for now + #print STDERR $line; + push @prefixes, "int lint_off_line_${lineno} = 1;\n"; + } + $lineno++; + $pos = $newpos+1; + } + #print STDERR "Line count: $lineno\n"; + $wholefile = join('',@prefixes) . $wholefile; + } + + print STDOUT "Content-Length: ".length($wholefile)."\n".$wholefile."\n"; + } else { + die "t_pipe_filter.pf: %Error: Unknown command: $cmd\n"; + } +} + +print STDOUT "t_pipe_filter.pf: Fin\n" if $Debug; +exit(0); diff --git a/test_regress/t/t_pipe_filter.pl b/test_regress/t/t_pipe_filter.pl new file mode 100755 index 000000000..ddc476eaf --- /dev/null +++ b/test_regress/t/t_pipe_filter.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2010-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. + +$Self->{golden_out} ||= "t/$Self->{name}.out"; +my $stdout_filename = "$Self->{obj_dir}/$Self->{name}__test.vpp"; + +if (!$Self->{v3}) { + ok(1); +} else { + compile ( + v_flags2 => ['-E --pipe-filter \'perl t/t_pipe_filter.pf\' '], + verilator_make_gcc=>0, + stdout_filename => $stdout_filename, + ); + ok(files_identical($stdout_filename, $Self->{golden_out})); +} + +1; diff --git a/test_regress/t/t_pipe_filter.v b/test_regress/t/t_pipe_filter.v new file mode 100644 index 000000000..8b96759b1 --- /dev/null +++ b/test_regress/t/t_pipe_filter.v @@ -0,0 +1,18 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2010 by Wilson Snyder. + +//=========================================================================== +// Includes + + +example line 10; +example line 11; + +`include "t_pipe_filter_inc.v" +// Twice to check caching of includes +`include "t_pipe_filter_inc.v" + +example line 15; +example line 16; diff --git a/test_regress/t/t_pipe_filter_inc.v b/test_regress/t/t_pipe_filter_inc.v new file mode 100644 index 000000000..021ede616 --- /dev/null +++ b/test_regress/t/t_pipe_filter_inc.v @@ -0,0 +1,9 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2010 by Wilson Snyder. + +inc line 6; +inc line 7; // example_lint_off_line FOO +inc line 8; // example_lint_off_line BAR +inc line 9;