diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 02db32636..634b5a6b7 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -32,18 +32,484 @@ #include "verilated.h" #include "verilated_vpi.h" +#include +#include +#include + +//====================================================================== +// Internal constants + +#define VL_DEBUG_IF_PLI VL_DEBUG_IF +#define VL_VPI_LINE_SIZE 8192 + +//====================================================================== +// Internal macros + +#define _VL_VPI_INTERNAL VerilatedVpiImp::error_info()->setMessage(vpiInternal)->setMessage +#define _VL_VPI_SYSTEM VerilatedVpiImp::error_info()->setMessage(vpiSystem )->setMessage +#define _VL_VPI_ERROR VerilatedVpiImp::error_info()->setMessage(vpiError )->setMessage +#define _VL_VPI_WARNING VerilatedVpiImp::error_info()->setMessage(vpiWarning )->setMessage +#define _VL_VPI_NOTICE VerilatedVpiImp::error_info()->setMessage(vpiNotice )->setMessage +#define _VL_VPI_ERROR_RESET VerilatedVpiImp::error_info()->resetError + +// Not supported yet +#define _VL_VPI_UNIMP() \ + _VL_VPI_ERROR(__FILE__,__LINE__,Verilated::catName("Unsupported VPI function: ",VL_FUNC)); + +//====================================================================== +// Implementation + +// Base VPI handled object +class VerilatedVpio { + // MEM MANGLEMENT + static vluint8_t* s_freeHead; + +public: + // CONSTRUCTORS + VerilatedVpio() {} + virtual ~VerilatedVpio() {} + inline static void* operator new(size_t size) { + // We new and delete tons of vpi structures, so keep them around + // To simplify our free list, we use a size large enough for all derived types + // We reserve word zero for the next pointer, as that's safer in case a + // dangling reference to the original remains around. + static size_t chunk = 96; + if (VL_UNLIKELY(size>chunk)) VL_FATAL_MT(__FILE__,__LINE__,"", "increase chunk"); + if (VL_LIKELY(s_freeHead)) { + vluint8_t* newp = s_freeHead; + s_freeHead = *((vluint8_t**)newp); + return newp+8; + } else { + // +8: 8 bytes for next + vluint8_t* newp = reinterpret_cast(::operator new(chunk+8)); + return newp+8; + } + } + inline static void operator delete(void* obj, size_t size) { + vluint8_t* oldp = ((vluint8_t*)obj)-8; + *((void**)oldp) = s_freeHead; + s_freeHead = oldp; + } + // MEMBERS + static inline VerilatedVpio* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + inline vpiHandle castVpiHandle() { return reinterpret_cast(this); } + // ACCESSORS + virtual const char* name() const { return ""; } + virtual const char* fullname() const { return ""; } + virtual const char* defname() const { return ""; } + virtual vluint32_t type() const { return 0; } + virtual vluint32_t size() const { return 0; } + virtual const VerilatedRange* rangep() const { return NULL; } + virtual vpiHandle dovpi_scan() { return 0; } +}; + +typedef PLI_INT32 (*VerilatedPliCb)(struct t_cb_data *); + +class VerilatedVpioCb : public VerilatedVpio { + t_cb_data m_cbData; + s_vpi_value m_value; + QData m_time; +public: + // cppcheck-suppress uninitVar // m_value + VerilatedVpioCb(const t_cb_data* cbDatap, QData time) + : m_cbData(*cbDatap), m_time(time) { + m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal; + m_cbData.value = &m_value; + } + virtual ~VerilatedVpioCb() {} + static inline VerilatedVpioCb* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + virtual vluint32_t type() const { 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; } +}; + +class VerilatedVpioConst : public VerilatedVpio { + vlsint32_t m_num; +public: + explicit VerilatedVpioConst(vlsint32_t num) : m_num(num) {} + virtual ~VerilatedVpioConst() {} + static inline VerilatedVpioConst* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + virtual vluint32_t type() const { return vpiUndefined; } + vlsint32_t num() const { return m_num; } +}; + +class VerilatedVpioRange : public VerilatedVpio { + const VerilatedRange* m_range; + vlsint32_t m_iteration; +public: + explicit VerilatedVpioRange(const VerilatedRange* range) : m_range(range), m_iteration(0) {} + virtual ~VerilatedVpioRange() {} + static inline VerilatedVpioRange* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + virtual vluint32_t type() const { return vpiRange; } + virtual vluint32_t size() const { return m_range->elements(); } + virtual const VerilatedRange* rangep() const { return m_range; } + int iteration() const { return m_iteration; } + void iterationInc() { ++m_iteration; } + virtual vpiHandle dovpi_scan() { + if (!iteration()) { + VerilatedVpioRange* nextp = new VerilatedVpioRange(*this); + nextp->iterationInc(); + return ((nextp)->castVpiHandle()); + } else { + return 0; // End of list - only one deep + } + } +}; + +class VerilatedVpioScope : public VerilatedVpio { + const VerilatedScope* m_scopep; +public: + explicit VerilatedVpioScope(const VerilatedScope* scopep) + : m_scopep(scopep) {} + virtual ~VerilatedVpioScope() {} + static inline VerilatedVpioScope* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + virtual vluint32_t type() const { return vpiScope; } + const VerilatedScope* scopep() const { return m_scopep; } + virtual const char* name() const { return m_scopep->name(); } + virtual const char* fullname() const { return m_scopep->name(); } +}; + +class VerilatedVpioVar : public VerilatedVpio { + const VerilatedVar* m_varp; + const VerilatedScope* m_scopep; + vluint8_t* m_prevDatap; // 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 +protected: + void* m_varDatap; // varp()->datap() adjusted for array entries + vlsint32_t m_index; + const VerilatedRange& get_range() const { + // Determine number of dimensions and return outermost + return (m_varp->dims()>1) ? m_varp->array() : m_varp->range(); + } +public: + VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep) + : m_varp(varp), m_scopep(scopep), m_index(0) { + m_prevDatap = NULL; + m_mask.u32 = VL_MASK_I(varp->range().elements()); + m_entSize = varp->entSize(); + m_varDatap = varp->datap(); + } + virtual ~VerilatedVpioVar() { + if (m_prevDatap) { delete [] m_prevDatap; m_prevDatap = NULL; } + } + static inline VerilatedVpioVar* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + const VerilatedVar* varp() const { return m_varp; } + const VerilatedScope* scopep() const { return m_scopep; } + vluint32_t mask() const { return m_mask.u32; } + vluint8_t mask_byte(int idx) { return m_mask.u8[idx & 3]; } + vluint32_t entSize() const { return m_entSize; } + vluint32_t index() { return m_index; } + virtual vluint32_t type() const { + if (varp()->vldir() != vpiNoDirection) return vpiPort; + return (varp()->dims()>1) ? vpiMemory : vpiReg; // but might be wire, logic + } + virtual vluint32_t size() const { return get_range().elements(); } + virtual const VerilatedRange* rangep() const { return &get_range(); } + virtual const char* name() const { return m_varp->name(); } + virtual const char* fullname() const { + static VL_THREAD_LOCAL std::string out; + out = std::string(m_scopep->name())+"."+name(); + return out.c_str(); + } + void* prevDatap() const { return m_prevDatap; } + void* varDatap() const { return m_varDatap; } + void createPrevDatap() { + if (VL_UNLIKELY(!m_prevDatap)) { + m_prevDatap = new vluint8_t [entSize()]; + memcpy(prevDatap(), varp()->datap(), entSize()); + } + } +}; + +class VerilatedVpioMemoryWord : public VerilatedVpioVar { +public: + VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep, + vlsint32_t index, int offset) + : VerilatedVpioVar(varp, scopep) { + m_index = index; + m_varDatap = ((vluint8_t*)varp->datap()) + entSize()*offset; + } + virtual ~VerilatedVpioMemoryWord() {} + static inline VerilatedVpioMemoryWord* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + virtual vluint32_t type() const { return vpiMemoryWord; } + virtual vluint32_t size() const { return varp()->range().elements(); } + virtual const VerilatedRange* rangep() const { return &(varp()->range()); } + virtual const char* fullname() const { + static VL_THREAD_LOCAL std::string out; + char num[20]; sprintf(num,"%d",m_index); + out = std::string(scopep()->name())+"."+name()+"["+num+"]"; + return out.c_str(); + } +}; + +class VerilatedVpioVarIter : public VerilatedVpio { + const VerilatedScope* m_scopep; + VerilatedVarNameMap::const_iterator m_it; + bool m_started; +public: + explicit VerilatedVpioVarIter(const VerilatedScope* scopep) + : m_scopep(scopep), m_started(false) { } + virtual ~VerilatedVpioVarIter() {} + static inline VerilatedVpioVarIter* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + virtual vluint32_t type() const { return vpiIterator; } + virtual vpiHandle dovpi_scan() { + if (VL_LIKELY(m_scopep->varsp())) { + VerilatedVarNameMap* varsp = m_scopep->varsp(); + if (VL_UNLIKELY(!m_started)) { m_it = varsp->begin(); m_started=true; } + else if (VL_UNLIKELY(m_it == varsp->end())) return 0; + else ++m_it; + if (m_it == varsp->end()) return 0; + return ((new VerilatedVpioVar(&(m_it->second), m_scopep)) + ->castVpiHandle()); + } else { + return 0; // End of list - only one deep + } + } +}; + +class VerilatedVpioMemoryWordIter : public VerilatedVpio { + const vpiHandle m_handle; + const VerilatedVar* m_varp; + vlsint32_t m_iteration; + vlsint32_t m_direction; + bool m_done; +public: + VerilatedVpioMemoryWordIter(const vpiHandle handle, const VerilatedVar* varp) + : m_handle(handle), m_varp(varp), m_iteration(varp->array().right()), m_direction(VL_LIKELY(varp->array().left()>varp->array().right())?1:-1), m_done(false) { } + virtual ~VerilatedVpioMemoryWordIter() {} + static inline VerilatedVpioMemoryWordIter* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + virtual vluint32_t type() const { return vpiIterator; } + void iterationInc() { + if (!(m_done = (m_iteration == m_varp->array().left()))) m_iteration+=m_direction; + } + virtual vpiHandle dovpi_scan() { + vpiHandle result; + if (m_done) return 0; + result = vpi_handle_by_index(m_handle, m_iteration); + iterationInc(); + return result; + } +}; + //====================================================================== -VerilatedVpi VerilatedVpi::s_s; // Singleton +struct VerilatedVpiTimedCbsCmp { + /// Ordering sets keyed by time, then callback descriptor + bool operator() (const std::pair& a, + const std::pair& b) const { + if (a.first < b.first) return 1; + if (a.first > b.first) return 0; + return a.second < b.second; + } +}; + +class VerilatedVpiError; + +class VerilatedVpiImp { + enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime+1 }; // Maxium callback reason + typedef std::list VpioCbList; + typedef std::set,VerilatedVpiTimedCbsCmp > VpioTimedCbs; + + struct product_info { + PLI_BYTE8* product; + }; + + 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 + + static VerilatedVpiImp s_s; // Singleton + +public: + VerilatedVpiImp() { m_errorInfop=NULL; } + ~VerilatedVpiImp() {} + static void cbReasonAdd(VerilatedVpioCb* vop) { + if (vop->reason() == cbValueChange) { + if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) { + varop->createPrevDatap(); + } + } + if (VL_UNLIKELY(vop->reason() >= CB_ENUM_MAX_VALUE)) VL_FATAL_MT(__FILE__,__LINE__,"", "vpi bb reason too large"); + s_s.m_cbObjLists[vop->reason()].push_back(vop); + } + static void cbTimedAdd(VerilatedVpioCb* vop) { + s_s.m_timedCbs.insert(std::make_pair(vop->time(), vop)); + } + static void cbReasonRemove(VerilatedVpioCb* cbp) { + VpioCbList& cbObjList = s_s.m_cbObjLists[cbp->reason()]; + // We do not remove it now as we may be iterating the list, + // instead set to NULL and will cleanup later + for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end(); ++it) { + if (*it == cbp) *it = NULL; + } + } + static void cbTimedRemove(VerilatedVpioCb* cbp) { + VpioTimedCbs::iterator 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 callTimedCbs() { + QData time = VL_TIME_Q(); + for (VpioTimedCbs::iterator it=s_s.m_timedCbs.begin(); it!=s_s.m_timedCbs.end(); ) { + if (VL_UNLIKELY(it->first <= time)) { + VerilatedVpioCb* vop = it->second; + ++it; // iterator may be deleted by callback + VL_DEBUG_IF_PLI(VL_PRINTF_MT("-vltVpi: timed_callback %p\n",vop);); + (vop->cb_rtnp()) (vop->cb_datap()); + } + else { ++it; } + } + } + static QData cbNextDeadline() { + VpioTimedCbs::const_iterator it=s_s.m_timedCbs.begin(); + if (VL_LIKELY(it!=s_s.m_timedCbs.end())) { + return it->first; + } else { + return ~VL_ULL(0); // maxquad + } + } + static void callCbs(vluint32_t reason) { + VpioCbList& cbObjList = s_s.m_cbObjLists[reason]; + for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end();) { + if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup + it = cbObjList.erase(it); + continue; + } + VerilatedVpioCb* vop = *it++; + VL_DEBUG_IF_PLI(VL_PRINTF_MT("-vltVpi: reason_callback %d %p\n",reason,vop);); + (vop->cb_rtnp()) (vop->cb_datap()); + } + } + static void callValueCbs() { + VpioCbList& cbObjList = s_s.m_cbObjLists[cbValueChange]; + typedef std::set VpioVarSet; + VpioVarSet update; // set of objects to update after callbacks + for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end();) { + if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup + it = cbObjList.erase(it); + continue; + } + VerilatedVpioCb* vop = *it++; + if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) { + void* newDatap = varop->varDatap(); + void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback + VL_DEBUG_IF_PLI(VL_PRINTF_MT("-vltVpi: value_test %s v[0]=%d/%d %p %p\n", + varop->fullname(), *((CData*)newDatap), *((CData*)prevDatap), + newDatap, prevDatap);); + if (memcmp(prevDatap, newDatap, varop->entSize())) { + VL_DEBUG_IF_PLI(VL_PRINTF_MT("-vltVpi: value_callback %p %s v[0]=%d\n", + vop,varop->fullname(), *((CData*)newDatap));); + update.insert(varop); + vpi_get_value(vop->cb_datap()->obj, vop->cb_datap()->value); + (vop->cb_rtnp()) (vop->cb_datap()); + } + } + } + for (VpioVarSet::const_iterator it=update.begin(); it!=update.end(); ++it) { + memcpy((*it)->prevDatap(), (*it)->varDatap(), (*it)->entSize()); + } + } + + static VerilatedVpiError* error_info(); // getter for vpi error info +}; + +class VerilatedVpiError { + //// Container for vpi error info + + t_vpi_error_info m_errorInfo; + bool m_flag; + char m_buff[VL_VPI_LINE_SIZE]; + void setError(PLI_BYTE8 *message, PLI_BYTE8 *code, PLI_BYTE8 *file, PLI_INT32 line) { + m_errorInfo.message = message; + m_errorInfo.file = file; + m_errorInfo.line = line; + m_errorInfo.code = code; + do_callbacks(); + } + void do_callbacks() { + if (getError()->level >= vpiError && Verilated::fatalOnVpiError()) { + // Stop on vpi error/unsupported + vpi_unsupported(); + } + // We need to run above code first because in the case that the callback executes further vpi + // functions we will loose the error as it will be overwritten. + VerilatedVpiImp::callCbs(cbPLIError); + } +public: + + VerilatedVpiError() : m_flag(false) { + m_buff[0] = '\0'; + m_errorInfo.product = (PLI_BYTE8*)Verilated::productName(); + } + ~VerilatedVpiError() {} + static void selfTest(); + VerilatedVpiError* setMessage(PLI_INT32 level) { + m_flag=true; + m_errorInfo.level = level; + return this; + } + void setMessage(std::string file, PLI_INT32 line, std::string message, ...) { + static VL_THREAD_LOCAL std::string filehold; + va_list args; + va_start(args, message); + VL_VSNPRINTF(m_buff, sizeof(m_buff), message.c_str(), args); + va_end(args); + m_errorInfo.state = vpiPLI; + filehold = file; + setError((PLI_BYTE8*)m_buff, NULL, (PLI_BYTE8*)filehold.c_str(), line); + } + p_vpi_error_info getError() { + if (m_flag) return &m_errorInfo; + return NULL; + } + void resetError() { + m_flag=false; + } + static void vpi_unsupported() { + // Not supported yet + p_vpi_error_info error_info_p = VerilatedVpiImp::error_info()->getError(); + if (error_info_p) { + VL_FATAL_MT(error_info_p->file, error_info_p->line, "", error_info_p->message); + return; + } + VL_FATAL_MT(__FILE__, __LINE__, "", "vpi_unsupported called without error info set"); + } + static const char* strFromVpiVal(PLI_INT32 vpiVal); + static const char* strFromVpiObjType(PLI_INT32 vpiVal); + static const char* strFromVpiMethod(PLI_INT32 vpiVal); + static const char* strFromVpiCallbackReason(PLI_INT32 vpiVal); + static const char* strFromVpiProp(PLI_INT32 vpiVal); +}; + +//====================================================================== + +VerilatedVpiImp VerilatedVpiImp::s_s; // Singleton vluint8_t* VerilatedVpio::s_freeHead = NULL; //====================================================================== -// VerilatedVpi Methods +// VerilatedVpi implementation +void VerilatedVpi::callTimedCbs() { + VerilatedVpiImp::callTimedCbs(); +} -VerilatedVpiError* VerilatedVpi::error_info() { - if (s_s.m_errorInfop == NULL) { - s_s.m_errorInfop = new VerilatedVpiError(); +void VerilatedVpi::callValueCbs() { + VerilatedVpiImp::callValueCbs(); +} + +//====================================================================== +// VerilatedVpiImp implementation + +VerilatedVpiError* VerilatedVpiImp::error_info() { + if (VL_UNLIKELY(!s_s.m_errorInfop)) { + s_s.m_errorInfop = new VerilatedVpiError(); } return s_s.m_errorInfop; } @@ -398,6 +864,9 @@ const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) { CHECK_RESULT_CSTR(strVal, #enum); \ } while (0) +void VerilatedVpi::selfTest() { + VerilatedVpiError::selfTest(); +} void VerilatedVpiError::selfTest() { CHECK_ENUM_STR(strFromVpiVal, vpiBinStrVal); CHECK_ENUM_STR(strFromVpiVal, vpiRawFourStateVal); @@ -445,7 +914,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) { 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_PRINTF_MT("-vltVpi: vpi_register_cb %d %p delay=%" VL_PRI64 "u\n",cb_data_p->reason,vop,time);); - VerilatedVpi::cbTimedAdd(vop); + VerilatedVpiImp::cbTimedAdd(vop); return vop->castVpiHandle(); } case cbReadWriteSynch: // FALLTHRU // Supported via vlt_main.cpp @@ -460,7 +929,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) { 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_PRINTF_MT("-vltVpi: vpi_register_cb %d %p\n",cb_data_p->reason,vop);); - VerilatedVpi::cbReasonAdd(vop); + VerilatedVpiImp::cbReasonAdd(vop); return vop->castVpiHandle(); } default: @@ -476,9 +945,9 @@ PLI_INT32 vpi_remove_cb(vpiHandle object) { _VL_VPI_ERROR_RESET(); // reset vpi error status if (VL_UNLIKELY(!vop)) return 0; if (vop->cb_datap()->reason == cbAfterDelay) { - VerilatedVpi::cbTimedRemove(vop); + VerilatedVpiImp::cbTimedRemove(vop); } else { - VerilatedVpi::cbReasonRemove(vop); + VerilatedVpiImp::cbReasonRemove(vop); } return 1; } @@ -1339,7 +1808,7 @@ PLI_INT32 vpi_compare_objects(vpiHandle object1, vpiHandle object2) { PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) { // executing vpi_chk_error does not reset error // error_info_p can be NULL, so only return level in that case - p_vpi_error_info _error_info_p = VerilatedVpi::error_info()->getError(); + p_vpi_error_info _error_info_p = VerilatedVpiImp::error_info()->getError(); if (error_info_p && _error_info_p) { *error_info_p = *_error_info_p; } diff --git a/include/verilated_vpi.h b/include/verilated_vpi.h index fb97399be..95a0c1b27 100644 --- a/include/verilated_vpi.h +++ b/include/verilated_vpi.h @@ -16,8 +16,8 @@ /// \file /// \brief Verilator: VPI implementation code /// -/// This file must be compiled and linked against all objects -/// created from Verilator or called by Verilator that use the VPI. +/// This file must be compiled and linked against all objects +/// created from Verilator or called by Verilator that use the VPI. /// /// Code available from: http://www.veripool.org/verilator /// @@ -29,472 +29,24 @@ #include "verilated.h" #include "verilated_syms.h" -#include -#include -#include - //====================================================================== // From IEEE 1800-2009 annex K #include "vltstd/vpi_user.h" //====================================================================== -// Internal constants - -#define VL_DEBUG_IF_PLI VL_DEBUG_IF -#define VL_VPI_LINE_SIZE 8192 - -//====================================================================== -// 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_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 -#define _VL_VPI_ERROR_RESET VerilatedVpi::error_info()->resetError - -// Not supported yet -#define _VL_VPI_UNIMP() \ - _VL_VPI_ERROR(__FILE__,__LINE__,Verilated::catName("Unsupported VPI function: ",VL_FUNC)); - -//====================================================================== -// Implementation - -// Base VPI handled object -class VerilatedVpio { - // MEM MANGLEMENT - static vluint8_t* s_freeHead; - -public: - // CONSTRUCTORS - VerilatedVpio() {} - virtual ~VerilatedVpio() {} - inline static void* operator new(size_t size) { - // We new and delete tons of vpi structures, so keep them around - // To simplify our free list, we use a size large enough for all derived types - // We reserve word zero for the next pointer, as that's safer in case a - // dangling reference to the original remains around. - static size_t chunk = 96; - if (VL_UNLIKELY(size>chunk)) VL_FATAL_MT(__FILE__,__LINE__,"", "increase chunk"); - if (VL_LIKELY(s_freeHead)) { - vluint8_t* newp = s_freeHead; - s_freeHead = *((vluint8_t**)newp); - return newp+8; - } else { - // +8: 8 bytes for next - vluint8_t* newp = reinterpret_cast(::operator new(chunk+8)); - return newp+8; - } - } - inline static void operator delete(void* obj, size_t size) { - vluint8_t* oldp = ((vluint8_t*)obj)-8; - *((void**)oldp) = s_freeHead; - s_freeHead = oldp; - } - // MEMBERS - static inline VerilatedVpio* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } - inline vpiHandle castVpiHandle() { return reinterpret_cast(this); } - // ACCESSORS - virtual const char* name() const { return ""; } - virtual const char* fullname() const { return ""; } - virtual const char* defname() const { return ""; } - virtual vluint32_t type() const { return 0; } - virtual vluint32_t size() const { return 0; } - virtual const VerilatedRange* rangep() const { return NULL; } - virtual vpiHandle dovpi_scan() { return 0; } -}; - -typedef PLI_INT32 (*VerilatedPliCb)(struct t_cb_data *); - -class VerilatedVpioCb : public VerilatedVpio { - t_cb_data m_cbData; - s_vpi_value m_value; - QData m_time; -public: - // cppcheck-suppress uninitVar // m_value - VerilatedVpioCb(const t_cb_data* cbDatap, QData time) - : m_cbData(*cbDatap), m_time(time) { - m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal; - m_cbData.value = &m_value; - } - virtual ~VerilatedVpioCb() {} - static inline VerilatedVpioCb* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } - virtual vluint32_t type() const { 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; } -}; - -class VerilatedVpioConst : public VerilatedVpio { - vlsint32_t m_num; -public: - explicit VerilatedVpioConst(vlsint32_t num) : m_num(num) {} - virtual ~VerilatedVpioConst() {} - static inline VerilatedVpioConst* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } - virtual vluint32_t type() const { return vpiUndefined; } - vlsint32_t num() const { return m_num; } -}; - -class VerilatedVpioRange : public VerilatedVpio { - const VerilatedRange* m_range; - vlsint32_t m_iteration; -public: - explicit VerilatedVpioRange(const VerilatedRange* range) : m_range(range), m_iteration(0) {} - virtual ~VerilatedVpioRange() {} - static inline VerilatedVpioRange* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } - virtual vluint32_t type() const { return vpiRange; } - virtual vluint32_t size() const { return m_range->elements(); } - virtual const VerilatedRange* rangep() const { return m_range; } - int iteration() const { return m_iteration; } - void iterationInc() { ++m_iteration; } - virtual vpiHandle dovpi_scan() { - if (!iteration()) { - VerilatedVpioRange* nextp = new VerilatedVpioRange(*this); - nextp->iterationInc(); - return ((nextp)->castVpiHandle()); - } else { - return 0; // End of list - only one deep - } - } -}; - -class VerilatedVpioScope : public VerilatedVpio { - const VerilatedScope* m_scopep; -public: - explicit VerilatedVpioScope(const VerilatedScope* scopep) - : m_scopep(scopep) {} - virtual ~VerilatedVpioScope() {} - static inline VerilatedVpioScope* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } - virtual vluint32_t type() const { return vpiScope; } - const VerilatedScope* scopep() const { return m_scopep; } - virtual const char* name() const { return m_scopep->name(); } - virtual const char* fullname() const { return m_scopep->name(); } -}; - -class VerilatedVpioVar : public VerilatedVpio { - const VerilatedVar* m_varp; - const VerilatedScope* m_scopep; - vluint8_t* m_prevDatap; // 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 -protected: - void* m_varDatap; // varp()->datap() adjusted for array entries - vlsint32_t m_index; - const VerilatedRange& get_range() const { - // Determine number of dimensions and return outermost - return (m_varp->dims()>1) ? m_varp->array() : m_varp->range(); - } -public: - VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep) - : m_varp(varp), m_scopep(scopep), m_index(0) { - m_prevDatap = NULL; - m_mask.u32 = VL_MASK_I(varp->range().elements()); - m_entSize = varp->entSize(); - m_varDatap = varp->datap(); - } - virtual ~VerilatedVpioVar() { - if (m_prevDatap) { delete [] m_prevDatap; m_prevDatap = NULL; } - } - static inline VerilatedVpioVar* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } - const VerilatedVar* varp() const { return m_varp; } - const VerilatedScope* scopep() const { return m_scopep; } - vluint32_t mask() const { return m_mask.u32; } - vluint8_t mask_byte(int idx) { return m_mask.u8[idx & 3]; } - vluint32_t entSize() const { return m_entSize; } - vluint32_t index() { return m_index; } - virtual vluint32_t type() const { - if (varp()->vldir() != vpiNoDirection) return vpiPort; - return (varp()->dims()>1) ? vpiMemory : vpiReg; /* but might be wire, logic */ - } - virtual vluint32_t size() const { return get_range().elements(); } - virtual const VerilatedRange* rangep() const { return &get_range(); } - virtual const char* name() const { return m_varp->name(); } - virtual const char* fullname() const { - static VL_THREAD_LOCAL std::string out; - out = std::string(m_scopep->name())+"."+name(); - return out.c_str(); - } - void* prevDatap() const { return m_prevDatap; } - void* varDatap() const { return m_varDatap; } - void createPrevDatap() { - if (VL_UNLIKELY(!m_prevDatap)) { - m_prevDatap = new vluint8_t [entSize()]; - memcpy(prevDatap(), varp()->datap(), entSize()); - } - } -}; - -class VerilatedVpioMemoryWord : public VerilatedVpioVar { -public: - VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep, - vlsint32_t index, int offset) - : VerilatedVpioVar(varp, scopep) { - m_index = index; - m_varDatap = ((vluint8_t*)varp->datap()) + entSize()*offset; - } - virtual ~VerilatedVpioMemoryWord() {} - static inline VerilatedVpioMemoryWord* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } - virtual vluint32_t type() const { return vpiMemoryWord; } - virtual vluint32_t size() const { return varp()->range().elements(); } - virtual const VerilatedRange* rangep() const { return &(varp()->range()); } - virtual const char* fullname() const { - static VL_THREAD_LOCAL std::string out; - char num[20]; sprintf(num,"%d",m_index); - out = std::string(scopep()->name())+"."+name()+"["+num+"]"; - return out.c_str(); - } -}; - -class VerilatedVpioVarIter : public VerilatedVpio { - const VerilatedScope* m_scopep; - VerilatedVarNameMap::const_iterator m_it; - bool m_started; -public: - explicit VerilatedVpioVarIter(const VerilatedScope* scopep) - : m_scopep(scopep), m_started(false) { } - virtual ~VerilatedVpioVarIter() {} - static inline VerilatedVpioVarIter* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } - virtual vluint32_t type() const { return vpiIterator; } - virtual vpiHandle dovpi_scan() { - if (VL_LIKELY(m_scopep->varsp())) { - VerilatedVarNameMap* varsp = m_scopep->varsp(); - if (VL_UNLIKELY(!m_started)) { m_it = varsp->begin(); m_started=true; } - else if (VL_UNLIKELY(m_it == varsp->end())) return 0; - else ++m_it; - if (m_it == varsp->end()) return 0; - return ((new VerilatedVpioVar(&(m_it->second), m_scopep)) - ->castVpiHandle()); - } else { - return 0; // End of list - only one deep - } - } -}; - -class VerilatedVpioMemoryWordIter : public VerilatedVpio { - const vpiHandle m_handle; - const VerilatedVar* m_varp; - vlsint32_t m_iteration; - vlsint32_t m_direction; - bool m_done; -public: - VerilatedVpioMemoryWordIter(const vpiHandle handle, const VerilatedVar* varp) - : m_handle(handle), m_varp(varp), m_iteration(varp->array().right()), m_direction(VL_LIKELY(varp->array().left()>varp->array().right())?1:-1), m_done(false) { } - virtual ~VerilatedVpioMemoryWordIter() {} - static inline VerilatedVpioMemoryWordIter* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } - virtual vluint32_t type() const { return vpiIterator; } - void iterationInc() { - if (!(m_done = (m_iteration == m_varp->array().left()))) m_iteration+=m_direction; - } - virtual vpiHandle dovpi_scan() { - vpiHandle result; - if (m_done) return 0; - result = vpi_handle_by_index(m_handle, m_iteration); - iterationInc(); - return result; - } -}; - -//====================================================================== - -struct VerilatedVpiTimedCbsCmp { - /// Ordering sets keyed by time, then callback descriptor - bool operator() (const std::pair& a, - const std::pair& b) const { - if (a.first < b.first) return 1; - if (a.first > b.first) return 0; - return a.second < b.second; - } -}; - -class VerilatedVpiError; class VerilatedVpi { - enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime+1 }; // Maxium callback reason - typedef std::list VpioCbList; - typedef std::set,VerilatedVpiTimedCbsCmp > VpioTimedCbs; - - struct product_info { - PLI_BYTE8* product; - }; - - 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 - - static VerilatedVpi s_s; // Singleton - public: - VerilatedVpi() { m_errorInfop=NULL; } - ~VerilatedVpi() {} - static void cbReasonAdd(VerilatedVpioCb* vop) { - if (vop->reason() == cbValueChange) { - if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) { - varop->createPrevDatap(); - } - } - if (VL_UNLIKELY(vop->reason() >= CB_ENUM_MAX_VALUE)) VL_FATAL_MT(__FILE__,__LINE__,"", "vpi bb reason too large"); - s_s.m_cbObjLists[vop->reason()].push_back(vop); - } - static void cbTimedAdd(VerilatedVpioCb* vop) { - s_s.m_timedCbs.insert(std::make_pair(vop->time(), vop)); - } - static void cbReasonRemove(VerilatedVpioCb* cbp) { - VpioCbList& cbObjList = s_s.m_cbObjLists[cbp->reason()]; - // We do not remove it now as we may be iterating the list, - // instead set to NULL and will cleanup later - for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end(); ++it) { - if (*it == cbp) *it = NULL; - } - } - static void cbTimedRemove(VerilatedVpioCb* cbp) { - VpioTimedCbs::iterator 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 callTimedCbs() { - QData time = VL_TIME_Q(); - for (VpioTimedCbs::iterator it=s_s.m_timedCbs.begin(); it!=s_s.m_timedCbs.end(); ) { - if (VL_UNLIKELY(it->first <= time)) { - VerilatedVpioCb* vop = it->second; - ++it; // iterator may be deleted by callback - VL_DEBUG_IF_PLI(VL_PRINTF_MT("-vltVpi: timed_callback %p\n",vop);); - (vop->cb_rtnp()) (vop->cb_datap()); - } - else { ++it; } - } - } - static QData cbNextDeadline() { - VpioTimedCbs::const_iterator it=s_s.m_timedCbs.begin(); - if (VL_LIKELY(it!=s_s.m_timedCbs.end())) { - return it->first; - } else { - return ~VL_ULL(0); // maxquad - } - } - static void callCbs(vluint32_t reason) { - VpioCbList& cbObjList = s_s.m_cbObjLists[reason]; - for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end();) { - if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup - it = cbObjList.erase(it); - continue; - } - VerilatedVpioCb* vop = *it++; - VL_DEBUG_IF_PLI(VL_PRINTF_MT("-vltVpi: reason_callback %d %p\n",reason,vop);); - (vop->cb_rtnp()) (vop->cb_datap()); - } - } - static void callValueCbs() { - VpioCbList& cbObjList = s_s.m_cbObjLists[cbValueChange]; - typedef std::set VpioVarSet; - VpioVarSet update; // set of objects to update after callbacks - for (VpioCbList::iterator it=cbObjList.begin(); it!=cbObjList.end();) { - if (VL_UNLIKELY(!*it)) { // Deleted earlier, cleanup - it = cbObjList.erase(it); - continue; - } - VerilatedVpioCb* vop = *it++; - if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(vop->cb_datap()->obj)) { - void* newDatap = varop->varDatap(); - void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback - VL_DEBUG_IF_PLI(VL_PRINTF_MT("-vltVpi: value_test %s v[0]=%d/%d %p %p\n", - varop->fullname(), *((CData*)newDatap), *((CData*)prevDatap), - newDatap, prevDatap);); - if (memcmp(prevDatap, newDatap, varop->entSize())) { - VL_DEBUG_IF_PLI(VL_PRINTF_MT("-vltVpi: value_callback %p %s v[0]=%d\n", - vop,varop->fullname(), *((CData*)newDatap));); - update.insert(varop); - vpi_get_value(vop->cb_datap()->obj, vop->cb_datap()->value); - (vop->cb_rtnp()) (vop->cb_datap()); - } - } - } - for (VpioVarSet::const_iterator it=update.begin(); it!=update.end(); ++it) { - memcpy((*it)->prevDatap(), (*it)->varDatap(), (*it)->entSize()); - } - } - - static VerilatedVpiError* error_info(); // getter for vpi error info -}; - -#define _VL_VPI_ERROR_SET \ - do { \ - va_list args; \ - va_start(args, message); \ - VL_VSNPRINTF(m_buff, sizeof(m_buff), message.c_str(), args); \ - va_end(args); \ - } while (0) - -class VerilatedVpiError { - //// Container for vpi error info - - t_vpi_error_info m_errorInfo; - bool m_flag; - char m_buff[VL_VPI_LINE_SIZE]; - void setError(PLI_BYTE8 *message, PLI_BYTE8 *code, PLI_BYTE8 *file, PLI_INT32 line) { - m_errorInfo.message = message; - m_errorInfo.file = file; - m_errorInfo.line = line; - m_errorInfo.code = code; - do_callbacks(); - } - void do_callbacks() { - if (getError()->level >= vpiError && Verilated::fatalOnVpiError()) { - // Stop on vpi error/unsupported - vpi_unsupported(); - } - // We need to run above code first because in the case that the callback executes further vpi - // functions we will loose the error as it will be overwritten. - VerilatedVpi::callCbs(cbPLIError); - } -public: - - VerilatedVpiError() : m_flag(false) { - m_buff[0] = '\0'; - m_errorInfo.product = (PLI_BYTE8*)Verilated::productName(); - } - ~VerilatedVpiError() {} + /// Call timed callbacks + /// Users should call this from their main loops + static void callTimedCbs(); + /// Call value based callbacks + /// Users should call this from their main loops + static void callValueCbs(); + /// Self test, for internal use only static void selfTest(); - VerilatedVpiError* setMessage(PLI_INT32 level) { - m_flag=true; - m_errorInfo.level = level; - return this; - } - void setMessage(std::string file, PLI_INT32 line, std::string message, ...) { - static VL_THREAD_LOCAL std::string filehold; - _VL_VPI_ERROR_SET; - m_errorInfo.state = vpiPLI; - filehold = file; - setError((PLI_BYTE8*)m_buff, NULL, (PLI_BYTE8*)filehold.c_str(), line); - } - p_vpi_error_info getError() { - if (m_flag) return &m_errorInfo; - return NULL; - } - void resetError() { - m_flag=false; - } - static void vpi_unsupported() { - // Not supported yet - p_vpi_error_info error_info_p = VerilatedVpi::error_info()->getError(); - if (error_info_p) { - VL_FATAL_MT(error_info_p->file, error_info_p->line, "", error_info_p->message); - return; - } - VL_FATAL_MT(__FILE__, __LINE__, "", "vpi_unsupported called without error info set"); - } - static const char* strFromVpiVal(PLI_INT32 vpiVal); - static const char* strFromVpiObjType(PLI_INT32 vpiVal); - static const char* strFromVpiMethod(PLI_INT32 vpiVal); - static const char* strFromVpiCallbackReason(PLI_INT32 vpiVal); - static const char* strFromVpiProp(PLI_INT32 vpiVal); }; -//====================================================================== #endif // Guard diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index c2fdfe065..2b28a1d35 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -562,7 +562,7 @@ int mon_check() { if (int status = _mon_check_putget_str(NULL)) return status; if (int status = _mon_check_vlog_info()) return status; #ifndef IS_VPI - VerilatedVpiError::selfTest(); + VerilatedVpi::selfTest(); #endif return 0; // Ok }