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:
ga 2024-01-24 10:34:46 +00:00
commit 95810b2f61
10 changed files with 599 additions and 374 deletions

View File

@ -17,6 +17,7 @@ Icarus Verilog.
ivlpp_flags
vvp_flags
vvp_debug
vvp_library
vhdlpp_flags
gtkwave
vpi

View File

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

View File

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

View File

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

View File

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

417
vvp/lib_main.cc Normal file
View File

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

78
vvp/libvvp.h Normal file
View File

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

View File

@ -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 <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(HAVE_GETOPT_H)
# include <getopt.h>
@ -49,9 +32,9 @@ const char COPYRIGHT[] =
# include <windows.h>
#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.<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;
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]);
}

View File

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

View File

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