diff --git a/Documentation/usage/index.rst b/Documentation/usage/index.rst index 9601baa54..453cd763a 100644 --- a/Documentation/usage/index.rst +++ b/Documentation/usage/index.rst @@ -17,6 +17,7 @@ Icarus Verilog. ivlpp_flags vvp_flags vvp_debug + vvp_library vhdlpp_flags gtkwave vpi diff --git a/Documentation/usage/installation.rst b/Documentation/usage/installation.rst index 5ee37cf2f..46ad5bcf1 100644 --- a/Documentation/usage/installation.rst +++ b/Documentation/usage/installation.rst @@ -99,7 +99,13 @@ time). :: 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 -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 ----------------------- diff --git a/Documentation/usage/vvp_library.rst b/Documentation/usage/vvp_library.rst new file mode 100644 index 000000000..62b892bd7 --- /dev/null +++ b/Documentation/usage/vvp_library.rst @@ -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. + diff --git a/configure.ac b/configure.ac index 7055f4f32..e5c430bd1 100644 --- a/configure.ac +++ b/configure.ac @@ -170,6 +170,14 @@ AC_SUBST(HAVE_LIBBZ2) AC_FUNC_ALLOCA 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 AC_ARG_WITH([valgrind], [AS_HELP_STRING([--with-valgrind],[Add valgrind hooks])], [], [check_valgrind=yes]) diff --git a/vvp/Makefile.in b/vvp/Makefile.in index 3dbd57b2f..458a57a3d 100644 --- a/vvp/Makefile.in +++ b/vvp/Makefile.in @@ -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 \ 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 \ permaheap.o reduce.o resolv.o \ sfunc.o stop.o \ @@ -98,7 +99,7 @@ else endif 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 distclean: clean @@ -121,20 +122,44 @@ dep: mkdir dep 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 # 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 # 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. -vvp@EXEEXT@: $O $(srcdir)/vvp.def - $(CXX) -o vvp$(suffix)@EXEEXT@ $(LDFLAGS) $O $(dllib) $(LIBS) +vvp@EXEEXT@: main.o $O $(srcdir)/vvp.def + $(CXX) -o vvp$(suffix)@EXEEXT@ $(LDFLAGS) main.o $O $(dllib) $(LIBS) $(DLLTOOL) --dllname vvp$(suffix)@EXEEXT@ --def $(srcdir)/vvp.def \ --output-exp vvp.exp 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 -vvp@EXEEXT@: $O - $(CXX) $(LDFLAGS) -o vvp@EXEEXT@ $O $(LIBS) $(dllib) +vvp@EXEEXT@: $O main.o + $(CXX) $(LDFLAGS) -o vvp@EXEEXT@ main.o $O $(LIBS) $(dllib) endif %.o: %.cc config.h @@ -207,6 +232,9 @@ installpdf: vvp.pdf installdirs installfiles: $(F) | installdirs $(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 $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(INSTALL_DOCDIR)" @@ -215,5 +243,8 @@ installdirs: $(srcdir)/../mkinstalldirs uninstall: $(UNINSTALL32) rm -f "$(DESTDIR)$(bindir)/vvp$(suffix)@EXEEXT@" 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) diff --git a/vvp/lib_main.cc b/vvp/lib_main.cc new file mode 100644 index 000000000..0696a61b9 --- /dev/null +++ b/vvp/lib_main.cc @@ -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 +# include +# include +# include +# include +#ifdef CHECK_WITH_VALGRIND +# include +#endif + +#if defined(HAVE_SYS_RESOURCE_H) +# include +# include +#endif // defined(HAVE_SYS_RESOURCE_H) + +#if defined(__MINGW32__) +# include +#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.. + // 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: + // . + 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::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; +} diff --git a/vvp/libvvp.h b/vvp/libvvp.h new file mode 100644 index 000000000..081030f02 --- /dev/null +++ b/vvp/libvvp.h @@ -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 + +/* 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_ diff --git a/vvp/main.cc b/vvp/main.cc index 68802ee6a..bb33c608c 100644 --- a/vvp/main.cc +++ b/vvp/main.cc @@ -20,26 +20,9 @@ const char COPYRIGHT[] = # include "version_base.h" # include "version_tag.h" # include "config.h" -# include "parse_misc.h" # include "compile.h" -# include "schedule.h" -# include "vpi_priv.h" -# include "statistics.h" -# include "vvp_cleanup.h" -# include "vvp_object.h" # include # include -# include -# include -# include -#ifdef CHECK_WITH_VALGRIND -# include -#endif - -#if defined(HAVE_SYS_RESOURCE_H) -# include -# include -#endif // defined(HAVE_SYS_RESOURCE_H) #if defined(HAVE_GETOPT_H) # include @@ -49,9 +32,9 @@ const char COPYRIGHT[] = # include #endif -using namespace std; +#include "libvvp.h" -ofstream debug_file; +using namespace std; #if defined(__MINGW32__) && !defined(HAVE_GETOPT_H) extern "C" int getopt(int argc, char*argv[], const char*fmt); @@ -59,229 +42,17 @@ extern "C" int optind; extern "C" const char*optarg; #endif -bool verbose_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.. - // 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: - // . - 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::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; 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 opt; unsigned flag_errors = 0; - const char*design_path = 0; - struct rusage cycles[3]; 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) { case 'h': fprintf(stderr, @@ -316,17 +87,16 @@ int main(int argc, char*argv[]) module_tab[module_cnt++] = optarg; break; case 'n': - stop_is_finish = true; + vvp_set_stop_is_finish(true); break; case 'N': - stop_is_finish = true; - stop_is_finish_exit_code = 1; + vvp_set_stop_is_finish_exit_code(true); break; case 's': schedule_stop(0); break; case 'v': - verbose_flag = true; + vvp_set_verbose_flag(true); break; case 'V': version_flag = true; @@ -335,8 +105,6 @@ int main(int argc, char*argv[]) flag_errors += 1; } - vpip_add_env_and_default_module_paths(); - if (flag_errors) return flag_errors; @@ -367,133 +135,10 @@ int main(int argc, char*argv[]) return -1; } - /* 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); - } - - 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(); + vvp_init(logfile_name, argc - optind, argv + optind); for (unsigned idx = 0 ; idx < module_cnt ; idx += 1) vpip_load_module(module_tab[idx]); - 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; + return vvp_run(argv[optind]); } diff --git a/vvp/schedule.cc b/vvp/schedule.cc index c9d117ef5..e1e574416 100644 --- a/vvp/schedule.cc +++ b/vvp/schedule.cc @@ -581,6 +581,7 @@ static struct event_s* schedule_final_list = 0; static bool schedule_runnable = true; static bool schedule_stopped_flag = false; static bool schedule_single_step_flag = false; +static bool no_signals_flag = false; void schedule_finish(int) { @@ -607,6 +608,11 @@ bool schedule_stopped(void) return schedule_stopped_flag; } +extern "C" void vvp_no_signals(void) +{ + no_signals_flag = true; +} + /* * These are the signal handling infrastructure. The SIGINT signal * 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) { + if (no_signals_flag) + return; #ifndef __MINGW32__ signal(SIGHUP, &signals_handler); #endif @@ -638,6 +646,8 @@ static void signals_capture(void) static void signals_revert(void) { + if (no_signals_flag) + return; #ifndef __MINGW32__ signal(SIGHUP, SIG_DFL); #endif diff --git a/vvp/vpi_priv.h b/vvp/vpi_priv.h index 1c5f01652..fbdedd090 100644 --- a/vvp/vpi_priv.h +++ b/vvp/vpi_priv.h @@ -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 * 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 void vpip_add_module_path(const char *path); -extern void vpip_add_env_and_default_module_paths(); +extern "C" void vpip_clear_module_paths(); +extern "C" void vpip_add_module_path(const char *path); +extern "C" void vpip_add_env_and_default_module_paths(); /* * The vpip_build_vpi_call function creates a __vpiSysTaskCall object