parent
d40036239b
commit
1e6c1ab106
1
Changes
1
Changes
|
|
@ -13,6 +13,7 @@ Verilator 5.047 devel
|
||||||
|
|
||||||
**Other:**
|
**Other:**
|
||||||
|
|
||||||
|
* Add VPI callback support to --main (#7145).
|
||||||
* Fix wide conditional short circuiting (#7155).
|
* Fix wide conditional short circuiting (#7155).
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ CPPFLAGS += $(OPT)
|
||||||
# On macOS, specify all weak symbols as dynamic_lookup.
|
# On macOS, specify all weak symbols as dynamic_lookup.
|
||||||
# Otherwise, you get undefined symbol errors.
|
# Otherwise, you get undefined symbol errors.
|
||||||
ifeq ($(UNAME_S),Darwin)
|
ifeq ($(UNAME_S),Darwin)
|
||||||
LDFLAGS += -Wl,-U,__Z15vl_time_stamp64v,-U,__Z13sc_time_stampv
|
LDFLAGS += -Wl,-U,__Z15vl_time_stamp64v,-U,__Z13sc_time_stampv,-U,vlog_startup_routines
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Allow upper level user makefiles to specify flags they want.
|
# Allow upper level user makefiles to specify flags they want.
|
||||||
|
|
|
||||||
|
|
@ -716,7 +716,7 @@ public:
|
||||||
// cppcheck-suppress uninitVar // m_value
|
// cppcheck-suppress uninitVar // m_value
|
||||||
VerilatedVpiCbHolder(uint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
|
VerilatedVpiCbHolder(uint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
|
||||||
: m_id{id}
|
: m_id{id}
|
||||||
, m_cbData{*cbDatap}
|
, m_cbData{*cbDatap} // vpi_register_cb checks cbDatap is non-null
|
||||||
, m_varo{varop} {
|
, m_varo{varop} {
|
||||||
m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
|
m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
|
||||||
m_cbData.value = &m_value;
|
m_cbData.value = &m_value;
|
||||||
|
|
|
||||||
|
|
@ -55,13 +55,21 @@ private:
|
||||||
// Heavily commented output, as users are likely to look at or copy this code
|
// Heavily commented output, as users are likely to look at or copy this code
|
||||||
|
|
||||||
puts("#include \"verilated.h\"\n");
|
puts("#include \"verilated.h\"\n");
|
||||||
|
if (v3Global.opt.vpi()) puts("#include \"verilated_vpi.h\"\n");
|
||||||
puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n");
|
puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n");
|
||||||
if (v3Global.opt.debugRuntimeTimeout()) {
|
if (v3Global.opt.debugRuntimeTimeout()) {
|
||||||
puts("\n");
|
puts("\n");
|
||||||
puts("#include <csignal>\n");
|
puts("#include <csignal>\n");
|
||||||
}
|
}
|
||||||
|
puts("\n");
|
||||||
|
|
||||||
puts("\n//======================\n\n");
|
if (v3Global.opt.vpi()) {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("//======================\n\n");
|
||||||
|
|
||||||
if (v3Global.opt.debugRuntimeTimeout()) {
|
if (v3Global.opt.debugRuntimeTimeout()) {
|
||||||
puts("void alarmHandler(int signum) {\n");
|
puts("void alarmHandler(int signum) {\n");
|
||||||
|
|
@ -92,18 +100,43 @@ private:
|
||||||
+ EmitCUtil::topClassName() + "{contextp.get(), \"" + topName + "\"}};\n");
|
+ EmitCUtil::topClassName() + "{contextp.get(), \"" + topName + "\"}};\n");
|
||||||
puts("\n");
|
puts("\n");
|
||||||
|
|
||||||
|
if (v3Global.opt.vpi()) {
|
||||||
|
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++)"
|
||||||
|
" (*routinep)();\n");
|
||||||
|
puts("}\n");
|
||||||
|
puts("VerilatedVpi::callCbs(cbStartOfSimulation);\n");
|
||||||
|
puts("\n");
|
||||||
|
}
|
||||||
|
|
||||||
puts("// Simulate until $finish\n");
|
puts("// Simulate until $finish\n");
|
||||||
puts("while (VL_LIKELY(!contextp->gotFinish())) {\n");
|
puts("while (VL_LIKELY(!contextp->gotFinish())) {\n");
|
||||||
|
if (v3Global.opt.vpi()) {
|
||||||
|
puts(/**/ "// VPI callbacks\n");
|
||||||
|
puts(/**/ "VerilatedVpi::callTimedCbs();\n");
|
||||||
|
puts(/**/ "VerilatedVpi::callCbs(cbNextSimTime);\n"); // Before next event queue
|
||||||
|
puts(/**/ "VerilatedVpi::callCbs(cbAtStartOfSimTime);\n"); // Before time queue
|
||||||
|
}
|
||||||
puts(/**/ "// Evaluate model\n");
|
puts(/**/ "// Evaluate model\n");
|
||||||
puts(/**/ "topp->eval();\n");
|
puts(/**/ "topp->eval();\n");
|
||||||
|
if (v3Global.opt.vpi()) {
|
||||||
|
puts(/**/ "// VPI callbacks\n");
|
||||||
|
puts(/**/ "VerilatedVpi::callValueCbs();\n");
|
||||||
|
puts(/**/ "VerilatedVpi::callCbs(cbAtEndOfSimTime);\n"); // After nonblocking events
|
||||||
|
puts(/**/ "VerilatedVpi::callCbs(cbReadWriteSynch);\n"); // After a specified time
|
||||||
|
puts(/**/ "VerilatedVpi::callCbs(cbReadOnlySynch);\n"); // After cbReadWriteSynch
|
||||||
|
}
|
||||||
puts(/**/ "// Advance time\n");
|
puts(/**/ "// Advance time\n");
|
||||||
if (v3Global.rootp()->delaySchedulerp() || v3Global.opt.timing()) {
|
if (v3Global.rootp()->delaySchedulerp() || v3Global.opt.timing()) {
|
||||||
puts("if (!topp->eventsPending()) break;\n");
|
puts("if (!topp->eventsPending()) break;\n");
|
||||||
}
|
}
|
||||||
if (v3Global.rootp()->delaySchedulerp()) {
|
const std::string nextSlot = v3Global.rootp()->delaySchedulerp() ? "topp->nextTimeSlot()"
|
||||||
puts("contextp->time(topp->nextTimeSlot());\n");
|
: "contextp->time() + 1";
|
||||||
|
if (v3Global.opt.vpi()) {
|
||||||
|
puts("contextp->time(std::min("s + nextSlot + ", VerilatedVpi::cbNextDeadline()));\n");
|
||||||
} else {
|
} else {
|
||||||
puts("contextp->timeInc(1);\n");
|
puts("contextp->time("s + nextSlot + ");\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
puts("}\n");
|
puts("}\n");
|
||||||
|
|
@ -116,6 +149,7 @@ private:
|
||||||
|
|
||||||
puts("// Execute 'final' processes\n");
|
puts("// Execute 'final' processes\n");
|
||||||
puts("topp->final();\n");
|
puts("topp->final();\n");
|
||||||
|
if (v3Global.opt.vpi()) puts("VerilatedVpi::callCbs(cbEndOfSimulation);\n");
|
||||||
puts("\n");
|
puts("\n");
|
||||||
|
|
||||||
if (v3Global.opt.coverage()) {
|
if (v3Global.opt.coverage()) {
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ void vpi_compat_bootstrap(void) {
|
||||||
|
|
||||||
// We're able to call vpi_main() here on Verilator/Xcelium,
|
// We're able to call vpi_main() here on Verilator/Xcelium,
|
||||||
// but Icarus complains (rightfully so)
|
// but Icarus complains (rightfully so)
|
||||||
s_cb_data cb_data;
|
s_cb_data cb_data{};
|
||||||
s_vpi_time vpi_time;
|
s_vpi_time vpi_time;
|
||||||
|
|
||||||
vpi_time.high = 0;
|
vpi_time.high = 0;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/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: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
|
||||||
|
# Checks that --vpi without a C file doesn't break builds
|
||||||
|
test.compile(verilator_flags2=["--binary --vpi"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t;
|
||||||
|
initial $finish;
|
||||||
|
endmodule
|
||||||
|
|
@ -12,21 +12,10 @@
|
||||||
// Setup multiple one-time callbacks with different time delays.
|
// Setup multiple one-time callbacks with different time delays.
|
||||||
// Ensure they are not called before the delay has elapsed.
|
// Ensure they are not called before the delay has elapsed.
|
||||||
|
|
||||||
#ifdef IS_VPI
|
|
||||||
|
|
||||||
#include "vpi_user.h"
|
#include "vpi_user.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#include "verilated.h"
|
|
||||||
#include "verilated_vpi.h"
|
|
||||||
|
|
||||||
#include VM_PREFIX_INCLUDE
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// These require the above. Comment prevents clang-format moving them
|
// These require the above. Comment prevents clang-format moving them
|
||||||
#include "TestSimulator.h"
|
#include "TestSimulator.h"
|
||||||
#include "TestVpi.h"
|
#include "TestVpi.h"
|
||||||
|
|
@ -45,7 +34,7 @@ typedef struct {
|
||||||
|
|
||||||
static cb_stats CallbackStats[cbAtEndOfSimTime + 1];
|
static cb_stats CallbackStats[cbAtEndOfSimTime + 1];
|
||||||
|
|
||||||
bool got_error = false;
|
int errors = 0;
|
||||||
|
|
||||||
static vpiHandle ValueHandle, ToggleHandle, ClockHandle;
|
static vpiHandle ValueHandle, ToggleHandle, ClockHandle;
|
||||||
|
|
||||||
|
|
@ -80,28 +69,40 @@ bool cb_time_is_delay(int cb_name) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vpiHandle test_vpi_register_cb(p_cb_data cb_datap) {
|
||||||
|
if (verbose)
|
||||||
|
vpi_printf(const_cast<char*>("- test_vpi_register_cb(%s @%d)\n"),
|
||||||
|
strFromVpiCallbackReason(cb_datap->reason),
|
||||||
|
(cb_datap->time ? cb_datap->time->low : 0));
|
||||||
|
return vpi_register_cb(cb_datap);
|
||||||
|
}
|
||||||
|
|
||||||
// forward declaration
|
// forward declaration
|
||||||
static PLI_INT32 TheCallback(s_cb_data* data);
|
static PLI_INT32 TheCallback(s_cb_data* data);
|
||||||
|
|
||||||
static PLI_INT32 AtEndOfSimTimeCallback(s_cb_data* data) {
|
static PLI_INT32 AtEndOfSimTimeCallback(s_cb_data* data) {
|
||||||
s_vpi_time t;
|
s_vpi_time t;
|
||||||
|
|
||||||
|
CHECK_RESULT(data->reason, cbAtEndOfSimTime);
|
||||||
cb_stats* stats = &CallbackStats[data->reason];
|
cb_stats* stats = &CallbackStats[data->reason];
|
||||||
|
|
||||||
t.type = vpiSimTime;
|
t.type = vpiSimTime;
|
||||||
vpi_get_time(0, &t);
|
vpi_get_time(0, &t);
|
||||||
|
|
||||||
if (verbose) vpi_printf(const_cast<char*>("- [@%d] AtEndOfSimTime Callback\n"), t.low);
|
if (verbose)
|
||||||
|
vpi_printf(const_cast<char*>("- [@%d] AtEndOfSimTime Callback (count=%d)\n"), t.low,
|
||||||
|
stats->count);
|
||||||
|
|
||||||
|
CHECK_RESULT(stats->count < stats->number_of_exp_times, 1);
|
||||||
CHECK_RESULT(t.low, stats->exp_times[stats->count]);
|
CHECK_RESULT(t.low, stats->exp_times[stats->count]);
|
||||||
stats->count += 1;
|
stats->count += 1;
|
||||||
|
|
||||||
s_cb_data cb_data;
|
s_cb_data cb_data{};
|
||||||
s_vpi_time time = {vpiSimTime, 0, 417, 0}; // non-zero time to check that it's ignored
|
s_vpi_time time = {vpiSimTime, 0, 417, 0}; // non-zero time to check that it's ignored
|
||||||
cb_data.time = &time;
|
cb_data.time = &time;
|
||||||
cb_data.reason = cbNextSimTime;
|
cb_data.reason = cbNextSimTime;
|
||||||
cb_data.cb_rtn = TheCallback;
|
cb_data.cb_rtn = TheCallback;
|
||||||
vpiHandle Handle = vpi_register_cb(&cb_data);
|
vpiHandle Handle = test_vpi_register_cb(&cb_data);
|
||||||
CHECK_RESULT_NZ(Handle);
|
CHECK_RESULT_NZ(Handle);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -116,16 +117,17 @@ static PLI_INT32 TheCallback(s_cb_data* data) {
|
||||||
vpi_get_time(0, &t);
|
vpi_get_time(0, &t);
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
vpi_printf(const_cast<char*>("- [@%d] %s Callback\n"), t.low,
|
vpi_printf(const_cast<char*>("- [@%d] %s Callback (count=%d)\n"), t.low,
|
||||||
cb_reason_to_string(data->reason));
|
cb_reason_to_string(data->reason), stats->count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CHECK_RESULT(stats->count < stats->number_of_exp_times, 1);
|
||||||
CHECK_RESULT(t.low, stats->exp_times[stats->count]);
|
CHECK_RESULT(t.low, stats->exp_times[stats->count]);
|
||||||
stats->count += 1;
|
stats->count += 1;
|
||||||
|
|
||||||
if (stats->count >= stats->number_of_exp_times) return 0;
|
if (stats->count >= stats->number_of_exp_times) return 0;
|
||||||
|
|
||||||
s_cb_data cb_data;
|
s_cb_data cb_data{};
|
||||||
PLI_UINT32 next_time;
|
PLI_UINT32 next_time;
|
||||||
|
|
||||||
if (data->reason == cbNextSimTime) {
|
if (data->reason == cbNextSimTime) {
|
||||||
|
|
@ -150,14 +152,14 @@ static PLI_INT32 TheCallback(s_cb_data* data) {
|
||||||
|
|
||||||
s_vpi_time time = {vpiSimTime, 0, next_time, 0};
|
s_vpi_time time = {vpiSimTime, 0, next_time, 0};
|
||||||
cb_data.time = &time;
|
cb_data.time = &time;
|
||||||
vpiHandle Handle = vpi_register_cb(&cb_data);
|
vpiHandle Handle = test_vpi_register_cb(&cb_data);
|
||||||
CHECK_RESULT_NZ(Handle);
|
CHECK_RESULT_NZ(Handle);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PLI_INT32 StartOfSimulationCallback(s_cb_data* data) {
|
static PLI_INT32 StartOfSimulationCallback(s_cb_data* data) {
|
||||||
s_cb_data cb_data;
|
s_cb_data cb_data{};
|
||||||
s_vpi_time timerec = {vpiSimTime, 0, 0, 0};
|
s_vpi_time timerec = {vpiSimTime, 0, 0, 0};
|
||||||
|
|
||||||
s_vpi_time t;
|
s_vpi_time t;
|
||||||
|
|
@ -179,32 +181,33 @@ static PLI_INT32 StartOfSimulationCallback(s_cb_data* data) {
|
||||||
CallbackStats[cbAtStartOfSimTime].number_of_exp_times = 3;
|
CallbackStats[cbAtStartOfSimTime].number_of_exp_times = 3;
|
||||||
timerec.low = 5;
|
timerec.low = 5;
|
||||||
cb_data.reason = cbAtStartOfSimTime;
|
cb_data.reason = cbAtStartOfSimTime;
|
||||||
vpiHandle ASOSHandle = vpi_register_cb(&cb_data);
|
vpiHandle ASOSHandle = test_vpi_register_cb(&cb_data);
|
||||||
CHECK_RESULT_NZ(ASOSHandle);
|
CHECK_RESULT_NZ(ASOSHandle);
|
||||||
|
|
||||||
CallbackStats[cbReadWriteSynch].exp_times = new PLI_UINT32[3]{6, 16, 21};
|
CallbackStats[cbReadWriteSynch].exp_times = new PLI_UINT32[3]{6, 16, 21};
|
||||||
CallbackStats[cbReadWriteSynch].number_of_exp_times = 3;
|
CallbackStats[cbReadWriteSynch].number_of_exp_times = 3;
|
||||||
timerec.low = 6;
|
timerec.low = 6;
|
||||||
cb_data.reason = cbReadWriteSynch;
|
cb_data.reason = cbReadWriteSynch;
|
||||||
vpiHandle RWHandle = vpi_register_cb(&cb_data);
|
vpiHandle RWHandle = test_vpi_register_cb(&cb_data);
|
||||||
CHECK_RESULT_NZ(RWHandle);
|
CHECK_RESULT_NZ(RWHandle);
|
||||||
|
|
||||||
CallbackStats[cbReadOnlySynch].exp_times = new PLI_UINT32[3]{7, 17, 22};
|
CallbackStats[cbReadOnlySynch].exp_times = new PLI_UINT32[3]{7, 17, 22};
|
||||||
CallbackStats[cbReadOnlySynch].number_of_exp_times = 3;
|
CallbackStats[cbReadOnlySynch].number_of_exp_times = 3;
|
||||||
timerec.low = 7;
|
timerec.low = 7;
|
||||||
cb_data.reason = cbReadOnlySynch;
|
cb_data.reason = cbReadOnlySynch;
|
||||||
vpiHandle ROHandle = vpi_register_cb(&cb_data);
|
vpiHandle ROHandle = test_vpi_register_cb(&cb_data);
|
||||||
CHECK_RESULT_NZ(ROHandle);
|
CHECK_RESULT_NZ(ROHandle);
|
||||||
|
|
||||||
CallbackStats[cbNextSimTime].exp_times = new PLI_UINT32[9]{5, 6, 7, 15, 16, 17, 20, 21, 22};
|
CallbackStats[cbNextSimTime].exp_times
|
||||||
CallbackStats[cbNextSimTime].number_of_exp_times = 9;
|
= new PLI_UINT32[10]{5, 6, 7, 10, 15, 16, 17, 20, 21, 22};
|
||||||
|
CallbackStats[cbNextSimTime].number_of_exp_times = 10;
|
||||||
timerec.low = 8;
|
timerec.low = 8;
|
||||||
cb_data.reason = cbNextSimTime;
|
cb_data.reason = cbNextSimTime;
|
||||||
vpiHandle NSTHandle = vpi_register_cb(&cb_data);
|
vpiHandle NSTHandle = test_vpi_register_cb(&cb_data);
|
||||||
CHECK_RESULT_NZ(NSTHandle);
|
CHECK_RESULT_NZ(NSTHandle);
|
||||||
|
|
||||||
CallbackStats[cbAtEndOfSimTime].exp_times = new PLI_UINT32[8]{5, 6, 7, 15, 16, 17, 20, 21};
|
CallbackStats[cbAtEndOfSimTime].exp_times = new PLI_UINT32[9]{5, 6, 7, 10, 15, 16, 17, 20, 21};
|
||||||
CallbackStats[cbAtEndOfSimTime].number_of_exp_times = 8;
|
CallbackStats[cbAtEndOfSimTime].number_of_exp_times = 9;
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
@ -223,23 +226,26 @@ static int EndOfSimulationCallback(p_cb_data cb_data) {
|
||||||
|
|
||||||
CHECK_RESULT(CallbackStats[cbStartOfSimulation].count, 1);
|
CHECK_RESULT(CallbackStats[cbStartOfSimulation].count, 1);
|
||||||
CHECK_RESULT(CallbackStats[cbAtStartOfSimTime].count, 3);
|
CHECK_RESULT(CallbackStats[cbAtStartOfSimTime].count, 3);
|
||||||
CHECK_RESULT(CallbackStats[cbNextSimTime].count, 9);
|
CHECK_RESULT(CallbackStats[cbNextSimTime].count, 10);
|
||||||
CHECK_RESULT(CallbackStats[cbReadWriteSynch].count, 3);
|
CHECK_RESULT(CallbackStats[cbReadWriteSynch].count, 3);
|
||||||
CHECK_RESULT(CallbackStats[cbReadOnlySynch].count, 3);
|
CHECK_RESULT(CallbackStats[cbReadOnlySynch].count, 3);
|
||||||
CHECK_RESULT(CallbackStats[cbAtEndOfSimTime].count, 8);
|
CHECK_RESULT(CallbackStats[cbAtEndOfSimTime].count, 9);
|
||||||
CHECK_RESULT(CallbackStats[cbEndOfSimulation].count, 1);
|
CHECK_RESULT(CallbackStats[cbEndOfSimulation].count, 1);
|
||||||
|
|
||||||
if (!got_error) printf("*-* All Finished *-*\n");
|
if (!errors) printf("*-* All Finished *-*\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cver entry
|
// cver entry
|
||||||
static void VPIRegister(void) {
|
static void VPIRegister(void) {
|
||||||
|
if (verbose) vpi_printf(const_cast<char*>("- VPIRegister callback\n"));
|
||||||
|
|
||||||
// Clear stats
|
// Clear stats
|
||||||
for (int cb = 1; cb <= cbAtEndOfSimTime; cb++) CallbackStats[cb].count = 0;
|
for (int cb = 1; cb <= cbAtEndOfSimTime; cb++) CallbackStats[cb].count = 0;
|
||||||
CallbackStats[cbStartOfSimulation].exp_times = new PLI_UINT32(0);
|
CallbackStats[cbStartOfSimulation].exp_times = new PLI_UINT32(0);
|
||||||
CallbackStats[cbEndOfSimulation].exp_times = new PLI_UINT32(22);
|
CallbackStats[cbEndOfSimulation].exp_times = new PLI_UINT32(100);
|
||||||
s_cb_data cb_data;
|
s_cb_data cb_data{};
|
||||||
s_vpi_time timerec = {vpiSuppressTime, 0, 0, 0};
|
s_vpi_time timerec = {vpiSuppressTime, 0, 0, 0};
|
||||||
|
|
||||||
cb_data.time = &timerec;
|
cb_data.time = &timerec;
|
||||||
|
|
@ -248,80 +254,12 @@ static void VPIRegister(void) {
|
||||||
cb_data.obj = 0;
|
cb_data.obj = 0;
|
||||||
cb_data.reason = cbStartOfSimulation;
|
cb_data.reason = cbStartOfSimulation;
|
||||||
cb_data.cb_rtn = StartOfSimulationCallback;
|
cb_data.cb_rtn = StartOfSimulationCallback;
|
||||||
|
test_vpi_register_cb(&cb_data);
|
||||||
vpi_register_cb(&cb_data);
|
|
||||||
|
|
||||||
cb_data.reason = cbEndOfSimulation;
|
cb_data.reason = cbEndOfSimulation;
|
||||||
cb_data.cb_rtn = EndOfSimulationCallback;
|
cb_data.cb_rtn = EndOfSimulationCallback;
|
||||||
vpi_register_cb(&cb_data);
|
test_vpi_register_cb(&cb_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef IS_VPI
|
// simulator entry
|
||||||
|
|
||||||
// icarus entry
|
|
||||||
void (*vlog_startup_routines[])(void) = {VPIRegister, 0};
|
void (*vlog_startup_routines[])(void) = {VPIRegister, 0};
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
int main(int argc, char** argv, char** env) {
|
|
||||||
double sim_time = 100;
|
|
||||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
|
||||||
|
|
||||||
bool cbs_called;
|
|
||||||
contextp->commandArgs(argc, argv);
|
|
||||||
// contextp->debug(9);
|
|
||||||
|
|
||||||
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
|
|
||||||
// Note null name - we're flattening it out
|
|
||||||
""}};
|
|
||||||
|
|
||||||
topp->clk = 1;
|
|
||||||
|
|
||||||
// StartOfSimulationCallback(nullptr);
|
|
||||||
VPIRegister();
|
|
||||||
|
|
||||||
VerilatedVpi::callCbs(cbStartOfSimulation);
|
|
||||||
|
|
||||||
topp->clk = 0;
|
|
||||||
topp->eval();
|
|
||||||
|
|
||||||
while (contextp->time() < sim_time && !contextp->gotFinish()) {
|
|
||||||
VerilatedVpi::callTimedCbs();
|
|
||||||
VerilatedVpi::callCbs(cbNextSimTime);
|
|
||||||
VerilatedVpi::callCbs(cbAtStartOfSimTime);
|
|
||||||
|
|
||||||
topp->eval();
|
|
||||||
|
|
||||||
VerilatedVpi::callValueCbs();
|
|
||||||
VerilatedVpi::callCbs(cbReadWriteSynch);
|
|
||||||
VerilatedVpi::callCbs(cbReadOnlySynch);
|
|
||||||
VerilatedVpi::callCbs(cbAtEndOfSimTime);
|
|
||||||
|
|
||||||
const uint64_t next_time = VerilatedVpi::cbNextDeadline();
|
|
||||||
if (next_time != -1) contextp->time(next_time);
|
|
||||||
if (verbose)
|
|
||||||
vpi_printf(const_cast<char*>("- [@%" PRId64 "] time change\n"), contextp->time());
|
|
||||||
if (next_time == -1 && !contextp->gotFinish()) {
|
|
||||||
if (got_error) {
|
|
||||||
vl_stop(__FILE__, __LINE__, "TOP-cpp");
|
|
||||||
} else {
|
|
||||||
VerilatedVpi::callCbs(cbEndOfSimulation);
|
|
||||||
contextp->gotFinish(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count updates on rising edge, so cycle through falling edge as well
|
|
||||||
topp->clk = !topp->clk;
|
|
||||||
topp->eval();
|
|
||||||
topp->clk = !topp->clk;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!contextp->gotFinish()) {
|
|
||||||
vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");
|
|
||||||
}
|
|
||||||
topp->final();
|
|
||||||
|
|
||||||
exit(0L);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,11 @@ import vltest_bootstrap
|
||||||
test.scenarios('simulator')
|
test.scenarios('simulator')
|
||||||
|
|
||||||
test.compile(make_top_shell=False,
|
test.compile(make_top_shell=False,
|
||||||
make_main=False,
|
|
||||||
make_pli=True,
|
make_pli=True,
|
||||||
verilator_flags2=["--exe --vpi", test.pli_filename],
|
verilator_flags2=["--binary --vpi", test.pli_filename],
|
||||||
iv_flags2=["-g2005-sv -D USE_VPI_NOT_DPI -DIVERILOG"],
|
iv_flags2=["-g2005-sv -D USE_VPI_NOT_DPI -DIVERILOG"],
|
||||||
v_flags2=["+define+USE_VPI_NOT_DPI"])
|
v_flags2=["+define+USE_VPI_NOT_DPI"])
|
||||||
|
|
||||||
test.execute(use_libvpi=True)
|
test.execute(check_finished=True, use_libvpi=True)
|
||||||
|
|
||||||
test.passes()
|
test.passes()
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,17 @@
|
||||||
// SPDX-License-Identifier: CC0-1.0
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
|
||||||
module t (/*AUTOARG*/
|
module t;
|
||||||
// Inputs
|
|
||||||
input clk
|
logic clk;
|
||||||
);
|
initial begin
|
||||||
|
clk = 0;
|
||||||
|
#10;
|
||||||
|
while ($time < 100) begin
|
||||||
|
clk = !clk;
|
||||||
|
#10;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
reg [31:0] count /*verilator public_flat_rd */;
|
reg [31:0] count /*verilator public_flat_rd */;
|
||||||
|
|
||||||
|
|
@ -21,4 +28,4 @@ module t (/*AUTOARG*/
|
||||||
count <= count + 2;
|
count <= count + 2;
|
||||||
end
|
end
|
||||||
|
|
||||||
endmodule : t
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ int _mon_check_unimpl(p_cb_data cb_data) {
|
||||||
|
|
||||||
handle = vpi_register_cb(NULL);
|
handle = vpi_register_cb(NULL);
|
||||||
CHECK_RESULT(handle, 0);
|
CHECK_RESULT(handle, 0);
|
||||||
s_cb_data cb_data_s;
|
s_cb_data cb_data_s{};
|
||||||
cb_data_s.reason = 0; // Bad
|
cb_data_s.reason = 0; // Bad
|
||||||
handle = vpi_register_cb(&cb_data_s);
|
handle = vpi_register_cb(&cb_data_s);
|
||||||
CHECK_RESULT(handle, 0);
|
CHECK_RESULT(handle, 0);
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,9 @@ if(NOT CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
add_link_options(-Wl,-U,__Z15vl_time_stamp64v,-U,__Z13sc_time_stampv)
|
add_link_options(
|
||||||
|
-Wl,-U,__Z15vl_time_stamp64v,-U,__Z13sc_time_stampv,-U,vlog_startup_routines
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
define_property(
|
define_property(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue