From 7dee344ea9e260836d5c7011e0f564d31eafc17c Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 25 Dec 2010 14:39:41 -0500 Subject: [PATCH] Add limited support for VPI access to public signals, see docs. --- Changes | 2 + bin/verilator | 60 ++- include/verilated.h | 3 + include/verilated_vpi.cpp | 33 ++ include/verilated_vpi.h | 835 +++++++++++++++++++++++++++++++++++ test_regress/t/t_vpi_var.cpp | 308 +++++++++++++ test_regress/t/t_vpi_var.pl | 21 + test_regress/t/t_vpi_var.v | 46 ++ 8 files changed, 1304 insertions(+), 4 deletions(-) create mode 100644 include/verilated_vpi.cpp create mode 100644 include/verilated_vpi.h create mode 100644 test_regress/t/t_vpi_var.cpp create mode 100755 test_regress/t/t_vpi_var.pl create mode 100644 test_regress/t/t_vpi_var.v diff --git a/Changes b/Changes index 33e424934..ff2f42f63 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,8 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.80*** +** Add limited support for VPI access to public signals, see docs. + *** Add -F option to read relative option files, bug297. [Neil Hamilton] *** Support ++,--,+= etc as standalone statements. [Alex Solomatnikov] diff --git a/bin/verilator b/bin/verilator index 218723bc5..ae6866521 100755 --- a/bin/verilator +++ b/bin/verilator @@ -1372,6 +1372,58 @@ Instead of DPI exporting, there's also Verilator public functions, which are slightly faster, but less compatible. +=head1 VERIFICATION PROCEDURAL INTERACE (VPI) + +Verilator supports a very limited subset of the VPI. This subset allows +inspection, examination, value change callbacks, and depositing of values +to public signals only. + +To access signals via the VPI, Verilator must be told exactly which signals +are to be accessed. This is done using the Verilator public pragmas +documented below. + +Verilator has an important difference from an event based simulator; signal +values that are changed by the VPI will not immediately propagate their +values, instead the top level header file's eval() method must be called. +Normally this would be part of the normal evaluation (IE the next clock +edge), not as part of the value change. This makes the performance of VPI +routines extremely fast compared to event based simulators, but can confuse +some test-benches that expect immediate propagation. + +Note the VPI by it's specified implementation will always be much slower +than accessing the Verilator values by direct reference +(structure->module->signame), as the VPI accessors perform lookup in +functions at runtime requiring at best hundreds of instructions, while the +direct references are evaluated by the compiler and result in only a couple +of instructions. + +=head2 VPI Example + +In the below example, we have readme marked read-only, and writeme which if +written from outside the model will have the same semantics as if it +changed on the specified clock edge. + + module t; + reg readme /*verilator public_flat_rd*/; + reg writeme /*verilator public_flat_rw @(posedge clk) */; + endmodule + +There are many online tutorials and books on the VPI, but an example that +accesses the above would be: + +void read_and_check() { + vpiHandle vh1 = vpi_handle_by_name((PLI_BYTE8*)"t.readme", NULL); + if (!vh1) { error... } + const char* name = vpi_get_str(vpiName, vh1); + printf("Module name: %s\n"); // Prints "readme" + + s_vpi_value v; + v.format = vpiIntVal; + vpi_get_value(vh1, &v); + printf("Value of v: %d\n", v.value.integer); // Prints "readme" +} + + =head1 CROSS COMPILATION Verilator supports cross-compiling Verilated code. This is generally used @@ -2856,11 +2908,11 @@ Makefile's link rule. =item Is the PLI supported? -No, but the DPI is. +Only somewhat. More specifically, the common PLI-ish calls $display, +$finish, $stop, $time, $write are converted to C++ equivalents. You can +also use the "import DPI" SystemVerilog feature to call C code (see the +chapter above). There is also limited VPI access to public signals. -More specifically, the common PLI-ish calls $display, $finish, $stop, -$time, $write are converted to C++ equivalents. You can also use the -"import DPI" SystemVerilog feature to call C code (see the chapter above). If you want something more complex, since Verilator emits standard C++ code, you can simply write your own C++ routines that can access and modify signal values without needing any PLI interface code, and call it with diff --git a/include/verilated.h b/include/verilated.h index 2d4b9851a..c6e82ec30 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -391,6 +391,9 @@ void _VL_DEBUG_PRINT_W(int lbits, WDataInP iwp); //========================================================================= // Pli macros +#ifndef VL_TIME_PRECISION +# define VL_TIME_PRECISION -12 ///< Timescale units only for for VPI return - picoseconds +#endif #ifndef VL_TIME_MULTIPLIER # define VL_TIME_MULTIPLIER 1 #endif diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp new file mode 100644 index 000000000..fed33f051 --- /dev/null +++ b/include/verilated_vpi.cpp @@ -0,0 +1,33 @@ +// -*- C++ -*- +//************************************************************************* +// +// Copyright 2009-2010 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 +/// +//========================================================================= + +#include "verilated_vpi.h" + +//====================================================================== + +VerilatedVpi VerilatedVpi::s_s; // Singleton +vluint8_t* VerilatedVpio::s_freeHead = NULL; + +//====================================================================== diff --git a/include/verilated_vpi.h b/include/verilated_vpi.h new file mode 100644 index 000000000..987b2ad1b --- /dev/null +++ b/include/verilated_vpi.h @@ -0,0 +1,835 @@ +// -*- C++ -*- +//************************************************************************* +// +// Copyright 2009-2010 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. +/// +/// "//-" indicates features that are as yet unimplemented. +/// +/// Code available from: http://www.veripool.org/verilator +/// +//========================================================================= + +#ifndef CHPI_VERILATED_VPI_H +#define CHPI_VERILATED_VPI_H 1 + +#include "verilated.h" +#include "verilated_syms.h" + +//====================================================================== +// From IEEE 1800-2009 annex K + +#include "vltstd/vpi_user.h" + +//====================================================================== +// Internal macros + +// Not supported yet +#define _VL_VPI_UNIMP() \ + vl_fatal(__FILE__,__LINE__,"",Verilated::catName("Unsupported VPI function: ",VL_FUNC)) + +//====================================================================== +// Implementation + +#include + +#define VL_DEBUG_IF_PLI VL_DEBUG_IF + +// 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(__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 = (vluint8_t*)(::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 (vpiHandle)(this); } + // ACCESSORS + virtual const char* name() { return ""; } + virtual const char* fullname() { return ""; } + virtual const char* defname() { return ""; } + virtual vpiHandle dovpi_scan() { return 0; } +}; + +typedef PLI_INT32 (*VerilatedPliCb)(struct t_cb_data *); + +class VerilatedVpioCb : public VerilatedVpio { + t_cb_data m_cbData; + QData m_time; +public: + VerilatedVpioCb(const t_cb_data* cbDatap, QData time) : m_cbData(*cbDatap), m_time(time) {} + virtual ~VerilatedVpioCb() {} + static inline VerilatedVpioCb* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + 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: + VerilatedVpioConst(vlsint32_t num) : m_num(num) {} + virtual ~VerilatedVpioConst() {} + static inline VerilatedVpioConst* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + vlsint32_t num() const { return m_num; } +}; + +class VerilatedVpioRange : public VerilatedVpio { + vlsint32_t m_lhs; // Ranges can be signed + vlsint32_t m_rhs; + bool m_iteration; +public: + VerilatedVpioRange(vlsint32_t lhs, vlsint32_t rhs) : m_lhs(lhs), m_rhs(rhs), m_iteration(0) {} + virtual ~VerilatedVpioRange() {} + static inline VerilatedVpioRange* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + vlsint32_t lhs() const { return m_lhs; } + vlsint32_t rhs() const { return m_rhs; } + 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: + VerilatedVpioScope(const VerilatedScope* scopep) + : m_scopep(scopep) {} + virtual ~VerilatedVpioScope() {} + static inline VerilatedVpioScope* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + const VerilatedScope* scopep() const { return m_scopep; } + virtual const char* name() { return m_scopep->name(); } + virtual const char* fullname() { 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 + vluint32_t 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; +public: + VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep) + : m_varp(varp), m_scopep(scopep), m_index(0) { + m_prevDatap = NULL; + m_mask = VL_MASK_I(varp->range().bits()); + 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; } + vluint32_t entSize() const { return m_entSize; } + virtual const char* name() { return m_varp->name(); } + virtual const char* fullname() { + static VL_THREAD string out; + out = 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 VerilatedVpioVarIndex : public VerilatedVpioVar { +public: + VerilatedVpioVarIndex(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 ~VerilatedVpioVarIndex() {} + static inline VerilatedVpioVarIndex* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + virtual const char* fullname() { + static VL_THREAD string out; + char num[20]; sprintf(num,"%d",m_index); + out = string(scopep()->name())+"."+name()+"["+num+"]"; + return out.c_str(); + } +}; + +class VerilatedVpioVarIter : public VerilatedVpio { + const VerilatedScope* m_scopep; + VerilatedVarNameMap::iterator m_it; + bool m_started; +public: + VerilatedVpioVarIter(const VerilatedScope* scopep) + : m_scopep(scopep), m_started(false) { } + virtual ~VerilatedVpioVarIter() {} + static inline VerilatedVpioVarIter* castp(vpiHandle h) { return dynamic_cast((VerilatedVpio*)h); } + virtual vpiHandle dovpi_scan() { + if (VL_LIKELY(m_scopep->varsp())) { + if (VL_UNLIKELY(!m_started)) { m_it = m_scopep->varsp()->begin(); m_started=true; } + else if (VL_UNLIKELY(m_it == m_scopep->varsp()->end())) return 0; + else ++m_it; + if (m_it == m_scopep->varsp()->end()) return 0; + return ((new VerilatedVpioVar(&(m_it->second), m_scopep)) + ->castVpiHandle()); + } else { + return 0; // End of list - only one deep + } + } +}; + +//====================================================================== + +struct VerilatedVpiTimedCbsCmp { + /// Ordering sets keyed by time, then callback descriptor + bool operator() (const pair& a, + const pair& b) const { + if (a.first < b.first) return 1; + if (a.first > b.first) return 0; + return a.second < b.second; + } +}; + +class VerilatedVpi { + enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime+1 }; // Maxium callback reason + typedef set VpioCbSet; + typedef set,VerilatedVpiTimedCbsCmp > VpioTimedCbs; + + VpioCbSet m_cbObjSets[CB_ENUM_MAX_VALUE]; // Callbacks for each supported reason + VpioTimedCbs m_timedCbs; // Time based callbacks + + static VerilatedVpi s_s; // Singleton + +public: + VerilatedVpi() {} + ~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(__FILE__,__LINE__,"", "vpi bb reason too large"); + s_s.m_cbObjSets[vop->reason()].insert(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); + } + } + static void cbTimedRemove(VerilatedVpioCb* cbp) { + VpioTimedCbs::iterator it=s_s.m_timedCbs.find(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("-vltVpi: timed_callback %p\n",vop);); + (vop->cb_rtnp()) (vop->cb_datap()); + } + else { ++it; } + } + } + static QData cbNextDeadline() { + VpioTimedCbs::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) { + VpioCbSet& cbObjSet = s_s.m_cbObjSets[reason]; + for (VpioCbSet::iterator it=cbObjSet.begin(); it!=cbObjSet.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);); + (vop->cb_rtnp()) (vop->cb_datap()); + } + } + static void callValueCbs() { + VpioCbSet& cbObjSet = s_s.m_cbObjSets[cbValueChange]; + for (VpioCbSet::iterator it=cbObjSet.begin(); it!=cbObjSet.end();) { + VerilatedVpioCb* vop = *it; + ++it; // iterator may be deleted by callback + 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("-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("-vltVpi: value_callback %p %s v[0]=%d\n", + vop,varop->fullname(), *((CData*)newDatap));); + memcpy(prevDatap, newDatap, varop->entSize()); + (vop->cb_rtnp()) (vop->cb_datap()); + } + } + } + } +}; + +// callback related + +vpiHandle vpi_register_cb(p_cb_data cb_data_p) { + if (VL_UNLIKELY(!cb_data_p)) return NULL; + switch (cb_data_p->reason) { + case cbAfterDelay: { + QData time = 0; + if (cb_data_p->time) time = _VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low); + VerilatedVpioCb* vop = new VerilatedVpioCb(cb_data_p, VL_TIME_Q()+time); + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_register_cb %d %p delay=%" VL_PRI64 "d\n",cb_data_p->reason,vop,time);); + VerilatedVpi::cbTimedAdd(vop); + return vop->castVpiHandle(); + } + case cbReadWriteSynch: // FALLTHRU // Supported via vlt_main.cpp + case cbReadOnlySynch: // FALLTHRU // Supported via vlt_main.cpp + case cbNextSimTime: // FALLTHRU // Supported via vlt_main.cpp + case cbStartOfSimulation: // FALLTHRU // Supported via vlt_main.cpp + case cbEndOfSimulation: // FALLTHRU // Supported via vlt_main.cpp + case cbValueChange: // FALLTHRU // Supported via vlt_main.cpp + case cbEnterInteractive: // FALLTHRU // NOP, but need to return handle, so make object + case cbExitInteractive: // FALLTHRU // NOP, but need to return handle, so make object + case cbInteractiveScopeChange: { // FALLTHRU // NOP, but need to return handle, so make object + VerilatedVpioCb* vop = new VerilatedVpioCb(cb_data_p, 0); + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_register_cb %d %p\n",cb_data_p->reason,vop);); + VerilatedVpi::cbReasonAdd(vop); + return vop->castVpiHandle(); + } + default: + _VL_VPI_UNIMP(); return NULL; + }; +} + +PLI_INT32 vpi_remove_cb(vpiHandle object) { + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_remove_cb %p\n",object);); + VerilatedVpioCb* vop = VerilatedVpioCb::castp(object); + if (VL_UNLIKELY(!vop)) return 0; + if (vop->cb_datap()->reason == cbAfterDelay) { + VerilatedVpi::cbTimedRemove(vop); + } else { + VerilatedVpi::cbReasonRemove(vop); + } + return 1; +} + +//-void vpi_get_cb_info(vpiHandle object, p_cb_data cb_data_p) { +//- _VL_VPI_UNIMP(); return 0; +//-} +//-vpiHandle vpi_register_systf(p_vpi_systf_data systf_data_p) { +//- _VL_VPI_UNIMP(); return 0; +//-} +//-void vpi_get_systf_info(vpiHandle object, p_vpi_systf_data systf_data_p) { +//- _VL_VPI_UNIMP(); return 0; +//-} + +// for obtaining handles + +vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { + if (VL_UNLIKELY(!namep)) return NULL; + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_handle_by_name %s %p\n",namep,scope);); + VerilatedVpioScope* voScopep = VerilatedVpioScope::castp(scope); + const VerilatedVar* varp; + const VerilatedScope* scopep; + string scopeAndName = namep; + if (voScopep) { + scopeAndName = string(voScopep->fullname()) + "." + namep; + namep = (PLI_BYTE8*)scopeAndName.c_str(); + } + { + // This doesn't yet follow the hierarchy in the proper way + scopep = Verilated::scopeFind(namep); + if (scopep) { // Whole thing found as a scope + return (new VerilatedVpioScope(scopep))->castVpiHandle(); + } + const char* baseNamep = scopeAndName.c_str(); + string scopename; + const char* dotp = strrchr(namep, '.'); + if (VL_LIKELY(dotp)) { + baseNamep = dotp+1; + scopename = string(namep,dotp-namep); + } + scopep = Verilated::scopeFind(scopename.c_str()); + if (!scopep) return NULL; + varp = scopep->varFind(baseNamep); + } + if (!varp) return NULL; + return (new VerilatedVpioVar(varp, scopep))->castVpiHandle(); +} + +vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) { + // Used to get array entries + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_handle_by_index %p %d\n",object, indx);); + VerilatedVpioVar* varop = VerilatedVpioVar::castp(object); + if (VL_LIKELY(varop)) { + if (varop->varp()->dims()<2) return 0; + if (VL_LIKELY(varop->varp()->array().lhs() >= varop->varp()->array().rhs())) { + if (VL_UNLIKELY(indx > varop->varp()->array().lhs() || indx < varop->varp()->array().rhs())) return 0; + return (new VerilatedVpioVarIndex(varop->varp(), varop->scopep(), indx, + indx - varop->varp()->array().rhs())) + ->castVpiHandle(); + } else { + if (VL_UNLIKELY(indx < varop->varp()->array().lhs() || indx > varop->varp()->array().rhs())) return 0; + return (new VerilatedVpioVarIndex(varop->varp(), varop->scopep(), indx, + indx - varop->varp()->array().lhs())) + ->castVpiHandle(); + } + } else { + _VL_VPI_UNIMP(); return 0; + } +} + +// for traversing relationships + +vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_handle %d %p\n",type,object);); + switch (type) { + case vpiLeftRange: // FALLTHRU + case vpiRightRange: { + if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) { + vluint32_t num = ((type==vpiLeftRange) + ? vop->varp()->range().lhs() + : vop->varp()->range().rhs()); + return (new VerilatedVpioConst(num))->castVpiHandle(); + } else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) { + vluint32_t num = ((type==vpiLeftRange) + ? vop->lhs() + : vop->rhs()); + return (new VerilatedVpioConst(num))->castVpiHandle(); + } else { + return 0; + } + } + default: + _VL_VPI_UNIMP(); + return 0; + } +} + +//-vpiHandle vpi_handle_multi(PLI_INT32 type, vpiHandle refHandle1, vpiHandle refHandle2, ... ) { +//- _VL_VPI_UNIMP(); return 0; +//-} + +vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_iterate %d %p\n",type,object);); + switch (type) { + case vpiMemoryWord: { + VerilatedVpioVar* vop = VerilatedVpioVar::castp(object); + if (VL_UNLIKELY(!vop)) return 0; + if (vop->varp()->dims() < 2) return 0; + // Unsupported is multidim list + return ((new VerilatedVpioRange(vop->varp()->array().lhs(), + vop->varp()->array().rhs())) + ->castVpiHandle()); + } + case vpiReg: { + VerilatedVpioScope* vop = VerilatedVpioScope::castp(object); + if (VL_UNLIKELY(!vop)) return 0; + return ((new VerilatedVpioVarIter(vop->scopep())) + ->castVpiHandle()); + } + case vpiIODecl: // Skipping - we'll put under reg + case vpiNet: // Skipping - we'll put under reg + return 0; + default: + _VL_VPI_UNIMP(); return 0; + } +} +vpiHandle vpi_scan(vpiHandle object) { + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_scan %p\n",object);); + VerilatedVpio* vop = VerilatedVpio::castp(object); + if (VL_UNLIKELY(!vop)) return NULL; + return vop->dovpi_scan(); +} + +// for processing properties + +PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) { + // Leave this in the header file - in many cases the compiler can constant propagate "object" + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_get %d %p\n",property,object);); + switch (property) { + case vpiTimePrecision: { + return VL_TIME_PRECISION; + } + case vpiType: { + VerilatedVpioVar* vop = VerilatedVpioVar::castp(object); + if (VL_UNLIKELY(!vop)) return 0; + return ((vop->varp()->dims()>1) ? vpiMemory : vpiReg); + } + case vpiDirection: { + // By forthought, the directions already are vpi enumerated + VerilatedVpioVar* vop = VerilatedVpioVar::castp(object); + if (VL_UNLIKELY(!vop)) return 0; + return vop->varp()->vldir(); + } + case vpiVector: { + VerilatedVpioVar* vop = VerilatedVpioVar::castp(object); + if (VL_UNLIKELY(!vop)) return 0; + if (vop->varp()->dims()==0) return 0; + else return 1; + } + default: + _VL_VPI_UNIMP(); + return 0; + } +} + +//-PLI_INT64 vpi_get64(PLI_INT32 property, vpiHandle object) { +//- _VL_VPI_UNIMP(); return 0; +//-} + +PLI_BYTE8 *vpi_get_str(PLI_INT32 property, vpiHandle object) { + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_get_str %d %p\n",property,object);); + VerilatedVpio* vop = VerilatedVpio::castp(object); + if (VL_UNLIKELY(!vop)) return NULL; + switch (property) { + case vpiName: { + return (PLI_BYTE8*)vop->name(); + } + case vpiFullName: { + return (PLI_BYTE8*)vop->fullname(); + } + case vpiDefName: { + return (PLI_BYTE8*)vop->defname(); + } + default: + _VL_VPI_UNIMP(); + return 0; + } +} + +// delay processing + +//-void vpi_get_delays(vpiHandle object, p_vpi_delay delay_p) { +//- _VL_VPI_UNIMP(); return 0; +//-} +//-void vpi_put_delays(vpiHandle object, p_vpi_delay delay_p) { +//- _VL_VPI_UNIMP(); return 0; +//-} + +// value processing + +void vpi_get_value(vpiHandle object, p_vpi_value value_p) { + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_get_value %p\n",object);); + if (VL_UNLIKELY(!value_p)) return; + if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) { + // We presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal + if (value_p->format == vpiVectorVal) { + // Vector pointer must come from our memory pool + // It only needs to persist until the next vpi_get_value + static VL_THREAD t_vpi_vecval out[VL_MULS_MAX_WORDS*2]; + value_p->value.vector = out; + switch (vop->varp()->vltype()) { + case VLVT_UINT8: + out[0].aval = *((CData*)(vop->varDatap())); + out[0].bval = 0; + return; + case VLVT_UINT16: + out[0].aval = *((SData*)(vop->varDatap())); + out[0].bval = 0; + return; + case VLVT_UINT32: + out[0].aval = *((IData*)(vop->varDatap())); + out[0].bval = 0; + return; + case VLVT_WDATA: { + int words = VL_WORDS_I(vop->varp()->range().bits()); + if (VL_UNLIKELY(words >= VL_MULS_MAX_WORDS)) { + vl_fatal(__FILE__,__LINE__,"", "vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile"); + } + WDataInP datap = ((IData*)(vop->varDatap())); + for (int i=0; ivarDatap())); + out[1].aval = (IData)(data>>VL_ULL(32)); + out[1].bval = 0; + out[0].aval = (IData)(data); + out[0].bval = 0; + return; + } + default: { + _VL_VPI_UNIMP(); + return; + } + } + } else { + switch (vop->varp()->vltype()) { + case VLVT_UINT8: + value_p->value.integer = *((CData*)(vop->varDatap())); + return; + case VLVT_UINT16: + value_p->value.integer = *((SData*)(vop->varDatap())); + return; + case VLVT_UINT32: + value_p->value.integer = *((IData*)(vop->varDatap())); + return; + case VLVT_WDATA: + case VLVT_UINT64: + // Not legal + value_p->value.integer = 0; + default: + _VL_VPI_UNIMP(); + return; + } + } + } + else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) { + value_p->value.integer = vop->num(); + return; + } + _VL_VPI_UNIMP(); +} + +vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p, + p_vpi_time time_p, PLI_INT32 flags) { + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_put_value %p %p\n",object, value_p);); + if (VL_UNLIKELY(!value_p)) return 0; + if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) { + // We presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_put_value name=%s fmt=%d vali=%d\n", + vop->fullname(), value_p->format, value_p->value.integer); + VL_PRINTF("-vltVpi: varp=%p putatp=%p\n", + vop->varp()->datap(), vop->varDatap());); + if (VL_UNLIKELY(!vop->varp()->isPublicRW())) { + VL_PRINTF("%%Warning: Ignoring vpi_put_value to signal marked read-only, use public_flat_rw instead: %s\n", + vop->fullname()); + return 0; + } + if (value_p->format == vpiVectorVal) { + if (VL_UNLIKELY(!value_p->value.vector)) return NULL; + switch (vop->varp()->vltype()) { + case VLVT_UINT8: + *((CData*)(vop->varDatap())) = value_p->value.vector[0].aval; + return object; + case VLVT_UINT16: + *((SData*)(vop->varDatap())) = value_p->value.vector[0].aval; + return object; + case VLVT_UINT32: + *((IData*)(vop->varDatap())) = value_p->value.vector[0].aval; + return object; + case VLVT_WDATA: { + int words = VL_WORDS_I(vop->varp()->range().bits()); + WDataOutP datap = ((IData*)(vop->varDatap())); + for (int i=0; ivalue.vector[i].aval; + } + return object; + } + case VLVT_UINT64: { + *((QData*)(vop->varDatap())) = _VL_SET_QII( + value_p->value.vector[1].aval, + value_p->value.vector[0].aval); + return object; + } + default: { + _VL_VPI_UNIMP(); + return NULL; + } + } + } else { + switch (vop->varp()->vltype()) { + case VLVT_UINT8: + *((CData*)(vop->varDatap())) = vop->mask() & value_p->value.integer; + return object; + case VLVT_UINT16: + *((SData*)(vop->varDatap())) = vop->mask() & value_p->value.integer; + return object; + case VLVT_UINT32: + *((IData*)(vop->varDatap())) = vop->mask() & value_p->value.integer; + return object; + case VLVT_WDATA: // FALLTHRU + case VLVT_UINT64: // FALLTHRU + default: + _VL_VPI_UNIMP(); + return 0; + } + } + } + _VL_VPI_UNIMP(); return NULL; +} + +//-void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, +//- PLI_INT32 *index_p, PLI_UINT32 num) { +//- _VL_VPI_UNIMP(); return 0; +//-} +//-void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, +//- PLI_INT32 *index_p, PLI_UINT32 num) { +//- _VL_VPI_UNIMP(); return 0; +//-} + + +// time processing + +//-void vpi_get_time(vpiHandle object, p_vpi_time time_p) { +//- _VL_VPI_UNIMP(); +//-} + +// I/O routines + +//-PLI_UINT32 vpi_mcd_open(PLI_BYTE8 *fileName) { +//- _VL_VPI_UNIMP(); return 0; +//-} +//-PLI_UINT32 vpi_mcd_close(PLI_UINT32 mcd) { +//- _VL_VPI_UNIMP(); return 0; +//-} +//-PLI_BYTE8 *vpi_mcd_name(PLI_UINT32 cd) { +//- _VL_VPI_UNIMP(); return 0; +//-} +//-PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, PLI_BYTE8 *format, ...) { +//- _VL_VPI_UNIMP(); return 0; +//-} + +PLI_INT32 vpi_printf(PLI_BYTE8 *formatp, ...) { + va_list ap; + va_start(ap,formatp); + int chars = vpi_vprintf(formatp, ap); + va_end(ap); + return chars; +} + +PLI_INT32 vpi_vprintf(PLI_BYTE8* formatp, va_list ap) { + return VL_VPRINTF(formatp, ap); +} + +//-PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8 *format, va_list ap) { +//- _VL_VPI_UNIMP(); return 0; +//-} +//-PLI_INT32 vpi_flush(void) { +//- _VL_VPI_UNIMP(); return 0; +//-} +//-PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) { +//- _VL_VPI_UNIMP(); return 0; +//-} + +// utility routines + +//-PLI_INT32 vpi_compare_objects(vpiHandle object1, vpiHandle object2) { +//- _VL_VPI_UNIMP(); return 0; +//-} +//-PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) { +//- _VL_VPI_UNIMP(); return 0; +//-} + +PLI_INT32 vpi_free_object(vpiHandle object) { + return vpi_release_handle(object); // Deprecated +} + +PLI_INT32 vpi_release_handle (vpiHandle object) { + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_release_handle %p\n",object);); + VerilatedVpio* vop = VerilatedVpio::castp(object); + if (VL_UNLIKELY(!vop)) return 0; + vpi_remove_cb(object); // May not be a callback, but that's ok + delete vop; + return 1; +} + +//-PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) { +//- _VL_VPI_UNIMP(); return 0; +//-} + +// routines added with 1364-2001 + +//-PLI_INT32 vpi_get_data(PLI_INT32 id, PLI_BYTE8 *dataLoc, PLI_INT32 numOfBytes) { +//- _VL_VPI_UNIMP(); return 0; +//-} +//-PLI_INT32 vpi_put_data(PLI_INT32 id, PLI_BYTE8 *dataLoc, PLI_INT32 numOfBytes) { +//- _VL_VPI_UNIMP(); return 0; +//-} +//-void *vpi_get_userdata(vpiHandle obj) { +//- _VL_VPI_UNIMP(); return 0; +//-} +//-PLI_INT32 vpi_put_userdata(vpiHandle obj, void *userdata) { +//- _VL_VPI_UNIMP(); return 0; +//-} + +PLI_INT32 vpi_control(PLI_INT32 operation, ...) { + VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_control %d\n",operation);); + switch (operation) { + case vpiFinish: { + vl_finish(__FILE__,__LINE__,"*VPI*"); + return 1; + } + case vpiStop: { + vl_stop(__FILE__,__LINE__,"*VPI*"); + return 1; + } + } + _VL_VPI_UNIMP(); return 0; +} + +//-vpiHandle vpi_handle_by_multi_index(vpiHandle obj, PLI_INT32 num_index, PLI_INT32 *index_array) { +//- _VL_VPI_UNIMP(); return 0; +//-} + +//====================================================================== + +#endif // Guard diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp new file mode 100644 index 000000000..1992021c6 --- /dev/null +++ b/test_regress/t/t_vpi_var.cpp @@ -0,0 +1,308 @@ +// -*- C++ -*- +//************************************************************************* +// +// Copyright 2010-2010 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. +// +//************************************************************************* + +#include "Vt_vpi_var.h" +#include "verilated.h" +#include "svdpi.h" + +#include "Vt_vpi_var__Dpi.h" + +#include "verilated_vpi.h" +#include "verilated_vpi.cpp" +#include "verilated_vcd_c.h" + +#include + +// __FILE__ is too long +#define FILENM "t_vpi_var.cpp" + +//====================================================================== + + +class VlVpiHandle { + /// For testing, etc, wrap vpiHandle in an auto-releasing class + vpiHandle m_handle; +public: + VlVpiHandle() : m_handle(NULL) { } + VlVpiHandle(vpiHandle h) : m_handle(h) { } + ~VlVpiHandle() { if (m_handle) { vpi_release_handle(m_handle); m_handle=NULL; } } + operator vpiHandle () const { return m_handle; } + inline VlVpiHandle& operator= (vpiHandle h) { m_handle = h; return *this; } +}; + +//====================================================================== + +#define CHECK_RESULT_VH(got, exp) \ + if ((got) != (exp)) { \ + printf("%%Error: %s:%d: GOT = %p EXP = %p\n", \ + FILENM,__LINE__, (got), (exp)); \ + return __LINE__; \ + } + +#define CHECK_RESULT_NZ(got) \ + if (!(got)) { \ + printf("%%Error: %s:%d: GOT = NULL EXP = !NULL\n", FILENM,__LINE__); \ + return __LINE__; \ + } + +// Use cout to avoid issues with %d/%lx etc +#define CHECK_RESULT(got, exp) \ + if ((got != exp)) { \ + cout<", (exp)?(exp):""); \ + return __LINE__; \ + } + +int _mon_check_callbacks() { + t_cb_data cb_data; + cb_data.reason = cbEndOfSimulation; + cb_data.cb_rtn = NULL; + cb_data.user_data = 0; + + vpiHandle vh = vpi_register_cb(&cb_data); + CHECK_RESULT_NZ(vh); + + PLI_INT32 status = vpi_remove_cb(vh); + CHECK_RESULT_NZ(status); + + return 0; +} + +int _mon_check_var() { + VlVpiHandle vh1 = vpi_handle_by_name((PLI_BYTE8*)"t.onebit", NULL); + CHECK_RESULT_NZ(vh1); + + VlVpiHandle vh2 = vpi_handle_by_name((PLI_BYTE8*)"t", NULL); + CHECK_RESULT_NZ(vh2); + + // scope attributes + const char* p; + p = vpi_get_str(vpiName, vh2); + CHECK_RESULT_CSTR(p, "t"); + p = vpi_get_str(vpiFullName, vh2); + CHECK_RESULT_CSTR(p, "t"); + + VlVpiHandle vh3 = vpi_handle_by_name((PLI_BYTE8*)"onebit", vh2); + CHECK_RESULT_NZ(vh3); + + // onebit attributes + PLI_INT32 d; + d = vpi_get(vpiType, vh3); + CHECK_RESULT(d, vpiReg); + d = vpi_get(vpiDirection, vh3); + CHECK_RESULT(d, vpiNoDirection); + d = vpi_get(vpiVector, vh3); + CHECK_RESULT(d, 0); + + p = vpi_get_str(vpiName, vh3); + CHECK_RESULT_CSTR(p, "onebit"); + p = vpi_get_str(vpiFullName, vh3); + CHECK_RESULT_CSTR(p, "t.onebit"); + + // array attributes + VlVpiHandle vh4 = vpi_handle_by_name((PLI_BYTE8*)"t.fourthreetwoone", NULL); + CHECK_RESULT_NZ(vh4); + d = vpi_get(vpiVector, vh4); + CHECK_RESULT(d, 1); + + t_vpi_value tmpValue; + tmpValue.format = vpiIntVal; + { + VlVpiHandle vh10 = vpi_handle(vpiLeftRange, vh4); + CHECK_RESULT_NZ(vh10); + vpi_get_value(vh10, &tmpValue); + CHECK_RESULT(tmpValue.value.integer,2); + } + { + VlVpiHandle vh10 = vpi_handle(vpiRightRange, vh4); + CHECK_RESULT_NZ(vh10); + vpi_get_value(vh10, &tmpValue); + CHECK_RESULT(tmpValue.value.integer,1); + } + { + VlVpiHandle vh10 = vpi_iterate(vpiMemoryWord, vh4); + CHECK_RESULT_NZ(vh10); + VlVpiHandle vh11 = vpi_scan(vh10); + CHECK_RESULT_NZ(vh11); + VlVpiHandle vh12 = vpi_handle(vpiLeftRange, vh11); + CHECK_RESULT_NZ(vh12); + vpi_get_value(vh12, &tmpValue); + CHECK_RESULT(tmpValue.value.integer,4); + VlVpiHandle vh13 = vpi_handle(vpiRightRange, vh11); + CHECK_RESULT_NZ(vh13); + vpi_get_value(vh13, &tmpValue); + CHECK_RESULT(tmpValue.value.integer,3); + } + + return 0; +} + +int _mon_check_varlist() { + const char* p; + + VlVpiHandle vh2 = vpi_handle_by_name((PLI_BYTE8*)"t.sub", NULL); + CHECK_RESULT_NZ(vh2); + + VlVpiHandle vh10 = vpi_iterate(vpiReg, vh2); + CHECK_RESULT_NZ(vh10); + + VlVpiHandle vh11 = vpi_scan(vh10); + CHECK_RESULT_NZ(vh11); + p = vpi_get_str(vpiFullName, vh11); + CHECK_RESULT_CSTR(p, "t.sub.subsig1"); + + VlVpiHandle vh12 = vpi_scan(vh10); + CHECK_RESULT_NZ(vh12); + p = vpi_get_str(vpiFullName, vh12); + CHECK_RESULT_CSTR(p, "t.sub.subsig2"); + + VlVpiHandle vh13 = vpi_scan(vh10); + CHECK_RESULT(vh13,0); + + return 0; +} + +int _mon_check_getput() { + VlVpiHandle vh2 = vpi_handle_by_name((PLI_BYTE8*)"t.onebit", NULL); + CHECK_RESULT_NZ(vh2); + + s_vpi_value v; + v.format = vpiIntVal; + vpi_get_value(vh2, &v); + CHECK_RESULT(v.value.integer, 0); + + s_vpi_time t; + t.type = vpiSimTime; + t.high = 0; + t.low = 0; + v.value.integer = 1; + vpi_put_value(vh2, &v, &t, vpiNoDelay); + + vpi_get_value(vh2, &v); + CHECK_RESULT(v.value.integer, 1); + + return 0; +} + +int _mon_check_quad() { + VlVpiHandle vh2 = vpi_handle_by_name((PLI_BYTE8*)"t.quads", NULL); + CHECK_RESULT_NZ(vh2); + + s_vpi_value v; + t_vpi_vecval vv; bzero(&vv,sizeof(vv)); + + s_vpi_time t; + t.type = vpiSimTime; + t.high = 0; + t.low = 0; + + VlVpiHandle vhidx2 = vpi_handle_by_index(vh2, 2); + CHECK_RESULT_NZ(vhidx2); + VlVpiHandle vhidx3 = vpi_handle_by_index(vh2, 3); + CHECK_RESULT_NZ(vhidx2); + + v.format = vpiVectorVal; + v.value.vector = &vv; + v.value.vector[1].aval = 0x12819213UL; + v.value.vector[0].aval = 0xabd31a1cUL; + vpi_put_value(vhidx2, &v, &t, vpiNoDelay); + + v.format = vpiVectorVal; + v.value.vector = &vv; + v.value.vector[1].aval = 0x1c77bb9bUL; + v.value.vector[0].aval = 0x3784ea09UL; + vpi_put_value(vhidx3, &v, &t, vpiNoDelay); + + vpi_get_value(vhidx2, &v); + CHECK_RESULT(v.value.vector[1].aval, 0x12819213UL); + CHECK_RESULT(v.value.vector[1].bval, 0); + + vpi_get_value(vhidx3, &v); + CHECK_RESULT(v.value.vector[1].aval, 0x1c77bb9bUL); + CHECK_RESULT(v.value.vector[1].bval, 0); + + return 0; +} + +int mon_check() { + // Callback from initial block in monitor + if (int status = _mon_check_callbacks()) return status; + if (int status = _mon_check_var()) return status; + if (int status = _mon_check_varlist()) return status; + if (int status = _mon_check_getput()) return status; + if (int status = _mon_check_quad()) return status; + return 0; // Ok +} + +//====================================================================== + +unsigned int main_time = false; + +double sc_time_stamp () { + return main_time; +} +int main(int argc, char **argv, char **env) { + double sim_time = 1100; + Verilated::commandArgs(argc, argv); + Verilated::debug(0); + + VM_PREFIX* topp = new VM_PREFIX (""); // Note null name - we're flattening it out + +#ifdef VERILATOR +# ifdef TEST_VERBOSE + Verilated::scopesDump(); +# endif +#endif + + Verilated::traceEverOn(true); + VerilatedVcdC* tfp = new VerilatedVcdC; +#if VM_TRACE + VL_PRINTF("Enabling waves...\n"); + topp->trace (tfp, 99); + tfp->open ("obj_dir/t_vpi_var/simx.vcd"); +#endif + + topp->eval(); + topp->clk = 0; + main_time += 10; + + while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + main_time += 1; + topp->eval(); + topp->clk = !topp->clk; + //mon_do(); +#if VM_TRACE + if (tfp) tfp->dump (main_time); +#endif + } + if (!Verilated::gotFinish()) { + vl_fatal(FILENM,__LINE__,"main", "%Error: Timeout; never got a $finish"); + } + topp->final(); + +#if VM_TRACE + if (tfp) tfp->close(); +#endif + + delete topp; topp=NULL; + exit(0L); +} diff --git a/test_regress/t/t_vpi_var.pl b/test_regress/t/t_vpi_var.pl new file mode 100755 index 000000000..d2a50b810 --- /dev/null +++ b/test_regress/t/t_vpi_var.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2010 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. + +compile ( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["-CFLAGS '-ggdb' --exe --no-l2name $Self->{t_dir}/t_vpi_var.cpp"], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_vpi_var.v b/test_regress/t/t_vpi_var.v new file mode 100644 index 000000000..6600fab52 --- /dev/null +++ b/test_regress/t/t_vpi_var.v @@ -0,0 +1,46 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2010 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. + +import "DPI-C" context function integer mon_check(); + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + reg onebit /*verilator public_flat_rw @(posedge clk) */; + reg [2:1] twoone /*verilator public_flat_rw @(posedge clk) */; + reg [4:3][2:1] fourthreetwoone /*verilator public_flat_rw @(posedge clk) */; + + reg [3:2][61:0] quads /*verilator public_flat_rw @(posedge clk) */; + + integer status; + + sub sub(); + + // Test loop + initial begin + onebit = 1'b0; + status = mon_check(); + if (status!=0) begin + $write("%%Error: t_vpi_var.cpp:%0d: C Test failed\n", status); + $stop; + end + if (onebit != 1'b1) $stop; + if (quads[2] != 62'h12819213_abd31a1c) $stop; + if (quads[3] != 62'h1c77bb9b_3784ea09) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule + +module sub; + reg subsig1 /*verilator public_flat_rd*/; + reg subsig2 /*verilator public_flat_rd*/; +endmodule