iverilog/driver/main.c

757 lines
18 KiB
C

/*
* Copyright (c) 2000-2001 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
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
#ident "$Id: main.c,v 1.57 2003/10/26 22:43:42 steve Exp $"
#endif
# include "config.h"
const char NOTICE[] =
" This program is free software; you can redistribute it and/or modify\n"
" it under the terms of the GNU General Public License as published by\n"
" the Free Software Foundation; either version 2 of the License, or\n"
" (at your option) any later version.\n"
"\n"
" This program is distributed in the hope that it will be useful,\n"
" but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
" GNU General Public License for more details.\n"
"\n"
" You should have received a copy of the GNU General Public License\n"
" along with this program; if not, write to the Free Software\n"
" Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA\n"
;
const char HELP[] =
"Usage: iverilog [-ESvV] [-B base] [-C path] [-c cmdfile] [-g1|-g2|-g3.0]\n"
" [-D macro[=defn]] [-I includedir] [-M depfile] [-m module]\n"
" [-N file] [-o filename] [-p flag=value]\n"
" [-s topmodule] [-t target] [-T min|typ|max]\n"
" [-W class] [-y dir] [-Y suf] source_file(s)\n"
"See man page for details.";
#define MAXSIZE 4096
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef __MINGW32__
#include <windows.h>
#ifdef HAVE_LIBIBERTY_H
#include <libiberty.h>
#endif
#endif
#if HAVE_GETOPT_H
#include <getopt.h>
#endif
#if defined(__MINGW32__) && !defined(HAVE_GETOPT_H)
extern int getopt(int argc, char*argv[], const char*fmt);
extern int optind;
extern const char*optarg;
#endif
#if !defined(WIFEXITED)
# define WIFEXITED(rc) ((rc&0x7f) == 0)
#endif
#if !defined(WEXITSTATUS)
# define WEXITSTATUS(rc) (rc>>8)
#endif
#ifndef IVL_ROOT
# define IVL_ROOT "."
#endif
# include "globals.h"
#ifdef __MINGW32__
const char sep = '\\';
#else
const char sep = '/';
#endif
extern void cfreset(FILE*fd, const char*path);
const char*base = 0;
const char*mtm = 0;
const char*opath = "a.out";
const char*npath = 0;
const char*targ = "vvp";
const char*depfile = 0;
const char*generation = "3.0";
char warning_flags[16] = "";
char*inc_list = 0;
char*def_list = 0;
char*mod_list = 0;
char*command_filename = 0;
char*f_list = 0;
/* These are used to collect the list of file names that will be
passed to ivlpp. Keep the list in a file because it can be a long
list. */
char*source_path = 0;
FILE*source_file = 0;
unsigned source_count = 0;
char*iconfig_path = 0;
FILE*iconfig_file = 0;
int synth_flag = 0;
int verbose_flag = 0;
int command_file = 0;
FILE *fp;
char line[MAXSIZE];
char tmp[MAXSIZE];
static char ivl_root[MAXSIZE];
#ifdef __MINGW32__
# include <io.h>
# include <fcntl.h>
static FILE*fopen_safe(const char*path)
{
FILE*file = 0;
int fd;
fd = _open(path, _O_WRONLY|_O_CREAT|_O_EXCL, 0700);
if (fd != -1)
file = _fdopen(fd, "w");
return file;
}
#else
# include <fcntl.h>
static FILE*fopen_safe(const char*path)
{
FILE*file = 0;
int fd;
fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0700);
if (fd != -1)
file = fdopen(fd, "w");
return file;
}
#endif
static const char*my_tempfile(const char*str, FILE**fout)
{
FILE*file;
int retry;
static char pathbuf[8192];
const char*tmpdir = getenv("TMP");
if (tmpdir == 0)
tmpdir = getenv("TMPDIR");
if (tmpdir == 0)
tmpdir = getenv("TEMP");
#ifdef __MINGW32__
if (tmpdir == 0)
tmpdir = "C:\\TEMP";
#else
if (tmpdir == 0)
tmpdir = "/tmp";
#endif
assert(tmpdir);
assert((strlen(tmpdir) + strlen(str)) < sizeof pathbuf - 10);
srand(getpid());
retry = 100;
file = NULL;
while ((retry > 0) && (file == NULL)) {
unsigned code = rand();
sprintf(pathbuf, "%s%c%s%04x", tmpdir, sep, str, code);
file = fopen_safe(pathbuf);
retry -= 1;
}
*fout = file;
return pathbuf;
}
/*
* This is the default target type. It looks up the bits that are
* needed to run the command from the configuration file (which is
* already parsed for us) so we can handle must of the generic cases.
*/
static int t_default(char*cmd, unsigned ncmd)
{
int rc;
const char*pattern;
pattern = lookup_pattern("<ivl>");
if (pattern == 0) {
fprintf(stderr, "No such target: %s\n", targ);
return -1;
}
tmp[0] = ' ';
tmp[1] = '|';
tmp[2] = ' ';
rc = build_string(tmp+3, sizeof tmp - 3, pattern);
cmd = realloc(cmd, ncmd+3+rc+1);
#ifdef __MINGW32__
{
char *t;
for (t = tmp; *t; t++)
{
if (*t == '/') *t = '\\';
}
}
#endif
strcpy(cmd+ncmd, tmp);
if (verbose_flag)
printf("translate: %s\n", cmd);
rc = system(cmd);
remove(source_path);
if ( ! getenv("IVERILOG_ICONFIG"))
remove(iconfig_path);
if (rc != 0) {
if (rc == 127) {
fprintf(stderr, "Failed to execute: %s\n", cmd);
return 1;
}
if (WIFEXITED(rc))
return WEXITSTATUS(rc);
fprintf(stderr, "Command signaled: %s\n", cmd);
return -1;
}
return 0;
}
static void process_warning_switch(const char*name)
{
if (strcmp(name,"all") == 0) {
strcat(warning_flags, "ipt");
} else if (strcmp(name,"implicit") == 0) {
if (! strchr(warning_flags+2, 'i'))
strcat(warning_flags, "i");
} else if (strcmp(name,"portbind") == 0) {
if (! strchr(warning_flags+2, 'p'))
strcat(warning_flags, "p");
} else if (strcmp(name,"timescale") == 0) {
if (! strchr(warning_flags+2, 't'))
strcat(warning_flags, "t");
} else if (strcmp(name,"no-implicit") == 0) {
char*cp = strchr(warning_flags+2, 'i');
if (cp) while (*cp) {
cp[0] = cp[1];
cp += 1;
}
} else if (strcmp(name,"no-portbind") == 0) {
char*cp = strchr(warning_flags+2, 'p');
if (cp) while (*cp) {
cp[0] = cp[1];
cp += 1;
}
} else if (strcmp(name,"no-timescale") == 0) {
char*cp = strchr(warning_flags+2, 't');
if (cp) while (*cp) {
cp[0] = cp[1];
cp += 1;
}
}
}
void process_library_switch(const char *name)
{
fprintf(iconfig_file, "-y:%s\n", name);
}
void process_library_nocase_switch(const char *name)
{
fprintf(iconfig_file, "-yl:%s\n", name);
}
void process_library2_switch(const char *name)
{
fprintf(iconfig_file, "-Y:%s\n", name);
}
void process_include_dir(const char *name)
{
if (inc_list == 0) {
inc_list = malloc(strlen(" -I")+strlen(name)+1);
strcpy(inc_list, " -I");
strcat(inc_list, name);
} else {
inc_list = realloc(inc_list, strlen(inc_list)
+ strlen(" -I")
+ strlen(name) + 1);
strcat(inc_list, " -I");
strcat(inc_list, name);
}
}
void process_define(const char*name)
{
if (def_list == 0) {
def_list = malloc(strlen(" -D")+strlen(name)+1);
strcpy(def_list, " -D");
strcat(def_list, name);
} else {
def_list = realloc(def_list, strlen(def_list)
+ strlen(" -D")
+ strlen(name) + 1);
strcat(def_list, " -D");
strcat(def_list, name);
}
}
void process_file_name(const char*name)
{
fprintf(source_file, "%s\n", name);
source_count += 1;
}
int process_generation(const char*name)
{
if (strcmp(name,"1") == 0)
generation = "1";
else if (strcmp(name,"2") == 0)
generation = "2";
else if (strcmp(name,"3.0") == 0)
generation = "3.0";
else {
fprintf(stderr, "Unknown/Unsupported Language generation "
"%s\n", name);
fprintf(stderr, "Supported generations are:\n");
fprintf(stderr, " 1 -- IEEE1364-1995 (Verilog 1)\n"
" 2 -- IEEE1364-2001 (Verilog 2001)\n"
" 3.0 -- SystemVerilog 3.0\n");
return 1;
}
return 0;
}
int main(int argc, char **argv)
{
const char*config_path = 0;
char*cmd;
unsigned ncmd;
int e_flag = 0;
int version_flag = 0;
int opt, idx, rc;
#ifdef __MINGW32__
{ char * s;
char basepath[1024];
GetModuleFileName(NULL,basepath,1024);
/* Calculate the ivl_root from the path to the command. This
is necessary because of the installation process in
Windows. Mostly, it is those darn drive letters, but oh
well. We know the command path is formed like this:
D:\iverilog\bin\iverilog.exe
The IVL_ROOT in a Windows installation is the path:
D:\iverilog\lib\ivl
so we chop the file name and the last directory by
turning the last two \ characters to null. Then we append
the lib\ivl to finish. */
strncpy(ivl_root, basepath, MAXSIZE);
s = strrchr(ivl_root, sep);
if (s) *s = 0;
s = strrchr(ivl_root, sep);
if (s) *s = 0;
strcat(ivl_root, "\\lib\\ivl");
base = ivl_root;
}
#else
/* In a UNIX environment, the IVL_ROOT from the Makefile is
dependable. It points to the $prefix/lib/ivl directory,
where the sub-parts are installed. */
strcpy(ivl_root, IVL_ROOT);
base = ivl_root;
#endif
/* Create a temporary file for communicating input parameters
to the preprocessor. */
source_path = strdup(my_tempfile("ivrlg", &source_file));
if (NULL == source_file) {
fprintf(stderr, "%s: Error opening temporary file %s\n",
argv[0], source_path);
fprintf(stderr, "%s: Please check TMP or TMPDIR.\n", argv[0]);
return 1;
}
/* Create another temporary file for passing configuration
information to ivl. */
if ( (iconfig_path = getenv("IVERILOG_ICONFIG")) ) {
fprintf(stderr, "%s: IVERILOG_ICONFIG=%s\n",
argv[0], iconfig_path);
iconfig_file = fopen(iconfig_path, "w");
} else {
iconfig_path = strdup(my_tempfile("ivrlh", &iconfig_file));
}
if (NULL == iconfig_file) {
fprintf(stderr, "%s: Error opening temporary file %s\n",
argv[0], iconfig_path);
fprintf(stderr, "%s: Please check TMP or TMPDIR.\n", argv[0]);
fclose(source_file);
remove(source_path);
return 1;
}
while ((opt = getopt(argc, argv, "B:C:c:D:Ef:g:hI:M:m:N::o:p:Ss:T:t:vVW:y:Y:")) != EOF) {
switch (opt) {
case 'B':
base = optarg;
break;
case 'C':
config_path = optarg;
break;
case 'c':
command_filename = malloc(strlen(optarg)+1);
strcpy(command_filename, optarg);
break;
case 'D':
process_define(optarg);
break;
case 'E':
e_flag = 1;
break;
case 'f':
fprintf(stderr, "warning: The -f flag is moved to -p\n");
case 'p':
if (f_list == 0) {
f_list = malloc(strlen(" -p")+strlen(optarg)+1);
strcpy(f_list, " -p");
strcat(f_list, optarg);
} else {
f_list = realloc(f_list, strlen(f_list) +
strlen(" -p") +
strlen(optarg) + 1);
strcat(f_list, " -p");
strcat(f_list, optarg);
}
break;
case 'g':
rc = process_generation(optarg);
if (rc != 0)
return -1;
break;
case 'h':
fprintf(stderr, "%s\n", HELP);
return 1;
case 'I':
process_include_dir(optarg);
break;
case 'M':
depfile = optarg;
break;
case 'm':
fprintf(iconfig_file, "module:%s\n", optarg);
break;
case 'N':
npath = optarg;
break;
case 'o':
opath = optarg;
break;
case 'S':
synth_flag = 1;
break;
case 's':
fprintf(iconfig_file, "root:%s\n", optarg);
break;
case 'T':
if (strcmp(optarg,"min") == 0) {
mtm = "min";
} else if (strcmp(optarg,"typ") == 0) {
mtm = "typ";
} else if (strcmp(optarg,"max") == 0) {
mtm = "max";
} else {
fprintf(stderr, "%s: invalid -T%s argument\n",
argv[0], optarg);
return 1;
}
break;
case 't':
targ = optarg;
break;
case 'v':
verbose_flag = 1;
break;
case 'V':
version_flag = 1;
break;
case 'W':
process_warning_switch(optarg);
break;
case 'y':
process_library_switch(optarg);
break;
case 'Y':
process_library2_switch(optarg);
break;
case '?':
default:
return 1;
}
}
if (version_flag || verbose_flag) {
printf("Icarus Verilog version " VERSION " ($Name: $)\n");
printf("Copyright 1998-2003 Stephen Williams\n");
puts(NOTICE);
if (version_flag)
return 0;
}
/* Write values to the iconfig file. */
if (mtm != 0) fprintf(iconfig_file, "-T:%s\n", mtm);
fprintf(iconfig_file, "generation:%s\n", generation);
fprintf(iconfig_file, "warnings:%s\n", warning_flags);
fprintf(iconfig_file, "out:%s\n", opath);
if (depfile) fprintf(iconfig_file, "depfile:%s\n", depfile);
if (command_filename) {
int rc;
if (( fp = fopen(command_filename, "r")) == NULL ) {
fprintf(stderr, "%s: Can't open %s\n",
argv[0], command_filename);
return 1;
}
cfreset(fp, command_filename);
rc = cfparse();
if (rc != 0) {
fprintf(stderr, "%s: error reading command file\n",
command_filename);
return 1;
}
}
/* Finally, process all the remaining words on the command
line as file names. */
for (idx = optind ; idx < argc ; idx += 1)
process_file_name(argv[idx]);
fclose(source_file);
source_file = 0;
if (source_count == 0) {
fprintf(stderr, "%s: No input files.\n", argv[0]);
fprintf(stderr, "%s\n", HELP);
return 1;
}
/* Load the iverilog.conf file to get our substitution
strings. */
{ char path[1024];
FILE*fd;
if (config_path) {
strcpy(path, config_path);
} else {
sprintf(path, "%s%civerilog.conf", base,sep);
}
fd = fopen(path, "r");
if (fd == 0) {
fprintf(stderr, "Config file \"%s\" not found\n",path);
return 1;
}
reset_lexor(fd);
yyparse();
}
/* Start building the preprocess command line. */
sprintf(tmp, "%s%civlpp %s%s -D__ICARUS__=1 -f%s ", base,sep,
verbose_flag?" -v":"",
e_flag?"":" -L", source_path);
ncmd = strlen(tmp);
cmd = malloc(ncmd + 1);
strcpy(cmd, tmp);
if (inc_list) {
cmd = realloc(cmd, ncmd + strlen(inc_list) + 1);
strcat(cmd, inc_list);
ncmd += strlen(inc_list);
}
if (def_list) {
cmd = realloc(cmd, ncmd + strlen(def_list) + 1);
strcat(cmd, def_list);
ncmd += strlen(def_list);
}
if (depfile) {
cmd = realloc(cmd, ncmd + strlen(depfile) + 5);
strcat(cmd, " -M ");
strcat(cmd, depfile);
ncmd += strlen(depfile) + 4;
}
/* If the -E flag was given on the command line, then all we
do is run the preprocessor and put the output where the
user wants it. */
if (e_flag) {
int rc;
if (strcmp(opath,"-") != 0) {
sprintf(tmp, " > %s", opath);
cmd = realloc(cmd, ncmd+strlen(tmp)+1);
strcpy(cmd+ncmd, tmp);
ncmd += strlen(tmp);
}
if (verbose_flag)
printf("preprocess: %s\n", cmd);
rc = system(cmd);
remove(source_path);
fclose(iconfig_file);
if ( ! getenv("IVERILOG_ICONFIG"))
remove(iconfig_path);
if (rc != 0) {
if (WIFEXITED(rc)) {
fprintf(stderr, "errors preprocessing Verilog program.\n");
return WEXITSTATUS(rc);
}
fprintf(stderr, "Command signaled: %s\n", cmd);
return -1;
}
return 0;
}
/* Write the preprocessor command needed to preprocess a
single file. This may be used to preprocess library
files. */
fprintf(iconfig_file, "ivlpp:%s%civlpp -D__ICARUS__ -L %s %s\n",
base, sep,
inc_list? inc_list : "",
def_list? def_list : "");
/* Done writing to the iconfig file. Close it now. */
fclose(iconfig_file);
return t_default(cmd, ncmd);
return 0;
}
/*
* $Log: main.c,v $
* Revision 1.57 2003/10/26 22:43:42 steve
* Improve -V messages,
*
* Revision 1.56 2003/09/26 21:25:58 steve
* Warnings cleanup.
*
* Revision 1.55 2003/09/23 05:57:15 steve
* Pass -m flag from driver via iconfig file.
*
* Revision 1.54 2003/09/22 01:12:09 steve
* Pass more ivl arguments through the iconfig file.
*
* Revision 1.53 2003/08/26 16:26:02 steve
* ifdef idents correctly.
*
* Revision 1.52 2003/02/22 04:55:36 steve
* portbind adds p, not i, flag.
*
* Revision 1.51 2003/02/22 04:12:49 steve
* Add the portbind warning.
*
* Revision 1.50 2003/01/10 19:01:04 steve
* Only use libiberty.h if available.
*
* Revision 1.49 2002/12/04 03:26:59 steve
* Mingw32 compatible temp file management.
*
* Revision 1.48 2002/12/04 02:29:36 steve
* Use O_EXCL when opening temp files.
*
* Revision 1.47 2002/08/12 01:27:48 steve
* Escape the backslash in the windows file name.
*
* Revision 1.46 2002/08/10 22:36:59 steve
* No longer any nead for -rdynamic flag
*
* Revision 1.45 2002/08/10 22:27:13 steve
* Kill links to vvm.
*
* Revision 1.44 2002/07/15 00:33:50 steve
* Improve temporary file name guess.
*
* Revision 1.43 2002/07/14 23:32:31 steve
* No longer need the .exe on generated files.
*/