From dfddbea26b3cad01e4420385d373b6740955813f Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sun, 15 Oct 2017 11:45:35 +0100 Subject: [PATCH] First step towards supporting separate compilation units in SV. This adds a -u option to the driver to allow the user to specify that they want each source file to be treated as a separate compilation unit, and modifies the compiler to accept a list of files (either on the command line or via a file specified by a new -F option). This list of files is then preprocessed and parsed separately, causing all compiler directives (including macro definitions) to only apply to the file containing them, as required by the SystemVerilog standard. --- compiler.h | 6 ++++- driver/iverilog.man.in | 12 ++++++--- driver/main.c | 40 +++++++++++++++++++-------- lexor.lex | 29 +++++++++++++++----- load_module.cc | 33 +++-------------------- main.cc | 61 +++++++++++++++++++++++++++++++++++++++--- parse_api.h | 12 ++++----- pform.cc | 42 +++++++++++++++++++++-------- 8 files changed, 163 insertions(+), 72 deletions(-) diff --git a/compiler.h b/compiler.h index eae1ec460..6eccc8743 100644 --- a/compiler.h +++ b/compiler.h @@ -1,7 +1,7 @@ #ifndef IVL_compiler_H #define IVL_compiler_H /* - * Copyright (c) 1999-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2017 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -120,6 +120,10 @@ extern bool debug_optimizer; /* Ignore errors about missing modules */ extern bool ignore_missing_modules; +/* Treat each source file as a separate compilation unit (as defined + by SystemVerilog). */ +extern bool separate_compilation; + /* Control evaluation of functions at compile time: * 0 = only for functions in constant expressions * 1 = only for automatic functions diff --git a/driver/iverilog.man.in b/driver/iverilog.man.in index ba05d2a0a..47ebf4669 100644 --- a/driver/iverilog.man.in +++ b/driver/iverilog.man.in @@ -1,10 +1,10 @@ -.TH iverilog 1 "Oct 2nd, 2016" "" "Version %M.%n%E" +.TH iverilog 1 "Oct 14th, 2017" "" "Version %M.%n%E" .SH NAME iverilog - Icarus Verilog compiler .SH SYNOPSIS .B iverilog -[\-ESVv] [\-Bpath] [\-ccmdfile|\-fcmdfile] [\-Dmacro[=defn]] +[\-ESuVv] [\-Bpath] [\-ccmdfile|\-fcmdfile] [\-Dmacro[=defn]] [\-Pparameter=value] [\-pflag=value] [\-dname] [\-g1995\:|\-g2001\:|\-g2005\:|\-g2005-sv\:|\-g2009\:|\-g2012\:|\-g] [\-Iincludedir] [\-mmodule] [\-M[mode=]file] [\-Nfile] [\-ooutputfilename] @@ -222,6 +222,12 @@ will suppress the warning that the compiler is making a choice. Use this switch to specify the target output format. See the \fBTARGETS\fP section below for a list of valid output formats. .TP 8 +.B -u +Treat each source file as a separate compilation unit (as defined in +SystemVerilog). If compiling for an \fIIEEE1364\fP generation, this +will just reset all compiler directives (including macro definitions) +before each new file is processed. +.TP 8 .B -v Turn on verbose messages. This will print the command lines that are executed to perform the actual compilation, along with version @@ -564,7 +570,7 @@ Tips on using, debugging, and developing the compiler can be found at .SH COPYRIGHT .nf -Copyright \(co 2002\-2016 Stephen Williams +Copyright \(co 2002\-2017 Stephen Williams This document can be freely redistributed according to the terms of the GNU General Public License version 2.0 diff --git a/driver/main.c b/driver/main.c index bda5697ca..fa038bfe6 100644 --- a/driver/main.c +++ b/driver/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -38,7 +38,7 @@ const char NOTICE[] = ; const char HELP[] = -"Usage: iverilog [-EiSvV] [-B base] [-c cmdfile|-f cmdfile]\n" +"Usage: iverilog [-EiSuvV] [-B base] [-c cmdfile|-f cmdfile]\n" " [-g1995|-g2001|-g2005|-g2005-sv|-g2009|-g2012] [-g]\n" " [-D macro[=defn]] [-I includedir]\n" " [-M [mode=]depfile] [-m module]\n" @@ -140,6 +140,8 @@ int gen_relative_include = 0; char warning_flags[16] = "n"; +int separate_compilation_flag = 0; + /* Boolean: true means ignore errors about missing modules */ int ignore_missing_modules = 0; @@ -344,10 +346,10 @@ static int t_version_only(void) static void build_preprocess_command(int e_flag) { - snprintf(tmp, sizeof tmp, "%s%civlpp %s%s -F\"%s\" -f\"%s\" -p\"%s\" ", + snprintf(tmp, sizeof tmp, "%s%civlpp %s%s -F\"%s\" -f\"%s\" -p\"%s\"%s", ivlpp_dir, sep, verbose_flag?" -v":"", e_flag?"":" -L", defines_path, source_path, - compiled_defines_path); + compiled_defines_path, e_flag?"":" | "); } static int t_preprocess_only(void) @@ -410,8 +412,12 @@ static int t_compile(void) { unsigned rc; - /* Start by building the preprocess command line. */ - build_preprocess_command(0); + /* Start by building the preprocess command line, if required. + This pipes into the main ivl command. */ + if (!separate_compilation_flag) + build_preprocess_command(0); + else + strcpy(tmp, ""); size_t ncmd = strlen(tmp); char*cmd = malloc(ncmd + 1); @@ -421,8 +427,8 @@ static int t_compile(void) int rtn; #endif - /* Build the ivl command and pipe it to the preprocessor. */ - snprintf(tmp, sizeof tmp, " | %s%civl", base, sep); + /* Build the ivl command. */ + snprintf(tmp, sizeof tmp, "%s%civl", base, sep); rc = strlen(tmp); cmd = realloc(cmd, ncmd+rc+1); strcpy(cmd+ncmd, tmp); @@ -450,7 +456,16 @@ static int t_compile(void) strcpy(cmd+ncmd, tmp); ncmd += rc; - snprintf(tmp, sizeof tmp, " -C\"%s\" -- -", iconfig_common_path); + snprintf(tmp, sizeof tmp, " -C\"%s\"", iconfig_common_path); + rc = strlen(tmp); + cmd = realloc(cmd, ncmd+rc+1); + strcpy(cmd+ncmd, tmp); + ncmd += rc; + + if (separate_compilation_flag) + snprintf(tmp, sizeof tmp, " -F\"%s\"", source_path); + else + snprintf(tmp, sizeof tmp, " -- -"); rc = strlen(tmp); cmd = realloc(cmd, ncmd+rc+1); strcpy(cmd+ncmd, tmp); @@ -990,7 +1005,7 @@ int main(int argc, char **argv) } } - while ((opt = getopt(argc, argv, "B:c:D:d:Ef:g:hl:I:iM:m:N:o:P:p:Ss:T:t:vVW:y:Y:")) != EOF) { + while ((opt = getopt(argc, argv, "B:c:D:d:Ef:g:hl:I:iM:m:N:o:P:p:Ss:T:t:uvVW:y:Y:")) != EOF) { switch (opt) { case 'B': @@ -1093,6 +1108,9 @@ int main(int argc, char **argv) case 't': targ = optarg; break; + case 'u': + separate_compilation_flag = 1; + break; case 'v': verbose_flag = 1; break; @@ -1135,7 +1153,7 @@ int main(int argc, char **argv) if (version_flag || verbose_flag) { printf("Icarus Verilog version " VERSION " (" VERSION_TAG ")\n\n"); - printf("Copyright 1998-2015 Stephen Williams\n\n"); + printf("Copyright 1998-2017 Stephen Williams\n\n"); puts(NOTICE); } diff --git a/lexor.lex b/lexor.lex index 06d9b1744..bac72dedd 100644 --- a/lexor.lex +++ b/lexor.lex @@ -4,7 +4,7 @@ %{ /* - * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2017 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -72,6 +72,7 @@ static const char* set_file_name(char*text) void reset_lexor(); static void line_directive(); static void line_directive2(); +static void reset_all(); verinum*make_unsized_binary(const char*txt); verinum*make_undef_highz_dec(const char*txt); @@ -566,11 +567,7 @@ TU [munpf] "definition." << endl; error_count += 1; } else { - pform_set_default_nettype(NetNet::WIRE, yylloc.text, - yylloc.first_line); - in_celldefine = false; - uc_drive = UCD_NONE; - pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); + reset_all(); } } /* Notice and handle the `unconnected_drive directive. */ @@ -1597,6 +1594,18 @@ static void line_directive2() yylloc.first_line = lineno; } +/* + * Reset all compiler directives. This will be called when a `resetall + * directive is encountered or when a new compilation unit is started. + */ +static void reset_all() +{ + pform_set_default_nettype(NetNet::WIRE, yylloc.text, yylloc.first_line); + in_celldefine = false; + uc_drive = UCD_NONE; + pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); +} + extern FILE*vl_input; void reset_lexor() { @@ -1605,6 +1614,14 @@ void reset_lexor() /* Announce the first file name. */ yylloc.text = set_file_name(strdupnew(vl_file.c_str())); + + if (separate_compilation) { + reset_all(); + if (!keyword_mask_stack.empty()) { + lexor_keyword_mask = keyword_mask_stack.back(); + keyword_mask_stack.clear(); + } + } } /* diff --git a/load_module.cc b/load_module.cc index 665b865e9..b285302f2 100644 --- a/load_module.cc +++ b/load_module.cc @@ -82,36 +82,10 @@ bool load_module(const char*type) fflush(depend_file); } - if (ivlpp_string) { - char*cmdline = (char*)malloc(strlen(ivlpp_string) + - strlen(path) + 4); - strcpy(cmdline, ivlpp_string); - strcat(cmdline, " \""); - strcat(cmdline, path); - strcat(cmdline, "\""); + if (verbose_flag) + cerr << "Loading library file " << path << "." << endl; - if (verbose_flag) - cerr << "Executing: " << cmdline << endl<< flush; - - FILE*file = popen(cmdline, "r"); - - if (verbose_flag) - cerr << "...parsing output from preprocessor..." << endl << flush; - - pform_parse(path, file); - pclose(file); - free(cmdline); - - } else { - if (verbose_flag) - cerr << "Loading library file " - << path << "." << endl; - - FILE*file = fopen(path, "r"); - assert(file); - pform_parse(path, file); - fclose(file); - } + pform_parse(path); if (verbose_flag) cerr << "... Load module complete." << endl << flush; @@ -119,7 +93,6 @@ bool load_module(const char*type) return true; } - return false; } diff --git a/main.cc b/main.cc index e80ba1983..5a87b3266 100644 --- a/main.cc +++ b/main.cc @@ -140,6 +140,8 @@ void add_vpi_module(const char*name) map missing_modules; map library_file_map; +vector source_files; + list library_suff; list roots; @@ -179,6 +181,11 @@ bool debug_emit = false; bool debug_synth2 = false; bool debug_optimizer = false; +/* + * Compilation control flags. + */ +bool separate_compilation = false; + /* * Optimization control flags. */ @@ -777,6 +784,38 @@ static void read_iconfig_file(const char*ipath) fclose(ifile); } +/* + * This function reads a list of source file names. Each name starts + * with the first non-space character, and ends with the last non-space + * character. Spaces in the middle are OK. + */ +static void read_sources_file(const char*path) +{ + char line_buf[2048]; + + FILE*fd = fopen(path, "r"); + if (fd == 0) { + cerr << "ERROR: Unable to read source file list: " << path << endl; + return; + } + + while (fgets(line_buf, sizeof line_buf, fd) != 0) { + char*cp = line_buf + strspn(line_buf, " \t\r\b\f"); + char*tail = cp + strlen(cp); + while (tail > cp) { + if (! isspace((int)tail[-1])) + break; + tail -= 1; + tail[0] = 0; + } + + if (cp < tail) + source_files.push_back(filename_strings.make(cp)); + } + + fclose(fd); +} + extern Design* elaborate(list root); #if defined(HAVE_TIMES) @@ -863,12 +902,14 @@ int main(int argc, char*argv[]) min_typ_max_flag = TYP; min_typ_max_warn = 10; - while ((opt = getopt(argc, argv, "C:f:hN:P:p:Vv")) != EOF) switch (opt) { + while ((opt = getopt(argc, argv, "C:F:f:hN:P:p:Vv")) != EOF) switch (opt) { case 'C': read_iconfig_file(optarg); break; - + case 'F': + read_sources_file(optarg); + break; case 'f': parm_to_flagmap(optarg); break; @@ -921,6 +962,7 @@ int main(int argc, char*argv[]) "usage: ivl \n" "options:\n" "\t-C Config file from driver.\n" +"\t-F List of source files from driver.\n" "\t-h Print usage information, and exit.\n" "\t-N Dump the elaborated netlist to .\n" "\t-P Write the parsed input to .\n" @@ -936,11 +978,19 @@ int main(int argc, char*argv[]) return 0; } - if (optind == argc) { + int arg = optind; + while (arg < argc) { + perm_string path = filename_strings.make(argv[arg++]); + source_files.push_back(path); + } + + if (source_files.empty()) { cerr << "No input files." << endl; return 1; } + separate_compilation = source_files.size() > 1; + if( depfile_name ) { depend_file = fopen(depfile_name, "a"); if(! depend_file) { @@ -1036,7 +1086,10 @@ int main(int argc, char*argv[]) /* Parse the input. Make the pform. */ pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); - int rc = pform_parse(argv[optind]); + int rc = 0; + for (unsigned idx = 0; idx < source_files.size(); idx += 1) { + rc += pform_parse(source_files[idx]); + } if (pf_path) { ofstream out (pf_path); diff --git a/parse_api.h b/parse_api.h index 87ae28d11..25be3f57f 100644 --- a/parse_api.h +++ b/parse_api.h @@ -57,13 +57,13 @@ extern void elaborate_rootscope_classes(Design*des); extern void elaborate_rootscope_tasks(Design*des); /* - * This code actually invokes the parser to make modules. The first - * parameter is the name of the file that is to be parsed. The - * optional second parameter is the opened descriptor for the file. If - * the descriptor is 0 (or skipped) then the function will attempt to - * open the file on its own. + * This code actually invokes the parser to make modules. If the path + * parameter is "-", the parser reads from stdin, otherwise it attempts + * to open and read the specified file. When reading from a file, if + * the ivlpp_string variable is not set to null, the file will be piped + * through the command specified by ivlpp_string before being parsed. */ -extern int pform_parse(const char*path, FILE*file =0); +extern int pform_parse(const char*path); extern string vl_file; diff --git a/pform.cc b/pform.cc index 0478fba79..ed10b46e9 100644 --- a/pform.cc +++ b/pform.cc @@ -3715,22 +3715,38 @@ void pform_add_modport_port(const struct vlltype&loc, FILE*vl_input = 0; extern void reset_lexor(); -int pform_parse(const char*path, FILE*file) +int pform_parse(const char*path) { vl_file = path; - if (file == 0) { + if (strcmp(path, "-") == 0) { + vl_input = stdin; + } else if (ivlpp_string) { + char*cmdline = (char*)malloc(strlen(ivlpp_string) + + strlen(path) + 4); + strcpy(cmdline, ivlpp_string); + strcat(cmdline, " \""); + strcat(cmdline, path); + strcat(cmdline, "\""); - if (strcmp(path, "-") == 0) - vl_input = stdin; - else - vl_input = fopen(path, "r"); + if (verbose_flag) + cerr << "Executing: " << cmdline << endl<< flush; + + vl_input = popen(cmdline, "r"); if (vl_input == 0) { - cerr << "Unable to open " <