Configure with --enable-libvvp builds a shared library containing
almost all of vvp that may be used by other programs. The vvp program becomes a small client of libvvp.
This commit is contained in:
commit
95810b2f61
|
|
@ -17,6 +17,7 @@ Icarus Verilog.
|
||||||
ivlpp_flags
|
ivlpp_flags
|
||||||
vvp_flags
|
vvp_flags
|
||||||
vvp_debug
|
vvp_debug
|
||||||
|
vvp_library
|
||||||
vhdlpp_flags
|
vhdlpp_flags
|
||||||
gtkwave
|
gtkwave
|
||||||
vpi
|
vpi
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,13 @@ time). ::
|
||||||
|
|
||||||
This option adds extra memory cleanup code and pool management code to allow
|
This option adds extra memory cleanup code and pool management code to allow
|
||||||
better memory leak checking when valgrind is available. This option is not
|
better memory leak checking when valgrind is available. This option is not
|
||||||
need when checking for basic errors with valgrind.
|
need when checking for basic errors with valgrind. ::
|
||||||
|
|
||||||
|
--enable-libvvp
|
||||||
|
|
||||||
|
The vvp progam is built as a small stub linked to a shared library,
|
||||||
|
libvvp.so, that may be linked with other programs so that they can host
|
||||||
|
a vvp simulation.
|
||||||
|
|
||||||
Compiling on Linux/Unix
|
Compiling on Linux/Unix
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
VVP as a library
|
||||||
|
================
|
||||||
|
|
||||||
|
If configured with ::
|
||||||
|
|
||||||
|
--enable-libvvp
|
||||||
|
|
||||||
|
the vvp program will be built as a small stub that
|
||||||
|
depends on a shared library, libvvp.so.
|
||||||
|
The library may also be used to include a vvp simulation
|
||||||
|
in a larger program. Typically, the simulation communicates
|
||||||
|
with its host program using VPI, but since
|
||||||
|
almost all the functions of vvp are included in the library
|
||||||
|
it may be possible to use text output and interactive mode.
|
||||||
|
|
||||||
|
The accessible functions of the library are defined and documented
|
||||||
|
in the header file, vvp/libvvp.h. Although vvp is a C++ program, the
|
||||||
|
header file presents a C interface.
|
||||||
|
|
||||||
|
Note that the vvp software was not designed to be used this way
|
||||||
|
and the library is a straightforward recompilation of the program code.
|
||||||
|
That imposes some restrictions, mostly arising from the use
|
||||||
|
of static variables: only a single run of a single simulation instance
|
||||||
|
can be expected to work without special actions.
|
||||||
|
To mitigate these restrictions, the library may by loaded dynamically
|
||||||
|
and unloaded at the end of each simulation run.
|
||||||
|
Parallel simulation should be possible by making multiple copies
|
||||||
|
of the library with different names.
|
||||||
|
|
||||||
|
|
@ -170,6 +170,14 @@ AC_SUBST(HAVE_LIBBZ2)
|
||||||
AC_FUNC_ALLOCA
|
AC_FUNC_ALLOCA
|
||||||
AC_FUNC_FSEEKO
|
AC_FUNC_FSEEKO
|
||||||
|
|
||||||
|
# Package Options
|
||||||
|
# ---------------
|
||||||
|
|
||||||
|
# Build VVP as a library and stub
|
||||||
|
AC_ARG_ENABLE([libvvp],
|
||||||
|
[AS_HELP_STRING([--enable-libvvp], [build VVP as a shared library])],
|
||||||
|
[AC_SUBST(LIBVVP, yes)],[])
|
||||||
|
|
||||||
# valgrind checks
|
# valgrind checks
|
||||||
AC_ARG_WITH([valgrind], [AS_HELP_STRING([--with-valgrind],[Add valgrind hooks])],
|
AC_ARG_WITH([valgrind], [AS_HELP_STRING([--with-valgrind],[Add valgrind hooks])],
|
||||||
[], [check_valgrind=yes])
|
[], [check_valgrind=yes])
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,8 @@ VPI = vpi_modules.o vpi_bit.o vpi_callback.o vpi_cobject.o vpi_const.o vpi_darra
|
||||||
vpi_vthr_vector.o vpip_bin.o vpip_hex.o vpip_oct.o \
|
vpi_vthr_vector.o vpip_bin.o vpip_hex.o vpip_oct.o \
|
||||||
vpip_to_dec.o vpip_format.o vvp_vpi.o
|
vpip_to_dec.o vpip_format.o vvp_vpi.o
|
||||||
|
|
||||||
O = main.o parse.o parse_misc.o lexor.o arith.o array_common.o array.o bufif.o compile.o \
|
O = lib_main.o \
|
||||||
|
parse.o parse_misc.o lexor.o arith.o array_common.o array.o bufif.o compile.o \
|
||||||
concat.o dff.o class_type.o enum_type.o extend.o file_line.o latch.o npmos.o part.o \
|
concat.o dff.o class_type.o enum_type.o extend.o file_line.o latch.o npmos.o part.o \
|
||||||
permaheap.o reduce.o resolv.o \
|
permaheap.o reduce.o resolv.o \
|
||||||
sfunc.o stop.o \
|
sfunc.o stop.o \
|
||||||
|
|
@ -98,7 +99,7 @@ else
|
||||||
endif
|
endif
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o *~ parse.cc parse.h lexor.cc tables.cc
|
rm -f *.o *~ parse.cc parse.h lexor.cc tables.cc libvvp.so
|
||||||
rm -rf dep vvp@EXEEXT@ parse.output vvp.man vvp.ps vvp.pdf vvp.exp
|
rm -rf dep vvp@EXEEXT@ parse.output vvp.man vvp.ps vvp.pdf vvp.exp
|
||||||
|
|
||||||
distclean: clean
|
distclean: clean
|
||||||
|
|
@ -121,20 +122,44 @@ dep:
|
||||||
mkdir dep
|
mkdir dep
|
||||||
|
|
||||||
ifeq (@WIN32@,yes)
|
ifeq (@WIN32@,yes)
|
||||||
|
ifeq (@LIBVVP@,yes)
|
||||||
|
|
||||||
|
CPPFLAGS+= -fpic
|
||||||
|
SLEXT=DLL
|
||||||
|
|
||||||
|
vvp@EXEEXT@: main.o $(srcdir)/vvp.def libvvp.DLL
|
||||||
|
$(CXX) $(LDFLAGS) -o vvp@EXEEXT@ main.o -L. $(LDFLAGS) -lvvp $(LIBS)
|
||||||
|
|
||||||
|
libvvp.DLL: $O
|
||||||
|
$(CXX) -shared $(LDFLAGS) -o libvvp.DLL $O $(LIBS) $(dllib)
|
||||||
|
else
|
||||||
# To support cocotb, we export the VPI functions directly. This allows
|
# To support cocotb, we export the VPI functions directly. This allows
|
||||||
# cocotb to build VPI modules without using our vpi_user.h and libvpi.a.
|
# cocotb to build VPI modules without using our vpi_user.h and libvpi.a.
|
||||||
# This requires making the vvp.exe in two steps. The first step makes
|
# This requires making the vvp.exe in two steps. The first step makes
|
||||||
# a vvp.exe that dlltool can use to make an export library, and the
|
# a vvp.exe that dlltool can use to make an export library, and the
|
||||||
# second step makes a vvp.exe that really exports those things.
|
# second step makes a vvp.exe that really exports those things.
|
||||||
vvp@EXEEXT@: $O $(srcdir)/vvp.def
|
vvp@EXEEXT@: main.o $O $(srcdir)/vvp.def
|
||||||
$(CXX) -o vvp$(suffix)@EXEEXT@ $(LDFLAGS) $O $(dllib) $(LIBS)
|
$(CXX) -o vvp$(suffix)@EXEEXT@ $(LDFLAGS) main.o $O $(dllib) $(LIBS)
|
||||||
$(DLLTOOL) --dllname vvp$(suffix)@EXEEXT@ --def $(srcdir)/vvp.def \
|
$(DLLTOOL) --dllname vvp$(suffix)@EXEEXT@ --def $(srcdir)/vvp.def \
|
||||||
--output-exp vvp.exp
|
--output-exp vvp.exp
|
||||||
rm -f vvp$(suffix)@EXEEXT@
|
rm -f vvp$(suffix)@EXEEXT@
|
||||||
$(CXX) $(LDFLAGS) -o vvp@EXEEXT@ vvp.exp $(LDFLAGS) $O $(dllib) $(LIBS)
|
$(CXX) $(LDFLAGS) -o vvp$(suffix)@EXEEXT@ vvp.exp $(LDFLAGS) main.o $O $(dllib) $(LIBS)
|
||||||
|
endif
|
||||||
|
else ifeq (@LIBVVP@,yes)
|
||||||
|
|
||||||
|
CPPFLAGS+= -fpic
|
||||||
|
SLEXT=so
|
||||||
|
|
||||||
|
# To avoid setting LD_LIBRARY_PATH when running vvp from the build tree,
|
||||||
|
# add option -Wl,-rpath=`pwd` to the CXX command below.
|
||||||
|
vvp@EXEEXT@: main.o libvvp.so
|
||||||
|
$(CXX) $(LDFLAGS) -o vvp@EXEEXT@ main.o -L. -lvvp $(LIBS)
|
||||||
|
|
||||||
|
libvvp.so: $O
|
||||||
|
$(CXX) -shared $(LDFLAGS) -o libvvp.so $O $(LIBS) $(dllib)
|
||||||
else
|
else
|
||||||
vvp@EXEEXT@: $O
|
vvp@EXEEXT@: $O main.o
|
||||||
$(CXX) $(LDFLAGS) -o vvp@EXEEXT@ $O $(LIBS) $(dllib)
|
$(CXX) $(LDFLAGS) -o vvp@EXEEXT@ main.o $O $(LIBS) $(dllib)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
%.o: %.cc config.h
|
%.o: %.cc config.h
|
||||||
|
|
@ -207,6 +232,9 @@ installpdf: vvp.pdf installdirs
|
||||||
|
|
||||||
installfiles: $(F) | installdirs
|
installfiles: $(F) | installdirs
|
||||||
$(INSTALL_PROGRAM) ./vvp@EXEEXT@ "$(DESTDIR)$(bindir)/vvp$(suffix)@EXEEXT@"
|
$(INSTALL_PROGRAM) ./vvp@EXEEXT@ "$(DESTDIR)$(bindir)/vvp$(suffix)@EXEEXT@"
|
||||||
|
ifeq (@LIBVVP@,yes)
|
||||||
|
$(INSTALL_PROGRAM) ./libvvp.$(SLEXT) "$(DESTDIR)$(libdir)/libvvp$(suffix).$(SLEXT)"
|
||||||
|
endif
|
||||||
|
|
||||||
installdirs: $(srcdir)/../mkinstalldirs
|
installdirs: $(srcdir)/../mkinstalldirs
|
||||||
$(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(INSTALL_DOCDIR)"
|
$(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(INSTALL_DOCDIR)"
|
||||||
|
|
@ -215,5 +243,8 @@ installdirs: $(srcdir)/../mkinstalldirs
|
||||||
uninstall: $(UNINSTALL32)
|
uninstall: $(UNINSTALL32)
|
||||||
rm -f "$(DESTDIR)$(bindir)/vvp$(suffix)@EXEEXT@"
|
rm -f "$(DESTDIR)$(bindir)/vvp$(suffix)@EXEEXT@"
|
||||||
rm -f "$(DESTDIR)$(mandir)/man1/vvp$(suffix).1" "$(DESTDIR)$(prefix)/vvp$(suffix).pdf"
|
rm -f "$(DESTDIR)$(mandir)/man1/vvp$(suffix).1" "$(DESTDIR)$(prefix)/vvp$(suffix).pdf"
|
||||||
|
ifeq (@LIBVVP@,yes)
|
||||||
|
rm -f "$(DESTDIR)$(libdir)/libvvp$(suffix).$(SLEXT)"
|
||||||
|
endif
|
||||||
|
|
||||||
-include $(patsubst %.o, dep/%.d, $O)
|
-include $(patsubst %.o, dep/%.d, $O)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,417 @@
|
||||||
|
const char COPYRIGHT[] =
|
||||||
|
"Copyright (c) 2001-2024 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
# include "version_base.h"
|
||||||
|
# include "config.h"
|
||||||
|
# include "compile.h"
|
||||||
|
# include "schedule.h"
|
||||||
|
# include "vpi_priv.h"
|
||||||
|
# include "statistics.h"
|
||||||
|
# include "vvp_cleanup.h"
|
||||||
|
# include "vvp_object.h"
|
||||||
|
# include <cstdio>
|
||||||
|
# include <cstdlib>
|
||||||
|
# include <cstring>
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <cassert>
|
||||||
|
#ifdef CHECK_WITH_VALGRIND
|
||||||
|
# include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_SYS_RESOURCE_H)
|
||||||
|
# include <sys/time.h>
|
||||||
|
# include <sys/resource.h>
|
||||||
|
#endif // defined(HAVE_SYS_RESOURCE_H)
|
||||||
|
|
||||||
|
#if defined(__MINGW32__)
|
||||||
|
# include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "libvvp.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
ofstream debug_file;
|
||||||
|
|
||||||
|
#if defined(__MINGW32__) && !defined(HAVE_GETOPT_H)
|
||||||
|
extern "C" int getopt(int argc, char*argv[], const char*fmt);
|
||||||
|
extern "C" int optind;
|
||||||
|
extern "C" const char*optarg;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool verbose_flag = false;
|
||||||
|
static int vvp_return_value = 0;
|
||||||
|
|
||||||
|
void vvp_set_stop_is_finish(bool flag)
|
||||||
|
{
|
||||||
|
extern bool stop_is_finish;
|
||||||
|
|
||||||
|
stop_is_finish = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vvp_set_stop_is_finish_exit_code(bool flag)
|
||||||
|
{
|
||||||
|
extern int stop_is_finish_exit_code;
|
||||||
|
|
||||||
|
stop_is_finish_exit_code = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vvp_set_verbose_flag(bool flag)
|
||||||
|
{
|
||||||
|
verbose_flag = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vpip_set_return_value(int value)
|
||||||
|
{
|
||||||
|
vvp_return_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char log_buffer[4096];
|
||||||
|
|
||||||
|
#if defined(HAVE_SYS_RESOURCE_H)
|
||||||
|
static void my_getrusage(struct rusage *a)
|
||||||
|
{
|
||||||
|
getrusage(RUSAGE_SELF, a);
|
||||||
|
|
||||||
|
# if defined(LINUX)
|
||||||
|
{
|
||||||
|
FILE *statm;
|
||||||
|
unsigned siz, rss, shd;
|
||||||
|
long page_size = sysconf(_SC_PAGESIZE);
|
||||||
|
if (page_size==-1) page_size=0;
|
||||||
|
statm = fopen("/proc/self/statm", "r");
|
||||||
|
if (!statm) {
|
||||||
|
perror("/proc/self/statm");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Given that these are in pages we'll limit the value to
|
||||||
|
* what will fit in a 32 bit integer to prevent undefined
|
||||||
|
* behavior in fscanf(). */
|
||||||
|
if (3 == fscanf(statm, "%9u %9u %9u", &siz, &rss, &shd)) {
|
||||||
|
a->ru_maxrss = page_size * siz;
|
||||||
|
a->ru_idrss = page_size * rss;
|
||||||
|
a->ru_ixrss = page_size * shd;
|
||||||
|
}
|
||||||
|
fclose(statm);
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_rusage(struct rusage *a, struct rusage *b)
|
||||||
|
{
|
||||||
|
double delta = a->ru_utime.tv_sec
|
||||||
|
+ a->ru_utime.tv_usec/1E6
|
||||||
|
+ a->ru_stime.tv_sec
|
||||||
|
+ a->ru_stime.tv_usec/1E6
|
||||||
|
- b->ru_utime.tv_sec
|
||||||
|
- b->ru_utime.tv_usec/1E6
|
||||||
|
- b->ru_stime.tv_sec
|
||||||
|
- b->ru_stime.tv_usec/1E6
|
||||||
|
;
|
||||||
|
|
||||||
|
vpi_mcd_printf(1,
|
||||||
|
" ... %G seconds,"
|
||||||
|
" %.1f/%.1f/%.1f KBytes size/rss/shared\n",
|
||||||
|
delta,
|
||||||
|
a->ru_maxrss/1024.0,
|
||||||
|
(a->ru_idrss+a->ru_isrss)/1024.0,
|
||||||
|
a->ru_ixrss/1024.0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // ! defined(HAVE_SYS_RESOURCE_H)
|
||||||
|
|
||||||
|
// Provide dummies
|
||||||
|
struct rusage { int x; };
|
||||||
|
inline static void my_getrusage(struct rusage *) { }
|
||||||
|
inline static void print_rusage(struct rusage *, struct rusage *){};
|
||||||
|
|
||||||
|
#endif // ! defined(HAVE_SYS_RESOURCE_H)
|
||||||
|
|
||||||
|
static bool have_ivl_version = false;
|
||||||
|
/*
|
||||||
|
* Verify that the input file has a compatible version.
|
||||||
|
*/
|
||||||
|
void verify_version(char*ivl_ver, char*commit)
|
||||||
|
{
|
||||||
|
have_ivl_version = true;
|
||||||
|
|
||||||
|
if (verbose_flag) {
|
||||||
|
vpi_mcd_printf(1, " ... VVP file version %s", ivl_ver);
|
||||||
|
if (commit) vpi_mcd_printf(1, " %s", commit);
|
||||||
|
vpi_mcd_printf(1, "\n");
|
||||||
|
}
|
||||||
|
delete[] commit;
|
||||||
|
|
||||||
|
int file_major, file_minor, file_minor2;
|
||||||
|
char file_extra[128];
|
||||||
|
|
||||||
|
// Old style format: 0.<major>.<minor> <extra>
|
||||||
|
// This also catches a potential new-new format that has
|
||||||
|
// another sub-minor number.
|
||||||
|
file_extra[0] = 0;
|
||||||
|
int rc = sscanf(ivl_ver, "%d.%d.%d %127s", &file_major, &file_minor, &file_minor2, file_extra);
|
||||||
|
|
||||||
|
// If it wasn't the old style format, try the new format:
|
||||||
|
// <major>.<minor> <extra>
|
||||||
|
if (rc == 2) {
|
||||||
|
file_extra[0] = 0;
|
||||||
|
rc = sscanf(ivl_ver, "%d.%d %127s", &file_major, &file_minor, file_extra);
|
||||||
|
assert((rc == 2) || (rc == 3));
|
||||||
|
file_minor2 = 0;
|
||||||
|
}
|
||||||
|
delete[] ivl_ver;
|
||||||
|
|
||||||
|
// If this was the old format, the file_major will be 0. In
|
||||||
|
// this case it is not really what we meant, so convert to the
|
||||||
|
// new format.
|
||||||
|
if (file_major == 0) {
|
||||||
|
file_major = file_minor;
|
||||||
|
file_minor = file_minor2;
|
||||||
|
file_minor2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VERSION_MAJOR != file_major) {
|
||||||
|
vpi_mcd_printf(1, "Error: VVP input file %d.%d can not "
|
||||||
|
"be run with run time version %s\n",
|
||||||
|
file_major, file_minor, VERSION);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VERSION_MINOR < file_minor) {
|
||||||
|
vpi_mcd_printf(1, "Warning: VVP input file sub version %d.%d"
|
||||||
|
" is greater than the run time version %s.\n",
|
||||||
|
file_major, file_minor, VERSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int vpip_delay_selection = _vpiDelaySelTypical;
|
||||||
|
void set_delay_selection(const char* sel)
|
||||||
|
{
|
||||||
|
if (strcmp("TYPICAL", sel) == 0) {
|
||||||
|
vpip_delay_selection = _vpiDelaySelTypical;
|
||||||
|
} else if (strcmp("MINIMUM", sel) == 0) {
|
||||||
|
vpip_delay_selection = _vpiDelaySelMinimum;
|
||||||
|
} else if (strcmp("MAXIMUM", sel) == 0) {
|
||||||
|
vpip_delay_selection = _vpiDelaySelMaximum;
|
||||||
|
} else {
|
||||||
|
vpi_mcd_printf(1, "Error: Unknown delay selection \"%s\"!", sel);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
delete[] sel;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void final_cleanup()
|
||||||
|
{
|
||||||
|
vvp_object::cleanup();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We only need to cleanup the memory if we are checking with valgrind.
|
||||||
|
*/
|
||||||
|
#ifdef CHECK_WITH_VALGRIND
|
||||||
|
/* Clean up the file name table. */
|
||||||
|
for (vector<const char*>::iterator cur = file_names.begin();
|
||||||
|
cur != file_names.end() ; ++ cur ) {
|
||||||
|
delete[] *cur;
|
||||||
|
}
|
||||||
|
/* Clear the static result buffer. */
|
||||||
|
(void)need_result_buf(0, RBUF_DEL);
|
||||||
|
codespace_delete();
|
||||||
|
root_table_delete();
|
||||||
|
def_table_delete();
|
||||||
|
vpi_mcd_delete();
|
||||||
|
dec_str_delete();
|
||||||
|
modpath_delete();
|
||||||
|
vpi_handle_delete();
|
||||||
|
vpi_stack_delete();
|
||||||
|
udp_defns_delete();
|
||||||
|
island_delete();
|
||||||
|
signal_pool_delete();
|
||||||
|
vvp_net_pool_delete();
|
||||||
|
ufunc_pool_delete();
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* Unload the VPI modules. This is essential for MinGW, to ensure
|
||||||
|
* dump files are flushed before the main process terminates, as
|
||||||
|
* the DLL termination code is called after all remaining open
|
||||||
|
* files are automatically closed.
|
||||||
|
*/
|
||||||
|
load_module_delete();
|
||||||
|
|
||||||
|
#ifdef CHECK_WITH_VALGRIND
|
||||||
|
simulator_cb_delete();
|
||||||
|
/* This is needed to prevent valgrind from complaining about
|
||||||
|
* _dlerror_run() having a memory leak. */
|
||||||
|
// HERE: Is this portable? Does it break anything?
|
||||||
|
pthread_exit(NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void vpip_mcd_init(FILE *log);
|
||||||
|
extern void vvp_vpi_init(void);
|
||||||
|
|
||||||
|
void vvp_init(const char *logfile_name, int argc, char*argv[])
|
||||||
|
{
|
||||||
|
struct rusage cycle;
|
||||||
|
FILE *logfile = 0x0;
|
||||||
|
extern void vpi_set_vlog_info(int, char**);
|
||||||
|
|
||||||
|
if( ::getenv("VVP_WAIT_FOR_DEBUGGER") != 0 ) {
|
||||||
|
fprintf( stderr, "Waiting for debugger...\n");
|
||||||
|
bool debugger_release = false;
|
||||||
|
while( !debugger_release ) {
|
||||||
|
#if defined(__MINGW32__)
|
||||||
|
Sleep(1000);
|
||||||
|
#else
|
||||||
|
sleep(1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vpip_add_env_and_default_module_paths();
|
||||||
|
|
||||||
|
/* If the VVP_DEBUG variable is set, then it contains the path
|
||||||
|
to the vvp debug file. Open it for output. */
|
||||||
|
|
||||||
|
if (char*path = getenv("VVP_DEBUG")) {
|
||||||
|
debug_file.open(path, ios::out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is needed to get the MCD I/O routines ready for
|
||||||
|
anything. It is done early because it is plausible that the
|
||||||
|
compile might affect it, and it is cheap to do. */
|
||||||
|
|
||||||
|
if (logfile_name) {
|
||||||
|
if (!strcmp(logfile_name, "-"))
|
||||||
|
logfile = stderr;
|
||||||
|
else {
|
||||||
|
logfile = fopen(logfile_name, "w");
|
||||||
|
if (!logfile) {
|
||||||
|
perror(logfile_name);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
setvbuf(logfile, log_buffer, _IOLBF, sizeof(log_buffer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vpip_mcd_init(logfile);
|
||||||
|
|
||||||
|
if (verbose_flag) {
|
||||||
|
my_getrusage(&cycle);
|
||||||
|
vpi_mcd_printf(1, "Compiling VVP ...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
vvp_vpi_init();
|
||||||
|
|
||||||
|
/* Make the extended arguments available to the simulation. */
|
||||||
|
vpi_set_vlog_info(argc, argv);
|
||||||
|
|
||||||
|
compile_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
int vvp_run(const char *design_path)
|
||||||
|
{
|
||||||
|
struct rusage cycles[3];
|
||||||
|
int ret_cd = compile_design(design_path);
|
||||||
|
|
||||||
|
destroy_lexor();
|
||||||
|
print_vpi_call_errors();
|
||||||
|
if (ret_cd) return ret_cd;
|
||||||
|
|
||||||
|
if (!have_ivl_version) {
|
||||||
|
if (verbose_flag) vpi_mcd_printf(1, "... ");
|
||||||
|
vpi_mcd_printf(1, "Warning: vvp input file may not be correct "
|
||||||
|
"version!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose_flag) {
|
||||||
|
vpi_mcd_printf(1, "Compile cleanup...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
compile_cleanup();
|
||||||
|
|
||||||
|
if (compile_errors > 0) {
|
||||||
|
vpi_mcd_printf(1, "%s: Program not runnable, %u errors.\n",
|
||||||
|
design_path, compile_errors);
|
||||||
|
final_cleanup();
|
||||||
|
return compile_errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose_flag) {
|
||||||
|
vpi_mcd_printf(1, " ... %8lu functors (net_fun pool=%zu bytes)\n",
|
||||||
|
count_functors, vvp_net_fun_t::heap_total());
|
||||||
|
vpi_mcd_printf(1, " %8lu logic\n", count_functors_logic);
|
||||||
|
vpi_mcd_printf(1, " %8lu bufif\n", count_functors_bufif);
|
||||||
|
vpi_mcd_printf(1, " %8lu resolv\n",count_functors_resolv);
|
||||||
|
vpi_mcd_printf(1, " %8lu signals\n", count_functors_sig);
|
||||||
|
vpi_mcd_printf(1, " ... %8lu filters (net_fil pool=%zu bytes)\n",
|
||||||
|
count_filters, vvp_net_fil_t::heap_total());
|
||||||
|
vpi_mcd_printf(1, " ... %8lu opcodes (%zu bytes)\n",
|
||||||
|
count_opcodes, size_opcodes);
|
||||||
|
vpi_mcd_printf(1, " ... %8lu nets\n", count_vpi_nets);
|
||||||
|
vpi_mcd_printf(1, " ... %8lu vvp_nets (%zu bytes)\n",
|
||||||
|
count_vvp_nets, size_vvp_nets);
|
||||||
|
vpi_mcd_printf(1, " ... %8lu arrays (%lu words)\n",
|
||||||
|
count_net_arrays, count_net_array_words);
|
||||||
|
vpi_mcd_printf(1, " ... %8lu memories\n",
|
||||||
|
count_var_arrays+count_real_arrays);
|
||||||
|
vpi_mcd_printf(1, " %8lu logic (%lu words)\n",
|
||||||
|
count_var_arrays, count_var_array_words);
|
||||||
|
vpi_mcd_printf(1, " %8lu real (%lu words)\n",
|
||||||
|
count_real_arrays, count_real_array_words);
|
||||||
|
vpi_mcd_printf(1, " ... %8lu scopes\n", count_vpi_scopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose_flag) {
|
||||||
|
my_getrusage(cycles+1);
|
||||||
|
print_rusage(cycles+1, cycles+0);
|
||||||
|
vpi_mcd_printf(1, "Running ...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
schedule_simulate();
|
||||||
|
|
||||||
|
if (verbose_flag) {
|
||||||
|
my_getrusage(cycles+2);
|
||||||
|
print_rusage(cycles+2, cycles+1);
|
||||||
|
|
||||||
|
vpi_mcd_printf(1, "Event counts:\n");
|
||||||
|
vpi_mcd_printf(1, " %8lu time steps (pool=%lu)\n",
|
||||||
|
count_time_events, count_time_pool());
|
||||||
|
vpi_mcd_printf(1, " %8lu thread schedule events\n",
|
||||||
|
count_thread_events);
|
||||||
|
vpi_mcd_printf(1, " %8lu assign events\n",
|
||||||
|
count_assign_events);
|
||||||
|
vpi_mcd_printf(1, " ...assign(vec4) pool=%lu\n",
|
||||||
|
count_assign4_pool());
|
||||||
|
vpi_mcd_printf(1, " ...assign(vec8) pool=%lu\n",
|
||||||
|
count_assign8_pool());
|
||||||
|
vpi_mcd_printf(1, " ...assign(real) pool=%lu\n",
|
||||||
|
count_assign_real_pool());
|
||||||
|
vpi_mcd_printf(1, " ...assign(word) pool=%lu\n",
|
||||||
|
count_assign_aword_pool());
|
||||||
|
vpi_mcd_printf(1, " ...assign(word/r) pool=%lu\n",
|
||||||
|
count_assign_arword_pool());
|
||||||
|
vpi_mcd_printf(1, " %8lu other events (pool=%lu)\n",
|
||||||
|
count_gen_events, count_gen_pool());
|
||||||
|
}
|
||||||
|
|
||||||
|
final_cleanup();
|
||||||
|
|
||||||
|
return vvp_return_value;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
#ifndef _LIBVVP_H_
|
||||||
|
#define _LIBVVP_H_
|
||||||
|
|
||||||
|
/* Interface definitions for libvvp.so.
|
||||||
|
*
|
||||||
|
* The main functions are vvp_init() and vvp_run() and they must be called
|
||||||
|
* in that order.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/* The first three functions may be called at any time.
|
||||||
|
* vvp_set_stop_is_finish(true) is equivalent to vvp's "-n" option.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void vvp_set_stop_is_finish(bool flag);
|
||||||
|
|
||||||
|
/* vvp_set_stop_is_finish_exit_code(true) is equivalent to vvp's "-N" option.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void vvp_set_stop_is_finish_exit_code(bool flag);
|
||||||
|
|
||||||
|
/* vvp_set_verbose(true) is equivalent to vvp's "-v" option. */
|
||||||
|
|
||||||
|
extern void vvp_set_verbose_flag(bool flag);
|
||||||
|
|
||||||
|
/* vvp_no_signals() may be called at any time before vvp_run() to prevent
|
||||||
|
* libvvp from handling signals generated by a terminal. It is recommended.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void vvp_no_signals(void);
|
||||||
|
|
||||||
|
/* vvp_init() initialises the simulator. The logfile_name argument is
|
||||||
|
* the path to a file to receive logging output from the simulation.
|
||||||
|
* It is equivalent to vvp's "-l" option, and may be NULL.
|
||||||
|
* The argc argument is the size of the string array argv. These strings
|
||||||
|
* are available to the simulation like additional non-option arguments
|
||||||
|
* to vvp.
|
||||||
|
*
|
||||||
|
* The effect of calling vvp_init() for a second time is undefined unless
|
||||||
|
* the library is first unloaded and reloaded.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void vvp_init(const char *logfile_name, int argc, char*argv[]);
|
||||||
|
|
||||||
|
/* vvp_run() starts the simulation and returns the exit status
|
||||||
|
* when it is complete. The argument is the path to a VVP file containing
|
||||||
|
* compiled Verilog code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int vvp_run(const char *design_path);
|
||||||
|
|
||||||
|
/* vpip_load_module(module_name) may be called after vvp_init() and before
|
||||||
|
* vvp_run() to load and initialise a VPI module. It is equivalent to
|
||||||
|
* vvp's "-m" option. If the module_name contains a directory separator
|
||||||
|
* it is assumed to be the path to a VPI file, otherwise
|
||||||
|
* it is searched for in directories on the current search path.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void vpip_load_module(const char*name);
|
||||||
|
|
||||||
|
/* vpip_add_module_path() and vpip_clear_module_paths() may be called
|
||||||
|
* after vvp_init() and before vpip_load_module() to manipulate
|
||||||
|
* the search path for VPI modules. They are equivalent to vvp's "-M" option.
|
||||||
|
* Some default paths are set by vvp_init().
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void vpip_add_module_path(const char*path);
|
||||||
|
extern void vpip_clear_module_paths(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif // _LIBVVP_H_
|
||||||
369
vvp/main.cc
369
vvp/main.cc
|
|
@ -20,26 +20,9 @@ const char COPYRIGHT[] =
|
||||||
# include "version_base.h"
|
# include "version_base.h"
|
||||||
# include "version_tag.h"
|
# include "version_tag.h"
|
||||||
# include "config.h"
|
# include "config.h"
|
||||||
# include "parse_misc.h"
|
|
||||||
# include "compile.h"
|
# include "compile.h"
|
||||||
# include "schedule.h"
|
|
||||||
# include "vpi_priv.h"
|
|
||||||
# include "statistics.h"
|
|
||||||
# include "vvp_cleanup.h"
|
|
||||||
# include "vvp_object.h"
|
|
||||||
# include <cstdio>
|
# include <cstdio>
|
||||||
# include <cstdlib>
|
# include <cstdlib>
|
||||||
# include <cstring>
|
|
||||||
# include <unistd.h>
|
|
||||||
# include <cassert>
|
|
||||||
#ifdef CHECK_WITH_VALGRIND
|
|
||||||
# include <pthread.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(HAVE_SYS_RESOURCE_H)
|
|
||||||
# include <sys/time.h>
|
|
||||||
# include <sys/resource.h>
|
|
||||||
#endif // defined(HAVE_SYS_RESOURCE_H)
|
|
||||||
|
|
||||||
#if defined(HAVE_GETOPT_H)
|
#if defined(HAVE_GETOPT_H)
|
||||||
# include <getopt.h>
|
# include <getopt.h>
|
||||||
|
|
@ -49,9 +32,9 @@ const char COPYRIGHT[] =
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace std;
|
#include "libvvp.h"
|
||||||
|
|
||||||
ofstream debug_file;
|
using namespace std;
|
||||||
|
|
||||||
#if defined(__MINGW32__) && !defined(HAVE_GETOPT_H)
|
#if defined(__MINGW32__) && !defined(HAVE_GETOPT_H)
|
||||||
extern "C" int getopt(int argc, char*argv[], const char*fmt);
|
extern "C" int getopt(int argc, char*argv[], const char*fmt);
|
||||||
|
|
@ -59,229 +42,17 @@ extern "C" int optind;
|
||||||
extern "C" const char*optarg;
|
extern "C" const char*optarg;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool verbose_flag = false;
|
|
||||||
bool version_flag = false;
|
bool version_flag = false;
|
||||||
static int vvp_return_value = 0;
|
|
||||||
|
|
||||||
void vpip_set_return_value(int value)
|
|
||||||
{
|
|
||||||
vvp_return_value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char log_buffer[4096];
|
|
||||||
|
|
||||||
#if defined(HAVE_SYS_RESOURCE_H)
|
|
||||||
static void my_getrusage(struct rusage *a)
|
|
||||||
{
|
|
||||||
getrusage(RUSAGE_SELF, a);
|
|
||||||
|
|
||||||
# if defined(LINUX)
|
|
||||||
{
|
|
||||||
FILE *statm;
|
|
||||||
unsigned siz, rss, shd;
|
|
||||||
long page_size = sysconf(_SC_PAGESIZE);
|
|
||||||
if (page_size==-1) page_size=0;
|
|
||||||
statm = fopen("/proc/self/statm", "r");
|
|
||||||
if (!statm) {
|
|
||||||
perror("/proc/self/statm");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Given that these are in pages we'll limit the value to
|
|
||||||
* what will fit in a 32 bit integer to prevent undefined
|
|
||||||
* behavior in fscanf(). */
|
|
||||||
if (3 == fscanf(statm, "%9u %9u %9u", &siz, &rss, &shd)) {
|
|
||||||
a->ru_maxrss = page_size * siz;
|
|
||||||
a->ru_idrss = page_size * rss;
|
|
||||||
a->ru_ixrss = page_size * shd;
|
|
||||||
}
|
|
||||||
fclose(statm);
|
|
||||||
}
|
|
||||||
# endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void print_rusage(struct rusage *a, struct rusage *b)
|
|
||||||
{
|
|
||||||
double delta = a->ru_utime.tv_sec
|
|
||||||
+ a->ru_utime.tv_usec/1E6
|
|
||||||
+ a->ru_stime.tv_sec
|
|
||||||
+ a->ru_stime.tv_usec/1E6
|
|
||||||
- b->ru_utime.tv_sec
|
|
||||||
- b->ru_utime.tv_usec/1E6
|
|
||||||
- b->ru_stime.tv_sec
|
|
||||||
- b->ru_stime.tv_usec/1E6
|
|
||||||
;
|
|
||||||
|
|
||||||
vpi_mcd_printf(1,
|
|
||||||
" ... %G seconds,"
|
|
||||||
" %.1f/%.1f/%.1f KBytes size/rss/shared\n",
|
|
||||||
delta,
|
|
||||||
a->ru_maxrss/1024.0,
|
|
||||||
(a->ru_idrss+a->ru_isrss)/1024.0,
|
|
||||||
a->ru_ixrss/1024.0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
#else // ! defined(HAVE_SYS_RESOURCE_H)
|
|
||||||
|
|
||||||
// Provide dummies
|
|
||||||
struct rusage { int x; };
|
|
||||||
inline static void my_getrusage(struct rusage *) { }
|
|
||||||
inline static void print_rusage(struct rusage *, struct rusage *){};
|
|
||||||
|
|
||||||
#endif // ! defined(HAVE_SYS_RESOURCE_H)
|
|
||||||
|
|
||||||
static bool have_ivl_version = false;
|
|
||||||
/*
|
|
||||||
* Verify that the input file has a compatible version.
|
|
||||||
*/
|
|
||||||
void verify_version(char*ivl_ver, char*commit)
|
|
||||||
{
|
|
||||||
have_ivl_version = true;
|
|
||||||
|
|
||||||
if (verbose_flag) {
|
|
||||||
vpi_mcd_printf(1, " ... VVP file version %s", ivl_ver);
|
|
||||||
if (commit) vpi_mcd_printf(1, " %s", commit);
|
|
||||||
vpi_mcd_printf(1, "\n");
|
|
||||||
}
|
|
||||||
delete[] commit;
|
|
||||||
|
|
||||||
int file_major, file_minor, file_minor2;
|
|
||||||
char file_extra[128];
|
|
||||||
|
|
||||||
// Old style format: 0.<major>.<minor> <extra>
|
|
||||||
// This also catches a potential new-new format that has
|
|
||||||
// another sub-minor number.
|
|
||||||
file_extra[0] = 0;
|
|
||||||
int rc = sscanf(ivl_ver, "%d.%d.%d %127s", &file_major, &file_minor, &file_minor2, file_extra);
|
|
||||||
|
|
||||||
// If it wasn't the old style format, try the new format:
|
|
||||||
// <major>.<minor> <extra>
|
|
||||||
if (rc == 2) {
|
|
||||||
file_extra[0] = 0;
|
|
||||||
rc = sscanf(ivl_ver, "%d.%d %127s", &file_major, &file_minor, file_extra);
|
|
||||||
assert((rc == 2) || (rc == 3));
|
|
||||||
file_minor2 = 0;
|
|
||||||
}
|
|
||||||
delete[] ivl_ver;
|
|
||||||
|
|
||||||
// If this was the old format, the file_major will be 0. In
|
|
||||||
// this case it is not really what we meant, so convert to the
|
|
||||||
// new format.
|
|
||||||
if (file_major == 0) {
|
|
||||||
file_major = file_minor;
|
|
||||||
file_minor = file_minor2;
|
|
||||||
file_minor2 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (VERSION_MAJOR != file_major) {
|
|
||||||
vpi_mcd_printf(1, "Error: VVP input file %d.%d can not "
|
|
||||||
"be run with run time version %s\n",
|
|
||||||
file_major, file_minor, VERSION);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (VERSION_MINOR < file_minor) {
|
|
||||||
vpi_mcd_printf(1, "Warning: VVP input file sub version %d.%d"
|
|
||||||
" is greater than the run time version %s.\n",
|
|
||||||
file_major, file_minor, VERSION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int vpip_delay_selection = _vpiDelaySelTypical;
|
|
||||||
void set_delay_selection(const char* sel)
|
|
||||||
{
|
|
||||||
if (strcmp("TYPICAL", sel) == 0) {
|
|
||||||
vpip_delay_selection = _vpiDelaySelTypical;
|
|
||||||
} else if (strcmp("MINIMUM", sel) == 0) {
|
|
||||||
vpip_delay_selection = _vpiDelaySelMinimum;
|
|
||||||
} else if (strcmp("MAXIMUM", sel) == 0) {
|
|
||||||
vpip_delay_selection = _vpiDelaySelMaximum;
|
|
||||||
} else {
|
|
||||||
vpi_mcd_printf(1, "Error: Unknown delay selection \"%s\"!", sel);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
delete[] sel;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void final_cleanup()
|
|
||||||
{
|
|
||||||
vvp_object::cleanup();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We only need to cleanup the memory if we are checking with valgrind.
|
|
||||||
*/
|
|
||||||
#ifdef CHECK_WITH_VALGRIND
|
|
||||||
/* Clean up the file name table. */
|
|
||||||
for (vector<const char*>::iterator cur = file_names.begin();
|
|
||||||
cur != file_names.end() ; ++ cur ) {
|
|
||||||
delete[] *cur;
|
|
||||||
}
|
|
||||||
/* Clear the static result buffer. */
|
|
||||||
(void)need_result_buf(0, RBUF_DEL);
|
|
||||||
codespace_delete();
|
|
||||||
root_table_delete();
|
|
||||||
def_table_delete();
|
|
||||||
vpi_mcd_delete();
|
|
||||||
dec_str_delete();
|
|
||||||
modpath_delete();
|
|
||||||
vpi_handle_delete();
|
|
||||||
vpi_stack_delete();
|
|
||||||
udp_defns_delete();
|
|
||||||
island_delete();
|
|
||||||
signal_pool_delete();
|
|
||||||
vvp_net_pool_delete();
|
|
||||||
ufunc_pool_delete();
|
|
||||||
#endif
|
|
||||||
/*
|
|
||||||
* Unload the VPI modules. This is essential for MinGW, to ensure
|
|
||||||
* dump files are flushed before the main process terminates, as
|
|
||||||
* the DLL termination code is called after all remaining open
|
|
||||||
* files are automatically closed.
|
|
||||||
*/
|
|
||||||
load_module_delete();
|
|
||||||
|
|
||||||
#ifdef CHECK_WITH_VALGRIND
|
|
||||||
simulator_cb_delete();
|
|
||||||
/* This is needed to prevent valgrind from complaining about
|
|
||||||
* _dlerror_run() having a memory leak. */
|
|
||||||
// HERE: Is this portable? Does it break anything?
|
|
||||||
pthread_exit(NULL);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned module_cnt = 0;
|
unsigned module_cnt = 0;
|
||||||
const char*module_tab[64];
|
const char*module_tab[64];
|
||||||
|
|
||||||
extern void vpip_mcd_init(FILE *log);
|
|
||||||
extern void vvp_vpi_init(void);
|
|
||||||
|
|
||||||
int main(int argc, char*argv[])
|
int main(int argc, char*argv[])
|
||||||
{
|
{
|
||||||
int opt;
|
int opt;
|
||||||
unsigned flag_errors = 0;
|
unsigned flag_errors = 0;
|
||||||
const char*design_path = 0;
|
|
||||||
struct rusage cycles[3];
|
|
||||||
const char *logfile_name = 0x0;
|
const char *logfile_name = 0x0;
|
||||||
FILE *logfile = 0x0;
|
|
||||||
extern void vpi_set_vlog_info(int, char**);
|
|
||||||
extern bool stop_is_finish;
|
|
||||||
extern int stop_is_finish_exit_code;
|
|
||||||
|
|
||||||
if( ::getenv("VVP_WAIT_FOR_DEBUGGER") != 0 ) {
|
|
||||||
fprintf( stderr, "Waiting for debugger...\n");
|
|
||||||
bool debugger_release = false;
|
|
||||||
while( !debugger_release ) {
|
|
||||||
#if defined(__MINGW32__)
|
|
||||||
Sleep(1000);
|
|
||||||
#else
|
|
||||||
sleep(1);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* For non-interactive runs we do not want to run the interactive
|
|
||||||
* debugger, so make $stop just execute a $finish. */
|
|
||||||
stop_is_finish = false;
|
|
||||||
while ((opt = getopt(argc, argv, "+hil:M:m:nNsvV")) != EOF) switch (opt) {
|
while ((opt = getopt(argc, argv, "+hil:M:m:nNsvV")) != EOF) switch (opt) {
|
||||||
case 'h':
|
case 'h':
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
|
@ -316,17 +87,16 @@ int main(int argc, char*argv[])
|
||||||
module_tab[module_cnt++] = optarg;
|
module_tab[module_cnt++] = optarg;
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
stop_is_finish = true;
|
vvp_set_stop_is_finish(true);
|
||||||
break;
|
break;
|
||||||
case 'N':
|
case 'N':
|
||||||
stop_is_finish = true;
|
vvp_set_stop_is_finish_exit_code(true);
|
||||||
stop_is_finish_exit_code = 1;
|
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
schedule_stop(0);
|
schedule_stop(0);
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose_flag = true;
|
vvp_set_verbose_flag(true);
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
version_flag = true;
|
version_flag = true;
|
||||||
|
|
@ -335,8 +105,6 @@ int main(int argc, char*argv[])
|
||||||
flag_errors += 1;
|
flag_errors += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
vpip_add_env_and_default_module_paths();
|
|
||||||
|
|
||||||
if (flag_errors)
|
if (flag_errors)
|
||||||
return flag_errors;
|
return flag_errors;
|
||||||
|
|
||||||
|
|
@ -367,133 +135,10 @@ int main(int argc, char*argv[])
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the VVP_DEBUG variable is set, then it contains the path
|
vvp_init(logfile_name, argc - optind, argv + optind);
|
||||||
to the vvp debug file. Open it for output. */
|
|
||||||
|
|
||||||
if (char*path = getenv("VVP_DEBUG")) {
|
|
||||||
debug_file.open(path, ios::out);
|
|
||||||
}
|
|
||||||
|
|
||||||
design_path = argv[optind];
|
|
||||||
|
|
||||||
/* This is needed to get the MCD I/O routines ready for
|
|
||||||
anything. It is done early because it is plausible that the
|
|
||||||
compile might affect it, and it is cheap to do. */
|
|
||||||
|
|
||||||
if (logfile_name) {
|
|
||||||
if (!strcmp(logfile_name, "-"))
|
|
||||||
logfile = stderr;
|
|
||||||
else {
|
|
||||||
logfile = fopen(logfile_name, "w");
|
|
||||||
if (!logfile) {
|
|
||||||
perror(logfile_name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
setvbuf(logfile, log_buffer, _IOLBF, sizeof(log_buffer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vpip_mcd_init(logfile);
|
|
||||||
|
|
||||||
if (verbose_flag) {
|
|
||||||
my_getrusage(cycles+0);
|
|
||||||
vpi_mcd_printf(1, "Compiling VVP ...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
vvp_vpi_init();
|
|
||||||
|
|
||||||
/* Make the extended arguments available to the simulation. */
|
|
||||||
vpi_set_vlog_info(argc-optind, argv+optind);
|
|
||||||
|
|
||||||
compile_init();
|
|
||||||
|
|
||||||
for (unsigned idx = 0 ; idx < module_cnt ; idx += 1)
|
for (unsigned idx = 0 ; idx < module_cnt ; idx += 1)
|
||||||
vpip_load_module(module_tab[idx]);
|
vpip_load_module(module_tab[idx]);
|
||||||
|
|
||||||
int ret_cd = compile_design(design_path);
|
return vvp_run(argv[optind]);
|
||||||
destroy_lexor();
|
|
||||||
print_vpi_call_errors();
|
|
||||||
if (ret_cd) return ret_cd;
|
|
||||||
|
|
||||||
if (!have_ivl_version) {
|
|
||||||
if (verbose_flag) vpi_mcd_printf(1, "... ");
|
|
||||||
vpi_mcd_printf(1, "Warning: vvp input file may not be correct "
|
|
||||||
"version!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose_flag) {
|
|
||||||
vpi_mcd_printf(1, "Compile cleanup...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
compile_cleanup();
|
|
||||||
|
|
||||||
if (compile_errors > 0) {
|
|
||||||
vpi_mcd_printf(1, "%s: Program not runnable, %u errors.\n",
|
|
||||||
design_path, compile_errors);
|
|
||||||
final_cleanup();
|
|
||||||
return compile_errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose_flag) {
|
|
||||||
vpi_mcd_printf(1, " ... %8lu functors (net_fun pool=%zu bytes)\n",
|
|
||||||
count_functors, vvp_net_fun_t::heap_total());
|
|
||||||
vpi_mcd_printf(1, " %8lu logic\n", count_functors_logic);
|
|
||||||
vpi_mcd_printf(1, " %8lu bufif\n", count_functors_bufif);
|
|
||||||
vpi_mcd_printf(1, " %8lu resolv\n",count_functors_resolv);
|
|
||||||
vpi_mcd_printf(1, " %8lu signals\n", count_functors_sig);
|
|
||||||
vpi_mcd_printf(1, " ... %8lu filters (net_fil pool=%zu bytes)\n",
|
|
||||||
count_filters, vvp_net_fil_t::heap_total());
|
|
||||||
vpi_mcd_printf(1, " ... %8lu opcodes (%zu bytes)\n",
|
|
||||||
count_opcodes, size_opcodes);
|
|
||||||
vpi_mcd_printf(1, " ... %8lu nets\n", count_vpi_nets);
|
|
||||||
vpi_mcd_printf(1, " ... %8lu vvp_nets (%zu bytes)\n",
|
|
||||||
count_vvp_nets, size_vvp_nets);
|
|
||||||
vpi_mcd_printf(1, " ... %8lu arrays (%lu words)\n",
|
|
||||||
count_net_arrays, count_net_array_words);
|
|
||||||
vpi_mcd_printf(1, " ... %8lu memories\n",
|
|
||||||
count_var_arrays+count_real_arrays);
|
|
||||||
vpi_mcd_printf(1, " %8lu logic (%lu words)\n",
|
|
||||||
count_var_arrays, count_var_array_words);
|
|
||||||
vpi_mcd_printf(1, " %8lu real (%lu words)\n",
|
|
||||||
count_real_arrays, count_real_array_words);
|
|
||||||
vpi_mcd_printf(1, " ... %8lu scopes\n", count_vpi_scopes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose_flag) {
|
|
||||||
my_getrusage(cycles+1);
|
|
||||||
print_rusage(cycles+1, cycles+0);
|
|
||||||
vpi_mcd_printf(1, "Running ...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
schedule_simulate();
|
|
||||||
|
|
||||||
if (verbose_flag) {
|
|
||||||
my_getrusage(cycles+2);
|
|
||||||
print_rusage(cycles+2, cycles+1);
|
|
||||||
|
|
||||||
vpi_mcd_printf(1, "Event counts:\n");
|
|
||||||
vpi_mcd_printf(1, " %8lu time steps (pool=%lu)\n",
|
|
||||||
count_time_events, count_time_pool());
|
|
||||||
vpi_mcd_printf(1, " %8lu thread schedule events\n",
|
|
||||||
count_thread_events);
|
|
||||||
vpi_mcd_printf(1, " %8lu assign events\n",
|
|
||||||
count_assign_events);
|
|
||||||
vpi_mcd_printf(1, " ...assign(vec4) pool=%lu\n",
|
|
||||||
count_assign4_pool());
|
|
||||||
vpi_mcd_printf(1, " ...assign(vec8) pool=%lu\n",
|
|
||||||
count_assign8_pool());
|
|
||||||
vpi_mcd_printf(1, " ...assign(real) pool=%lu\n",
|
|
||||||
count_assign_real_pool());
|
|
||||||
vpi_mcd_printf(1, " ...assign(word) pool=%lu\n",
|
|
||||||
count_assign_aword_pool());
|
|
||||||
vpi_mcd_printf(1, " ...assign(word/r) pool=%lu\n",
|
|
||||||
count_assign_arword_pool());
|
|
||||||
vpi_mcd_printf(1, " %8lu other events (pool=%lu)\n",
|
|
||||||
count_gen_events, count_gen_pool());
|
|
||||||
}
|
|
||||||
|
|
||||||
final_cleanup();
|
|
||||||
|
|
||||||
return vvp_return_value;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -581,6 +581,7 @@ static struct event_s* schedule_final_list = 0;
|
||||||
static bool schedule_runnable = true;
|
static bool schedule_runnable = true;
|
||||||
static bool schedule_stopped_flag = false;
|
static bool schedule_stopped_flag = false;
|
||||||
static bool schedule_single_step_flag = false;
|
static bool schedule_single_step_flag = false;
|
||||||
|
static bool no_signals_flag = false;
|
||||||
|
|
||||||
void schedule_finish(int)
|
void schedule_finish(int)
|
||||||
{
|
{
|
||||||
|
|
@ -607,6 +608,11 @@ bool schedule_stopped(void)
|
||||||
return schedule_stopped_flag;
|
return schedule_stopped_flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" void vvp_no_signals(void)
|
||||||
|
{
|
||||||
|
no_signals_flag = true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These are the signal handling infrastructure. The SIGINT signal
|
* These are the signal handling infrastructure. The SIGINT signal
|
||||||
* leads to an implicit $stop. The SIGHUP and SIGTERM signals lead
|
* leads to an implicit $stop. The SIGHUP and SIGTERM signals lead
|
||||||
|
|
@ -629,6 +635,8 @@ extern "C" void signals_handler(int signum)
|
||||||
|
|
||||||
static void signals_capture(void)
|
static void signals_capture(void)
|
||||||
{
|
{
|
||||||
|
if (no_signals_flag)
|
||||||
|
return;
|
||||||
#ifndef __MINGW32__
|
#ifndef __MINGW32__
|
||||||
signal(SIGHUP, &signals_handler);
|
signal(SIGHUP, &signals_handler);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -638,6 +646,8 @@ static void signals_capture(void)
|
||||||
|
|
||||||
static void signals_revert(void)
|
static void signals_revert(void)
|
||||||
{
|
{
|
||||||
|
if (no_signals_flag)
|
||||||
|
return;
|
||||||
#ifndef __MINGW32__
|
#ifndef __MINGW32__
|
||||||
signal(SIGHUP, SIG_DFL);
|
signal(SIGHUP, SIG_DFL);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1022,11 +1022,11 @@ vpiHandle vpip_make_vthr_APV(char*label, unsigned index, unsigned bit, unsigned
|
||||||
* contained functions before compilation commences. It is called only
|
* contained functions before compilation commences. It is called only
|
||||||
* once per module.
|
* once per module.
|
||||||
*/
|
*/
|
||||||
extern void vpip_load_module(const char*name);
|
extern "C" void vpip_load_module(const char*name);
|
||||||
|
|
||||||
extern void vpip_clear_module_paths();
|
extern "C" void vpip_clear_module_paths();
|
||||||
extern void vpip_add_module_path(const char *path);
|
extern "C" void vpip_add_module_path(const char *path);
|
||||||
extern void vpip_add_env_and_default_module_paths();
|
extern "C" void vpip_add_env_and_default_module_paths();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The vpip_build_vpi_call function creates a __vpiSysTaskCall object
|
* The vpip_build_vpi_call function creates a __vpiSysTaskCall object
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue