// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: VPI test library for t_flag_main_vpi // // Loaded at runtime via +verilator+vpi+ 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 #include // 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("*-* 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("t.count"), nullptr); if (!s_count_handle) { vpi_printf(const_cast("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("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 :my_vpi_bootstrap void my_vpi_bootstrap() { register_callbacks(); } }