Fix vpi_release_handle to be called implicitly per IEEE (#2706).

This commit is contained in:
Wilson Snyder 2020-12-18 21:16:57 -05:00
parent 43811821d6
commit 1e34ae31d2
9 changed files with 375 additions and 234 deletions

View File

@ -27,6 +27,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
**** Fix genblk naming to match IEEE (#2686). [tinshark]
**** Fix vpi_release_handle to be called implicitly per IEEE (#2706).
* Verilator 4.106 2020-12-02

View File

@ -93,8 +93,12 @@ public:
"vpi_release_handle() called on same object twice, or on non-Verilator "
"VPI object");
}
#ifdef VL_VPI_IMMEDIATE_FREE // Define to aid in finding leaky handles
::operator delete(oldp);
#else
*(reinterpret_cast<void**>(oldp)) = t_freeHead;
t_freeHead = oldp;
#endif
}
// MEMBERS
static VerilatedVpio* castp(vpiHandle h) {
@ -109,32 +113,44 @@ public:
virtual vluint32_t size() const { return 0; }
virtual const VerilatedRange* rangep() const { return nullptr; }
virtual vpiHandle dovpi_scan() { return nullptr; }
virtual PLI_INT32 dovpi_remove_cb() { return 0; }
};
typedef PLI_INT32 (*VerilatedPliCb)(struct t_cb_data*);
class VerilatedVpioCb final : public VerilatedVpio {
t_cb_data m_cbData;
s_vpi_value m_value;
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
vluint64_t m_id; // Unique id/sequence number to find schedule's event
QData m_time;
public:
// cppcheck-suppress uninitVar // m_value
VerilatedVpioCb(const t_cb_data* cbDatap, QData time)
: m_cbData(*cbDatap)
, m_time(time) { // Need () or GCC 4.8 false warning
m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
m_cbData.value = &m_value;
}
virtual ~VerilatedVpioCb() override = default;
static VerilatedVpioCb* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioCb*>(reinterpret_cast<VerilatedVpio*>(h));
VerilatedVpioTimedCb(vluint64_t id, QData time)
: m_id(id)
, m_time{time} {}
virtual ~VerilatedVpioTimedCb() override = default;
static VerilatedVpioTimedCb* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioTimedCb*>(reinterpret_cast<VerilatedVpioTimedCb*>(h));
}
virtual vluint32_t type() const override { return vpiCallback; }
vluint32_t reason() const { return m_cbData.reason; }
VerilatedPliCb cb_rtnp() const { return m_cbData.cb_rtn; }
t_cb_data* cb_datap() { return &(m_cbData); }
QData time() const { return m_time; }
virtual PLI_INT32 dovpi_remove_cb() override;
};
class VerilatedVpioReasonCb final : public VerilatedVpio {
// A handle to a non-timed callback created with vpi_register_cb
// User can call vpi_remove_cb or vpi_release_handle on it
vluint64_t m_id; // Unique id/sequence number to find schedule's event
PLI_INT32 m_reason; // VPI callback reason code
public:
// cppcheck-suppress uninitVar // m_value
VerilatedVpioReasonCb(vluint64_t id, PLI_INT32 reason)
: m_id(id)
, m_reason{reason} {}
virtual ~VerilatedVpioReasonCb() override = default;
static VerilatedVpioReasonCb* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioReasonCb*>(reinterpret_cast<VerilatedVpioReasonCb*>(h));
}
virtual vluint32_t type() const override { return vpiCallback; }
virtual PLI_INT32 dovpi_remove_cb() override;
};
class VerilatedVpioConst final : public VerilatedVpio {
@ -159,7 +175,6 @@ public:
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
: m_varp{varp}
, m_scopep{scopep} {}
virtual ~VerilatedVpioParam() override = default;
static VerilatedVpioParam* castp(vpiHandle h) {
@ -206,7 +221,10 @@ public:
}
virtual vluint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
if (m_done) return nullptr;
if (VL_UNLIKELY(m_done)) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
m_done = true;
return ((new VerilatedVpioRange(m_range))->castVpiHandle());
}
@ -230,16 +248,16 @@ public:
};
class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpio {
const VerilatedVar* m_varp;
const VerilatedScope* m_scopep;
const VerilatedVar* m_varp = nullptr;
const VerilatedScope* m_scopep = nullptr;
vluint8_t* m_prevDatap = nullptr; // Previous value of data, for cbValueChange
union {
vluint8_t u8[4];
vluint32_t u32;
} m_mask; // memoized variable mask
vluint32_t m_entSize; // memoized variable size
vluint32_t m_entSize = 0; // memoized variable size
protected:
void* m_varDatap; // varp()->datap() adjusted for array entries
void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries
vlsint32_t m_index = 0;
const VerilatedRange& get_range() const {
// Determine number of dimensions and return outermost
@ -254,6 +272,19 @@ public:
m_entSize = varp->entSize();
m_varDatap = varp->datap();
}
VerilatedVpioVar(const VerilatedVpioVar* varp) {
if (varp) {
m_varp = varp->m_varp;
m_scopep = varp->m_scopep;
m_mask.u32 = varp->m_mask.u32;
m_entSize = varp->m_entSize;
m_varDatap = varp->m_varDatap;
m_index = varp->m_index;
// Not copying m_prevDatap, must be nullptr
} else {
m_mask.u32 = 0;
}
}
virtual ~VerilatedVpioVar() override {
if (m_prevDatap) VL_DO_CLEAR(delete[] m_prevDatap, m_prevDatap = nullptr);
}
@ -331,13 +362,18 @@ public:
m_it = varsp->begin();
m_started = true;
} else if (VL_UNLIKELY(m_it == varsp->end())) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
} else {
++m_it;
}
if (m_it == varsp->end()) return nullptr;
if (VL_UNLIKELY(m_it == varsp->end())) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
return ((new VerilatedVpioVar(&(m_it->second), m_scopep))->castVpiHandle());
}
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr; // End of list - only one deep
}
};
@ -364,9 +400,11 @@ public:
if (!(m_done = (m_iteration == m_varp->unpacked().left()))) m_iteration += m_direction;
}
virtual vpiHandle dovpi_scan() override {
vpiHandle result;
if (m_done) return nullptr;
result = vpi_handle_by_index(m_handle, m_iteration);
if (VL_UNLIKELY(m_done)) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
vpiHandle result = vpi_handle_by_index(m_handle, m_iteration);
iterationInc();
return result;
}
@ -406,7 +444,10 @@ public:
}
virtual vluint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
if (m_it == m_vec->end()) return nullptr;
if (m_it == m_vec->end()) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
const VerilatedScope* modp = *m_it++;
return (new VerilatedVpioModule(modp))->castVpiHandle();
}
@ -414,10 +455,42 @@ public:
//======================================================================
typedef PLI_INT32 (*VerilatedPliCb)(struct t_cb_data*);
class VerilatedVpiCbHolder final {
// Holds information needed to call a callback
vluint64_t m_id;
s_cb_data m_cbData;
s_vpi_value m_value;
VerilatedVpioVar m_varo; // If a cbValueChange callback, the object we will return
public:
// cppcheck-suppress uninitVar // m_value
VerilatedVpiCbHolder(vluint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
: m_id(id)
, m_cbData(*cbDatap)
, m_varo(varop) {
m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
m_cbData.value = &m_value;
if (varop) {
m_cbData.obj = m_varo.castVpiHandle();
m_varo.createPrevDatap();
} else {
m_cbData.obj = NULL;
}
}
~VerilatedVpiCbHolder() = default;
VerilatedPliCb cb_rtnp() const { return m_cbData.cb_rtn; }
s_cb_data* cb_datap() { return &m_cbData; }
vluint64_t id() const { return m_id; }
bool invalid() const { return !m_id; }
void invalidate() { m_id = 0; }
};
struct VerilatedVpiTimedCbsCmp {
/// Ordering sets keyed by time, then callback descriptor
bool operator()(const std::pair<QData, VerilatedVpioCb*>& a,
const std::pair<QData, VerilatedVpioCb*>& b) const {
/// Ordering sets keyed by time, then callback unique id
bool operator()(const std::pair<QData, vluint64_t>& a,
const std::pair<QData, vluint64_t>& b) const {
if (a.first < b.first) return true;
if (a.first > b.first) return false;
return a.second < b.second;
@ -428,55 +501,70 @@ class VerilatedVpiError;
class VerilatedVpiImp final {
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maxium callback reason
typedef std::list<VerilatedVpioCb*> VpioCbList;
typedef std::set<std::pair<QData, VerilatedVpioCb*>, VerilatedVpiTimedCbsCmp> VpioTimedCbs;
typedef std::list<VerilatedVpiCbHolder> VpioCbList;
typedef std::map<std::pair<QData, vluint64_t>, VerilatedVpiCbHolder> VpioTimedCbs;
VpioCbList m_cbObjLists[CB_ENUM_MAX_VALUE]; // Callbacks for each supported reason
VpioTimedCbs m_timedCbs; // Time based callbacks
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
vluint64_t m_nextCallbackId = 1; // Id to identify callback
static VerilatedVpiImp s_s; // Singleton
public:
VerilatedVpiImp() = default;
~VerilatedVpiImp() = default;
static void assertOneCheck() { s_s.m_assertOne.check(); }
static void cbReasonAdd(VerilatedVpioCb* vop) {
if (vop->reason() == cbValueChange) {
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) {
varop->createPrevDatap();
}
}
if (VL_UNCOVERABLE(vop->reason() >= CB_ENUM_MAX_VALUE)) {
static vluint64_t nextCallbackId() { return ++s_s.m_nextCallbackId; }
static void cbReasonAdd(vluint64_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");
}
s_s.m_cbObjLists[vop->reason()].push_back(vop);
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" VL_PRI64 "d obj=%p\n",
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_s.m_cbObjLists[cb_data_p->reason].emplace_back(id, cb_data_p, varop);
}
static void cbTimedAdd(VerilatedVpioCb* vop) { s_s.m_timedCbs.emplace(vop->time(), vop); }
static void cbReasonRemove(VerilatedVpioCb* cbp) {
VpioCbList& cbObjList = s_s.m_cbObjLists[cbp->reason()];
static void cbTimedAdd(vluint64_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=%" VL_PRI64
"d delay=%" VL_PRI64 "u\n",
cb_data_p->reason, id, time););
s_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));
}
static void cbReasonRemove(vluint64_t id, vluint32_t reason) {
// Id might no longer exist, if already removed due to call after event, or teardown
VpioCbList& cbObjList = s_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 == cbp) ir = nullptr;
if (ir.id() == id) ir.invalidate();
}
}
static void cbTimedRemove(VerilatedVpioCb* cbp) {
const auto it = s_s.m_timedCbs.find(std::make_pair(cbp->time(), cbp));
if (VL_LIKELY(it != s_s.m_timedCbs.end())) s_s.m_timedCbs.erase(it);
static void cbTimedRemove(vluint64_t id, QData time) {
// Id might no longer exist, if already removed due to call after event, or teardown
const auto it = s_s.m_timedCbs.find(std::make_pair(time, id));
if (VL_LIKELY(it != s_s.m_timedCbs.end())) it->second.invalidate();
}
static void callTimedCbs() VL_MT_UNSAFE_ONE {
assertOneCheck();
QData time = VL_TIME_Q();
for (auto it = s_s.m_timedCbs.begin(); it != s_s.m_timedCbs.end();) {
if (VL_UNLIKELY(it->first <= time)) {
VerilatedVpioCb* vop = it->second;
if (VL_UNLIKELY(it->first.first <= time)) {
VerilatedVpiCbHolder& ho = it->second;
const auto last_it = it;
++it; // Timed callbacks are one-shot
++it;
if (VL_UNLIKELY(!ho.invalid())) {
VL_DEBUG_IF_PLI(
VL_DBG_MSGF("- vpi: timed_callback id=%" VL_PRI64 "d\n", ho.id()););
ho.invalidate(); // Timed callbacks are one-shot
(ho.cb_rtnp())(ho.cb_datap());
}
s_s.m_timedCbs.erase(last_it);
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: timed_callback %p\n", vop););
(vop->cb_rtnp())(vop->cb_datap());
} else {
++it;
}
@ -484,7 +572,7 @@ public:
}
static QData cbNextDeadline() {
const auto it = s_s.m_timedCbs.cbegin();
if (VL_LIKELY(it != s_s.m_timedCbs.cend())) return it->first;
if (VL_LIKELY(it != s_s.m_timedCbs.cend())) return it->first.first;
return ~0ULL; // maxquad
}
static bool callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE {
@ -495,16 +583,18 @@ public:
for (auto it = cbObjList.begin(); true;) {
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
bool was_last = it == last;
if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup
if (VL_UNLIKELY(it->invalid())) { // Deleted earlier, cleanup
it = cbObjList.erase(it);
if (was_last) break;
continue;
}
VerilatedVpioCb* vop = *it++;
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: reason_callback %d %p\n", reason, vop););
(vop->cb_rtnp())(vop->cb_datap());
VerilatedVpiCbHolder& ho = *it;
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: reason_callback reason=%d id=%" VL_PRI64 "d\n",
reason, ho.id()););
(ho.cb_rtnp())(ho.cb_datap());
called = true;
if (was_last) break;
++it;
}
return called;
}
@ -519,24 +609,25 @@ public:
for (auto it = cbObjList.begin(); true;) {
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
bool was_last = it == last;
if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup
if (VL_UNLIKELY(it->invalid())) { // Deleted earlier, cleanup
it = cbObjList.erase(it);
if (was_last) break;
continue;
}
VerilatedVpioCb* vop = *it++;
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) {
VerilatedVpiCbHolder& ho = *it++;
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
void* newDatap = varop->varDatap();
void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
varop->fullname(), *((CData*)newDatap),
*((CData*)prevDatap), newDatap, prevDatap););
if (memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %p %s v[0]=%d\n", vop,
varop->fullname(), *((CData*)newDatap)););
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64
"d %s v[0]=%d\n",
ho.id(), varop->fullname(), *((CData*)newDatap)););
update.insert(varop);
vpi_get_value(vop->cb_datap()->obj, vop->cb_datap()->value);
(vop->cb_rtnp())(vop->cb_datap());
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
(ho.cb_rtnp())(ho.cb_datap());
called = true;
}
}
@ -635,6 +726,17 @@ bool VerilatedVpi::callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE {
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;
}
PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
VerilatedVpiImp::cbReasonRemove(m_id, m_reason);
delete this; // IEEE 37.2.2 a vpi_remove_cb does a vpi_release_handle
return 1;
}
//======================================================================
// VerilatedVpiImp implementation
@ -1037,6 +1139,9 @@ void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
// callback related
vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
// Returns handle so user can remove the callback, user must vpi_release_handle it
// Don't confuse with the callback-activated t_cb_data object handle
// which is the object causing the callback rather than the callback itself
VerilatedVpiImp::assertOneCheck();
_VL_VPI_ERROR_RESET();
// cppcheck-suppress nullPointer
@ -1048,10 +1153,10 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
case cbAfterDelay: {
QData time = 0;
if (cb_data_p->time) time = _VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
VerilatedVpioCb* vop = new VerilatedVpioCb(cb_data_p, VL_TIME_Q() + time);
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb %d %p delay=%" VL_PRI64 "u\n",
cb_data_p->reason, vop, time););
VerilatedVpiImp::cbTimedAdd(vop);
QData abstime = VL_TIME_Q() + time;
vluint64_t id = VerilatedVpiImp::nextCallbackId();
VerilatedVpioTimedCb* vop = new VerilatedVpioTimedCb{id, abstime};
VerilatedVpiImp::cbTimedAdd(id, cb_data_p, abstime);
return vop->castVpiHandle();
}
case cbReadWriteSynch: // FALLTHRU // Supported via vlt_main.cpp
@ -1064,9 +1169,9 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
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
VerilatedVpioCb* vop = new VerilatedVpioCb(cb_data_p, 0);
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb %d %p\n", cb_data_p->reason, vop););
VerilatedVpiImp::cbReasonAdd(vop);
vluint64_t id = VerilatedVpiImp::nextCallbackId();
VerilatedVpioReasonCb* vop = new VerilatedVpioReasonCb{id, cb_data_p->reason};
VerilatedVpiImp::cbReasonAdd(id, cb_data_p);
return vop->castVpiHandle();
}
default:
@ -1079,15 +1184,10 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
PLI_INT32 vpi_remove_cb(vpiHandle cb_obj) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_remove_cb %p\n", cb_obj););
VerilatedVpiImp::assertOneCheck();
VerilatedVpioCb* vop = VerilatedVpioCb::castp(cb_obj);
_VL_VPI_ERROR_RESET();
VerilatedVpio* vop = VerilatedVpio::castp(cb_obj);
if (VL_UNLIKELY(!vop)) return 0;
if (vop->cb_datap()->reason == cbAfterDelay) {
VerilatedVpiImp::cbTimedRemove(vop);
} else {
VerilatedVpiImp::cbReasonRemove(vop);
}
return 1;
return vop->dovpi_remove_cb();
}
void vpi_get_cb_info(vpiHandle /*object*/, p_cb_data /*cb_data_p*/) { _VL_VPI_UNIMP(); }
@ -2023,8 +2123,6 @@ PLI_INT32 vpi_release_handle(vpiHandle object) {
VerilatedVpio* vop = VerilatedVpio::castp(object);
_VL_VPI_ERROR_RESET();
if (VL_UNLIKELY(!vop)) return 0;
vpi_remove_cb(object); // May not be a callback, but that's ok
VL_DO_DANGLING(delete vop, vop);
return 1;
}

