Support dynamic loading of VPI extensions (#7727)
This commit is contained in:
parent
3853301367
commit
d023b3b075
|
|
@ -653,6 +653,7 @@ description of these arguments.
|
|||
+verilator+solver+file+<filename> Set random solver log filename
|
||||
+verilator+V Show verbose version and config
|
||||
+verilator+version Show version and exit
|
||||
+verilator+vpi+<library>[:<bootstrap>] Load VPI shared library
|
||||
+verilator+wno+unsatconstr+<value> Disable constraint warnings
|
||||
|
||||
|
||||
|
|
|
|||
28
configure.ac
28
configure.ac
|
|
@ -589,6 +589,34 @@ m4_foreach([ldflag], [
|
|||
AC_SUBST(CFG_LDLIBS_THREADS)
|
||||
AC_SUBST(CFG_LDFLAGS_THREADS_CMAKE)
|
||||
|
||||
# Find link flags for runtime VPI library loading (+verilator+vpi+<lib>).
|
||||
# The model executable must export its VPI symbols so the dlopen'd library can
|
||||
# resolve them: -rdynamic (GNU ld) or -Wl,-export_dynamic (Darwin); the first the
|
||||
# linker accepts wins. -ldl provides dlopen/dlsym where it is a separate library.
|
||||
_MY_LDLIBS_CHECK_SET(CFG_LDFLAGS_DYNAMIC, -rdynamic)
|
||||
# -Wl,-export_dynamic contains a comma, so probe it directly rather than through
|
||||
# the _MY_LDLIBS_CHECK_* macros (which re-expand their flag argument unquoted).
|
||||
if test "$CFG_LDFLAGS_DYNAMIC" = ""; then
|
||||
ACO_SAVE_LIBS="$LIBS"
|
||||
LIBS="$LIBS -Wl,-export_dynamic"
|
||||
AC_MSG_CHECKING([whether $CXX linker accepts -Wl,-export_dynamic])
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([[]])],
|
||||
[_my_result=yes
|
||||
if test -s conftest.err; then
|
||||
if grep -e "-export_dynamic" conftest.err >/dev/null; then
|
||||
_my_result=no
|
||||
fi
|
||||
fi],
|
||||
[_my_result=no])
|
||||
AC_MSG_RESULT($_my_result)
|
||||
LIBS="$ACO_SAVE_LIBS"
|
||||
if test "$_my_result" = "yes"; then CFG_LDFLAGS_DYNAMIC="-Wl,-export_dynamic"; fi
|
||||
fi
|
||||
AC_SUBST(CFG_LDFLAGS_DYNAMIC)
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_DYNAMIC, -ldl)
|
||||
AC_SUBST(CFG_LDLIBS_DYNAMIC)
|
||||
|
||||
# If 'mold' is installed, use it to link for faster buildtimes
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -fuse-ld=mold)
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_VERILATED, -fuse-ld=mold)
|
||||
|
|
|
|||
|
|
@ -139,6 +139,20 @@ Options:
|
|||
|
||||
Displays program version and exits.
|
||||
|
||||
.. option:: +verilator+vpi+<library>[:<bootstrap>]
|
||||
|
||||
Load a VPI shared library before simulation starts. Only available when the
|
||||
model was Verilated with :vlopt:`--vpi` and :vlopt:`--main` (or
|
||||
:vlopt:`--binary`). ``<library>`` is the path to the shared library. If
|
||||
``:<bootstrap>`` is given, that named no-argument function is called;
|
||||
otherwise the library's ``vlog_startup_routines`` array (IEEE 1800 38.37.2) is
|
||||
invoked. May be repeated to load multiple libraries.
|
||||
|
||||
Runtime loading is supported on POSIX platforms only (it relies on the
|
||||
executable exporting its VPI symbols to the loaded library); on Windows the
|
||||
argument is rejected and the VPI code must instead be statically linked
|
||||
into the model.
|
||||
|
||||
.. option:: +verilator+wno+unsatconstr+<value>
|
||||
|
||||
Disable unsatisfied constraint warnings at simulation runtime. When set to
|
||||
|
|
|
|||
|
|
@ -89,6 +89,12 @@
|
|||
# include <unistd.h>
|
||||
# define _VL_HAVE_GETRLIMIT
|
||||
#endif
|
||||
#if VM_VPI
|
||||
# include <cstring>
|
||||
# ifndef _WIN32
|
||||
# include <dlfcn.h> // dlopen
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "verilated_threads.h"
|
||||
// clang-format on
|
||||
|
|
@ -260,6 +266,58 @@ void VL_WARN_MT(const char* filename, int linenum, const char* hier, const char*
|
|||
}});
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Runtime VPI shared library loading (--vpi)
|
||||
|
||||
// Load one VPI shared library named by a +verilator+vpi+<lib>[:<bootstrap>] argument.
|
||||
// 'arg' is the payload after the prefix: either "<lib>" (invoke the library's
|
||||
// vlog_startup_routines array) or "<lib>:<bootstrap>" (invoke the named bootstrap).
|
||||
void Verilated::loadVpiLib(const std::string& arg) VL_MT_UNSAFE {
|
||||
#if VM_VPI
|
||||
if (arg.empty()) return;
|
||||
#ifdef _WIN32
|
||||
VL_FATAL_MT("", 0, "",
|
||||
"+verilator+vpi+: runtime VPI library loading is not supported on"
|
||||
" Windows; link the VPI code into the model instead");
|
||||
#else
|
||||
using vlog_startup_t = void (*)();
|
||||
// Split <lib>:<bootstrap> on the last ':'
|
||||
const std::string::size_type colon_pos = arg.rfind(':');
|
||||
const bool has_entry = (colon_pos != std::string::npos);
|
||||
const std::string libpath = has_entry ? arg.substr(0, colon_pos) : arg;
|
||||
const std::string entry_name = has_entry ? arg.substr(colon_pos + 1) : std::string{};
|
||||
void* handle = dlopen(libpath.c_str(), RTLD_LAZY);
|
||||
if (!handle)
|
||||
// The library path is stable; the dlerror() text is platform-specific, so put it on
|
||||
// a separate "- " line (test golden files strip "- " lines, keeping output portable).
|
||||
VL_FATAL_MT(
|
||||
"", 0, "",
|
||||
(std::string{"Cannot load VPI library: "} + libpath + "\n- dlerror: " + dlerror())
|
||||
.c_str());
|
||||
if (has_entry) {
|
||||
vlog_startup_t bsp = reinterpret_cast<vlog_startup_t>(dlsym(handle, entry_name.c_str()));
|
||||
if (!bsp)
|
||||
VL_FATAL_MT(
|
||||
"", 0, "",
|
||||
(std::string{"Cannot find VPI bootstrap '"} + entry_name + "' in: " + libpath)
|
||||
.c_str());
|
||||
bsp();
|
||||
} else {
|
||||
vlog_startup_t* routinesp
|
||||
= reinterpret_cast<vlog_startup_t*>(dlsym(handle, "vlog_startup_routines"));
|
||||
if (!routinesp)
|
||||
VL_FATAL_MT(
|
||||
"", 0, "",
|
||||
(std::string{"Cannot find 'vlog_startup_routines' in: "} + libpath).c_str());
|
||||
for (int j = 0; routinesp[j]; ++j) routinesp[j]();
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
// Never reached: the command-line handler only calls this when compiled with --vpi.
|
||||
(void)arg;
|
||||
#endif
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Debug prints
|
||||
|
||||
|
|
@ -3544,6 +3602,17 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
|||
// and the run can be reproduced by passing +verilator+seed+<that_value>.
|
||||
if (u64 == 0) u64 = pickRandomSeed();
|
||||
randSeed(static_cast<int>(u64));
|
||||
} else if (commandArgVlString(arg, "+verilator+vpi+", str)) {
|
||||
// With --vpi, load the requested shared library now. Without --vpi there is
|
||||
// no VPI runtime, so warn the argument is ignored.
|
||||
#if VM_VPI
|
||||
Verilated::loadVpiLib(str);
|
||||
#else
|
||||
VL_WARN_MT(
|
||||
"COMMAND_LINE", 0, "",
|
||||
("+verilator+vpi+ ignored: simulation was not compiled with --vpi '" + arg + "'")
|
||||
.c_str()); // LCOV_EXCL_LINE (gcov zeroes this wrapped continuation line)
|
||||
#endif
|
||||
} else if (arg == "+verilator+V") {
|
||||
VerilatedImp::versionDump(); // Someday more info too
|
||||
VL_FATAL_MT("COMMAND_LINE", 0, "",
|
||||
|
|
|
|||
|
|
@ -1036,6 +1036,9 @@ public:
|
|||
static void scTraceBeforeElaborationError() VL_ATTR_NORETURN VL_MT_SAFE;
|
||||
static void stackCheck(QData needSize) VL_MT_UNSAFE;
|
||||
|
||||
// Internal: Load a VPI shared library (+verilator+vpi+<lib>[:<bootstrap>])
|
||||
static void loadVpiLib(const std::string& arg) VL_MT_UNSAFE;
|
||||
|
||||
// Internal: Get and set DPI context
|
||||
static const VerilatedScope* dpiScope() VL_MT_SAFE { return t_s.t_dpiScopep; }
|
||||
static void dpiScope(const VerilatedScope* scopep) VL_MT_SAFE { t_s.t_dpiScopep = scopep; }
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ CFG_GCH_IF_CLANG = @CFG_GCH_IF_CLANG@
|
|||
CFG_LDFLAGS_VERILATED = @CFG_LDFLAGS_VERILATED@
|
||||
# Linker libraries for multithreading
|
||||
CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
|
||||
# Linker flags/libraries for runtime VPI library loading (+verilator+vpi+<lib>)
|
||||
CFG_LDFLAGS_DYNAMIC = @CFG_LDFLAGS_DYNAMIC@
|
||||
CFG_LDLIBS_DYNAMIC = @CFG_LDLIBS_DYNAMIC@
|
||||
|
||||
######################################################################
|
||||
# Programs
|
||||
|
|
@ -93,6 +96,7 @@ VK_CPPFLAGS_ALWAYS += \
|
|||
-DVM_TRACE_FST=$(VM_TRACE_FST) \
|
||||
-DVM_TRACE_VCD=$(VM_TRACE_VCD) \
|
||||
-DVM_TRACE_SAIF=$(VM_TRACE_SAIF) \
|
||||
-DVM_VPI=$(VM_VPI) \
|
||||
$(CFG_CXXFLAGS_NO_UNUSED) \
|
||||
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
|
|
|
|||
|
|
@ -104,6 +104,8 @@ private:
|
|||
puts("\n");
|
||||
|
||||
if (v3Global.opt.vpi()) {
|
||||
// VPI shared libraries requested via +verilator+vpi+<lib> are loaded by
|
||||
// contextp->commandArgs() above, before the statically-linked startup routines.
|
||||
puts("// Hook VPI startup routines and invoke callback\n");
|
||||
puts("if (vlog_startup_routines) {\n");
|
||||
puts(/**/ "for (auto routinep = &vlog_startup_routines[0]; *routinep; routinep++)"
|
||||
|
|
|
|||
|
|
@ -567,6 +567,12 @@ public:
|
|||
of.puts("VM_TRACE_VCD = ");
|
||||
of.puts(v3Global.opt.traceEnabledVcd() ? "1" : "0");
|
||||
of.puts("\n");
|
||||
of.puts("# VPI enabled? 0/1 (from --vpi)\n");
|
||||
of.puts("VM_VPI = ");
|
||||
of.puts(v3Global.opt.vpi() ? "1" : "0");
|
||||
of.puts("\n");
|
||||
// Link flags for runtime VPI library loading are emitted by emitOverallMake() after
|
||||
// verilated.mk is included (so $(CFG_LDFLAGS_DYNAMIC)/$(CFG_LDLIBS_DYNAMIC) are defined).
|
||||
|
||||
of.puts("\n### Object file lists...\n");
|
||||
for (int support = 0; support < 3; ++support) {
|
||||
|
|
@ -729,6 +735,17 @@ public:
|
|||
of.puts("\n### Executable rules... (from --exe)\n");
|
||||
of.puts("VPATH += $(VM_USER_DIR)\n");
|
||||
of.puts("\n");
|
||||
|
||||
if (v3Global.opt.vpi()) {
|
||||
// Runtime VPI library loading (+verilator+vpi+<lib>) needs the executable to
|
||||
// export its VPI symbols so the dlopen'd library can resolve them, plus the
|
||||
// dl library for dlopen/dlsym. The exact flags are probed at configure time
|
||||
// (CFG_LDFLAGS_DYNAMIC / CFG_LDLIBS_DYNAMIC in verilated.mk).
|
||||
of.puts("# Runtime VPI library loading (+verilator+vpi+) link requirements\n");
|
||||
of.puts("LDFLAGS += $(CFG_LDFLAGS_DYNAMIC)\n");
|
||||
of.puts("LDLIBS += $(CFG_LDLIBS_DYNAMIC)\n");
|
||||
of.puts("\n");
|
||||
}
|
||||
}
|
||||
|
||||
const string compilerIncludePch
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: VPI test library for t_flag_main_vpi
|
||||
//
|
||||
// Loaded at runtime via +verilator+vpi+<path> to verify that --binary --vpi
|
||||
// correctly loads shared libraries and invokes vlog_startup_routines[] (or a
|
||||
// named bootstrap). The design drives its own clock; this library only
|
||||
// observes 'count' via a cbValueChange callback and calls $finish after
|
||||
// MAX_TICKS edges -- so a successful $finish proves the library was loaded
|
||||
// and is able to register callbacks and reach signals by name.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "vpi_user.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
// Number of count increments to observe before calling $finish
|
||||
static const int MAX_TICKS = 10;
|
||||
static vpiHandle s_count_handle = nullptr;
|
||||
|
||||
static PLI_INT32 count_change_cb(p_cb_data /*cb_data*/) {
|
||||
if (!s_count_handle) return 0;
|
||||
s_vpi_value val;
|
||||
val.format = vpiIntVal;
|
||||
vpi_get_value(s_count_handle, &val);
|
||||
if (val.value.integer >= MAX_TICKS) {
|
||||
vpi_printf(const_cast<char*>("*-* All Finished *-*\n"));
|
||||
vpi_control(vpiFinish, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PLI_INT32 start_of_sim_cb(p_cb_data /*cb_data*/) {
|
||||
s_count_handle = vpi_handle_by_name(const_cast<char*>("t.count"), nullptr);
|
||||
if (!s_count_handle) {
|
||||
vpi_printf(const_cast<char*>("ERROR: cannot find t.count\n"));
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
// Observe count: fire a callback whenever it changes
|
||||
t_cb_data cb_data;
|
||||
s_vpi_time t;
|
||||
s_vpi_value val;
|
||||
t.type = vpiSuppressTime;
|
||||
val.format = vpiSuppressVal;
|
||||
cb_data.reason = cbValueChange;
|
||||
cb_data.cb_rtn = count_change_cb;
|
||||
cb_data.obj = s_count_handle;
|
||||
cb_data.time = &t;
|
||||
cb_data.value = &val;
|
||||
cb_data.user_data = nullptr;
|
||||
vpi_register_cb(&cb_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PLI_INT32 end_of_sim_cb(p_cb_data /*cb_data*/) {
|
||||
vpi_printf(const_cast<char*>("VPI end of simulation\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void register_callbacks() {
|
||||
// cbStartOfSimulation
|
||||
t_cb_data cb_data;
|
||||
s_vpi_time t;
|
||||
t.type = vpiSuppressTime;
|
||||
cb_data.reason = cbStartOfSimulation;
|
||||
cb_data.cb_rtn = start_of_sim_cb;
|
||||
cb_data.obj = nullptr;
|
||||
cb_data.time = &t;
|
||||
cb_data.value = nullptr;
|
||||
cb_data.user_data = nullptr;
|
||||
vpi_register_cb(&cb_data);
|
||||
|
||||
// cbEndOfSimulation
|
||||
cb_data.reason = cbEndOfSimulation;
|
||||
cb_data.cb_rtn = end_of_sim_cb;
|
||||
vpi_register_cb(&cb_data);
|
||||
}
|
||||
|
||||
// IEEE 1800 section 37: vlog_startup_routines[] -- null-terminated array of startup functions
|
||||
extern "C" {
|
||||
void (*vlog_startup_routines[])() = {register_callbacks, nullptr};
|
||||
|
||||
// Named bootstrap entrypoint -- used when library is loaded as <path>:my_vpi_bootstrap
|
||||
void my_vpi_bootstrap() { register_callbacks(); }
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
*-* All Finished *-*
|
||||
VPI end of simulation
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
# Compile with --binary --vpi to exercise the VPI-aware generated main.
|
||||
# Also compile a VPI shared library to be loaded at runtime via +verilator+vpi+.
|
||||
test.compile(make_pli=True, verilator_flags2=["--binary --vpi --public-flat-rw"])
|
||||
|
||||
# Run the generated binary; load the VPI library via the +verilator+vpi+ plusarg.
|
||||
# The VPI library's output (observed 'count' reaching MAX_TICKS, then end-of-sim) is
|
||||
# checked against the golden .out file.
|
||||
# Also pass a non-VPI plusarg (skipped by the loader's prefix check) and a bare
|
||||
# +verilator+vpi+ with an empty payload (skipped by the empty-arg check), so both
|
||||
# loader-skip branches are exercised alongside the real library load.
|
||||
test.execute(all_run_flags=[
|
||||
"+othertest", "+verilator+vpi+", "+verilator+vpi+" + test.obj_dir + "/libvpi.so"
|
||||
],
|
||||
check_finished=True,
|
||||
expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test for --binary --vpi runtime library loading. The design provides its
|
||||
// own clock (so the simulation has Verilog event activity); the VPI library
|
||||
// (t_flag_main_vpi.cpp), loaded at runtime via +verilator+vpi+, observes
|
||||
// 'count' via a cbValueChange callback and calls $finish after MAX_TICKS
|
||||
// edges. Signals are public so the library can reach them by name
|
||||
// (requires --public-flat-rw).
|
||||
module t;
|
||||
|
||||
reg clk /*verilator public_flat_rw*/;
|
||||
reg [31:0] count /*verilator public_flat_rw*/;
|
||||
|
||||
initial begin
|
||||
clk = 0;
|
||||
count = 0;
|
||||
end
|
||||
|
||||
// Self-driving clock: the design itself keeps the simulation alive
|
||||
always #5 clk = ~clk;
|
||||
|
||||
always @(posedge clk) count <= count + 1;
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
%Error: Cannot find VPI bootstrap 'no_such_fn' in: obj_vlt/t_flag_main_vpi_badentry/libvpi.so
|
||||
Aborting...
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
# A valid library loaded with a :<bootstrap> entry that does not exist must
|
||||
# fail with a clear error (the missing-named-bootstrap branch of the loader).
|
||||
test.top_filename = 't/t_flag_main_vpi.v'
|
||||
test.pli_filename = 't/t_flag_main_vpi.cpp'
|
||||
|
||||
test.compile(make_pli=True, verilator_flags2=["--binary --vpi --public-flat-rw"])
|
||||
|
||||
test.execute(fails=True,
|
||||
all_run_flags=["+verilator+vpi+" + test.obj_dir + "/libvpi.so:no_such_fn"],
|
||||
expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
%Error: Cannot load VPI library: obj_vlt/t_flag_main_vpi_badlib/nonexistent.so
|
||||
Aborting...
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
# +verilator+vpi+ pointing at a non-existent library must fail with a clear
|
||||
# error (the dlopen-failure branch of the runtime loader).
|
||||
test.top_filename = 't/t_flag_main_vpi.v'
|
||||
|
||||
test.compile(verilator_flags2=["--binary --vpi --public-flat-rw"])
|
||||
|
||||
# The fatal names the (stable, relative) library path; the platform-specific dlerror()
|
||||
# detail is emitted on a "- " line, which golden comparison strips, so the .out is portable.
|
||||
test.execute(fails=True,
|
||||
all_run_flags=["+verilator+vpi+" + test.obj_dir + "/nonexistent.so"],
|
||||
expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
*-* All Finished *-*
|
||||
VPI end of simulation
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
# Same design and VPI library as t_flag_main_vpi, but loaded via the
|
||||
# +verilator+vpi+<path>:<name> named-bootstrap syntax instead of vlog_startup_routines[].
|
||||
test.top_filename = 't/t_flag_main_vpi.v'
|
||||
test.pli_filename = 't/t_flag_main_vpi.cpp'
|
||||
|
||||
test.compile(make_pli=True, verilator_flags2=["--binary --vpi --public-flat-rw"])
|
||||
|
||||
test.execute(all_run_flags=["+verilator+vpi+" + test.obj_dir + "/libvpi.so:my_vpi_bootstrap"],
|
||||
check_finished=True,
|
||||
expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Second VPI test library for t_flag_main_vpi_multi
|
||||
//
|
||||
// A second, independent VPI library loaded alongside t_flag_main_vpi.cpp via a
|
||||
// repeated +verilator+vpi+ argument, to verify multiple libraries are loaded.
|
||||
// Its startup routine prints a marker proving it was loaded; it does not drive
|
||||
// or finish the simulation (the first library does that).
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "vpi_user.h"
|
||||
|
||||
static void lib2_startup() { vpi_printf(const_cast<char*>("second VPI library loaded\n")); }
|
||||
|
||||
// IEEE 1800 section 37: vlog_startup_routines[] -- null-terminated array of startup functions
|
||||
extern "C" {
|
||||
void (*vlog_startup_routines[])() = {lib2_startup, nullptr};
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
second VPI library loaded
|
||||
*-* All Finished *-*
|
||||
VPI end of simulation
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import os
|
||||
import platform
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
# Two +verilator+vpi+ arguments load two independent libraries: the first
|
||||
# (t_flag_main_vpi.cpp) observes the design and calls $finish; the second
|
||||
# (t_flag_main_vpi_lib2.cpp) prints a marker proving it too was loaded.
|
||||
test.top_filename = 't/t_flag_main_vpi.v'
|
||||
test.pli_filename = 't/t_flag_main_vpi.cpp'
|
||||
|
||||
test.compile(make_pli=True, verilator_flags2=["--binary --vpi --public-flat-rw"])
|
||||
|
||||
# Build the second VPI library (make_pli only builds libvpi.so), mirroring the
|
||||
# driver's own pli flags.
|
||||
root = os.environ['VERILATOR_ROOT']
|
||||
pli2_cmd = [
|
||||
os.environ['CXX'], "-I" + root + "/include/vltstd", "-I" + root + "/include", "-fPIC",
|
||||
"-shared"
|
||||
]
|
||||
pli2_cmd += (["-Wl,-undefined,dynamic_lookup"] if platform.system() == 'Darwin' else ["-rdynamic"])
|
||||
pli2_cmd += ["-o", test.obj_dir + "/libvpi2.so", "t/t_flag_main_vpi_lib2.cpp"]
|
||||
test.run(logfile=test.obj_dir + "/pli2_compile.log", cmd=pli2_cmd)
|
||||
|
||||
test.execute(all_run_flags=[
|
||||
"+verilator+vpi+" + test.obj_dir + "/libvpi.so",
|
||||
"+verilator+vpi+" + test.obj_dir + "/libvpi2.so"
|
||||
],
|
||||
check_finished=True,
|
||||
expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: VPI test library lacking vlog_startup_routines
|
||||
//
|
||||
// Loaded via +verilator+vpi+<path> with no :<bootstrap> entry, to exercise
|
||||
// the loader's error path when a library defines neither a named bootstrap
|
||||
// nor the vlog_startup_routines[] array.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "vpi_user.h"
|
||||
|
||||
// Intentionally no vlog_startup_routines and no bootstrap; just some symbol so
|
||||
// the shared object is non-empty and loads successfully.
|
||||
extern "C" void t_flag_main_vpi_noarray_present() {}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
%Error: Cannot find 'vlog_startup_routines' in: obj_vlt/t_flag_main_vpi_noarray/libvpi.so
|
||||
Aborting...
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
# A library loaded with no :<bootstrap> entry that lacks vlog_startup_routines
|
||||
# must fail with a clear error (the missing-array branch of the loader).
|
||||
test.top_filename = 't/t_flag_main_vpi.v'
|
||||
test.pli_filename = 't/t_flag_main_vpi_noarray.cpp'
|
||||
|
||||
test.compile(make_pli=True, verilator_flags2=["--binary --vpi --public-flat-rw"])
|
||||
|
||||
test.execute(fails=True,
|
||||
all_run_flags=["+verilator+vpi+" + test.obj_dir + "/libvpi.so"],
|
||||
expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
# --vpi without --exe (no generated main, library-only output): the Makefile
|
||||
# must still report VM_VPI = 1, but must NOT add the runtime-VPI link flags since
|
||||
# there is no executable to export VPI symbols from. Exercises the exe()==false
|
||||
# branch of the VPI link-flag gate in V3EmitMk.
|
||||
test.top_filename = 't/t_flag_main_vpi.v'
|
||||
|
||||
test.compile(make_main=False, verilator_make_gmake=False, verilator_flags2=["--vpi --timing"])
|
||||
|
||||
test.file_grep(test.obj_dir + "/V" + test.name + "_classes.mk", r'VM_VPI = 1')
|
||||
# Without --exe there is no executable to export symbols from or to dlopen into,
|
||||
# so the runtime-VPI link flags (CFG_LDFLAGS_DYNAMIC/CFG_LDLIBS_DYNAMIC, probed at
|
||||
# configure time) must not be referenced in the generated Makefile.
|
||||
mk = test.obj_dir + "/V" + test.name + ".mk"
|
||||
test.file_grep_not(mk, r'CFG_LDFLAGS_DYNAMIC')
|
||||
test.file_grep_not(mk, r'CFG_LDLIBS_DYNAMIC')
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
%Warning: COMMAND_LINE:0: +verilator+vpi+ ignored: simulation was not compiled with --vpi '+verilator+vpi+/nonexistent.so'
|
||||
[0] Hello
|
||||
*-* All Finished *-*
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
# Compile with --binary but WITHOUT --vpi.
|
||||
# Passing +verilator+vpi+ at runtime should emit a warning, not load anything.
|
||||
test.compile(top_filename='t/t_flag_main.v', verilator_flags2=["--binary"])
|
||||
|
||||
# Without --vpi there is no loader, so the plusarg is ignored with a warning (checked via
|
||||
# the golden .out). The plusarg value is fixed, so the warning text is portable.
|
||||
test.execute(all_run_flags=["+verilator+vpi+/nonexistent.so"],
|
||||
check_finished=True,
|
||||
expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
Loading…
Reference in New Issue