// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // // Copyright 2009-2017 by Wilson Snyder. This program is free software; you can // redistribute it and/or modify it under the terms of either the GNU // Lesser General Public License Version 3 or the Perl Artistic License. // Version 2.0. // // Verilator is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //========================================================================= /// /// \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. /// /// Code available from: http://www.veripool.org/verilator /// //========================================================================= #ifndef _VERILATED_VPI_H_ #define _VERILATED_VPI_H_ 1 ///< Header Guard #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() {} 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