From 24e32420b7a81d537130f478b71a9913aec283bd Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 3 Oct 2013 07:24:50 -0400 Subject: [PATCH] Fix multiple VPI variable callbacks, bug679. --- Changes | 1 + include/verilated_vpi.h | 31 +++++++++++++++++-------------- test_regress/t/t_vpi_var.cpp | 29 +++++++++++++++++++++++++++-- 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/Changes b/Changes index 5373153cc..654e2eb47 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,7 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.854 devel +**** Fix multiple VPI variable callbacks, bug679. [Rich Porter] * Verilator 3.853 2013-09-30 diff --git a/include/verilated_vpi.h b/include/verilated_vpi.h index 1c1a123f3..e1d9d1e7f 100644 --- a/include/verilated_vpi.h +++ b/include/verilated_vpi.h @@ -38,7 +38,7 @@ // Internal macros #define _VL_VPI_INTERNAL VerilatedVpi::error_info()->setMessage(vpiInternal)->setMessage -#define _VL_VPI_SYSTEM VerilatedVpi::error_info()->setMessage(vpiSystem )->setMessage +#define _VL_VPI_SYSTEM VerilatedVpi::error_info()->setMessage(vpiSystem )->setMessage #define _VL_VPI_ERROR VerilatedVpi::error_info()->setMessage(vpiError )->setMessage #define _VL_VPI_WARNING VerilatedVpi::error_info()->setMessage(vpiWarning )->setMessage #define _VL_VPI_NOTICE VerilatedVpi::error_info()->setMessage(vpiNotice )->setMessage @@ -52,6 +52,8 @@ // Implementation #include +#include +#include #define VL_DEBUG_IF_PLI VL_DEBUG_IF #define VL_VPI_LINE_SIZE 8192 @@ -301,7 +303,7 @@ class VerilatedVpiError; class VerilatedVpi { enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime+1 }; // Maxium callback reason - typedef set VpioCbSet; + typedef list VpioCbList; typedef set,VerilatedVpiTimedCbsCmp > VpioTimedCbs; struct product_info { @@ -309,7 +311,7 @@ class VerilatedVpi { PLI_BYTE8* version; }; - VpioCbSet m_cbObjSets[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 VerilatedVpiError* m_errorInfop; // Container for vpi error info @@ -325,17 +327,14 @@ public: } } if (VL_UNLIKELY(vop->reason() >= CB_ENUM_MAX_VALUE)) vl_fatal(__FILE__,__LINE__,"", "vpi bb reason too large"); - s_s.m_cbObjSets[vop->reason()].insert(vop); + s_s.m_cbObjLists[vop->reason()].push_back(vop); } static void cbTimedAdd(VerilatedVpioCb* vop) { s_s.m_timedCbs.insert(make_pair(vop->time(), vop)); } static void cbReasonRemove(VerilatedVpioCb* cbp) { - VpioCbSet& cbObjSet = s_s.m_cbObjSets[cbp->reason()]; - VpioCbSet::iterator it=cbObjSet.find(cbp); - if (VL_LIKELY(it != cbObjSet.end())) { - cbObjSet.erase(it); - } + VpioCbList& cbObjList = s_s.m_cbObjLists[cbp->reason()]; + cbObjList.remove(cbp); } static void cbTimedRemove(VerilatedVpioCb* cbp) { VpioTimedCbs::iterator it=s_s.m_timedCbs.find(make_pair(cbp->time(),cbp)); @@ -364,8 +363,8 @@ public: } } static void callCbs(vluint32_t reason) { - VpioCbSet& cbObjSet = s_s.m_cbObjSets[reason]; - for (VpioCbSet::iterator it=cbObjSet.begin(); it!=cbObjSet.end();) { + VpioCbList& cbObjList = s_s.m_cbObjLists[reason]; + for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end();) { VerilatedVpioCb* vop = *it; ++it; // iterator may be deleted by callback VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: reason_callback %d %p\n",reason,vop);); @@ -373,8 +372,9 @@ public: } } static void callValueCbs() { - VpioCbSet& cbObjSet = s_s.m_cbObjSets[cbValueChange]; - for (VpioCbSet::iterator it=cbObjSet.begin(); it!=cbObjSet.end();) { + VpioCbList& cbObjList = s_s.m_cbObjLists[cbValueChange]; + set update; // set of objects to update after callbacks + for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end();) { VerilatedVpioCb* vop = *it; ++it; // iterator may be deleted by callback if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) { @@ -386,12 +386,15 @@ public: if (memcmp(prevDatap, newDatap, varop->entSize())) { VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: value_callback %p %s v[0]=%d\n", vop,varop->fullname(), *((CData*)newDatap));); - memcpy(prevDatap, newDatap, varop->entSize()); + update.insert(varop); vpi_get_value(vop->cb_datap()->obj, vop->cb_datap()->value); (vop->cb_rtnp()) (vop->cb_datap()); } } } + for (set::iterator it=update.begin(); it!=update.end(); it++ ) { + memcpy((*it)->prevDatap(), (*it)->varDatap(), (*it)->entSize()); + } } static VerilatedVpiError* error_info(); // getter for vpi error info diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index 893f91860..42ae963a3 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -30,13 +30,28 @@ #define TEST_MSG if (0) printf +struct countdown { + int count; + vpiHandle callback; + countdown(int count) : count(count) {}; + int decrement() { + if (!--count) { + vpi_remove_cb(callback); + } + return count; + } + PLI_BYTE8* ptr() { + return (PLI_BYTE8*)this; + } +}; + unsigned int main_time = false; unsigned int callback_count = false; unsigned int callback_count_half = false; unsigned int callback_count_quad = false; unsigned int callback_count_strs = false; unsigned int callback_count_strs_max = 500; - +countdown cd(200); //====================================================================== @@ -142,6 +157,10 @@ int _value_callback(p_cb_data cb_data) { return 0; } +int _value_callback_countdown(p_cb_data cb_data) { + return ((countdown*)(cb_data->user_data))->decrement() < 1; +}; + int _value_callback_half(p_cb_data cb_data) { CHECK_RESULT(cb_data->value->value.integer*2+10, main_time); callback_count_half++; @@ -170,10 +189,15 @@ int _mon_check_value_callbacks() { cb_data.cb_rtn = _value_callback; cb_data.obj = vh1; cb_data.value = &v; - vpiHandle vh = vpi_register_cb(&cb_data); CHECK_RESULT_NZ(vh); + // add another callback to same signal + cb_data.cb_rtn = _value_callback_countdown; + cb_data.user_data = cd.ptr(); + cd.callback = vpi_register_cb(&cb_data); + CHECK_RESULT_NZ(cd.callback); + vh1 = vpi_handle_by_name((PLI_BYTE8*)"t.half_count", NULL); CHECK_RESULT_NZ(vh1); @@ -628,6 +652,7 @@ int main(int argc, char **argv, char **env) { CHECK_RESULT(callback_count_half, 250); CHECK_RESULT(callback_count_quad, 2); CHECK_RESULT(callback_count_strs, callback_count_strs_max); + CHECK_RESULT(cd.count, 0) if (!Verilated::gotFinish()) { vl_fatal(FILENM,__LINE__,"main", "%Error: Timeout; never got a $finish"); }