Support dynamic loading of VPI extensions (#7727)

This commit is contained in:
Matthew Ballance 2026-06-28 06:28:09 -07:00 committed by GitHub
parent 3853301367
commit d023b3b075
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 549 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,2 @@
*-* All Finished *-*
VPI end of simulation

View File

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

View File

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

View File

@ -0,0 +1,2 @@
%Error: Cannot find VPI bootstrap 'no_such_fn' in: obj_vlt/t_flag_main_vpi_badentry/libvpi.so
Aborting...

View File

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

View File

@ -0,0 +1,2 @@
%Error: Cannot load VPI library: obj_vlt/t_flag_main_vpi_badlib/nonexistent.so
Aborting...

View File

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

View File

@ -0,0 +1,2 @@
*-* All Finished *-*
VPI end of simulation

View File

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

View File

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

View File

@ -0,0 +1,3 @@
second VPI library loaded
*-* All Finished *-*
VPI end of simulation

View File

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

View File

@ -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() {}

View File

@ -0,0 +1,2 @@
%Error: Cannot find 'vlog_startup_routines' in: obj_vlt/t_flag_main_vpi_noarray/libvpi.so
Aborting...

View File

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

View File

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

View File

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

View File

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