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.
This commit is contained in:
Martin Whitaker 2017-10-15 11:45:35 +01:00
parent 7d78f5b2ea
commit dfddbea26b
8 changed files with 163 additions and 72 deletions

View File

@ -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

View File

@ -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<feature>]
[\-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

View File

@ -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<feature>]\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);
}

View File

@ -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();
}
}
}
/*

View File

@ -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;
}

61
main.cc
View File

@ -140,6 +140,8 @@ void add_vpi_module(const char*name)
map<perm_string,unsigned> missing_modules;
map<perm_string,bool> library_file_map;
vector<perm_string> source_files;
list<const char*> library_suff;
list<perm_string> 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 <perm_string> 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 <options> <file>\n"
"options:\n"
"\t-C <name> Config file from driver.\n"
"\t-F <file> List of source files from driver.\n"
"\t-h Print usage information, and exit.\n"
"\t-N <file> Dump the elaborated netlist to <file>.\n"
"\t-P <file> Write the parsed input to <file>.\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);

View File

@ -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;

View File

@ -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 " <<vl_file << "." << endl;
return 11;
cerr << "Unable to preprocess " << path << "." << endl;
return 1;
}
if (verbose_flag)
cerr << "...parsing output from preprocessor..." << endl << flush;
free(cmdline);
} else {
vl_input = file;
vl_input = fopen(path, "r");
if (vl_input == 0) {
cerr << "Unable to open " << path << "." << endl;
return 1;
}
}
reset_lexor();
@ -3738,8 +3754,12 @@ int pform_parse(const char*path, FILE*file)
warn_count = 0;
int rc = VLparse();
if (file == 0)
fclose(vl_input);
if (vl_input != stdin) {
if (ivlpp_string)
pclose(vl_input);
else
fclose(vl_input);
}
if (rc) {
cerr << "I give up." << endl;