View File

@ -15,33 +15,30 @@
class TestVpiHandle {
/// For testing, etc, wrap vpiHandle in an auto-releasing class
vpiHandle m_handle;
bool m_free;
vpiHandle m_handle = NULL;
bool m_freeit = true;
public:
TestVpiHandle()
: m_handle(NULL)
, m_free(true) {}
TestVpiHandle() {}
TestVpiHandle(vpiHandle h)
: m_handle(h)
, m_free(true) {}
~TestVpiHandle() {
if (m_handle && m_free) {
// Below not VL_DO_DANGLING so is portable
{
vpi_release_handle(m_handle);
m_handle = NULL;
}
}
}
: m_handle(h) {}
~TestVpiHandle() { release(); }
operator vpiHandle() const { return m_handle; }
inline TestVpiHandle& operator=(vpiHandle h) {
release();
m_handle = h;
return *this;
}
void release() {
if (m_handle && m_freeit) {
// Below not VL_DO_DANGLING so is portable
vpi_release_handle(m_handle);
m_handle = NULL;
}
}
// Freed by another action e.g. vpi_scan; so empty and don't free again
void freed() {
m_handle = NULL;
m_free = false;
m_freeit = false;
}
};

View File

@ -26,8 +26,8 @@
bool got_error = false;
vpiHandle vh_value_cb = 0;
vpiHandle vh_rw_cb = 0;
TestVpiHandle vh_value_cb;
TestVpiHandle vh_rw_cb;
unsigned int last_value_cb_time = 0;
unsigned int last_rw_cb_time = 0;
@ -78,6 +78,7 @@ static void reregister_value_cb() {
if (vh_value_cb) {
if (verbose) { vpi_printf(const_cast<char*>("- Removing cbValueChange callback\n")); }
int ret = vpi_remove_cb(vh_value_cb);
vh_value_cb.freed();
CHECK_RESULT(ret, 1);
if (verbose) {
@ -93,7 +94,7 @@ static void reregister_value_cb() {
cb_data_testcase.cb_rtn = the_value_callback;
cb_data_testcase.reason = cbValueChange;
vpiHandle vh1 = VPI_HANDLE("count");
TestVpiHandle vh1 = VPI_HANDLE("count");
CHECK_RESULT_NZ(vh1);
s_vpi_value v;
@ -110,6 +111,7 @@ static void reregister_rw_cb() {
if (vh_rw_cb) {
if (verbose) { vpi_printf(const_cast<char*>("- Removing cbReadWriteSynch callback\n")); }
int ret = vpi_remove_cb(vh_rw_cb);
vh_rw_cb.freed();
CHECK_RESULT(ret, 1);
if (verbose) {
@ -140,8 +142,8 @@ static void register_filler_cb() {
cb_data_1.cb_rtn = the_filler_callback;
cb_data_1.reason = cbReadWriteSynch;
vpiHandle vh1 = vpi_register_cb(&cb_data_1);
CHECK_RESULT_NZ(vh1);
TestVpiHandle cb_data_1_h = vpi_register_cb(&cb_data_1);
CHECK_RESULT_NZ(cb_data_1_h);
if (verbose) {
vpi_printf(const_cast<char*>("- Registering filler cbValueChange callback\n"));
@ -151,7 +153,7 @@ static void register_filler_cb() {
cb_data_2.cb_rtn = the_filler_callback;
cb_data_2.reason = cbValueChange;
vpiHandle vh2 = VPI_HANDLE("count");
TestVpiHandle vh2 = VPI_HANDLE("count");
CHECK_RESULT_NZ(vh2);
s_vpi_value v;
@ -160,8 +162,8 @@ static void register_filler_cb() {
cb_data_2.obj = vh2;
cb_data_2.value = &v;
vpiHandle vh3 = vpi_register_cb(&cb_data_2);
CHECK_RESULT_NZ(vh3);
TestVpiHandle cb_data_2_h = vpi_register_cb(&cb_data_2);
CHECK_RESULT_NZ(cb_data_2_h);
}
double sc_time_stamp() { return main_time; }

View File

@ -32,7 +32,7 @@ const std::vector<CallbackState> cb_states{PRE_REGISTER, ACTIVE, ACTIVE_AGAIN, R
POST_REMOVE};
#define CB_COUNT cbAtEndOfSimTime + 1
vpiHandle vh_registered_cbs[CB_COUNT] = {0};
TestVpiHandle vh_registered_cbs[CB_COUNT] = {0};
unsigned int callback_counts[CB_COUNT] = {0};
unsigned int callback_expected_counts[CB_COUNT] = {0};
@ -43,7 +43,6 @@ bool callbacks_expected_called[CB_COUNT] = {false};
std::vector<int>::const_iterator cb_iter;
std::vector<CallbackState>::const_iterator state_iter;
vpiHandle vh_test_cb = 0;
unsigned int main_time = 0;
bool got_error = false;
@ -94,18 +93,17 @@ static int the_callback(p_cb_data cb_data) {
static int register_cb(const int next_state) {
int cb = *cb_iter;
t_cb_data cb_data_testcase;
s_vpi_value v; // Needed in this scope as is in cb_data
bzero(&cb_data_testcase, sizeof(cb_data_testcase));
cb_data_testcase.cb_rtn = the_callback;
cb_data_testcase.reason = cb;
TestVpiHandle count_h = VPI_HANDLE("count"); // Needed in this scope as is in cb_data
CHECK_RESULT_NZ(count_h);
if (cb == cbValueChange) {
vpiHandle vh1 = VPI_HANDLE("count");
CHECK_RESULT_NZ(vh1);
s_vpi_value v;
v.format = vpiSuppressVal;
cb_data_testcase.obj = vh1;
cb_data_testcase.obj = count_h;
cb_data_testcase.value = &v;
}
@ -117,6 +115,7 @@ static int register_cb(const int next_state) {
vpi_printf(const_cast<char*>(" - Registering callback %s\n"),
cb_reason_to_string(cb));
}
vh_registered_cbs[cb].release();
vh_registered_cbs[cb] = vpi_register_cb(&cb_data_testcase);
break;
}
@ -126,6 +125,7 @@ static int register_cb(const int next_state) {
cb_reason_to_string(cb));
}
int ret = vpi_remove_cb(vh_registered_cbs[cb]);
vh_registered_cbs[cb].freed();
CHECK_RESULT(ret, 1);
vh_registered_cbs[cb] = vpi_register_cb(&cb_data_testcase);
break;
@ -136,6 +136,7 @@ static int register_cb(const int next_state) {
cb_reason_to_string(cb));
}
int ret = vpi_remove_cb(vh_registered_cbs[cb]);
vh_registered_cbs[cb].freed();
CHECK_RESULT(ret, 1);
break;
}
@ -215,7 +216,7 @@ static int test_callbacks(p_cb_data cb_data) {
t1.low = 1;
cb_data_n.time = &t1;
cb_data_n.cb_rtn = test_callbacks;
vh_test_cb = vpi_register_cb(&cb_data_n);
TestVpiHandle vh_test_cb = vpi_register_cb(&cb_data_n);
CHECK_RESULT_NZ(vh_test_cb);
}
@ -235,7 +236,7 @@ static int register_test_callback() {
t1.low = 1;
cb_data.time = &t1;
cb_data.cb_rtn = test_callbacks;
vh_test_cb = vpi_register_cb(&cb_data);
TestVpiHandle vh_test_cb = vpi_register_cb(&cb_data);
CHECK_RESULT_NZ(vh_test_cb);
cb_iter = cbs_to_test.cbegin();
@ -250,7 +251,6 @@ int main(int argc, char** argv, char** env) {
double sim_time = 100;
bool cbs_called;
Verilated::commandArgs(argc, argv);
Verilated::debug(0);
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out

View File

@ -80,26 +80,32 @@ unsigned int main_time = 0;
#define CHECK_RESULT_CSTR_STRIP(got, exp) CHECK_RESULT_CSTR(got + strspn(got, " "), exp)
int _mon_check_range(TestVpiHandle& handle, int size, int left, int right) {
TestVpiHandle iter_h, left_h, right_h;
int _mon_check_range(const TestVpiHandle& handle, int size, int left, int right) {
s_vpi_value value;
value.format = vpiIntVal;
value.value.integer = 0;
// check size of object
int vpisize = vpi_get(vpiSize, handle);
CHECK_RESULT(vpisize, size);
// check left hand side of range
left_h = vpi_handle(vpiLeftRange, handle);
CHECK_RESULT_NZ(left_h);
vpi_get_value(left_h, &value);
CHECK_RESULT(value.value.integer, left);
int coherency = value.value.integer;
// check right hand side of range
right_h = vpi_handle(vpiRightRange, handle);
CHECK_RESULT_NZ(right_h);
vpi_get_value(right_h, &value);
CHECK_RESULT(value.value.integer, right);
coherency -= value.value.integer;
{
int vpisize = vpi_get(vpiSize, handle);
CHECK_RESULT(vpisize, size);
}
int coherency;
{
// check left hand side of range
TestVpiHandle left_h = vpi_handle(vpiLeftRange, handle);
CHECK_RESULT_NZ(left_h);
vpi_get_value(left_h, &value);
CHECK_RESULT(value.value.integer, left);
coherency = value.value.integer;
}
{
// check right hand side of range
TestVpiHandle right_h = vpi_handle(vpiRightRange, handle);
CHECK_RESULT_NZ(right_h);
vpi_get_value(right_h, &value);
CHECK_RESULT(value.value.integer, right);
coherency -= value.value.integer;
}
// calculate size & check
coherency = abs(coherency) + 1;
CHECK_RESULT(coherency, size);
@ -107,40 +113,46 @@ int _mon_check_range(TestVpiHandle& handle, int size, int left, int right) {
}
int _mon_check_memory() {
int cnt;
TestVpiHandle mem_h, lcl_h, side_h;
vpiHandle iter_h; // Icarus does not like auto free of iterator handles
s_vpi_value value;
value.format = vpiIntVal;
value.value.integer = 0;
s_vpi_error_info e;
vpi_printf((PLI_BYTE8*)"Check memory vpi ...\n");
mem_h = vpi_handle_by_name((PLI_BYTE8*)TestSimulator::rooted("mem0"), NULL);
TestVpiHandle mem_h = vpi_handle_by_name((PLI_BYTE8*)TestSimulator::rooted("mem0"), NULL);
CHECK_RESULT_NZ(mem_h);
// check type
int vpitype = vpi_get(vpiType, mem_h);
CHECK_RESULT(vpitype, vpiMemory);
{
// check type
int vpitype = vpi_get(vpiType, mem_h);
CHECK_RESULT(vpitype, vpiMemory);
}
if (int status = _mon_check_range(mem_h, 16, 16, 1)) return status;
// iterate and store
iter_h = vpi_iterate(vpiMemoryWord, mem_h);
cnt = 0;
while ((lcl_h = vpi_scan(iter_h))) {
value.value.integer = ++cnt;
vpi_put_value(lcl_h, &value, NULL, vpiNoDelay);
// check size and range
if (int status = _mon_check_range(lcl_h, 32, 31, 0)) return status;
{
TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h);
int cnt = 0;
while (TestVpiHandle lcl_h = vpi_scan(iter_h)) {
value.value.integer = ++cnt;
vpi_put_value(lcl_h, &value, NULL, vpiNoDelay);
// check size and range
if (int status = _mon_check_range(lcl_h, 32, 31, 0)) return status;
}
iter_h.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
CHECK_RESULT(cnt, 16); // should be 16 addresses
}
CHECK_RESULT(cnt, 16); // should be 16 addresses
// iterate and accumulate
iter_h = vpi_iterate(vpiMemoryWord, mem_h);
cnt = 0;
while ((lcl_h = vpi_scan(iter_h))) {
++cnt;
vpi_get_value(lcl_h, &value);
CHECK_RESULT(value.value.integer, cnt);
{
// iterate and accumulate
TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h);
int cnt = 0;
while (TestVpiHandle lcl_h = vpi_scan(iter_h)) {
++cnt;
vpi_get_value(lcl_h, &value);
CHECK_RESULT(value.value.integer, cnt);
}
iter_h.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
CHECK_RESULT(cnt, 16); // should be 16 addresses
}
CHECK_RESULT(cnt, 16); // should be 16 addresses
// don't care for non verilator
// (crashes on Icarus)
if (TestSimulator::is_icarus()) {
@ -148,40 +160,49 @@ int _mon_check_memory() {
TestSimulator::get_info().product);
return 0; // Ok
}
// make sure trying to get properties that don't exist
// doesn't crash
int should_be_0 = vpi_get(vpiSize, iter_h);
CHECK_RESULT(should_be_0, 0);
should_be_0 = vpi_get(vpiIndex, iter_h);
CHECK_RESULT(should_be_0, 0);
vpiHandle should_be_NULL = vpi_handle(vpiLeftRange, iter_h);
CHECK_RESULT(should_be_NULL, 0);
should_be_NULL = vpi_handle(vpiRightRange, iter_h);
CHECK_RESULT(should_be_NULL, 0);
should_be_NULL = vpi_handle(vpiScope, iter_h);
CHECK_RESULT(should_be_NULL, 0);
// check vpiRange
iter_h = vpi_iterate(vpiRange, mem_h);
CHECK_RESULT_NZ(iter_h);
lcl_h = vpi_scan(iter_h);
CHECK_RESULT_NZ(lcl_h);
side_h = vpi_handle(vpiLeftRange, lcl_h);
CHECK_RESULT_NZ(side_h);
vpi_get_value(side_h, &value);
CHECK_RESULT(value.value.integer, 16);
side_h = vpi_handle(vpiRightRange, lcl_h);
CHECK_RESULT_NZ(side_h);
vpi_get_value(side_h, &value);
CHECK_RESULT(value.value.integer, 1);
// iterator should exhaust after 1 dimension
lcl_h = vpi_scan(iter_h);
CHECK_RESULT(lcl_h, 0);
// check writing to vpiConstant
vpi_put_value(side_h, &value, NULL, vpiNoDelay);
CHECK_RESULT_NZ(vpi_chk_error(&e));
{
// make sure trying to get properties that don't exist
// doesn't crash
TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h);
int should_be_0 = vpi_get(vpiSize, iter_h);
CHECK_RESULT(should_be_0, 0);
should_be_0 = vpi_get(vpiIndex, iter_h);
CHECK_RESULT(should_be_0, 0);
vpiHandle should_be_NULL = vpi_handle(vpiLeftRange, iter_h);
CHECK_RESULT(should_be_NULL, 0);
should_be_NULL = vpi_handle(vpiRightRange, iter_h);
CHECK_RESULT(should_be_NULL, 0);
should_be_NULL = vpi_handle(vpiScope, iter_h);
CHECK_RESULT(should_be_NULL, 0);
}
{
// check vpiRange
TestVpiHandle iter_h = vpi_iterate(vpiRange, mem_h);
CHECK_RESULT_NZ(iter_h);
TestVpiHandle lcl_h = vpi_scan(iter_h);
CHECK_RESULT_NZ(lcl_h);
{
TestVpiHandle side_h = vpi_handle(vpiLeftRange, lcl_h);
CHECK_RESULT_NZ(side_h);
vpi_get_value(side_h, &value);
CHECK_RESULT(value.value.integer, 16);
}
{
TestVpiHandle side_h = vpi_handle(vpiRightRange, lcl_h);
CHECK_RESULT_NZ(side_h);
vpi_get_value(side_h, &value);
CHECK_RESULT(value.value.integer, 1);
// check writing to vpiConstant
vpi_put_value(side_h, &value, NULL, vpiNoDelay);
CHECK_RESULT_NZ(vpi_chk_error(&e));
}
{
// iterator should exhaust after 1 dimension
TestVpiHandle zero_h = vpi_scan(iter_h);
iter_h.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
CHECK_RESULT(zero_h, 0);
}
}
return 0; // Ok
}

View File

@ -106,7 +106,7 @@ static int _time_cb1(p_cb_data cb_data) {
t.low = 1;
cb_data_n.time = &t;
cb_data_n.cb_rtn = _time_cb1;
vpi_register_cb(&cb_data_n);
TestVpiHandle cb_data_n1_h = vpi_register_cb(&cb_data_n);
return 0;
}
@ -127,7 +127,7 @@ static int _time_cb2(p_cb_data cb_data) {
t.low = 1;
cb_data_n.time = &t;
cb_data_n.cb_rtn = _time_cb2;
vpi_register_cb(&cb_data_n);
TestVpiHandle cb_data_n2_h = vpi_register_cb(&cb_data_n);
return 0;
}
@ -143,7 +143,7 @@ static int _start_of_sim_cb(p_cb_data cb_data) {
t1.low = 3;
cb_data_n1.time = &t1;
cb_data_n1.cb_rtn = _time_cb1;
vpi_register_cb(&cb_data_n1);
TestVpiHandle cb_data_n1_h = vpi_register_cb(&cb_data_n1);
cb_data_n2.reason = cbAfterDelay;
t2.type = vpiSimTime;
@ -151,7 +151,7 @@ static int _start_of_sim_cb(p_cb_data cb_data) {
t2.low = 4;
cb_data_n2.time = &t2;
cb_data_n2.cb_rtn = _time_cb2;
vpi_register_cb(&cb_data_n2);
TestVpiHandle cb_data_n2_h = vpi_register_cb(&cb_data_n2);
callback_count_start_of_sim++;
return 0;
}
@ -177,12 +177,12 @@ void vpi_compat_bootstrap(void) {
cb_data.reason = cbStartOfSimulation;
cb_data.time = 0;
cb_data.cb_rtn = _start_of_sim_cb;
vpi_register_cb(&cb_data);
TestVpiHandle _start_of_sim_cb_h = 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);
TestVpiHandle _end_of_sim_cb_h = vpi_register_cb(&cb_data);
}
// icarus entry

View File

@ -49,6 +49,12 @@ unsigned int callback_count_strs_max = 500;
//======================================================================
#ifdef TEST_VERBOSE
bool verbose = true;
#else
bool verbose = false;
#endif
#define CHECK_RESULT_VH(got, exp) \
if ((got) != (exp)) { \
printf("%%Error: %s:%d: GOT = %p EXP = %p\n", FILENM, __LINE__, (got), (exp)); \
@ -138,17 +144,18 @@ int _mon_check_callbacks() {
cb_data.value = NULL;
cb_data.time = NULL;
vpiHandle vh = vpi_register_cb(&cb_data);
TestVpiHandle vh = vpi_register_cb(&cb_data);
CHECK_RESULT_NZ(vh);
PLI_INT32 status = vpi_remove_cb(vh);
vh.freed();
CHECK_RESULT_NZ(status);
return 0;
}
int _value_callback(p_cb_data cb_data) {
if (verbose) vpi_printf(const_cast<char*>(" _value_callback:\n"));
if (TestSimulator::is_verilator()) {
// this check only makes sense in Verilator
CHECK_RESULT(cb_data->value->value.integer + 10, main_time);
@ -178,50 +185,59 @@ int _value_callback_quad(p_cb_data cb_data) {
}
int _mon_check_value_callbacks() {
vpiHandle vh1 = VPI_HANDLE("count");
CHECK_RESULT_NZ(vh1);
s_vpi_value v;
v.format = vpiIntVal;
vpi_get_value(vh1, &v);
t_cb_data cb_data;
cb_data.reason = cbValueChange;
cb_data.cb_rtn = _value_callback;
cb_data.obj = vh1;
cb_data.value = &v;
cb_data.time = NULL;
vpiHandle vh = vpi_register_cb(&cb_data);
CHECK_RESULT_NZ(vh);
{
TestVpiHandle vh1 = VPI_HANDLE("count");
CHECK_RESULT_NZ(vh1);
vh1 = VPI_HANDLE("half_count");
CHECK_RESULT_NZ(vh1);
vpi_get_value(vh1, &v);
cb_data.value = &v;
cb_data.obj = vh1;
cb_data.cb_rtn = _value_callback;
cb_data.obj = vh1;
cb_data.cb_rtn = _value_callback_half;
if (verbose) vpi_printf(const_cast<char*>(" vpi_register_cb(_value_callback):\n"));
TestVpiHandle callback_h = vpi_register_cb(&cb_data);
CHECK_RESULT_NZ(callback_h);
}
{
TestVpiHandle vh1 = VPI_HANDLE("half_count");
CHECK_RESULT_NZ(vh1);
vh = vpi_register_cb(&cb_data);
CHECK_RESULT_NZ(vh);
cb_data.obj = vh1;
cb_data.cb_rtn = _value_callback_half;
vh1 = VPI_HANDLE("quads");
CHECK_RESULT_NZ(vh1);
TestVpiHandle callback_h = vpi_register_cb(&cb_data);
CHECK_RESULT_NZ(callback_h);
}
{
TestVpiHandle vh1 = VPI_HANDLE("quads");
CHECK_RESULT_NZ(vh1);
v.format = vpiVectorVal;
cb_data.obj = vh1;
cb_data.cb_rtn = _value_callback_quad;
v.format = vpiVectorVal;
cb_data.obj = vh1;
cb_data.cb_rtn = _value_callback_quad;
vh = vpi_register_cb(&cb_data);
CHECK_RESULT_NZ(vh);
TestVpiHandle callback_h = vpi_register_cb(&cb_data);
CHECK_RESULT_NZ(callback_h);
}
{
TestVpiHandle vh1 = VPI_HANDLE("quads");
CHECK_RESULT_NZ(vh1);
TestVpiHandle vh2 = vpi_handle_by_index(vh1, 2);
CHECK_RESULT_NZ(vh2);
vh1 = vpi_handle_by_index(vh1, 2);
CHECK_RESULT_NZ(vh1);
cb_data.obj = vh2;
cb_data.cb_rtn = _value_callback_quad;
cb_data.obj = vh1;
cb_data.cb_rtn = _value_callback_quad;
vh = vpi_register_cb(&cb_data);
CHECK_RESULT_NZ(vh);
TestVpiHandle callback_h = vpi_register_cb(&cb_data);
CHECK_RESULT_NZ(callback_h);
}
return 0;
}
@ -463,7 +479,10 @@ int _mon_check_putget_str(p_cb_data cb_data) {
s_vpi_vecval vector[4];
} value; // reference
} data[129];
if (cb_data) {
if (verbose) vpi_printf(const_cast<char*>(" _mon_check_putget_str callback:\n"));
// this is the callback
static unsigned int seed = 1;
s_vpi_time t;
@ -542,6 +561,7 @@ int _mon_check_putget_str(p_cb_data cb_data) {
}
if (++callback_count_strs == callback_count_strs_max) {
int success = vpi_remove_cb(cb);
cb.freed();
CHECK_RESULT_NZ(success);
};
} else {
@ -560,7 +580,7 @@ int _mon_check_putget_str(p_cb_data cb_data) {
static t_cb_data cb_data;
static s_vpi_value v;
static TestVpiHandle count_h = VPI_HANDLE("count");
TestVpiHandle count_h = VPI_HANDLE("count");
cb_data.reason = cbValueChange;
cb_data.cb_rtn = _mon_check_putget_str; // this function
@ -570,6 +590,7 @@ int _mon_check_putget_str(p_cb_data cb_data) {
v.format = vpiIntVal;
cb = vpi_register_cb(&cb_data);
// It is legal to free the callback handle immediately if not otherwise needed
CHECK_RESULT_NZ(cb);
}
return 0;

View File

@ -100,7 +100,7 @@ static int _start_of_sim_cb(p_cb_data cb_data) {
t.low = 0;
cb_data_n.time = &t;
cb_data_n.cb_rtn = _zero_time_cb;
vpi_register_cb(&cb_data_n);
TestVpiHandle _cb_data_n_h = vpi_register_cb(&cb_data_n);
callback_count_start_of_sim++;
return 0;
}
@ -127,12 +127,12 @@ void vpi_compat_bootstrap(void) {
cb_data.reason = cbStartOfSimulation;
cb_data.time = 0;
cb_data.cb_rtn = _start_of_sim_cb;
vpi_register_cb(&cb_data);
TestVpiHandle _start_of_sim_cb_h = 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);
TestVpiHandle _end_of_sim_cb_h = vpi_register_cb(&cb_data);
}
// icarus entry