Add support for dynamically loading VPI extensions on POSIX platforms
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
This commit is contained in:
parent
0cd13f80c9
commit
47b3e6dd6c
|
|
@ -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 (requires --vpi --main)
|
||||
+verilator+wno+unsatconstr+<value> Disable constraint warnings
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -139,6 +139,22 @@ 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 Section 37) is
|
||||
invoked. May be repeated to load multiple libraries. For signals to be
|
||||
accessible by name, also Verilate with :vlopt:`--public-flat-rw`. If passed
|
||||
to a model not compiled with :vlopt:`--vpi`, a warning is emitted and the
|
||||
argument is ignored.
|
||||
|
||||
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 linked into the model.
|
||||
|
||||
.. option:: +verilator+wno+unsatconstr+<value>
|
||||
|
||||
Disable unsatisfied constraint warnings at simulation runtime. When set to
|
||||
|
|
|
|||
|
|
@ -3544,6 +3544,15 @@ 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 (0 == std::strncmp(arg.c_str(), "+verilator+vpi+", std::strlen("+verilator+vpi+"))) {
|
||||
// With --vpi the generated --main (vl_load_vpi_libs) consumes this, so accept
|
||||
// it silently here. Without --vpi there is no loader, so warn it is ignored.
|
||||
#if !VM_VPI
|
||||
VL_WARN_MT("COMMAND_LINE", 0, "",
|
||||
("+verilator+vpi+ ignored: simulation was not compiled with --vpi (" + arg
|
||||
+ ")")
|
||||
.c_str());
|
||||
#endif
|
||||
} else if (arg == "+verilator+V") {
|
||||
VerilatedImp::versionDump(); // Someday more info too
|
||||
VL_FATAL_MT("COMMAND_LINE", 0, "",
|
||||
|
|
|
|||
|
|
@ -80,6 +80,9 @@ UNAME_S := $(shell uname -s)
|
|||
######################################################################
|
||||
# C Preprocessor flags
|
||||
|
||||
# Default for makefiles generated before --vpi tracking was added
|
||||
VM_VPI ?= 0
|
||||
|
||||
# Add -MMD -MP if you're using a recent version of GCC.
|
||||
VK_CPPFLAGS_ALWAYS += \
|
||||
-MMD \
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -55,7 +55,13 @@ private:
|
|||
// Heavily commented output, as users are likely to look at or copy this code
|
||||
|
||||
puts("#include \"verilated.h\"\n");
|
||||
if (v3Global.opt.vpi()) puts("#include \"verilated_vpi.h\"\n");
|
||||
if (v3Global.opt.vpi()) {
|
||||
puts("#include \"verilated_vpi.h\"\n");
|
||||
puts("#ifndef _WIN32\n");
|
||||
puts("# include <dlfcn.h> // For runtime +verilator+vpi+ library loading\n");
|
||||
puts("#endif\n");
|
||||
puts("#include <cstring>\n");
|
||||
}
|
||||
puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n");
|
||||
if (v3Global.opt.debugRuntimeTimeout()) {
|
||||
puts("\n");
|
||||
|
|
@ -67,6 +73,53 @@ private:
|
|||
puts("// User VPI code adds to this array to get startup main() callbacks\n");
|
||||
puts("extern \"C\" void (*vlog_startup_routines[])() VL_ATTR_WEAK;\n");
|
||||
puts("\n");
|
||||
|
||||
// Runtime loader for VPI shared libraries requested via +verilator+vpi+<lib>.
|
||||
// POSIX only: relies on the executable exporting its VPI symbols.
|
||||
puts("// Load VPI shared libraries requested via +verilator+vpi+<lib>[:<bootstrap>]\n");
|
||||
puts("static void vl_load_vpi_libs(int argc, char** argv) {\n");
|
||||
puts(/**/ "const char* prefix = \"+verilator+vpi+\";\n");
|
||||
puts(/**/ "const size_t pfx_len = std::strlen(prefix);\n");
|
||||
puts(/**/ "for (int i = 1; i < argc; ++i) {\n");
|
||||
puts(/****/ "if (std::strncmp(argv[i], prefix, pfx_len) != 0) continue;\n");
|
||||
puts(/****/ "const std::string arg(argv[i] + pfx_len);\n");
|
||||
puts(/****/ "if (arg.empty()) continue;\n");
|
||||
puts("#ifdef _WIN32\n");
|
||||
puts(/****/ "VL_FATAL_MT(\"\", 0, \"\","
|
||||
" \"+verilator+vpi+: runtime VPI library loading is not supported on\"\n");
|
||||
puts(/******/ "\" Windows; link the VPI code into the model instead\");\n");
|
||||
puts("#else\n");
|
||||
puts(/****/ "using vlog_startup_t = void (*)();\n");
|
||||
puts(/****/ "// Split <lib>:<bootstrap> on the last ':'\n");
|
||||
puts(/****/ "const std::string::size_type colon_pos = arg.rfind(':');\n");
|
||||
puts(/****/ "const bool has_entry = (colon_pos != std::string::npos);\n");
|
||||
puts(/****/ "const std::string libpath = has_entry ? arg.substr(0, colon_pos) : arg;\n");
|
||||
puts(/****/ "const std::string entry_name\n");
|
||||
puts(/******/ "= has_entry ? arg.substr(colon_pos + 1) : std::string{};\n");
|
||||
puts(/****/ "void* handle = dlopen(libpath.c_str(), RTLD_LAZY);\n");
|
||||
puts(/****/ "if (!handle)\n");
|
||||
puts(/******/ "VL_FATAL_MT(\"\", 0, \"\","
|
||||
" (std::string{\"Cannot load VPI library: \"} + dlerror()).c_str());\n");
|
||||
puts(/****/ "if (has_entry) {\n");
|
||||
puts(/******/ "vlog_startup_t bsp = reinterpret_cast<vlog_startup_t>("
|
||||
"dlsym(handle, entry_name.c_str()));\n");
|
||||
puts(/******/ "if (!bsp)\n");
|
||||
puts(/********/ "VL_FATAL_MT(\"\", 0, \"\", (std::string{\"Cannot find VPI bootstrap '\"}\n");
|
||||
puts(/**********/ "+ entry_name + \"' in: \" + libpath).c_str());\n");
|
||||
puts(/******/ "bsp();\n");
|
||||
puts(/****/ "} else {\n");
|
||||
puts(/******/ "vlog_startup_t* routinesp = reinterpret_cast<vlog_startup_t*>(\n");
|
||||
puts(/********/ "dlsym(handle, \"vlog_startup_routines\"));\n");
|
||||
puts(/******/ "if (!routinesp)\n");
|
||||
puts(/********/ "VL_FATAL_MT(\"\", 0, \"\","
|
||||
" (std::string{\"Cannot find 'vlog_startup_routines' in: \"}\n");
|
||||
puts(/**********/ "+ libpath).c_str());\n");
|
||||
puts(/******/ "for (int j = 0; routinesp[j]; ++j) routinesp[j]();\n");
|
||||
puts(/****/ "}\n");
|
||||
puts("#endif\n");
|
||||
puts(/**/ "}\n");
|
||||
puts("}\n");
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
puts("//======================\n\n");
|
||||
|
|
@ -104,6 +157,8 @@ private:
|
|||
puts("\n");
|
||||
|
||||
if (v3Global.opt.vpi()) {
|
||||
puts("// Load VPI shared libraries requested via +verilator+vpi+<lib>\n");
|
||||
puts("vl_load_vpi_libs(argc, argv);\n");
|
||||
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 platform-specific and emitted by
|
||||
// emitOverallMake() after verilated.mk is included (so $(UNAME_S) is 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 platform-specific
|
||||
// link flags. Resolve them with $(UNAME_S)
|
||||
of.puts("# Runtime VPI library loading (+verilator+vpi+) link requirements\n");
|
||||
of.puts("ifeq ($(UNAME_S),Linux)\n");
|
||||
of.puts(/**/ "LDFLAGS += -rdynamic\n");
|
||||
of.puts(/**/ "LDLIBS += -ldl\n");
|
||||
of.puts("endif\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 §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 @@
|
|||
- VPI end of simulation
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#!/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"])
|
||||
|
||||
# With --vpi and an executable (--binary implies --exe), the Makefile must, on Linux,
|
||||
# export the executable's symbols (-rdynamic) and link libdl for the generated loader's
|
||||
# dlopen/dlsym calls. These are gated on $(UNAME_S) so macOS/Windows are unaffected.
|
||||
mk = test.obj_dir + "/V" + test.name + ".mk"
|
||||
test.file_grep(mk, r'ifeq \(\$\(UNAME_S\),Linux\)')
|
||||
test.file_grep(mk, r'LDFLAGS \+= -rdynamic')
|
||||
test.file_grep(mk, r'LDLIBS \+= -ldl')
|
||||
|
||||
# Run the generated binary; load the VPI library via the +verilator+vpi+ plusarg.
|
||||
test.execute(all_run_flags=["+verilator+vpi+" + test.obj_dir + "/libvpi.so"],
|
||||
check_finished=True)
|
||||
|
||||
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,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')
|
||||
|
||||
# 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"])
|
||||
|
||||
test.file_grep(test.run_log_filename, r"Cannot find VPI bootstrap 'no_such_fn'")
|
||||
|
||||
test.passes()
|
||||
|
|
@ -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')
|
||||
|
||||
# +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"])
|
||||
|
||||
test.execute(fails=True, all_run_flags=["+verilator+vpi+" + test.obj_dir + "/nonexistent.so"])
|
||||
|
||||
test.file_grep(test.run_log_filename, r'Cannot load VPI library')
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#!/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)
|
||||
|
||||
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 §37: vlog_startup_routines[] — null-terminated array of startup functions
|
||||
extern "C" {
|
||||
void (*vlog_startup_routines[])() = {lib2_startup, nullptr};
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
#!/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)
|
||||
|
||||
test.file_grep(test.run_log_filename, r'- second VPI library loaded')
|
||||
|
||||
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,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"])
|
||||
|
||||
test.file_grep(test.run_log_filename, r"Cannot find 'vlog_startup_routines'")
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#!/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 '-rdynamic' since there is no
|
||||
# executable to export VPI symbols from. Exercises the exe()==false branch of
|
||||
# the VPI -rdynamic 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 must not be emitted into the Makefile at all.
|
||||
mk = test.obj_dir + "/V" + test.name + ".mk"
|
||||
test.file_grep_not(mk, r'-rdynamic')
|
||||
test.file_grep_not(mk, r'-ldl')
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#!/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"])
|
||||
|
||||
test.execute(
|
||||
all_run_flags=["+verilator+vpi+/nonexistent.so"],
|
||||
check_finished=True)
|
||||
|
||||
test.file_grep(test.run_log_filename,
|
||||
r'%Warning: COMMAND_LINE:0: \+verilator\+vpi\+ ignored: simulation was not compiled with --vpi')
|
||||
|
||||
test.passes()
|
||||
Loading…
Reference in New Issue