mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
Fix VPI one-time timed callbacks (#2778).
This commit is contained in:
parent
8918f17c17
commit
c2bdd06fcc
1
Changes
1
Changes
@ -19,6 +19,7 @@ Verilator 5.005 devel
|
||||
* Add IMPLICITSTATIC warning when a ftask/function is implicitly static (#3839). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Optimize expansion of extend operators.
|
||||
* Internal multithreading tests. [Mariusz Glebocki, et al, Antmicro Ltd]
|
||||
* Fix VPI one-time timed callbacks (#2778). [Marlon James, et al]
|
||||
* Fix initiation of function variables (#3815). [Dan Gisselquist]
|
||||
* Fix to zero possibly uninitialized bits in replications (#3815).
|
||||
* Fix crash in DFT due to width use after free (#3817) (#3820). [Jevin Sweval]
|
||||
|
@ -126,34 +126,18 @@ public:
|
||||
virtual PLI_INT32 dovpi_remove_cb() { return 0; }
|
||||
};
|
||||
|
||||
class VerilatedVpioTimedCb final : public VerilatedVpio {
|
||||
// A handle to a timed callback created with vpi_register_cb
|
||||
// User can call vpi_remove_cb or vpi_release_handle on it
|
||||
const uint64_t m_id; // Unique id/sequence number to find schedule's event
|
||||
const QData m_time;
|
||||
|
||||
public:
|
||||
VerilatedVpioTimedCb(uint64_t id, QData time)
|
||||
: m_id{id}
|
||||
, m_time{time} {}
|
||||
~VerilatedVpioTimedCb() override = default;
|
||||
static VerilatedVpioTimedCb* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioTimedCb*>(reinterpret_cast<VerilatedVpioTimedCb*>(h));
|
||||
}
|
||||
uint32_t type() const override { return vpiCallback; }
|
||||
PLI_INT32 dovpi_remove_cb() override;
|
||||
};
|
||||
|
||||
class VerilatedVpioReasonCb final : public VerilatedVpio {
|
||||
// A handle to a non-timed callback created with vpi_register_cb
|
||||
// A handle to a timed or non-timed callback created with vpi_register_cb
|
||||
// User can call vpi_remove_cb or vpi_release_handle on it
|
||||
const uint64_t m_id; // Unique id/sequence number to find schedule's event
|
||||
const QData m_time; // Scheduled time, or 0 = not timed
|
||||
const PLI_INT32 m_reason; // VPI callback reason code
|
||||
|
||||
public:
|
||||
// cppcheck-suppress uninitVar // m_value
|
||||
VerilatedVpioReasonCb(uint64_t id, PLI_INT32 reason)
|
||||
VerilatedVpioReasonCb(uint64_t id, QData time, PLI_INT32 reason)
|
||||
: m_id{id}
|
||||
, m_time{time}
|
||||
, m_reason{reason} {}
|
||||
~VerilatedVpioReasonCb() override = default;
|
||||
static VerilatedVpioReasonCb* castp(vpiHandle h) {
|
||||
@ -474,7 +458,7 @@ using VerilatedPliCb = PLI_INT32 (*)(struct t_cb_data*);
|
||||
|
||||
class VerilatedVpiCbHolder final {
|
||||
// Holds information needed to call a callback
|
||||
uint64_t m_id;
|
||||
uint64_t m_id; // Unique id/sequence number to find schedule's event, 0 = invalid
|
||||
s_cb_data m_cbData;
|
||||
s_vpi_value m_value;
|
||||
VerilatedVpioVar m_varo; // If a cbValueChange callback, the object we will return
|
||||
@ -517,11 +501,13 @@ class VerilatedVpiError;
|
||||
class VerilatedVpiImp final {
|
||||
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maximum callback reason
|
||||
using VpioCbList = std::list<VerilatedVpiCbHolder>;
|
||||
using VpioTimedCbs = std::map<std::pair<QData, uint64_t>, VerilatedVpiCbHolder>;
|
||||
using VpioFutureCbs = std::map<std::pair<QData, uint64_t>, VerilatedVpiCbHolder>;
|
||||
|
||||
// All only medium-speed, so use singleton function
|
||||
VpioCbList m_cbObjLists[CB_ENUM_MAX_VALUE]; // Callbacks for each supported reason
|
||||
VpioTimedCbs m_timedCbs; // Time based callbacks
|
||||
// Callbacks that are past or at current timestamp
|
||||
std::array<VpioCbList, CB_ENUM_MAX_VALUE> m_cbCurrentLists;
|
||||
VpioFutureCbs m_futureCbs; // Time based callbacks for future timestamps
|
||||
VpioFutureCbs m_nextCbs; // cbNextSimTime callbacks
|
||||
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
|
||||
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
|
||||
uint64_t m_nextCallbackId = 1; // Id to identify callback
|
||||
@ -535,7 +521,7 @@ public:
|
||||
static void assertOneCheck() { s().m_assertOne.check(); }
|
||||
static uint64_t nextCallbackId() { return ++s().m_nextCallbackId; }
|
||||
|
||||
static void cbReasonAdd(uint64_t id, const s_cb_data* cb_data_p) {
|
||||
static void cbCurrentAdd(uint64_t id, const s_cb_data* cb_data_p) {
|
||||
// The passed cb_data_p was property of the user, so need to recreate
|
||||
if (VL_UNCOVERABLE(cb_data_p->reason >= CB_ENUM_MAX_VALUE)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi bb reason too large");
|
||||
@ -544,82 +530,109 @@ public:
|
||||
cb_data_p->reason, id, cb_data_p->obj););
|
||||
VerilatedVpioVar* varop = nullptr;
|
||||
if (cb_data_p->reason == cbValueChange) varop = VerilatedVpioVar::castp(cb_data_p->obj);
|
||||
s().m_cbObjLists[cb_data_p->reason].emplace_back(id, cb_data_p, varop);
|
||||
s().m_cbCurrentLists[cb_data_p->reason].emplace_back(id, cb_data_p, varop);
|
||||
}
|
||||
static void cbTimedAdd(uint64_t id, const s_cb_data* cb_data_p, QData time) {
|
||||
static void cbFutureAdd(uint64_t id, const s_cb_data* cb_data_p, QData time) {
|
||||
// The passed cb_data_p was property of the user, so need to recreate
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" PRId64
|
||||
" delay=%" PRIu64 "\n",
|
||||
cb_data_p->reason, id, time););
|
||||
s().m_timedCbs.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::make_pair(time, id)),
|
||||
std::forward_as_tuple(id, cb_data_p, nullptr));
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" PRId64 " time=%" PRIu64
|
||||
" obj=%p\n",
|
||||
cb_data_p->reason, id, time, cb_data_p->obj););
|
||||
s().m_futureCbs.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::make_pair(time, id)),
|
||||
std::forward_as_tuple(id, cb_data_p, nullptr));
|
||||
}
|
||||
static void cbReasonRemove(uint64_t id, uint32_t reason) {
|
||||
static void cbNextAdd(uint64_t id, const s_cb_data* cb_data_p, QData time) {
|
||||
// The passed cb_data_p was property of the user, so need to recreate
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d(NEXT) id=%" PRId64
|
||||
" time=%" PRIu64 " obj=%p\n",
|
||||
cb_data_p->reason, id, time, cb_data_p->obj););
|
||||
s().m_nextCbs.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::make_pair(time, id)),
|
||||
std::forward_as_tuple(id, cb_data_p, nullptr));
|
||||
}
|
||||
static void cbReasonRemove(uint64_t id, uint32_t reason, QData time) {
|
||||
// Id might no longer exist, if already removed due to call after event, or teardown
|
||||
VpioCbList& cbObjList = s().m_cbObjLists[reason];
|
||||
// We do not remove it now as we may be iterating the list,
|
||||
// instead set to nullptr and will cleanup later
|
||||
for (auto& ir : cbObjList) {
|
||||
if (ir.id() == id) ir.invalidate();
|
||||
// Remove from cbCurrent queue
|
||||
for (auto& ir : s().m_cbCurrentLists[reason]) {
|
||||
if (ir.id() == id) {
|
||||
ir.invalidate();
|
||||
return; // Once found, it won't also be in m_futureCbs
|
||||
}
|
||||
}
|
||||
{ // Remove from cbFuture queue
|
||||
const auto it = s().m_futureCbs.find(std::make_pair(time, id));
|
||||
if (it != s().m_futureCbs.end()) {
|
||||
it->second.invalidate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
{ // Remove from cbNext
|
||||
const auto it = s().m_nextCbs.find(std::make_pair(time, id));
|
||||
if (it != s().m_nextCbs.end()) {
|
||||
it->second.invalidate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void cbTimedRemove(uint64_t id, QData time) {
|
||||
// Id might no longer exist, if already removed due to call after event, or teardown
|
||||
const auto it = s().m_timedCbs.find(std::make_pair(time, id));
|
||||
if (VL_LIKELY(it != s().m_timedCbs.end())) it->second.invalidate();
|
||||
}
|
||||
static void callTimedCbs() VL_MT_UNSAFE_ONE {
|
||||
assertOneCheck();
|
||||
static void moveFutureCbs() VL_MT_UNSAFE_ONE {
|
||||
// For any events past current time, move from cbFuture queue to cbCurrent queue
|
||||
if (s().m_futureCbs.empty() && s().m_nextCbs.empty()) return;
|
||||
// VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: moveFutureCbs\n"); dumpCbs(); );
|
||||
const QData time = VL_TIME_Q();
|
||||
for (auto it = s().m_timedCbs.begin(); it != s().m_timedCbs.end();) {
|
||||
if (VL_UNLIKELY(it->first.first <= time)) {
|
||||
VerilatedVpiCbHolder& ho = it->second;
|
||||
const auto last_it = it;
|
||||
++it;
|
||||
if (VL_UNLIKELY(!ho.invalid())) {
|
||||
VL_DEBUG_IF_PLI(
|
||||
VL_DBG_MSGF("- vpi: timed_callback id=%" PRId64 "\n", ho.id()););
|
||||
ho.invalidate(); // Timed callbacks are one-shot
|
||||
(ho.cb_rtnp())(ho.cb_datap());
|
||||
}
|
||||
s().m_timedCbs.erase(last_it);
|
||||
} else {
|
||||
++it;
|
||||
for (auto it = s().m_futureCbs.begin(); //
|
||||
VL_UNLIKELY(it != s().m_futureCbs.end() && it->first.first <= time);) {
|
||||
VerilatedVpiCbHolder& hor = it->second;
|
||||
const auto last_it = it;
|
||||
++it;
|
||||
if (VL_UNLIKELY(!hor.invalid())) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: moveFutureCbs id=%" PRId64 "\n", hor.id()););
|
||||
s().m_cbCurrentLists[hor.cb_datap()->reason].emplace_back(hor);
|
||||
}
|
||||
s().m_futureCbs.erase(last_it);
|
||||
}
|
||||
for (auto it = s().m_nextCbs.begin(); //
|
||||
VL_UNLIKELY(it != s().m_nextCbs.end() && it->first.first < time);) {
|
||||
VerilatedVpiCbHolder& hor = it->second;
|
||||
const auto last_it = it;
|
||||
++it;
|
||||
if (VL_UNLIKELY(!hor.invalid())) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: moveFutureCbs id=%" PRId64 "\n", hor.id()););
|
||||
s().m_cbCurrentLists[hor.cb_datap()->reason].emplace_back(hor);
|
||||
}
|
||||
s().m_nextCbs.erase(last_it);
|
||||
}
|
||||
}
|
||||
static QData cbNextDeadline() {
|
||||
const auto it = s().m_timedCbs.cbegin();
|
||||
if (VL_LIKELY(it != s().m_timedCbs.cend())) return it->first.first;
|
||||
const auto it = s().m_futureCbs.cbegin();
|
||||
if (VL_LIKELY(it != s().m_futureCbs.cend())) return it->first.first;
|
||||
return ~0ULL; // maxquad
|
||||
}
|
||||
static bool callCbs(const uint32_t reason) VL_MT_UNSAFE_ONE {
|
||||
VpioCbList& cbObjList = s().m_cbObjLists[reason];
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: callCbs reason=%u\n", reason););
|
||||
assertOneCheck();
|
||||
moveFutureCbs();
|
||||
if (s().m_cbCurrentLists[reason].empty()) return false;
|
||||
// Iterate on old list, making new list empty, to prevent looping over newly added elements
|
||||
VpioCbList cbObjList;
|
||||
std::swap(s().m_cbCurrentLists[reason], cbObjList);
|
||||
bool called = false;
|
||||
if (cbObjList.empty()) return called;
|
||||
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
|
||||
for (auto it = cbObjList.begin(); true;) {
|
||||
for (VerilatedVpiCbHolder& ihor : cbObjList) {
|
||||
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
|
||||
const bool was_last = it == last;
|
||||
if (VL_UNLIKELY(it->invalid())) { // Deleted earlier, cleanup
|
||||
it = cbObjList.erase(it);
|
||||
if (was_last) break;
|
||||
continue;
|
||||
if (VL_LIKELY(!ihor.invalid())) { // Not deleted earlier
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: reason_callback reason=%d id=%" PRId64 "\n",
|
||||
reason, ihor.id()););
|
||||
ihor.invalidate(); // Timed callbacks are one-shot
|
||||
(ihor.cb_rtnp())(ihor.cb_datap());
|
||||
called = true;
|
||||
}
|
||||
VerilatedVpiCbHolder& ho = *it;
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: reason_callback reason=%d id=%" PRId64 "\n",
|
||||
reason, ho.id()););
|
||||
(ho.cb_rtnp())(ho.cb_datap());
|
||||
called = true;
|
||||
if (was_last) break;
|
||||
++it;
|
||||
}
|
||||
return called;
|
||||
}
|
||||
static bool callValueCbs() VL_MT_UNSAFE_ONE {
|
||||
assertOneCheck();
|
||||
VpioCbList& cbObjList = s().m_cbObjLists[cbValueChange];
|
||||
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
|
||||
bool called = false;
|
||||
std::unordered_set<VerilatedVpioVar*> update; // set of objects to update after callbacks
|
||||
if (cbObjList.empty()) return called;
|
||||
@ -658,7 +671,7 @@ public:
|
||||
}
|
||||
return called;
|
||||
}
|
||||
|
||||
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
||||
static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info
|
||||
};
|
||||
|
||||
@ -742,23 +755,21 @@ public:
|
||||
//======================================================================
|
||||
// VerilatedVpi implementation
|
||||
|
||||
void VerilatedVpi::callTimedCbs() VL_MT_UNSAFE_ONE { VerilatedVpiImp::callTimedCbs(); }
|
||||
|
||||
bool VerilatedVpi::callValueCbs() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::callValueCbs(); }
|
||||
|
||||
bool VerilatedVpi::callCbs(uint32_t reason) VL_MT_UNSAFE_ONE {
|
||||
return VerilatedVpiImp::callCbs(reason);
|
||||
}
|
||||
|
||||
// Historical, before we had multiple kinds of timed callbacks
|
||||
void VerilatedVpi::callTimedCbs() VL_MT_UNSAFE_ONE { VerilatedVpiImp::callCbs(cbAfterDelay); }
|
||||
|
||||
bool VerilatedVpi::callValueCbs() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::callValueCbs(); }
|
||||
|
||||
QData VerilatedVpi::cbNextDeadline() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::cbNextDeadline(); }
|
||||
|
||||
PLI_INT32 VerilatedVpioTimedCb::dovpi_remove_cb() {
|
||||
VerilatedVpiImp::cbTimedRemove(m_id, m_time);
|
||||
delete this; // IEEE 37.2.2 a vpi_remove_cb does a vpi_release_handle
|
||||
return 1;
|
||||
}
|
||||
void VerilatedVpi::dumpCbs() VL_MT_UNSAFE_ONE { VerilatedVpiImp::dumpCbs(); }
|
||||
|
||||
PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
|
||||
VerilatedVpiImp::cbReasonRemove(m_id, m_reason);
|
||||
VerilatedVpiImp::cbReasonRemove(m_id, m_reason, m_time);
|
||||
delete this; // IEEE 37.2.2 a vpi_remove_cb does a vpi_release_handle
|
||||
return 1;
|
||||
}
|
||||
@ -766,6 +777,42 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
|
||||
//======================================================================
|
||||
// VerilatedVpiImp implementation
|
||||
|
||||
void VerilatedVpiImp::dumpCbs() VL_MT_UNSAFE_ONE {
|
||||
assertOneCheck();
|
||||
VL_DBG_MSGF("- vpi: dumpCbs\n");
|
||||
for (uint32_t reason = 0; reason < CB_ENUM_MAX_VALUE; ++reason) {
|
||||
VpioCbList& cbObjList = s().m_cbCurrentLists[reason];
|
||||
for (auto& ho : cbObjList) {
|
||||
if (VL_UNLIKELY(!ho.invalid())) {
|
||||
VL_DBG_MSGF("- vpi: reason=%d=%s id=%" PRId64 "\n", reason,
|
||||
VerilatedVpiError::strFromVpiCallbackReason(reason), ho.id());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& ifuture : s().m_nextCbs) {
|
||||
const QData time = ifuture.first.first;
|
||||
const uint64_t id = ifuture.first.second;
|
||||
VerilatedVpiCbHolder& ho = ifuture.second;
|
||||
if (VL_UNLIKELY(!ho.invalid())) {
|
||||
VL_DBG_MSGF("- vpi: time=%" PRId64 "(NEXT) reason=%d=%s id=%" PRId64 "\n", time,
|
||||
ho.cb_datap()->reason,
|
||||
VerilatedVpiError::strFromVpiCallbackReason(ho.cb_datap()->reason),
|
||||
ho.id());
|
||||
}
|
||||
}
|
||||
for (auto& ifuture : s().m_futureCbs) {
|
||||
const QData time = ifuture.first.first;
|
||||
const uint64_t id = ifuture.first.second;
|
||||
VerilatedVpiCbHolder& ho = ifuture.second;
|
||||
if (VL_UNLIKELY(!ho.invalid())) {
|
||||
VL_DBG_MSGF("- vpi: time=%" PRId64 " reason=%d=%s id=%" PRId64 "\n", time,
|
||||
ho.cb_datap()->reason,
|
||||
VerilatedVpiError::strFromVpiCallbackReason(ho.cb_datap()->reason),
|
||||
ho.id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError;
|
||||
@ -1306,34 +1353,54 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "%s : callback data pointer is null", __func__);
|
||||
return nullptr;
|
||||
}
|
||||
switch (cb_data_p->reason) {
|
||||
case cbAfterDelay: {
|
||||
QData time = 0;
|
||||
if (cb_data_p->time) time = VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
|
||||
const QData abstime = VL_TIME_Q() + time;
|
||||
const PLI_INT32 reason = cb_data_p->reason;
|
||||
switch (reason) {
|
||||
case cbAfterDelay: // FALLTHRU // One-shot; time relative
|
||||
case cbAtEndOfSimTime: // FALLTHRU // One-shot; time absolute; supported via vlt_main.cpp
|
||||
case cbAtStartOfSimTime: // FALLTHRU // One-shot; time absolute; supported via vlt_main.cpp
|
||||
case cbReadOnlySynch: // FALLTHRU // One-shot; time relative; supported via vlt_main.cpp
|
||||
case cbReadWriteSynch: { // One-shot; time relative; supported via vlt_main.cpp
|
||||
const bool abs = reason == cbAtStartOfSimTime || reason == cbAtEndOfSimTime;
|
||||
const QData time = VL_TIME_Q();
|
||||
QData abstime = 0;
|
||||
if (cb_data_p->time) {
|
||||
if (abs) {
|
||||
abstime = VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
|
||||
} else {
|
||||
abstime = time + VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
|
||||
}
|
||||
}
|
||||
const uint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||
VerilatedVpioTimedCb* const vop = new VerilatedVpioTimedCb{id, abstime};
|
||||
VerilatedVpiImp::cbTimedAdd(id, cb_data_p, abstime);
|
||||
VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, abstime, reason};
|
||||
if (abstime <= time) {
|
||||
VerilatedVpiImp::cbCurrentAdd(id, cb_data_p);
|
||||
} else {
|
||||
VerilatedVpiImp::cbFutureAdd(id, cb_data_p, abstime);
|
||||
}
|
||||
return vop->castVpiHandle();
|
||||
}
|
||||
case cbReadWriteSynch: // FALLTHRU // Supported via vlt_main.cpp
|
||||
case cbReadOnlySynch: // FALLTHRU // Supported via vlt_main.cpp
|
||||
case cbNextSimTime: // FALLTHRU // Supported via vlt_main.cpp
|
||||
case cbStartOfSimulation: // FALLTHRU // Supported via vlt_main.cpp
|
||||
case cbEndOfSimulation: // FALLTHRU // Supported via vlt_main.cpp
|
||||
case cbValueChange: // FALLTHRU // Supported via vlt_main.cpp
|
||||
case cbPLIError: // FALLTHRU // NOP, but need to return handle, so make object
|
||||
case cbNextSimTime: { // One-shot; time always next; supported via vlt_main.cpp
|
||||
const QData time = VL_TIME_Q();
|
||||
const uint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||
VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, 0, reason};
|
||||
VerilatedVpiImp::cbNextAdd(id, cb_data_p, time);
|
||||
return vop->castVpiHandle();
|
||||
}
|
||||
case cbEndOfSimulation: // FALLTHRU // One-shot; time ignored; supported via vlt_main.cpp
|
||||
case cbEnterInteractive: // FALLTHRU // NOP, but need to return handle, so make object
|
||||
case cbExitInteractive: // FALLTHRU // NOP, but need to return handle, so make object
|
||||
case cbInteractiveScopeChange: { // FALLTHRU // NOP, but need to return handle, so make object
|
||||
case cbInteractiveScopeChange: // FALLTHRU // NOP, but need to return handle, so make object
|
||||
case cbPLIError: // FALLTHRU // NOP, but need to return handle, so make object
|
||||
case cbStartOfSimulation: // FALLTHRU // One-shot; time ignored; supported via vlt_main.cpp
|
||||
case cbValueChange: { // Multi-shot; supported via vlt_main.cpp
|
||||
const uint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||
VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, cb_data_p->reason};
|
||||
VerilatedVpiImp::cbReasonAdd(id, cb_data_p);
|
||||
VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, 0, reason};
|
||||
VerilatedVpiImp::cbCurrentAdd(id, cb_data_p);
|
||||
return vop->castVpiHandle();
|
||||
}
|
||||
default:
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported callback type %s", __func__,
|
||||
VerilatedVpiError::strFromVpiCallbackReason(cb_data_p->reason));
|
||||
VerilatedVpiError::strFromVpiCallbackReason(reason));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,8 @@ public:
|
||||
/// Returns time of the next registered VPI callback, or
|
||||
/// ~(0ULL) if none are registered
|
||||
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
|
||||
/// Debug dump of callbacks
|
||||
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
||||
|
||||
// Self test, for internal use only
|
||||
static void selfTest() VL_MT_UNSAFE_ONE;
|
||||
|
342
test_regress/t/t_vpi_onetime_cbs.cpp
Normal file
342
test_regress/t/t_vpi_onetime_cbs.cpp
Normal file
@ -0,0 +1,342 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2021 by Wilson Snyder and Marlon James. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
// Setup multiple one-time callbacks with different time delays.
|
||||
// Ensure they are not called before the delay has elapsed.
|
||||
|
||||
#ifdef IS_VPI
|
||||
|
||||
#include "vpi_user.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#else
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_vpi.h"
|
||||
|
||||
#include "Vt_vpi_onetime_cbs.h"
|
||||
|
||||
#endif
|
||||
|
||||
#include "TestSimulator.h"
|
||||
#include "TestVpi.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
typedef struct {
|
||||
PLI_UINT32 count;
|
||||
PLI_UINT32* exp_times;
|
||||
PLI_UINT32 number_of_exp_times;
|
||||
} cb_stats;
|
||||
|
||||
static cb_stats CallbackStats[cbAtEndOfSimTime + 1];
|
||||
|
||||
bool got_error = false;
|
||||
|
||||
static vpiHandle ValueHandle, ToggleHandle, ClockHandle;
|
||||
|
||||
#ifdef TEST_VERBOSE
|
||||
bool verbose = true;
|
||||
#else
|
||||
bool verbose = false;
|
||||
#endif
|
||||
|
||||
#define CHECK_RESULT_NZ(got) \
|
||||
if (!(got)) { \
|
||||
printf("%%Error: %s:%d: GOT = NULL EXP = !NULL\n", __FILE__, __LINE__); \
|
||||
got_error = true; \
|
||||
return __LINE__; \
|
||||
}
|
||||
|
||||
// Use cout to avoid issues with %d/%lx etc
|
||||
#define CHECK_RESULT(got, exp) \
|
||||
if ((got) != (exp)) { \
|
||||
std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << ": GOT = " << (got) \
|
||||
<< " EXP = " << (exp) << std::endl; \
|
||||
got_error = true; \
|
||||
return __LINE__; \
|
||||
}
|
||||
|
||||
#define STRINGIFY_CB_CASE(_cb) \
|
||||
case _cb: return #_cb
|
||||
|
||||
static const char* cb_reason_to_string(int cb_name) {
|
||||
switch (cb_name) {
|
||||
STRINGIFY_CB_CASE(cbAtStartOfSimTime);
|
||||
STRINGIFY_CB_CASE(cbReadWriteSynch);
|
||||
STRINGIFY_CB_CASE(cbReadOnlySynch);
|
||||
STRINGIFY_CB_CASE(cbNextSimTime);
|
||||
STRINGIFY_CB_CASE(cbStartOfSimulation);
|
||||
STRINGIFY_CB_CASE(cbEndOfSimulation);
|
||||
STRINGIFY_CB_CASE(cbAtEndOfSimTime);
|
||||
default: return "Unsupported callback";
|
||||
}
|
||||
}
|
||||
|
||||
#undef STRINGIFY_CB_CASE
|
||||
|
||||
bool cb_time_is_delay(int cb_name) {
|
||||
// For some callbacks, time is interpreted as a delay from current time
|
||||
// instead of an absolute time
|
||||
if (cb_name == cbReadOnlySynch || cb_name == cbReadWriteSynch) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
// forward declaration
|
||||
static PLI_INT32 TheCallback(s_cb_data* data);
|
||||
|
||||
static PLI_INT32 AtEndOfSimTimeCallback(s_cb_data* data) {
|
||||
s_vpi_time t;
|
||||
|
||||
cb_stats* stats = &CallbackStats[data->reason];
|
||||
|
||||
t.type = vpiSimTime;
|
||||
vpi_get_time(0, &t);
|
||||
|
||||
if (verbose) vpi_printf(const_cast<char*>("- [@%d] AtEndOfSimTime Callback\n"), t.low);
|
||||
|
||||
CHECK_RESULT(t.low, stats->exp_times[stats->count]);
|
||||
stats->count += 1;
|
||||
|
||||
s_cb_data cb_data;
|
||||
s_vpi_time time = {vpiSimTime, 0, 417, 0}; // non-zero time to check that it's ignored
|
||||
cb_data.time = &time;
|
||||
cb_data.reason = cbNextSimTime;
|
||||
cb_data.cb_rtn = TheCallback;
|
||||
vpiHandle Handle = vpi_register_cb(&cb_data);
|
||||
CHECK_RESULT_NZ(Handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PLI_INT32 TheCallback(s_cb_data* data) {
|
||||
s_vpi_time t;
|
||||
|
||||
cb_stats* stats = &CallbackStats[data->reason];
|
||||
|
||||
t.type = vpiSimTime;
|
||||
vpi_get_time(0, &t);
|
||||
|
||||
if (verbose) {
|
||||
vpi_printf(const_cast<char*>("- [@%d] %s Callback\n"), t.low,
|
||||
cb_reason_to_string(data->reason));
|
||||
}
|
||||
|
||||
CHECK_RESULT(t.low, stats->exp_times[stats->count]);
|
||||
stats->count += 1;
|
||||
|
||||
if (stats->count >= stats->number_of_exp_times) return 0;
|
||||
|
||||
s_cb_data cb_data;
|
||||
PLI_UINT32 next_time;
|
||||
|
||||
if (data->reason == cbNextSimTime) {
|
||||
// if a cbNextSimTime calback is scheduled from
|
||||
// another cbNextSimTime callback, it will
|
||||
// be called in the same timestep, so we need
|
||||
// to delay registering
|
||||
next_time = t.low;
|
||||
cb_data.reason = cbAtEndOfSimTime;
|
||||
cb_data.cb_rtn = AtEndOfSimTimeCallback;
|
||||
} else {
|
||||
next_time = stats->exp_times[stats->count];
|
||||
if (cb_time_is_delay(data->reason)) { next_time -= t.low; }
|
||||
cb_data.reason = data->reason;
|
||||
cb_data.cb_rtn = TheCallback;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
vpi_printf(const_cast<char*>("- [@%d] Registering %s Callback with time = %d\n"), t.low,
|
||||
cb_reason_to_string(cb_data.reason), next_time);
|
||||
}
|
||||
|
||||
s_vpi_time time = {vpiSimTime, 0, next_time, 0};
|
||||
cb_data.time = &time;
|
||||
vpiHandle Handle = vpi_register_cb(&cb_data);
|
||||
CHECK_RESULT_NZ(Handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PLI_INT32 StartOfSimulationCallback(s_cb_data* data) {
|
||||
s_cb_data cb_data;
|
||||
s_vpi_time timerec = {vpiSimTime, 0, 0, 0};
|
||||
|
||||
s_vpi_time t;
|
||||
t.type = vpiSimTime;
|
||||
vpi_get_time(0, &t);
|
||||
|
||||
if (verbose) vpi_printf(const_cast<char*>("- [@%d] cbStartOfSimulation Callback\n"), t.low);
|
||||
|
||||
CHECK_RESULT(t.low, CallbackStats[data->reason].exp_times[0]);
|
||||
CallbackStats[data->reason].count += 1;
|
||||
|
||||
cb_data.time = &timerec;
|
||||
cb_data.value = 0;
|
||||
cb_data.user_data = 0;
|
||||
cb_data.obj = 0;
|
||||
cb_data.cb_rtn = TheCallback;
|
||||
|
||||
CallbackStats[cbAtStartOfSimTime].exp_times = new PLI_UINT32[3]{5, 15, 20};
|
||||
CallbackStats[cbAtStartOfSimTime].number_of_exp_times = 3;
|
||||
timerec.low = 5;
|
||||
cb_data.reason = cbAtStartOfSimTime;
|
||||
vpiHandle ASOSHandle = vpi_register_cb(&cb_data);
|
||||
CHECK_RESULT_NZ(ASOSHandle);
|
||||
|
||||
CallbackStats[cbReadWriteSynch].exp_times = new PLI_UINT32[3]{6, 16, 21};
|
||||
CallbackStats[cbReadWriteSynch].number_of_exp_times = 3;
|
||||
timerec.low = 6;
|
||||
cb_data.reason = cbReadWriteSynch;
|
||||
vpiHandle RWHandle = vpi_register_cb(&cb_data);
|
||||
CHECK_RESULT_NZ(RWHandle);
|
||||
|
||||
CallbackStats[cbReadOnlySynch].exp_times = new PLI_UINT32[3]{7, 17, 22};
|
||||
CallbackStats[cbReadOnlySynch].number_of_exp_times = 3;
|
||||
timerec.low = 7;
|
||||
cb_data.reason = cbReadOnlySynch;
|
||||
vpiHandle ROHandle = vpi_register_cb(&cb_data);
|
||||
CHECK_RESULT_NZ(ROHandle);
|
||||
|
||||
CallbackStats[cbNextSimTime].exp_times = new PLI_UINT32[9]{5, 6, 7, 15, 16, 17, 20, 21, 22};
|
||||
CallbackStats[cbNextSimTime].number_of_exp_times = 9;
|
||||
timerec.low = 8;
|
||||
cb_data.reason = cbNextSimTime;
|
||||
vpiHandle NSTHandle = vpi_register_cb(&cb_data);
|
||||
CHECK_RESULT_NZ(NSTHandle);
|
||||
|
||||
CallbackStats[cbAtEndOfSimTime].exp_times = new PLI_UINT32[8]{5, 6, 7, 15, 16, 17, 20, 21};
|
||||
CallbackStats[cbAtEndOfSimTime].number_of_exp_times = 8;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int EndOfSimulationCallback(p_cb_data cb_data) {
|
||||
s_vpi_time t;
|
||||
t.type = vpiSimTime;
|
||||
vpi_get_time(0, &t);
|
||||
|
||||
(void)cb_data; // Unused
|
||||
|
||||
if (verbose) vpi_printf(const_cast<char*>("- [@%d] cbEndOfSimulation Callback\n"), t.low);
|
||||
|
||||
CHECK_RESULT(t.low, CallbackStats[cbEndOfSimulation].exp_times[0]);
|
||||
CallbackStats[cbEndOfSimulation].count += 1;
|
||||
|
||||
CHECK_RESULT(CallbackStats[cbStartOfSimulation].count, 1);
|
||||
CHECK_RESULT(CallbackStats[cbAtStartOfSimTime].count, 3);
|
||||
CHECK_RESULT(CallbackStats[cbNextSimTime].count, 9);
|
||||
CHECK_RESULT(CallbackStats[cbReadWriteSynch].count, 3);
|
||||
CHECK_RESULT(CallbackStats[cbReadOnlySynch].count, 3);
|
||||
CHECK_RESULT(CallbackStats[cbAtEndOfSimTime].count, 8);
|
||||
CHECK_RESULT(CallbackStats[cbEndOfSimulation].count, 1);
|
||||
|
||||
if (!got_error) { printf("*-* All Finished *-*\n"); }
|
||||
return 0;
|
||||
}
|
||||
|
||||
// cver entry
|
||||
static void VPIRegister(void) {
|
||||
// Clear stats
|
||||
for (int cb = 1; cb <= cbAtEndOfSimTime; cb++) { CallbackStats[cb].count = 0; }
|
||||
CallbackStats[cbStartOfSimulation].exp_times = new PLI_UINT32(0);
|
||||
CallbackStats[cbEndOfSimulation].exp_times = new PLI_UINT32(22);
|
||||
s_cb_data cb_data;
|
||||
s_vpi_time timerec = {vpiSuppressTime, 0, 0, 0};
|
||||
|
||||
cb_data.time = &timerec;
|
||||
cb_data.value = 0;
|
||||
cb_data.user_data = 0;
|
||||
cb_data.obj = 0;
|
||||
cb_data.reason = cbStartOfSimulation;
|
||||
cb_data.cb_rtn = StartOfSimulationCallback;
|
||||
|
||||
vpi_register_cb(&cb_data);
|
||||
|
||||
cb_data.reason = cbEndOfSimulation;
|
||||
cb_data.cb_rtn = EndOfSimulationCallback;
|
||||
vpi_register_cb(&cb_data);
|
||||
}
|
||||
|
||||
#ifdef IS_VPI
|
||||
|
||||
// icarus entry
|
||||
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
|
@ -8,16 +8,20 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
scenarios(vlt => 1);
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
make_pli => 1,
|
||||
verilator_flags2 => ["--exe --vpi $Self->{t_dir}/$Self->{name}.cpp"],
|
||||
iv_flags2 => ["-g2005-sv -D USE_VPI_NOT_DPI -DIVERILOG"],
|
||||
v_flags2 => ["+define+USE_VPI_NOT_DPI"],
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
use_libvpi => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
@ -9,11 +9,19 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifdef IS_VPI
|
||||
|
||||
#include "vpi_user.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#else
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_vpi.h"
|
||||
|
||||
#include "Vt_vpi_cbs_called.h"
|
||||
#include "vpi_user.h"
|
||||
#include "Vt_vpi_repetitive_cbs.h"
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
@ -25,8 +33,7 @@
|
||||
#include "TestSimulator.h"
|
||||
#include "TestVpi.h"
|
||||
|
||||
const std::vector<int> cbs_to_test{cbReadWriteSynch, cbReadOnlySynch, cbNextSimTime,
|
||||
cbStartOfSimulation, cbEndOfSimulation, cbValueChange};
|
||||
const std::vector<int> cbs_to_test{cbValueChange};
|
||||
|
||||
enum CallbackState { PRE_REGISTER, ACTIVE, ACTIVE_AGAIN, REM_REREG_ACTIVE, POST_REMOVE };
|
||||
const std::vector<CallbackState> cb_states{PRE_REGISTER, ACTIVE, ACTIVE_AGAIN, REM_REREG_ACTIVE,
|
||||
@ -46,17 +53,29 @@ std::vector<CallbackState>::const_iterator state_iter;
|
||||
|
||||
bool got_error = false;
|
||||
|
||||
#ifdef IS_VPI
|
||||
vpiHandle clk_h;
|
||||
#endif
|
||||
|
||||
#ifdef TEST_VERBOSE
|
||||
bool verbose = true;
|
||||
#else
|
||||
bool verbose = false;
|
||||
#endif
|
||||
|
||||
#ifdef IS_VPI
|
||||
#define END_TEST \
|
||||
vpi_control(vpiStop); \
|
||||
return 0;
|
||||
#else
|
||||
#define END_TEST return __LINE__;
|
||||
#endif
|
||||
|
||||
#define CHECK_RESULT_NZ(got) \
|
||||
if (!(got)) { \
|
||||
printf("%%Error: %s:%d: GOT = NULL EXP = !NULL\n", __FILE__, __LINE__); \
|
||||
got_error = true; \
|
||||
return __LINE__; \
|
||||
END_TEST \
|
||||
}
|
||||
|
||||
// Use cout to avoid issues with %d/%lx etc
|
||||
@ -65,7 +84,7 @@ bool verbose = false;
|
||||
std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << ": GOT = " << (got) \
|
||||
<< " EXP = " << (exp) << std::endl; \
|
||||
got_error = true; \
|
||||
return __LINE__; \
|
||||
END_TEST \
|
||||
}
|
||||
|
||||
#define STRINGIFY_CB_CASE(_cb) \
|
||||
@ -73,11 +92,6 @@ bool verbose = false;
|
||||
|
||||
static const char* cb_reason_to_string(int cb_name) {
|
||||
switch (cb_name) {
|
||||
STRINGIFY_CB_CASE(cbReadWriteSynch);
|
||||
STRINGIFY_CB_CASE(cbReadOnlySynch);
|
||||
STRINGIFY_CB_CASE(cbNextSimTime);
|
||||
STRINGIFY_CB_CASE(cbStartOfSimulation);
|
||||
STRINGIFY_CB_CASE(cbEndOfSimulation);
|
||||
STRINGIFY_CB_CASE(cbValueChange);
|
||||
default: return "Unsupported callback";
|
||||
}
|
||||
@ -86,6 +100,7 @@ static const char* cb_reason_to_string(int cb_name) {
|
||||
#undef STRINGIFY_CB_CASE
|
||||
|
||||
static int the_callback(p_cb_data cb_data) {
|
||||
vpi_printf(const_cast<char*>(" The callback\n"));
|
||||
callback_counts[cb_data->reason] = callback_counts[cb_data->reason] + 1;
|
||||
return 0;
|
||||
}
|
||||
@ -98,7 +113,12 @@ static int register_cb(const int next_state) {
|
||||
cb_data_testcase.cb_rtn = the_callback;
|
||||
cb_data_testcase.reason = cb;
|
||||
|
||||
#ifdef IS_VPI
|
||||
TestVpiHandle count_h = vpi_handle_by_name(const_cast<char*>("t.count"),
|
||||
0); // Needed in this scope as is in cb_data
|
||||
#else
|
||||
TestVpiHandle count_h = VPI_HANDLE("count"); // Needed in this scope as is in cb_data
|
||||
#endif
|
||||
CHECK_RESULT_NZ(count_h);
|
||||
if (cb == cbValueChange) {
|
||||
v.format = vpiSuppressVal;
|
||||
@ -170,9 +190,11 @@ static int test_callbacks(p_cb_data cb_data) {
|
||||
auto exp_count = callback_expected_counts[cb];
|
||||
CHECK_RESULT(count, exp_count);
|
||||
|
||||
#if !defined(IS_VPI)
|
||||
bool called = callbacks_called[cb];
|
||||
bool exp_called = callbacks_expected_called[cb];
|
||||
CHECK_RESULT(called, exp_called);
|
||||
#endif
|
||||
|
||||
// Update expected values based on state of callback in next time through main loop
|
||||
reset_expected();
|
||||
@ -213,7 +235,7 @@ static int test_callbacks(p_cb_data cb_data) {
|
||||
cb_data_n.reason = cbAfterDelay;
|
||||
t1.type = vpiSimTime;
|
||||
t1.high = 0;
|
||||
t1.low = 1;
|
||||
t1.low = 10;
|
||||
cb_data_n.time = &t1;
|
||||
cb_data_n.cb_rtn = test_callbacks;
|
||||
TestVpiHandle vh_test_cb = vpi_register_cb(&cb_data_n);
|
||||
@ -223,7 +245,34 @@ static int test_callbacks(p_cb_data cb_data) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int register_test_callback() {
|
||||
#ifdef IS_VPI
|
||||
static int toggle_clock(p_cb_data data) {
|
||||
s_vpi_value val;
|
||||
s_vpi_time time = {vpiSimTime, 0, 0, 0};
|
||||
|
||||
val.format = vpiIntVal;
|
||||
vpi_get_value(clk_h, &val);
|
||||
val.value.integer = !val.value.integer;
|
||||
vpi_put_value(clk_h, &val, &time, vpiInertialDelay);
|
||||
|
||||
s_vpi_time cur_time = {vpiSimTime, 0, 0, 0};
|
||||
vpi_get_time(0, &cur_time);
|
||||
|
||||
if (cur_time.low < 100 && !got_error) {
|
||||
t_cb_data cb_data;
|
||||
bzero(&cb_data, sizeof(cb_data));
|
||||
time.low = 5;
|
||||
cb_data.reason = cbAfterDelay;
|
||||
cb_data.time = &time;
|
||||
cb_data.cb_rtn = toggle_clock;
|
||||
vpi_register_cb(&cb_data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int register_test_callback(p_cb_data data) {
|
||||
t_cb_data cb_data;
|
||||
bzero(&cb_data, sizeof(cb_data));
|
||||
s_vpi_time t1;
|
||||
@ -233,7 +282,7 @@ static int register_test_callback() {
|
||||
cb_data.reason = cbAfterDelay;
|
||||
t1.type = vpiSimTime;
|
||||
t1.high = 0;
|
||||
t1.low = 1;
|
||||
t1.low = 10;
|
||||
cb_data.time = &t1;
|
||||
cb_data.cb_rtn = test_callbacks;
|
||||
TestVpiHandle vh_test_cb = vpi_register_cb(&cb_data);
|
||||
@ -242,9 +291,49 @@ static int register_test_callback() {
|
||||
cb_iter = cbs_to_test.cbegin();
|
||||
state_iter = cb_states.cbegin();
|
||||
|
||||
#ifdef IS_VPI
|
||||
t1.low = 1;
|
||||
cb_data.cb_rtn = toggle_clock;
|
||||
TestVpiHandle vh_toggle_cb = vpi_register_cb(&cb_data);
|
||||
CHECK_RESULT_NZ(vh_toggle_cb);
|
||||
|
||||
clk_h = vpi_handle_by_name(const_cast<char*>("t.clk"), 0);
|
||||
CHECK_RESULT_NZ(clk_h);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef IS_VPI
|
||||
|
||||
static int end_of_sim_cb(p_cb_data cb_data) {
|
||||
if (!got_error) { fprintf(stdout, "*-* All Finished *-*\n"); }
|
||||
return 0;
|
||||
}
|
||||
|
||||
// cver entry
|
||||
void vpi_compat_bootstrap(void) {
|
||||
t_cb_data cb_data;
|
||||
bzero(&cb_data, sizeof(cb_data));
|
||||
{
|
||||
vpi_printf(const_cast<char*>("register start-of-sim callback\n"));
|
||||
cb_data.reason = cbStartOfSimulation;
|
||||
cb_data.time = 0;
|
||||
cb_data.cb_rtn = register_test_callback;
|
||||
vpi_register_cb(&cb_data);
|
||||
}
|
||||
{
|
||||
cb_data.reason = cbEndOfSimulation;
|
||||
cb_data.time = 0;
|
||||
cb_data.cb_rtn = end_of_sim_cb;
|
||||
vpi_register_cb(&cb_data);
|
||||
}
|
||||
}
|
||||
// icarus entry
|
||||
void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0};
|
||||
|
||||
#else
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||
|
||||
@ -258,7 +347,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
if (verbose) VL_PRINTF("-- { Sim Time %" PRId64 " } --\n", contextp->time());
|
||||
|
||||
register_test_callback();
|
||||
register_test_callback(nullptr);
|
||||
|
||||
topp->eval();
|
||||
topp->clk = 0;
|
||||
@ -274,17 +363,22 @@ int main(int argc, char** argv) {
|
||||
|
||||
for (const auto& i : cbs_to_test) {
|
||||
if (verbose) {
|
||||
VL_PRINTF(" Calling %s (%d) callbacks\t", cb_reason_to_string(i), i);
|
||||
VL_PRINTF(" Calling %s (%d) callbacks\n >>>>\n", cb_reason_to_string(i),
|
||||
i);
|
||||
}
|
||||
if (i == cbValueChange) {
|
||||
cbs_called = VerilatedVpi::callValueCbs();
|
||||
} else {
|
||||
cbs_called = VerilatedVpi::callCbs(i);
|
||||
}
|
||||
if (verbose) VL_PRINTF(" - any callbacks called? %s\n", cbs_called ? "YES" : "NO");
|
||||
if (verbose)
|
||||
VL_PRINTF(" <<<<\n Any callbacks called? %s\n", cbs_called ? "YES" : "NO");
|
||||
callbacks_called[i] = cbs_called;
|
||||
}
|
||||
|
||||
// Always calling this so we can get code coverage on the Verilator debug routine
|
||||
VerilatedVpi::dumpCbs();
|
||||
|
||||
VerilatedVpi::callTimedCbs();
|
||||
|
||||
int64_t next_time = VerilatedVpi::cbNextDeadline();
|
||||
@ -314,3 +408,5 @@ int main(int argc, char** argv) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
28
test_regress/t/t_vpi_repetitive_cbs.pl
Executable file
28
test_regress/t/t_vpi_repetitive_cbs.pl
Executable file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2020 by Wilson Snyder and Marlon James. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
make_pli => 1,
|
||||
verilator_flags2 => ["--exe --vpi $Self->{t_dir}/$Self->{name}.cpp"],
|
||||
iv_flags2 => ["-g2005-sv -D USE_VPI_NOT_DPI -DIVERILOG"],
|
||||
v_flags2 => ["+define+USE_VPI_NOT_DPI"],
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
use_libvpi => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
24
test_regress/t/t_vpi_repetitive_cbs.v
Normal file
24
test_regress/t/t_vpi_repetitive_cbs.v
Normal file
@ -0,0 +1,24 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2020 Wilson Snyder and Marlon James.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
input clk
|
||||
);
|
||||
|
||||
reg [31:0] count /*verilator public_flat_rd */;
|
||||
|
||||
// Test loop
|
||||
initial begin
|
||||
count = 0;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
count <= count + 2;
|
||||
end
|
||||
|
||||
endmodule : t
|
@ -188,7 +188,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
uint64_t sim_time = 1100;
|
||||
contextp->commandArgs(argc, argv);
|
||||
contextp->debug(0);
|
||||
// contextp->debug(9);
|
||||
// we're going to be checking for these errors do don't crash out
|
||||
contextp->fatalOnVpiError(0);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user