Fix vpi_release_handle to be called implicitly per IEEE (#2706).
This commit is contained in:
parent
43811821d6
commit
1e34ae31d2
2
Changes
2
Changes
|
|
@ -27,6 +27,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
|
||||||
|
|
||||||
**** Fix genblk naming to match IEEE (#2686). [tinshark]
|
**** 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
|
* Verilator 4.106 2020-12-02
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,8 +93,12 @@ public:
|
||||||
"vpi_release_handle() called on same object twice, or on non-Verilator "
|
"vpi_release_handle() called on same object twice, or on non-Verilator "
|
||||||
"VPI object");
|
"VPI object");
|
||||||
}
|
}
|
||||||
|
#ifdef VL_VPI_IMMEDIATE_FREE // Define to aid in finding leaky handles
|
||||||
|
::operator delete(oldp);
|
||||||
|
#else
|
||||||
*(reinterpret_cast<void**>(oldp)) = t_freeHead;
|
*(reinterpret_cast<void**>(oldp)) = t_freeHead;
|
||||||
t_freeHead = oldp;
|
t_freeHead = oldp;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
// MEMBERS
|
// MEMBERS
|
||||||
static VerilatedVpio* castp(vpiHandle h) {
|
static VerilatedVpio* castp(vpiHandle h) {
|
||||||
|
|
@ -109,32 +113,44 @@ public:
|
||||||
virtual vluint32_t size() const { return 0; }
|
virtual vluint32_t size() const { return 0; }
|
||||||
virtual const VerilatedRange* rangep() const { return nullptr; }
|
virtual const VerilatedRange* rangep() const { return nullptr; }
|
||||||
virtual vpiHandle dovpi_scan() { 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 VerilatedVpioTimedCb final : public VerilatedVpio {
|
||||||
|
// A handle to a timed callback created with vpi_register_cb
|
||||||
class VerilatedVpioCb final : public VerilatedVpio {
|
// User can call vpi_remove_cb or vpi_release_handle on it
|
||||||
t_cb_data m_cbData;
|
vluint64_t m_id; // Unique id/sequence number to find schedule's event
|
||||||
s_vpi_value m_value;
|
|
||||||
QData m_time;
|
QData m_time;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// cppcheck-suppress uninitVar // m_value
|
VerilatedVpioTimedCb(vluint64_t id, QData time)
|
||||||
VerilatedVpioCb(const t_cb_data* cbDatap, QData time)
|
: m_id(id)
|
||||||
: m_cbData(*cbDatap)
|
, m_time{time} {}
|
||||||
, m_time(time) { // Need () or GCC 4.8 false warning
|
virtual ~VerilatedVpioTimedCb() override = default;
|
||||||
m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
|
static VerilatedVpioTimedCb* castp(vpiHandle h) {
|
||||||
m_cbData.value = &m_value;
|
return dynamic_cast<VerilatedVpioTimedCb*>(reinterpret_cast<VerilatedVpioTimedCb*>(h));
|
||||||
}
|
|
||||||
virtual ~VerilatedVpioCb() override = default;
|
|
||||||
static VerilatedVpioCb* castp(vpiHandle h) {
|
|
||||||
return dynamic_cast<VerilatedVpioCb*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
||||||
}
|
}
|
||||||
virtual vluint32_t type() const override { return vpiCallback; }
|
virtual vluint32_t type() const override { return vpiCallback; }
|
||||||
vluint32_t reason() const { return m_cbData.reason; }
|
virtual PLI_INT32 dovpi_remove_cb() override;
|
||||||
VerilatedPliCb cb_rtnp() const { return m_cbData.cb_rtn; }
|
};
|
||||||
t_cb_data* cb_datap() { return &(m_cbData); }
|
|
||||||
QData time() const { return m_time; }
|
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 {
|
class VerilatedVpioConst final : public VerilatedVpio {
|
||||||
|
|
@ -159,7 +175,6 @@ public:
|
||||||
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
|
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||||
: m_varp{varp}
|
: m_varp{varp}
|
||||||
, m_scopep{scopep} {}
|
, m_scopep{scopep} {}
|
||||||
|
|
||||||
virtual ~VerilatedVpioParam() override = default;
|
virtual ~VerilatedVpioParam() override = default;
|
||||||
|
|
||||||
static VerilatedVpioParam* castp(vpiHandle h) {
|
static VerilatedVpioParam* castp(vpiHandle h) {
|
||||||
|
|
@ -206,7 +221,10 @@ public:
|
||||||
}
|
}
|
||||||
virtual vluint32_t type() const override { return vpiIterator; }
|
virtual vluint32_t type() const override { return vpiIterator; }
|
||||||
virtual vpiHandle dovpi_scan() override {
|
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;
|
m_done = true;
|
||||||
return ((new VerilatedVpioRange(m_range))->castVpiHandle());
|
return ((new VerilatedVpioRange(m_range))->castVpiHandle());
|
||||||
}
|
}
|
||||||
|
|
@ -230,16 +248,16 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpio {
|
class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpio {
|
||||||
const VerilatedVar* m_varp;
|
const VerilatedVar* m_varp = nullptr;
|
||||||
const VerilatedScope* m_scopep;
|
const VerilatedScope* m_scopep = nullptr;
|
||||||
vluint8_t* m_prevDatap = nullptr; // Previous value of data, for cbValueChange
|
vluint8_t* m_prevDatap = nullptr; // Previous value of data, for cbValueChange
|
||||||
union {
|
union {
|
||||||
vluint8_t u8[4];
|
vluint8_t u8[4];
|
||||||
vluint32_t u32;
|
vluint32_t u32;
|
||||||
} m_mask; // memoized variable mask
|
} m_mask; // memoized variable mask
|
||||||
vluint32_t m_entSize; // memoized variable size
|
vluint32_t m_entSize = 0; // memoized variable size
|
||||||
protected:
|
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;
|
vlsint32_t m_index = 0;
|
||||||
const VerilatedRange& get_range() const {
|
const VerilatedRange& get_range() const {
|
||||||
// Determine number of dimensions and return outermost
|
// Determine number of dimensions and return outermost
|
||||||
|
|
@ -254,6 +272,19 @@ public:
|
||||||
m_entSize = varp->entSize();
|
m_entSize = varp->entSize();
|
||||||
m_varDatap = varp->datap();
|
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 {
|
virtual ~VerilatedVpioVar() override {
|
||||||
if (m_prevDatap) VL_DO_CLEAR(delete[] m_prevDatap, m_prevDatap = nullptr);
|
if (m_prevDatap) VL_DO_CLEAR(delete[] m_prevDatap, m_prevDatap = nullptr);
|
||||||
}
|
}
|
||||||
|
|
@ -331,13 +362,18 @@ public:
|
||||||
m_it = varsp->begin();
|
m_it = varsp->begin();
|
||||||
m_started = true;
|
m_started = true;
|
||||||
} else if (VL_UNLIKELY(m_it == varsp->end())) {
|
} 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;
|
return nullptr;
|
||||||
} else {
|
} else {
|
||||||
++m_it;
|
++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());
|
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
|
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;
|
if (!(m_done = (m_iteration == m_varp->unpacked().left()))) m_iteration += m_direction;
|
||||||
}
|
}
|
||||||
virtual vpiHandle dovpi_scan() override {
|
virtual vpiHandle dovpi_scan() override {
|
||||||
vpiHandle result;
|
if (VL_UNLIKELY(m_done)) {
|
||||||
if (m_done) return nullptr;
|
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||||
result = vpi_handle_by_index(m_handle, m_iteration);
|
return nullptr;
|
||||||
|
}
|
||||||
|
vpiHandle result = vpi_handle_by_index(m_handle, m_iteration);
|
||||||
iterationInc();
|
iterationInc();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -406,7 +444,10 @@ public:
|
||||||
}
|
}
|
||||||
virtual vluint32_t type() const override { return vpiIterator; }
|
virtual vluint32_t type() const override { return vpiIterator; }
|
||||||
virtual vpiHandle dovpi_scan() override {
|
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++;
|
const VerilatedScope* modp = *m_it++;
|
||||||
return (new VerilatedVpioModule(modp))->castVpiHandle();
|
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 {
|
struct VerilatedVpiTimedCbsCmp {
|
||||||
/// Ordering sets keyed by time, then callback descriptor
|
/// Ordering sets keyed by time, then callback unique id
|
||||||
bool operator()(const std::pair<QData, VerilatedVpioCb*>& a,
|
bool operator()(const std::pair<QData, vluint64_t>& a,
|
||||||
const std::pair<QData, VerilatedVpioCb*>& b) const {
|
const std::pair<QData, vluint64_t>& b) const {
|
||||||
if (a.first < b.first) return true;
|
if (a.first < b.first) return true;
|
||||||
if (a.first > b.first) return false;
|
if (a.first > b.first) return false;
|
||||||
return a.second < b.second;
|
return a.second < b.second;
|
||||||
|
|
@ -428,55 +501,70 @@ class VerilatedVpiError;
|
||||||
|
|
||||||
class VerilatedVpiImp final {
|
class VerilatedVpiImp final {
|
||||||
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maxium callback reason
|
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maxium callback reason
|
||||||
typedef std::list<VerilatedVpioCb*> VpioCbList;
|
typedef std::list<VerilatedVpiCbHolder> VpioCbList;
|
||||||
typedef std::set<std::pair<QData, VerilatedVpioCb*>, VerilatedVpiTimedCbsCmp> VpioTimedCbs;
|
typedef std::map<std::pair<QData, vluint64_t>, VerilatedVpiCbHolder> VpioTimedCbs;
|
||||||
|
|
||||||
VpioCbList m_cbObjLists[CB_ENUM_MAX_VALUE]; // Callbacks for each supported reason
|
VpioCbList m_cbObjLists[CB_ENUM_MAX_VALUE]; // Callbacks for each supported reason
|
||||||
VpioTimedCbs m_timedCbs; // Time based callbacks
|
VpioTimedCbs m_timedCbs; // Time based callbacks
|
||||||
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
|
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
|
||||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||||
|
vluint64_t m_nextCallbackId = 1; // Id to identify callback
|
||||||
|
|
||||||
static VerilatedVpiImp s_s; // Singleton
|
static VerilatedVpiImp s_s; // Singleton
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VerilatedVpiImp() = default;
|
|
||||||
~VerilatedVpiImp() = default;
|
|
||||||
static void assertOneCheck() { s_s.m_assertOne.check(); }
|
static void assertOneCheck() { s_s.m_assertOne.check(); }
|
||||||
static void cbReasonAdd(VerilatedVpioCb* vop) {
|
static vluint64_t nextCallbackId() { return ++s_s.m_nextCallbackId; }
|
||||||
if (vop->reason() == cbValueChange) {
|
|
||||||
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) {
|
static void cbReasonAdd(vluint64_t id, const s_cb_data* cb_data_p) {
|
||||||
varop->createPrevDatap();
|
// 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)) {
|
||||||
}
|
|
||||||
if (VL_UNCOVERABLE(vop->reason() >= CB_ENUM_MAX_VALUE)) {
|
|
||||||
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi bb reason too large");
|
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 cbTimedAdd(vluint64_t id, const s_cb_data* cb_data_p, QData time) {
|
||||||
static void cbReasonRemove(VerilatedVpioCb* cbp) {
|
// The passed cb_data_p was property of the user, so need to recreate
|
||||||
VpioCbList& cbObjList = s_s.m_cbObjLists[cbp->reason()];
|
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,
|
// We do not remove it now as we may be iterating the list,
|
||||||
// instead set to nullptr and will cleanup later
|
// instead set to nullptr and will cleanup later
|
||||||
for (auto& ir : cbObjList) {
|
for (auto& ir : cbObjList) {
|
||||||
if (ir == cbp) ir = nullptr;
|
if (ir.id() == id) ir.invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void cbTimedRemove(VerilatedVpioCb* cbp) {
|
static void cbTimedRemove(vluint64_t id, QData time) {
|
||||||
const auto it = s_s.m_timedCbs.find(std::make_pair(cbp->time(), cbp));
|
// Id might no longer exist, if already removed due to call after event, or teardown
|
||||||
if (VL_LIKELY(it != s_s.m_timedCbs.end())) s_s.m_timedCbs.erase(it);
|
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 {
|
static void callTimedCbs() VL_MT_UNSAFE_ONE {
|
||||||
assertOneCheck();
|
assertOneCheck();
|
||||||
QData time = VL_TIME_Q();
|
QData time = VL_TIME_Q();
|
||||||
for (auto it = s_s.m_timedCbs.begin(); it != s_s.m_timedCbs.end();) {
|
for (auto it = s_s.m_timedCbs.begin(); it != s_s.m_timedCbs.end();) {
|
||||||
if (VL_UNLIKELY(it->first <= time)) {
|
if (VL_UNLIKELY(it->first.first <= time)) {
|
||||||
VerilatedVpioCb* vop = it->second;
|
VerilatedVpiCbHolder& ho = it->second;
|
||||||
const auto last_it = it;
|
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);
|
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 {
|
} else {
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|
@ -484,7 +572,7 @@ public:
|
||||||
}
|
}
|
||||||
static QData cbNextDeadline() {
|
static QData cbNextDeadline() {
|
||||||
const auto it = s_s.m_timedCbs.cbegin();
|
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
|
return ~0ULL; // maxquad
|
||||||
}
|
}
|
||||||
static bool callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE {
|
static bool callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE {
|
||||||
|
|
@ -495,16 +583,18 @@ public:
|
||||||
for (auto it = cbObjList.begin(); true;) {
|
for (auto it = cbObjList.begin(); true;) {
|
||||||
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
|
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
|
||||||
bool was_last = it == last;
|
bool was_last = it == last;
|
||||||
if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup
|
if (VL_UNLIKELY(it->invalid())) { // Deleted earlier, cleanup
|
||||||
it = cbObjList.erase(it);
|
it = cbObjList.erase(it);
|
||||||
if (was_last) break;
|
if (was_last) break;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
VerilatedVpioCb* vop = *it++;
|
VerilatedVpiCbHolder& ho = *it;
|
||||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: reason_callback %d %p\n", reason, vop););
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: reason_callback reason=%d id=%" VL_PRI64 "d\n",
|
||||||
(vop->cb_rtnp())(vop->cb_datap());
|
reason, ho.id()););
|
||||||
|
(ho.cb_rtnp())(ho.cb_datap());
|
||||||
called = true;
|
called = true;
|
||||||
if (was_last) break;
|
if (was_last) break;
|
||||||
|
++it;
|
||||||
}
|
}
|
||||||
return called;
|
return called;
|
||||||
}
|
}
|
||||||
|
|
@ -519,24 +609,25 @@ public:
|
||||||
for (auto it = cbObjList.begin(); true;) {
|
for (auto it = cbObjList.begin(); true;) {
|
||||||
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
|
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
|
||||||
bool was_last = it == last;
|
bool was_last = it == last;
|
||||||
if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup
|
if (VL_UNLIKELY(it->invalid())) { // Deleted earlier, cleanup
|
||||||
it = cbObjList.erase(it);
|
it = cbObjList.erase(it);
|
||||||
if (was_last) break;
|
if (was_last) break;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
VerilatedVpioCb* vop = *it++;
|
VerilatedVpiCbHolder& ho = *it++;
|
||||||
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) {
|
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
|
||||||
void* newDatap = varop->varDatap();
|
void* newDatap = varop->varDatap();
|
||||||
void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback
|
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",
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
|
||||||
varop->fullname(), *((CData*)newDatap),
|
varop->fullname(), *((CData*)newDatap),
|
||||||
*((CData*)prevDatap), newDatap, prevDatap););
|
*((CData*)prevDatap), newDatap, prevDatap););
|
||||||
if (memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
|
if (memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
|
||||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %p %s v[0]=%d\n", vop,
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64
|
||||||
varop->fullname(), *((CData*)newDatap)););
|
"d %s v[0]=%d\n",
|
||||||
|
ho.id(), varop->fullname(), *((CData*)newDatap)););
|
||||||
update.insert(varop);
|
update.insert(varop);
|
||||||
vpi_get_value(vop->cb_datap()->obj, vop->cb_datap()->value);
|
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
|
||||||
(vop->cb_rtnp())(vop->cb_datap());
|
(ho.cb_rtnp())(ho.cb_datap());
|
||||||
called = true;
|
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(); }
|
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
|
// VerilatedVpiImp implementation
|
||||||
|
|
||||||
|
|
@ -1037,6 +1139,9 @@ void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
|
||||||
// callback related
|
// callback related
|
||||||
|
|
||||||
vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
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();
|
VerilatedVpiImp::assertOneCheck();
|
||||||
_VL_VPI_ERROR_RESET();
|
_VL_VPI_ERROR_RESET();
|
||||||
// cppcheck-suppress nullPointer
|
// cppcheck-suppress nullPointer
|
||||||
|
|
@ -1048,10 +1153,10 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||||
case cbAfterDelay: {
|
case cbAfterDelay: {
|
||||||
QData time = 0;
|
QData time = 0;
|
||||||
if (cb_data_p->time) time = _VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
|
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);
|
QData abstime = VL_TIME_Q() + time;
|
||||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb %d %p delay=%" VL_PRI64 "u\n",
|
vluint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||||
cb_data_p->reason, vop, time););
|
VerilatedVpioTimedCb* vop = new VerilatedVpioTimedCb{id, abstime};
|
||||||
VerilatedVpiImp::cbTimedAdd(vop);
|
VerilatedVpiImp::cbTimedAdd(id, cb_data_p, abstime);
|
||||||
return vop->castVpiHandle();
|
return vop->castVpiHandle();
|
||||||
}
|
}
|
||||||
case cbReadWriteSynch: // FALLTHRU // Supported via vlt_main.cpp
|
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 cbEnterInteractive: // FALLTHRU // NOP, but need to return handle, so make object
|
||||||
case cbExitInteractive: // 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
|
||||||
VerilatedVpioCb* vop = new VerilatedVpioCb(cb_data_p, 0);
|
vluint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb %d %p\n", cb_data_p->reason, vop););
|
VerilatedVpioReasonCb* vop = new VerilatedVpioReasonCb{id, cb_data_p->reason};
|
||||||
VerilatedVpiImp::cbReasonAdd(vop);
|
VerilatedVpiImp::cbReasonAdd(id, cb_data_p);
|
||||||
return vop->castVpiHandle();
|
return vop->castVpiHandle();
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
@ -1079,15 +1184,10 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||||
PLI_INT32 vpi_remove_cb(vpiHandle cb_obj) {
|
PLI_INT32 vpi_remove_cb(vpiHandle cb_obj) {
|
||||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_remove_cb %p\n", cb_obj););
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_remove_cb %p\n", cb_obj););
|
||||||
VerilatedVpiImp::assertOneCheck();
|
VerilatedVpiImp::assertOneCheck();
|
||||||
VerilatedVpioCb* vop = VerilatedVpioCb::castp(cb_obj);
|
|
||||||
_VL_VPI_ERROR_RESET();
|
_VL_VPI_ERROR_RESET();
|
||||||
|
VerilatedVpio* vop = VerilatedVpio::castp(cb_obj);
|
||||||
if (VL_UNLIKELY(!vop)) return 0;
|
if (VL_UNLIKELY(!vop)) return 0;
|
||||||
if (vop->cb_datap()->reason == cbAfterDelay) {
|
return vop->dovpi_remove_cb();
|
||||||
VerilatedVpiImp::cbTimedRemove(vop);
|
|
||||||
} else {
|
|
||||||
VerilatedVpiImp::cbReasonRemove(vop);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void vpi_get_cb_info(vpiHandle /*object*/, p_cb_data /*cb_data_p*/) { _VL_VPI_UNIMP(); }
|
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);
|
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||||
_VL_VPI_ERROR_RESET();
|
_VL_VPI_ERROR_RESET();
|
||||||
if (VL_UNLIKELY(!vop)) return 0;
|
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);
|
VL_DO_DANGLING(delete vop, vop);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,33 +15,30 @@
|
||||||
|
|
||||||
class TestVpiHandle {
|
class TestVpiHandle {
|
||||||
/// For testing, etc, wrap vpiHandle in an auto-releasing class
|
/// For testing, etc, wrap vpiHandle in an auto-releasing class
|
||||||
vpiHandle m_handle;
|
vpiHandle m_handle = NULL;
|
||||||
bool m_free;
|
bool m_freeit = true;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TestVpiHandle()
|
TestVpiHandle() {}
|
||||||
: m_handle(NULL)
|
|
||||||
, m_free(true) {}
|
|
||||||
TestVpiHandle(vpiHandle h)
|
TestVpiHandle(vpiHandle h)
|
||||||
: m_handle(h)
|
: m_handle(h) {}
|
||||||
, m_free(true) {}
|
~TestVpiHandle() { release(); }
|
||||||
~TestVpiHandle() {
|
operator vpiHandle() const { return m_handle; }
|
||||||
if (m_handle && m_free) {
|
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
|
// Below not VL_DO_DANGLING so is portable
|
||||||
{
|
|
||||||
vpi_release_handle(m_handle);
|
vpi_release_handle(m_handle);
|
||||||
m_handle = NULL;
|
m_handle = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
operator vpiHandle() const { return m_handle; }
|
|
||||||
inline TestVpiHandle& operator=(vpiHandle h) {
|
|
||||||
m_handle = h;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
// Freed by another action e.g. vpi_scan; so empty and don't free again
|
// Freed by another action e.g. vpi_scan; so empty and don't free again
|
||||||
void freed() {
|
void freed() {
|
||||||
m_handle = NULL;
|
m_handle = NULL;
|
||||||
m_free = false;
|
m_freeit = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@
|
||||||
|
|
||||||
bool got_error = false;
|
bool got_error = false;
|
||||||
|
|
||||||
vpiHandle vh_value_cb = 0;
|
TestVpiHandle vh_value_cb;
|
||||||
vpiHandle vh_rw_cb = 0;
|
TestVpiHandle vh_rw_cb;
|
||||||
|
|
||||||
unsigned int last_value_cb_time = 0;
|
unsigned int last_value_cb_time = 0;
|
||||||
unsigned int last_rw_cb_time = 0;
|
unsigned int last_rw_cb_time = 0;
|
||||||
|
|
@ -78,6 +78,7 @@ static void reregister_value_cb() {
|
||||||
if (vh_value_cb) {
|
if (vh_value_cb) {
|
||||||
if (verbose) { vpi_printf(const_cast<char*>("- Removing cbValueChange callback\n")); }
|
if (verbose) { vpi_printf(const_cast<char*>("- Removing cbValueChange callback\n")); }
|
||||||
int ret = vpi_remove_cb(vh_value_cb);
|
int ret = vpi_remove_cb(vh_value_cb);
|
||||||
|
vh_value_cb.freed();
|
||||||
CHECK_RESULT(ret, 1);
|
CHECK_RESULT(ret, 1);
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
|
|
@ -93,7 +94,7 @@ static void reregister_value_cb() {
|
||||||
cb_data_testcase.cb_rtn = the_value_callback;
|
cb_data_testcase.cb_rtn = the_value_callback;
|
||||||
cb_data_testcase.reason = cbValueChange;
|
cb_data_testcase.reason = cbValueChange;
|
||||||
|
|
||||||
vpiHandle vh1 = VPI_HANDLE("count");
|
TestVpiHandle vh1 = VPI_HANDLE("count");
|
||||||
CHECK_RESULT_NZ(vh1);
|
CHECK_RESULT_NZ(vh1);
|
||||||
|
|
||||||
s_vpi_value v;
|
s_vpi_value v;
|
||||||
|
|
@ -110,6 +111,7 @@ static void reregister_rw_cb() {
|
||||||
if (vh_rw_cb) {
|
if (vh_rw_cb) {
|
||||||
if (verbose) { vpi_printf(const_cast<char*>("- Removing cbReadWriteSynch callback\n")); }
|
if (verbose) { vpi_printf(const_cast<char*>("- Removing cbReadWriteSynch callback\n")); }
|
||||||
int ret = vpi_remove_cb(vh_rw_cb);
|
int ret = vpi_remove_cb(vh_rw_cb);
|
||||||
|
vh_rw_cb.freed();
|
||||||
CHECK_RESULT(ret, 1);
|
CHECK_RESULT(ret, 1);
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
|
|
@ -140,8 +142,8 @@ static void register_filler_cb() {
|
||||||
cb_data_1.cb_rtn = the_filler_callback;
|
cb_data_1.cb_rtn = the_filler_callback;
|
||||||
cb_data_1.reason = cbReadWriteSynch;
|
cb_data_1.reason = cbReadWriteSynch;
|
||||||
|
|
||||||
vpiHandle vh1 = vpi_register_cb(&cb_data_1);
|
TestVpiHandle cb_data_1_h = vpi_register_cb(&cb_data_1);
|
||||||
CHECK_RESULT_NZ(vh1);
|
CHECK_RESULT_NZ(cb_data_1_h);
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
vpi_printf(const_cast<char*>("- Registering filler cbValueChange callback\n"));
|
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.cb_rtn = the_filler_callback;
|
||||||
cb_data_2.reason = cbValueChange;
|
cb_data_2.reason = cbValueChange;
|
||||||
|
|
||||||
vpiHandle vh2 = VPI_HANDLE("count");
|
TestVpiHandle vh2 = VPI_HANDLE("count");
|
||||||
CHECK_RESULT_NZ(vh2);
|
CHECK_RESULT_NZ(vh2);
|
||||||
|
|
||||||
s_vpi_value v;
|
s_vpi_value v;
|
||||||
|
|
@ -160,8 +162,8 @@ static void register_filler_cb() {
|
||||||
cb_data_2.obj = vh2;
|
cb_data_2.obj = vh2;
|
||||||
cb_data_2.value = &v;
|
cb_data_2.value = &v;
|
||||||
|
|
||||||
vpiHandle vh3 = vpi_register_cb(&cb_data_2);
|
TestVpiHandle cb_data_2_h = vpi_register_cb(&cb_data_2);
|
||||||
CHECK_RESULT_NZ(vh3);
|
CHECK_RESULT_NZ(cb_data_2_h);
|
||||||
}
|
}
|
||||||
|
|
||||||
double sc_time_stamp() { return main_time; }
|
double sc_time_stamp() { return main_time; }
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ const std::vector<CallbackState> cb_states{PRE_REGISTER, ACTIVE, ACTIVE_AGAIN, R
|
||||||
POST_REMOVE};
|
POST_REMOVE};
|
||||||
|
|
||||||
#define CB_COUNT cbAtEndOfSimTime + 1
|
#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_counts[CB_COUNT] = {0};
|
||||||
unsigned int callback_expected_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<int>::const_iterator cb_iter;
|
||||||
std::vector<CallbackState>::const_iterator state_iter;
|
std::vector<CallbackState>::const_iterator state_iter;
|
||||||
|
|
||||||
vpiHandle vh_test_cb = 0;
|
|
||||||
unsigned int main_time = 0;
|
unsigned int main_time = 0;
|
||||||
bool got_error = false;
|
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) {
|
static int register_cb(const int next_state) {
|
||||||
int cb = *cb_iter;
|
int cb = *cb_iter;
|
||||||
t_cb_data cb_data_testcase;
|
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));
|
bzero(&cb_data_testcase, sizeof(cb_data_testcase));
|
||||||
cb_data_testcase.cb_rtn = the_callback;
|
cb_data_testcase.cb_rtn = the_callback;
|
||||||
cb_data_testcase.reason = cb;
|
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) {
|
if (cb == cbValueChange) {
|
||||||
vpiHandle vh1 = VPI_HANDLE("count");
|
|
||||||
CHECK_RESULT_NZ(vh1);
|
|
||||||
|
|
||||||
s_vpi_value v;
|
|
||||||
v.format = vpiSuppressVal;
|
v.format = vpiSuppressVal;
|
||||||
|
|
||||||
cb_data_testcase.obj = vh1;
|
cb_data_testcase.obj = count_h;
|
||||||
cb_data_testcase.value = &v;
|
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"),
|
vpi_printf(const_cast<char*>(" - Registering callback %s\n"),
|
||||||
cb_reason_to_string(cb));
|
cb_reason_to_string(cb));
|
||||||
}
|
}
|
||||||
|
vh_registered_cbs[cb].release();
|
||||||
vh_registered_cbs[cb] = vpi_register_cb(&cb_data_testcase);
|
vh_registered_cbs[cb] = vpi_register_cb(&cb_data_testcase);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -126,6 +125,7 @@ static int register_cb(const int next_state) {
|
||||||
cb_reason_to_string(cb));
|
cb_reason_to_string(cb));
|
||||||
}
|
}
|
||||||
int ret = vpi_remove_cb(vh_registered_cbs[cb]);
|
int ret = vpi_remove_cb(vh_registered_cbs[cb]);
|
||||||
|
vh_registered_cbs[cb].freed();
|
||||||
CHECK_RESULT(ret, 1);
|
CHECK_RESULT(ret, 1);
|
||||||
vh_registered_cbs[cb] = vpi_register_cb(&cb_data_testcase);
|
vh_registered_cbs[cb] = vpi_register_cb(&cb_data_testcase);
|
||||||
break;
|
break;
|
||||||
|
|
@ -136,6 +136,7 @@ static int register_cb(const int next_state) {
|
||||||
cb_reason_to_string(cb));
|
cb_reason_to_string(cb));
|
||||||
}
|
}
|
||||||
int ret = vpi_remove_cb(vh_registered_cbs[cb]);
|
int ret = vpi_remove_cb(vh_registered_cbs[cb]);
|
||||||
|
vh_registered_cbs[cb].freed();
|
||||||
CHECK_RESULT(ret, 1);
|
CHECK_RESULT(ret, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -215,7 +216,7 @@ static int test_callbacks(p_cb_data cb_data) {
|
||||||
t1.low = 1;
|
t1.low = 1;
|
||||||
cb_data_n.time = &t1;
|
cb_data_n.time = &t1;
|
||||||
cb_data_n.cb_rtn = test_callbacks;
|
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);
|
CHECK_RESULT_NZ(vh_test_cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,7 +236,7 @@ static int register_test_callback() {
|
||||||
t1.low = 1;
|
t1.low = 1;
|
||||||
cb_data.time = &t1;
|
cb_data.time = &t1;
|
||||||
cb_data.cb_rtn = test_callbacks;
|
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);
|
CHECK_RESULT_NZ(vh_test_cb);
|
||||||
|
|
||||||
cb_iter = cbs_to_test.cbegin();
|
cb_iter = cbs_to_test.cbegin();
|
||||||
|
|
@ -250,7 +251,6 @@ int main(int argc, char** argv, char** env) {
|
||||||
double sim_time = 100;
|
double sim_time = 100;
|
||||||
bool cbs_called;
|
bool cbs_called;
|
||||||
Verilated::commandArgs(argc, argv);
|
Verilated::commandArgs(argc, argv);
|
||||||
Verilated::debug(0);
|
|
||||||
|
|
||||||
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out
|
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,26 +80,32 @@ unsigned int main_time = 0;
|
||||||
|
|
||||||
#define CHECK_RESULT_CSTR_STRIP(got, exp) CHECK_RESULT_CSTR(got + strspn(got, " "), exp)
|
#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) {
|
int _mon_check_range(const TestVpiHandle& handle, int size, int left, int right) {
|
||||||
TestVpiHandle iter_h, left_h, right_h;
|
|
||||||
s_vpi_value value;
|
s_vpi_value value;
|
||||||
value.format = vpiIntVal;
|
value.format = vpiIntVal;
|
||||||
value.value.integer = 0;
|
value.value.integer = 0;
|
||||||
// check size of object
|
// check size of object
|
||||||
|
{
|
||||||
int vpisize = vpi_get(vpiSize, handle);
|
int vpisize = vpi_get(vpiSize, handle);
|
||||||
CHECK_RESULT(vpisize, size);
|
CHECK_RESULT(vpisize, size);
|
||||||
|
}
|
||||||
|
int coherency;
|
||||||
|
{
|
||||||
// check left hand side of range
|
// check left hand side of range
|
||||||
left_h = vpi_handle(vpiLeftRange, handle);
|
TestVpiHandle left_h = vpi_handle(vpiLeftRange, handle);
|
||||||
CHECK_RESULT_NZ(left_h);
|
CHECK_RESULT_NZ(left_h);
|
||||||
vpi_get_value(left_h, &value);
|
vpi_get_value(left_h, &value);
|
||||||
CHECK_RESULT(value.value.integer, left);
|
CHECK_RESULT(value.value.integer, left);
|
||||||
int coherency = value.value.integer;
|
coherency = value.value.integer;
|
||||||
|
}
|
||||||
|
{
|
||||||
// check right hand side of range
|
// check right hand side of range
|
||||||
right_h = vpi_handle(vpiRightRange, handle);
|
TestVpiHandle right_h = vpi_handle(vpiRightRange, handle);
|
||||||
CHECK_RESULT_NZ(right_h);
|
CHECK_RESULT_NZ(right_h);
|
||||||
vpi_get_value(right_h, &value);
|
vpi_get_value(right_h, &value);
|
||||||
CHECK_RESULT(value.value.integer, right);
|
CHECK_RESULT(value.value.integer, right);
|
||||||
coherency -= value.value.integer;
|
coherency -= value.value.integer;
|
||||||
|
}
|
||||||
// calculate size & check
|
// calculate size & check
|
||||||
coherency = abs(coherency) + 1;
|
coherency = abs(coherency) + 1;
|
||||||
CHECK_RESULT(coherency, size);
|
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 _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;
|
s_vpi_value value;
|
||||||
value.format = vpiIntVal;
|
value.format = vpiIntVal;
|
||||||
value.value.integer = 0;
|
value.value.integer = 0;
|
||||||
s_vpi_error_info e;
|
s_vpi_error_info e;
|
||||||
|
|
||||||
vpi_printf((PLI_BYTE8*)"Check memory vpi ...\n");
|
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_RESULT_NZ(mem_h);
|
||||||
|
{
|
||||||
// check type
|
// check type
|
||||||
int vpitype = vpi_get(vpiType, mem_h);
|
int vpitype = vpi_get(vpiType, mem_h);
|
||||||
CHECK_RESULT(vpitype, vpiMemory);
|
CHECK_RESULT(vpitype, vpiMemory);
|
||||||
|
}
|
||||||
if (int status = _mon_check_range(mem_h, 16, 16, 1)) return status;
|
if (int status = _mon_check_range(mem_h, 16, 16, 1)) return status;
|
||||||
// iterate and store
|
// iterate and store
|
||||||
iter_h = vpi_iterate(vpiMemoryWord, mem_h);
|
{
|
||||||
cnt = 0;
|
TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h);
|
||||||
while ((lcl_h = vpi_scan(iter_h))) {
|
int cnt = 0;
|
||||||
|
while (TestVpiHandle lcl_h = vpi_scan(iter_h)) {
|
||||||
value.value.integer = ++cnt;
|
value.value.integer = ++cnt;
|
||||||
vpi_put_value(lcl_h, &value, NULL, vpiNoDelay);
|
vpi_put_value(lcl_h, &value, NULL, vpiNoDelay);
|
||||||
// check size and range
|
// check size and range
|
||||||
if (int status = _mon_check_range(lcl_h, 32, 31, 0)) return status;
|
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
|
// iterate and accumulate
|
||||||
iter_h = vpi_iterate(vpiMemoryWord, mem_h);
|
TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h);
|
||||||
cnt = 0;
|
int cnt = 0;
|
||||||
while ((lcl_h = vpi_scan(iter_h))) {
|
while (TestVpiHandle lcl_h = vpi_scan(iter_h)) {
|
||||||
++cnt;
|
++cnt;
|
||||||
vpi_get_value(lcl_h, &value);
|
vpi_get_value(lcl_h, &value);
|
||||||
CHECK_RESULT(value.value.integer, cnt);
|
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
|
// don't care for non verilator
|
||||||
// (crashes on Icarus)
|
// (crashes on Icarus)
|
||||||
if (TestSimulator::is_icarus()) {
|
if (TestSimulator::is_icarus()) {
|
||||||
|
|
@ -148,8 +160,10 @@ int _mon_check_memory() {
|
||||||
TestSimulator::get_info().product);
|
TestSimulator::get_info().product);
|
||||||
return 0; // Ok
|
return 0; // Ok
|
||||||
}
|
}
|
||||||
|
{
|
||||||
// make sure trying to get properties that don't exist
|
// make sure trying to get properties that don't exist
|
||||||
// doesn't crash
|
// doesn't crash
|
||||||
|
TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h);
|
||||||
int should_be_0 = vpi_get(vpiSize, iter_h);
|
int should_be_0 = vpi_get(vpiSize, iter_h);
|
||||||
CHECK_RESULT(should_be_0, 0);
|
CHECK_RESULT(should_be_0, 0);
|
||||||
should_be_0 = vpi_get(vpiIndex, iter_h);
|
should_be_0 = vpi_get(vpiIndex, iter_h);
|
||||||
|
|
@ -160,28 +174,35 @@ int _mon_check_memory() {
|
||||||
CHECK_RESULT(should_be_NULL, 0);
|
CHECK_RESULT(should_be_NULL, 0);
|
||||||
should_be_NULL = vpi_handle(vpiScope, iter_h);
|
should_be_NULL = vpi_handle(vpiScope, iter_h);
|
||||||
CHECK_RESULT(should_be_NULL, 0);
|
CHECK_RESULT(should_be_NULL, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
// check vpiRange
|
// check vpiRange
|
||||||
iter_h = vpi_iterate(vpiRange, mem_h);
|
TestVpiHandle iter_h = vpi_iterate(vpiRange, mem_h);
|
||||||
CHECK_RESULT_NZ(iter_h);
|
CHECK_RESULT_NZ(iter_h);
|
||||||
lcl_h = vpi_scan(iter_h);
|
TestVpiHandle lcl_h = vpi_scan(iter_h);
|
||||||
CHECK_RESULT_NZ(lcl_h);
|
CHECK_RESULT_NZ(lcl_h);
|
||||||
side_h = vpi_handle(vpiLeftRange, lcl_h);
|
{
|
||||||
|
TestVpiHandle side_h = vpi_handle(vpiLeftRange, lcl_h);
|
||||||
CHECK_RESULT_NZ(side_h);
|
CHECK_RESULT_NZ(side_h);
|
||||||
vpi_get_value(side_h, &value);
|
vpi_get_value(side_h, &value);
|
||||||
CHECK_RESULT(value.value.integer, 16);
|
CHECK_RESULT(value.value.integer, 16);
|
||||||
side_h = vpi_handle(vpiRightRange, lcl_h);
|
}
|
||||||
|
{
|
||||||
|
TestVpiHandle side_h = vpi_handle(vpiRightRange, lcl_h);
|
||||||
CHECK_RESULT_NZ(side_h);
|
CHECK_RESULT_NZ(side_h);
|
||||||
vpi_get_value(side_h, &value);
|
vpi_get_value(side_h, &value);
|
||||||
CHECK_RESULT(value.value.integer, 1);
|
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
|
// check writing to vpiConstant
|
||||||
vpi_put_value(side_h, &value, NULL, vpiNoDelay);
|
vpi_put_value(side_h, &value, NULL, vpiNoDelay);
|
||||||
CHECK_RESULT_NZ(vpi_chk_error(&e));
|
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
|
return 0; // Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ static int _time_cb1(p_cb_data cb_data) {
|
||||||
t.low = 1;
|
t.low = 1;
|
||||||
cb_data_n.time = &t;
|
cb_data_n.time = &t;
|
||||||
cb_data_n.cb_rtn = _time_cb1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,7 +127,7 @@ static int _time_cb2(p_cb_data cb_data) {
|
||||||
t.low = 1;
|
t.low = 1;
|
||||||
cb_data_n.time = &t;
|
cb_data_n.time = &t;
|
||||||
cb_data_n.cb_rtn = _time_cb2;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +143,7 @@ static int _start_of_sim_cb(p_cb_data cb_data) {
|
||||||
t1.low = 3;
|
t1.low = 3;
|
||||||
cb_data_n1.time = &t1;
|
cb_data_n1.time = &t1;
|
||||||
cb_data_n1.cb_rtn = _time_cb1;
|
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;
|
cb_data_n2.reason = cbAfterDelay;
|
||||||
t2.type = vpiSimTime;
|
t2.type = vpiSimTime;
|
||||||
|
|
@ -151,7 +151,7 @@ static int _start_of_sim_cb(p_cb_data cb_data) {
|
||||||
t2.low = 4;
|
t2.low = 4;
|
||||||
cb_data_n2.time = &t2;
|
cb_data_n2.time = &t2;
|
||||||
cb_data_n2.cb_rtn = _time_cb2;
|
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++;
|
callback_count_start_of_sim++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -177,12 +177,12 @@ void vpi_compat_bootstrap(void) {
|
||||||
cb_data.reason = cbStartOfSimulation;
|
cb_data.reason = cbStartOfSimulation;
|
||||||
cb_data.time = 0;
|
cb_data.time = 0;
|
||||||
cb_data.cb_rtn = _start_of_sim_cb;
|
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.reason = cbEndOfSimulation;
|
||||||
cb_data.time = 0;
|
cb_data.time = 0;
|
||||||
cb_data.cb_rtn = _end_of_sim_cb;
|
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
|
// icarus entry
|
||||||
|
|
|
||||||
|
|
@ -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) \
|
#define CHECK_RESULT_VH(got, exp) \
|
||||||
if ((got) != (exp)) { \
|
if ((got) != (exp)) { \
|
||||||
printf("%%Error: %s:%d: GOT = %p EXP = %p\n", FILENM, __LINE__, (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.value = NULL;
|
||||||
cb_data.time = NULL;
|
cb_data.time = NULL;
|
||||||
|
|
||||||
vpiHandle vh = vpi_register_cb(&cb_data);
|
TestVpiHandle vh = vpi_register_cb(&cb_data);
|
||||||
CHECK_RESULT_NZ(vh);
|
CHECK_RESULT_NZ(vh);
|
||||||
|
|
||||||
PLI_INT32 status = vpi_remove_cb(vh);
|
PLI_INT32 status = vpi_remove_cb(vh);
|
||||||
|
vh.freed();
|
||||||
CHECK_RESULT_NZ(status);
|
CHECK_RESULT_NZ(status);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _value_callback(p_cb_data cb_data) {
|
int _value_callback(p_cb_data cb_data) {
|
||||||
|
if (verbose) vpi_printf(const_cast<char*>(" _value_callback:\n"));
|
||||||
if (TestSimulator::is_verilator()) {
|
if (TestSimulator::is_verilator()) {
|
||||||
// this check only makes sense in Verilator
|
// this check only makes sense in Verilator
|
||||||
CHECK_RESULT(cb_data->value->value.integer + 10, main_time);
|
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() {
|
int _mon_check_value_callbacks() {
|
||||||
vpiHandle vh1 = VPI_HANDLE("count");
|
|
||||||
CHECK_RESULT_NZ(vh1);
|
|
||||||
|
|
||||||
s_vpi_value v;
|
s_vpi_value v;
|
||||||
v.format = vpiIntVal;
|
v.format = vpiIntVal;
|
||||||
vpi_get_value(vh1, &v);
|
|
||||||
|
|
||||||
t_cb_data cb_data;
|
t_cb_data cb_data;
|
||||||
cb_data.reason = cbValueChange;
|
cb_data.reason = cbValueChange;
|
||||||
cb_data.cb_rtn = _value_callback;
|
|
||||||
cb_data.obj = vh1;
|
|
||||||
cb_data.value = &v;
|
|
||||||
cb_data.time = NULL;
|
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");
|
vpi_get_value(vh1, &v);
|
||||||
|
cb_data.value = &v;
|
||||||
|
cb_data.obj = vh1;
|
||||||
|
cb_data.cb_rtn = _value_callback;
|
||||||
|
|
||||||
|
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);
|
CHECK_RESULT_NZ(vh1);
|
||||||
|
|
||||||
cb_data.obj = vh1;
|
cb_data.obj = vh1;
|
||||||
cb_data.cb_rtn = _value_callback_half;
|
cb_data.cb_rtn = _value_callback_half;
|
||||||
|
|
||||||
vh = vpi_register_cb(&cb_data);
|
TestVpiHandle callback_h = vpi_register_cb(&cb_data);
|
||||||
CHECK_RESULT_NZ(vh);
|
CHECK_RESULT_NZ(callback_h);
|
||||||
|
}
|
||||||
vh1 = VPI_HANDLE("quads");
|
{
|
||||||
|
TestVpiHandle vh1 = VPI_HANDLE("quads");
|
||||||
CHECK_RESULT_NZ(vh1);
|
CHECK_RESULT_NZ(vh1);
|
||||||
|
|
||||||
v.format = vpiVectorVal;
|
v.format = vpiVectorVal;
|
||||||
cb_data.obj = vh1;
|
cb_data.obj = vh1;
|
||||||
cb_data.cb_rtn = _value_callback_quad;
|
cb_data.cb_rtn = _value_callback_quad;
|
||||||
|
|
||||||
vh = vpi_register_cb(&cb_data);
|
TestVpiHandle callback_h = vpi_register_cb(&cb_data);
|
||||||
CHECK_RESULT_NZ(vh);
|
CHECK_RESULT_NZ(callback_h);
|
||||||
|
}
|
||||||
vh1 = vpi_handle_by_index(vh1, 2);
|
{
|
||||||
|
TestVpiHandle vh1 = VPI_HANDLE("quads");
|
||||||
CHECK_RESULT_NZ(vh1);
|
CHECK_RESULT_NZ(vh1);
|
||||||
|
TestVpiHandle vh2 = vpi_handle_by_index(vh1, 2);
|
||||||
|
CHECK_RESULT_NZ(vh2);
|
||||||
|
|
||||||
cb_data.obj = vh1;
|
cb_data.obj = vh2;
|
||||||
cb_data.cb_rtn = _value_callback_quad;
|
cb_data.cb_rtn = _value_callback_quad;
|
||||||
|
|
||||||
vh = vpi_register_cb(&cb_data);
|
TestVpiHandle callback_h = vpi_register_cb(&cb_data);
|
||||||
CHECK_RESULT_NZ(vh);
|
CHECK_RESULT_NZ(callback_h);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -463,7 +479,10 @@ int _mon_check_putget_str(p_cb_data cb_data) {
|
||||||
s_vpi_vecval vector[4];
|
s_vpi_vecval vector[4];
|
||||||
} value; // reference
|
} value; // reference
|
||||||
} data[129];
|
} data[129];
|
||||||
|
|
||||||
if (cb_data) {
|
if (cb_data) {
|
||||||
|
if (verbose) vpi_printf(const_cast<char*>(" _mon_check_putget_str callback:\n"));
|
||||||
|
|
||||||
// this is the callback
|
// this is the callback
|
||||||
static unsigned int seed = 1;
|
static unsigned int seed = 1;
|
||||||
s_vpi_time t;
|
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) {
|
if (++callback_count_strs == callback_count_strs_max) {
|
||||||
int success = vpi_remove_cb(cb);
|
int success = vpi_remove_cb(cb);
|
||||||
|
cb.freed();
|
||||||
CHECK_RESULT_NZ(success);
|
CHECK_RESULT_NZ(success);
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -560,7 +580,7 @@ int _mon_check_putget_str(p_cb_data cb_data) {
|
||||||
|
|
||||||
static t_cb_data cb_data;
|
static t_cb_data cb_data;
|
||||||
static s_vpi_value v;
|
static s_vpi_value v;
|
||||||
static TestVpiHandle count_h = VPI_HANDLE("count");
|
TestVpiHandle count_h = VPI_HANDLE("count");
|
||||||
|
|
||||||
cb_data.reason = cbValueChange;
|
cb_data.reason = cbValueChange;
|
||||||
cb_data.cb_rtn = _mon_check_putget_str; // this function
|
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;
|
v.format = vpiIntVal;
|
||||||
|
|
||||||
cb = vpi_register_cb(&cb_data);
|
cb = vpi_register_cb(&cb_data);
|
||||||
|
// It is legal to free the callback handle immediately if not otherwise needed
|
||||||
CHECK_RESULT_NZ(cb);
|
CHECK_RESULT_NZ(cb);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ static int _start_of_sim_cb(p_cb_data cb_data) {
|
||||||
t.low = 0;
|
t.low = 0;
|
||||||
cb_data_n.time = &t;
|
cb_data_n.time = &t;
|
||||||
cb_data_n.cb_rtn = _zero_time_cb;
|
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++;
|
callback_count_start_of_sim++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -127,12 +127,12 @@ void vpi_compat_bootstrap(void) {
|
||||||
cb_data.reason = cbStartOfSimulation;
|
cb_data.reason = cbStartOfSimulation;
|
||||||
cb_data.time = 0;
|
cb_data.time = 0;
|
||||||
cb_data.cb_rtn = _start_of_sim_cb;
|
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.reason = cbEndOfSimulation;
|
||||||
cb_data.time = 0;
|
cb_data.time = 0;
|
||||||
cb_data.cb_rtn = _end_of_sim_cb;
|
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
|
// icarus entry
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue