3711 lines
150 KiB
C++
3711 lines
150 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
// Copyright 2009-2025 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.
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
//
|
|
//=========================================================================
|
|
///
|
|
/// \file
|
|
/// \brief Verilated VPI implementation code
|
|
///
|
|
/// This file must be compiled and linked against all Verilated objects
|
|
/// that use the VPI.
|
|
///
|
|
/// Use "verilator --vpi" to add this to the Makefile for the linker.
|
|
///
|
|
/// For documentation on the exported functions (named vpi_*) that are
|
|
/// implemented here, refer to the IEEE DPI chapter.
|
|
///
|
|
//=========================================================================
|
|
|
|
#define VERILATOR_VERILATED_VPI_CPP_
|
|
|
|
#include "verilated_vpi.h"
|
|
|
|
#include "verilated.h"
|
|
#include "verilated_imp.h"
|
|
|
|
#include "vltstd/vpi_user.h"
|
|
|
|
#include <cstdarg>
|
|
#include <cstdio>
|
|
#include <list>
|
|
#include <map>
|
|
#include <set>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
//======================================================================
|
|
// Internal constants
|
|
|
|
#define VL_DEBUG_IF_PLI VL_DEBUG_IF
|
|
constexpr unsigned 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: ", __func__)))
|
|
|
|
//======================================================================
|
|
// Implementation
|
|
|
|
// Base VPI handled object
|
|
class VerilatedVpio VL_NOT_FINAL {
|
|
// CONSTANTS
|
|
// Magic value stored in front of object to detect double free etc
|
|
// Must be odd, as aligned pointer can never be odd
|
|
static constexpr uint32_t activeMagic() VL_PURE { return 0xfeed100f; }
|
|
|
|
// MEM MANGLEMENT
|
|
// Internal note: Globals may multi-construct, see verilated.cpp top.
|
|
static thread_local uint8_t* t_freeHeadp;
|
|
|
|
public:
|
|
// CONSTRUCTORS
|
|
VerilatedVpio() = default;
|
|
virtual ~VerilatedVpio() = default;
|
|
static void* operator new(size_t size) VL_MT_SAFE {
|
|
// 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 constexpr size_t CHUNK_SIZE = 256;
|
|
if (VL_UNCOVERABLE(size > CHUNK_SIZE))
|
|
VL_FATAL_MT(__FILE__, __LINE__, "", "increase CHUNK_SIZE");
|
|
if (VL_LIKELY(t_freeHeadp)) {
|
|
uint8_t* const newp = t_freeHeadp;
|
|
t_freeHeadp = *(reinterpret_cast<uint8_t**>(newp));
|
|
*(reinterpret_cast<uint32_t*>(newp)) = activeMagic();
|
|
return newp + 8;
|
|
}
|
|
// +8: 8 bytes for next
|
|
uint8_t* newp = reinterpret_cast<uint8_t*>(::operator new(CHUNK_SIZE + 8));
|
|
*(reinterpret_cast<uint32_t*>(newp)) = activeMagic();
|
|
return newp + 8;
|
|
}
|
|
static void operator delete(void* obj, size_t /*size*/) VL_MT_SAFE {
|
|
uint8_t* const oldp = (static_cast<uint8_t*>(obj)) - 8;
|
|
if (VL_UNLIKELY(*(reinterpret_cast<uint32_t*>(oldp)) != activeMagic())) {
|
|
VL_FATAL_MT(__FILE__, __LINE__, "",
|
|
"vpi_release_handle() called on same object twice, or on non-Verilator "
|
|
"VPI object");
|
|
}
|
|
#ifdef VL_VPI_IMMEDIATE_FREE // Define to aid in finding leaky handles
|
|
::operator delete(oldp);
|
|
#else
|
|
*(reinterpret_cast<void**>(oldp)) = t_freeHeadp;
|
|
t_freeHeadp = oldp;
|
|
#endif
|
|
}
|
|
// MEMBERS
|
|
static VerilatedVpio* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpio*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
vpiHandle castVpiHandle() { return reinterpret_cast<vpiHandle>(this); }
|
|
// ACCESSORS
|
|
virtual const char* name() const { return "<null>"; }
|
|
virtual const char* fullname() const { return "<null>"; }
|
|
virtual const char* defname() const { return "<null>"; }
|
|
virtual uint32_t type() const { return 0; }
|
|
virtual uint32_t constType() const { return vpiUndefined; }
|
|
virtual uint32_t size() const { return 0; }
|
|
virtual const VerilatedRange* rangep() const { return nullptr; }
|
|
virtual vpiHandle dovpi_scan() { return nullptr; }
|
|
virtual PLI_INT32 dovpi_remove_cb() { return 0; }
|
|
};
|
|
|
|
class VerilatedVpioReasonCb final : public VerilatedVpio {
|
|
// A handle to a timed or non-timed callback created with vpi_register_cb
|
|
// User can call vpi_remove_cb or vpi_release_handle on it
|
|
const uint64_t m_id; // Unique id/sequence number to find schedule's event
|
|
const QData m_time; // Scheduled time, or 0 = not timed
|
|
const PLI_INT32 m_reason; // VPI callback reason code
|
|
|
|
public:
|
|
// cppcheck-suppress uninitVar // m_value
|
|
VerilatedVpioReasonCb(uint64_t id, QData time, PLI_INT32 reason)
|
|
: m_id{id}
|
|
, m_time{time}
|
|
, m_reason{reason} {}
|
|
~VerilatedVpioReasonCb() override = default;
|
|
static VerilatedVpioReasonCb* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioReasonCb*>(reinterpret_cast<VerilatedVpioReasonCb*>(h));
|
|
}
|
|
uint32_t type() const override { return vpiCallback; }
|
|
PLI_INT32 dovpi_remove_cb() override;
|
|
};
|
|
|
|
class VerilatedVpioConst final : public VerilatedVpio {
|
|
const int32_t m_num;
|
|
|
|
public:
|
|
explicit VerilatedVpioConst(int32_t num)
|
|
: m_num{num} {}
|
|
~VerilatedVpioConst() override = default;
|
|
static VerilatedVpioConst* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioConst*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
uint32_t type() const override { return vpiConstant; }
|
|
uint32_t constType() const override { return vpiDecConst; }
|
|
int32_t num() const { return m_num; }
|
|
};
|
|
|
|
class VerilatedVpioVarBase VL_NOT_FINAL : public VerilatedVpio {
|
|
protected:
|
|
const VerilatedVar* m_varp = nullptr;
|
|
const VerilatedScope* m_scopep = nullptr;
|
|
std::string m_fullname;
|
|
int32_t m_indexedDim = -1;
|
|
const VerilatedRange* get_range() const { return m_varp->range(m_indexedDim + 1); }
|
|
|
|
public:
|
|
VerilatedVpioVarBase(const VerilatedVar* varp, const VerilatedScope* scopep)
|
|
: m_varp{varp}
|
|
, m_scopep{scopep}
|
|
, m_fullname{std::string{m_scopep->name()} + '.' + m_varp->name()} {}
|
|
explicit VerilatedVpioVarBase(const VerilatedVpioVarBase* varp) {
|
|
if (varp) {
|
|
m_varp = varp->m_varp;
|
|
m_scopep = varp->m_scopep;
|
|
m_fullname = varp->m_fullname;
|
|
m_indexedDim = varp->m_indexedDim;
|
|
}
|
|
}
|
|
static VerilatedVpioVarBase* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioVarBase*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
const VerilatedVar* varp() const { return m_varp; }
|
|
const VerilatedScope* scopep() const { return m_scopep; }
|
|
// Returns the number of the currently indexed dimension (starting at -1 for none).
|
|
int32_t indexedDim() const { return m_indexedDim; }
|
|
// Returns whether the currently indexed dimension is unpacked.
|
|
bool isIndexedDimUnpacked() const { return indexedDim() + 1 < varp()->udims(); }
|
|
// Returns a maximum accessible dimension number, counting only unpacked dimensions
|
|
// (if onlyUnpacked == true), or both unpacked + packed.
|
|
int32_t maxDim(bool onlyUnpacked) const {
|
|
return onlyUnpacked ? varp()->udims() - 1 : varp()->dims() - 1;
|
|
}
|
|
// Returns a number of elements in the array, stopping at the unpacked-packed boundary.
|
|
uint32_t size() const override {
|
|
const int maxDimNum = maxDim(isIndexedDimUnpacked());
|
|
int size = 1;
|
|
for (int dim = indexedDim() + 1; dim <= maxDimNum; ++dim)
|
|
size *= varp()->range(dim)->elements();
|
|
return size;
|
|
}
|
|
// If the array is unpacked, returns the bitsize of a single underlying packed element.
|
|
// If the array is packed, returns the bitsize of the whole array.
|
|
uint32_t bitSize() const {
|
|
if (isIndexedDimUnpacked())
|
|
return varp()->entBits();
|
|
else
|
|
return size();
|
|
}
|
|
const VerilatedRange* rangep() const override { return get_range(); }
|
|
const char* name() const override { return m_varp->name(); }
|
|
const char* fullname() const override { return m_fullname.c_str(); }
|
|
virtual void* varDatap() const { return m_varp->datap(); }
|
|
CData* varCDatap() const {
|
|
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT8););
|
|
return reinterpret_cast<CData*>(varDatap());
|
|
}
|
|
SData* varSDatap() const {
|
|
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT16););
|
|
return reinterpret_cast<SData*>(varDatap());
|
|
}
|
|
IData* varIDatap() const {
|
|
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT32););
|
|
return reinterpret_cast<IData*>(varDatap());
|
|
}
|
|
QData* varQDatap() const {
|
|
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT64););
|
|
return reinterpret_cast<QData*>(varDatap());
|
|
}
|
|
EData* varEDatap() const {
|
|
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_WDATA););
|
|
return reinterpret_cast<EData*>(varDatap());
|
|
}
|
|
double* varRealDatap() const {
|
|
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_REAL););
|
|
return reinterpret_cast<double*>(varDatap());
|
|
}
|
|
std::string* varStringDatap() const {
|
|
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_STRING););
|
|
return reinterpret_cast<std::string*>(varDatap());
|
|
}
|
|
virtual uint32_t bitOffset() const { return 0; }
|
|
};
|
|
|
|
class VerilatedVpioParam final : public VerilatedVpioVarBase {
|
|
public:
|
|
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
|
|
: VerilatedVpioVarBase{varp, scopep} {}
|
|
~VerilatedVpioParam() override = default;
|
|
|
|
static VerilatedVpioParam* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioParam*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
uint32_t type() const override { return vpiParameter; }
|
|
uint32_t constType() const override {
|
|
switch (m_varp->vltype()) {
|
|
case VLVT_UINT8:
|
|
case VLVT_UINT16:
|
|
case VLVT_UINT32:
|
|
case VLVT_UINT64:
|
|
case VLVT_WDATA: return vpiDecConst;
|
|
case VLVT_STRING: return vpiStringConst;
|
|
case VLVT_REAL: return vpiRealConst;
|
|
default: return vpiUndefined;
|
|
}
|
|
}
|
|
};
|
|
|
|
class VerilatedVpioRange final : public VerilatedVpio {
|
|
const VerilatedRange* const m_rangep;
|
|
|
|
public:
|
|
explicit VerilatedVpioRange(const VerilatedRange* rangep)
|
|
: m_rangep{rangep} {}
|
|
~VerilatedVpioRange() override = default;
|
|
static VerilatedVpioRange* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioRange*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
uint32_t type() const override { return vpiRange; }
|
|
uint32_t size() const override { return m_rangep->elements(); }
|
|
const VerilatedRange* rangep() const override { return m_rangep; }
|
|
};
|
|
|
|
class VerilatedVpioRangeIter final : public VerilatedVpio {
|
|
const std::vector<VerilatedRange> m_ranges;
|
|
std::vector<VerilatedRange>::const_iterator m_iter;
|
|
|
|
public:
|
|
explicit VerilatedVpioRangeIter(const std::vector<VerilatedRange>& ranges)
|
|
: m_ranges{ranges} {
|
|
m_iter = m_ranges.begin();
|
|
}
|
|
~VerilatedVpioRangeIter() override = default;
|
|
static VerilatedVpioRangeIter* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioRangeIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
uint32_t type() const override { return vpiIterator; }
|
|
vpiHandle dovpi_scan() override {
|
|
if (VL_UNLIKELY(m_iter == m_ranges.end())) {
|
|
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
|
return nullptr;
|
|
}
|
|
VerilatedRange* const rangep = new VerilatedRange(*m_iter);
|
|
++m_iter;
|
|
return ((new VerilatedVpioRange{rangep})->castVpiHandle());
|
|
}
|
|
};
|
|
|
|
class VerilatedVpioScope VL_NOT_FINAL : public VerilatedVpio {
|
|
protected:
|
|
const VerilatedScope* const m_scopep;
|
|
bool m_toplevel = false;
|
|
const char* m_name;
|
|
const char* m_fullname;
|
|
const char* m_defname;
|
|
|
|
public:
|
|
explicit VerilatedVpioScope(const VerilatedScope* scopep)
|
|
: m_scopep{scopep} {
|
|
m_fullname = m_scopep->name();
|
|
if (std::strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
|
|
m_name = m_scopep->identifier();
|
|
m_defname = m_scopep->defname();
|
|
}
|
|
~VerilatedVpioScope() override = default;
|
|
static VerilatedVpioScope* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioScope*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
uint32_t type() const override { return vpiGenScope; }
|
|
const VerilatedScope* scopep() const { return m_scopep; }
|
|
const char* name() const override { return m_name; }
|
|
const char* fullname() const override { return m_fullname; }
|
|
const char* defname() const override { return m_defname; }
|
|
bool toplevel() const { return m_toplevel; }
|
|
};
|
|
|
|
class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase {
|
|
uint8_t* m_prevDatap = nullptr; // Previous value of data, for cbValueChange
|
|
union {
|
|
uint8_t u8[4];
|
|
uint32_t u32;
|
|
} m_mask; // memoized variable mask
|
|
uint32_t m_entSize = 0; // memoized variable size
|
|
uint32_t m_bitOffset = 0;
|
|
|
|
protected:
|
|
void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries
|
|
std::vector<int32_t> m_index;
|
|
|
|
public:
|
|
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
|
|
: VerilatedVpioVarBase{varp, scopep} {
|
|
m_mask.u32 = VL_MASK_I(varp->entBits());
|
|
m_entSize = varp->entSize();
|
|
m_varDatap = varp->datap();
|
|
}
|
|
explicit VerilatedVpioVar(const VerilatedVpioVar* varp)
|
|
: VerilatedVpioVarBase{varp} {
|
|
if (varp) {
|
|
m_mask.u32 = varp->m_mask.u32;
|
|
m_entSize = varp->m_entSize;
|
|
m_varDatap = varp->m_varDatap;
|
|
m_index = varp->m_index;
|
|
// Not copying m_prevDatap, must be nullptr
|
|
} else {
|
|
m_mask.u32 = 0;
|
|
}
|
|
}
|
|
~VerilatedVpioVar() override {
|
|
if (m_prevDatap) VL_DO_CLEAR(delete[] m_prevDatap, m_prevDatap = nullptr);
|
|
}
|
|
static VerilatedVpioVar* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioVar*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
uint32_t bitOffset() const override { return m_bitOffset; }
|
|
uint32_t mask() const { return m_mask.u32; }
|
|
uint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
|
|
uint32_t entSize() const { return m_entSize; }
|
|
const std::vector<int32_t>& index() const { return m_index; }
|
|
VerilatedVpioVar* withIndex(int32_t index) const {
|
|
if (VL_UNLIKELY(indexedDim() + 1 >= varp()->dims())) return nullptr;
|
|
|
|
auto ret = new VerilatedVpioVar{this};
|
|
ret->m_index.push_back(index);
|
|
ret->m_indexedDim++;
|
|
|
|
int chunkSize = 1;
|
|
for (int dim = maxDim(isIndexedDimUnpacked()); dim > indexedDim() + 1; dim--)
|
|
chunkSize *= varp()->range(dim)->elements();
|
|
|
|
if (isIndexedDimUnpacked())
|
|
ret->m_varDatap = (static_cast<uint8_t*>(ret->m_varDatap))
|
|
+ entSize() * chunkSize * (index - get_range()->low());
|
|
else
|
|
ret->m_bitOffset += chunkSize * (index - get_range()->low());
|
|
|
|
return ret;
|
|
}
|
|
uint32_t type() const override {
|
|
uint32_t type = vpiReg;
|
|
switch (varp()->vltype()) {
|
|
case VLVT_REAL: type = vpiRealVar; break;
|
|
case VLVT_STRING: type = vpiStringVar; break;
|
|
default: break;
|
|
}
|
|
if (isIndexedDimUnpacked())
|
|
return vpiRegArray;
|
|
else
|
|
return type;
|
|
}
|
|
const char* fullname() const override {
|
|
static thread_local std::string t_out;
|
|
t_out = std::string{scopep()->name()} + "." + name();
|
|
for (auto idx : index()) t_out += "[" + std::to_string(idx) + "]";
|
|
return t_out.c_str();
|
|
}
|
|
void* prevDatap() const { return m_prevDatap; }
|
|
void* varDatap() const override { return m_varDatap; }
|
|
void createPrevDatap() {
|
|
if (VL_UNLIKELY(!m_prevDatap)) {
|
|
m_prevDatap = new uint8_t[entSize()];
|
|
std::memcpy(prevDatap(), varp()->datap(), entSize());
|
|
}
|
|
}
|
|
};
|
|
|
|
class VerilatedVpioVarIter final : public VerilatedVpio {
|
|
const VerilatedScope* const m_scopep;
|
|
VerilatedVarNameMap::const_iterator m_it;
|
|
bool m_started = false;
|
|
const VerilatedScope* m_topscopep = nullptr;
|
|
bool m_onlyParams;
|
|
|
|
public:
|
|
explicit VerilatedVpioVarIter(const VerilatedVpioScope* vop, bool onlyParams = false)
|
|
: m_scopep{vop->scopep()}
|
|
, m_onlyParams{onlyParams} {
|
|
if (VL_UNLIKELY(vop->toplevel()))
|
|
// This is a toplevel, so get TOP scope to search for ports during vpi_scan.
|
|
m_topscopep = Verilated::threadContextp()->scopeFind("TOP");
|
|
}
|
|
~VerilatedVpioVarIter() override = default;
|
|
static VerilatedVpioVarIter* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioVarIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
uint32_t type() const override { return vpiIterator; }
|
|
vpiHandle dovpi_scan() override {
|
|
if (VL_UNLIKELY(!m_scopep->varsp())) {
|
|
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
|
return nullptr; // End of list - only one deep
|
|
}
|
|
while (true) {
|
|
const VerilatedVarNameMap* const varsp = m_scopep->varsp();
|
|
if (VL_UNLIKELY(!m_started)) {
|
|
m_it = varsp->begin();
|
|
m_started = true;
|
|
} else if (VL_UNLIKELY(m_it == varsp->end())) {
|
|
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
|
return nullptr;
|
|
} else {
|
|
++m_it;
|
|
}
|
|
if (VL_UNLIKELY(m_it == varsp->end())) {
|
|
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
|
return nullptr;
|
|
}
|
|
if (m_onlyParams && !m_it->second.isParam()) continue;
|
|
if (VL_UNLIKELY(m_topscopep)) {
|
|
if (const VerilatedVar* topvarp = m_topscopep->varFind(m_it->second.name())) {
|
|
if (topvarp->isParam()) {
|
|
return ((new VerilatedVpioParam{topvarp, m_topscopep})->castVpiHandle());
|
|
} else {
|
|
return ((new VerilatedVpioVar{topvarp, m_topscopep})->castVpiHandle());
|
|
}
|
|
}
|
|
}
|
|
if (m_it->second.isParam()) {
|
|
return ((new VerilatedVpioParam{&(m_it->second), m_scopep})->castVpiHandle());
|
|
} else {
|
|
return ((new VerilatedVpioVar{&(m_it->second), m_scopep})->castVpiHandle());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class VerilatedVpioRegIter final : public VerilatedVpio {
|
|
VerilatedVpioVar* m_var;
|
|
std::vector<VerilatedRange> m_ranges;
|
|
std::vector<int> m_nextIndex;
|
|
const int32_t m_maxDim;
|
|
|
|
public:
|
|
explicit VerilatedVpioRegIter(const VerilatedVpioVar* vop)
|
|
: m_var{new VerilatedVpioVar(vop)}
|
|
, m_maxDim{vop->varp()->udims() - 1} {
|
|
for (auto it = vop->indexedDim() + 1; it <= m_maxDim; ++it)
|
|
m_ranges.push_back(*vop->varp()->range(it));
|
|
for (auto it : m_ranges) m_nextIndex.push_back(it.right());
|
|
}
|
|
~VerilatedVpioRegIter() override = default;
|
|
static VerilatedVpioRegIter* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioRegIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
uint32_t type() const override { return vpiIterator; }
|
|
vpiHandle dovpi_scan() override {
|
|
if (VL_UNLIKELY(m_var->indexedDim() >= m_maxDim)) {
|
|
// Trying to iterate over a non-array object
|
|
delete this;
|
|
return nullptr;
|
|
}
|
|
if (m_nextIndex.front() > m_ranges.front().high()
|
|
|| m_nextIndex.front() < m_ranges.front().low()) {
|
|
// Finished iterating
|
|
delete this;
|
|
return nullptr;
|
|
}
|
|
|
|
VerilatedVpioVar* ret = m_var;
|
|
for (auto it : m_nextIndex) ret = ret->withIndex(it);
|
|
|
|
// Increase the index, pretending the dimensions are flattened
|
|
for (int32_t it = m_ranges.size() - 1; it >= 0; it--) {
|
|
m_nextIndex.at(it) += m_ranges.at(it).increment();
|
|
if (m_nextIndex.at(it) <= m_ranges.at(it).high()
|
|
&& m_nextIndex.at(it) >= m_ranges.at(it).low())
|
|
break;
|
|
else if (it > 0)
|
|
m_nextIndex.at(it) = m_ranges.at(it).right();
|
|
}
|
|
|
|
return ret->castVpiHandle();
|
|
}
|
|
};
|
|
|
|
class VerilatedVpioModule final : public VerilatedVpioScope {
|
|
|
|
public:
|
|
explicit VerilatedVpioModule(const VerilatedScope* modulep)
|
|
: VerilatedVpioScope{modulep} {
|
|
// Look for '.' not inside escaped identifier
|
|
const std::string scopename = m_fullname;
|
|
std::string::size_type pos = std::string::npos;
|
|
size_t i = 0;
|
|
while (i < scopename.length()) {
|
|
if (scopename[i] == '\\') {
|
|
while (i < scopename.length() && scopename[i] != ' ') ++i;
|
|
++i; // Proc ' ', it should always be there. Then grab '.' on next cycle
|
|
} else {
|
|
while (i < scopename.length() && scopename[i] != '.') ++i;
|
|
if (i < scopename.length()) pos = i++;
|
|
}
|
|
}
|
|
if (VL_UNLIKELY(pos == std::string::npos)) m_toplevel = true;
|
|
}
|
|
static VerilatedVpioModule* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioModule*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
uint32_t type() const override { return vpiModule; }
|
|
};
|
|
|
|
class VerilatedVpioModuleIter final : public VerilatedVpio {
|
|
const std::vector<const VerilatedScope*>* m_vec;
|
|
std::vector<const VerilatedScope*>::const_iterator m_it;
|
|
|
|
public:
|
|
explicit VerilatedVpioModuleIter(const std::vector<const VerilatedScope*>& vec)
|
|
: m_vec{&vec} {
|
|
m_it = m_vec->begin();
|
|
}
|
|
~VerilatedVpioModuleIter() override = default;
|
|
static VerilatedVpioModuleIter* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioModuleIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
uint32_t type() const override { return vpiIterator; }
|
|
vpiHandle dovpi_scan() override {
|
|
while (true) {
|
|
if (m_it == m_vec->end()) {
|
|
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
|
return nullptr;
|
|
}
|
|
const VerilatedScope* const modp = *m_it++;
|
|
const VerilatedScope::Type itype = modp->type();
|
|
if (itype == VerilatedScope::SCOPE_MODULE) {
|
|
return (new VerilatedVpioModule{modp})->castVpiHandle();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class VerilatedVpioScopeIter final : public VerilatedVpio {
|
|
const std::vector<const VerilatedScope*>* m_vec;
|
|
std::vector<const VerilatedScope*>::const_iterator m_it;
|
|
|
|
public:
|
|
explicit VerilatedVpioScopeIter(const std::vector<const VerilatedScope*>& vec)
|
|
: m_vec{&vec} {
|
|
m_it = m_vec->begin();
|
|
}
|
|
~VerilatedVpioScopeIter() override = default;
|
|
static VerilatedVpioScopeIter* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioScopeIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
uint32_t type() const override { return vpiIterator; }
|
|
vpiHandle dovpi_scan() override {
|
|
while (true) {
|
|
if (m_it == m_vec->end()) {
|
|
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
|
return nullptr;
|
|
}
|
|
const VerilatedScope* const modp = *m_it++;
|
|
const VerilatedScope::Type itype = modp->type();
|
|
if (itype == VerilatedScope::SCOPE_OTHER) {
|
|
return (new VerilatedVpioScope{modp})->castVpiHandle();
|
|
} else if (itype == VerilatedScope::SCOPE_MODULE) {
|
|
return (new VerilatedVpioModule{modp})->castVpiHandle();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
static const char* d_unit = "$unit";
|
|
class VerilatedVpioPackage final : public VerilatedVpioScope {
|
|
std::string m_fullname_string;
|
|
|
|
public:
|
|
explicit VerilatedVpioPackage(const VerilatedScope* modulep)
|
|
: VerilatedVpioScope{modulep} {
|
|
m_fullname_string = std::string{m_fullname} + "::";
|
|
if (m_fullname_string == "\\$unit ::") m_fullname_string = "$unit::";
|
|
|
|
if (strcmp(m_name, "\\$unit ") == 0) m_name = d_unit;
|
|
}
|
|
static VerilatedVpioPackage* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioPackage*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
const char* fullname() const override { return m_fullname_string.c_str(); }
|
|
uint32_t type() const override { return vpiPackage; }
|
|
};
|
|
|
|
class VerilatedVpioInstanceIter final : public VerilatedVpio {
|
|
const std::vector<const VerilatedScope*>* m_vec;
|
|
std::vector<const VerilatedScope*>::const_iterator m_it;
|
|
|
|
public:
|
|
explicit VerilatedVpioInstanceIter(const std::vector<const VerilatedScope*>& vec)
|
|
: m_vec{&vec} {
|
|
m_it = m_vec->begin();
|
|
}
|
|
~VerilatedVpioInstanceIter() override = default;
|
|
static VerilatedVpioInstanceIter* castp(vpiHandle h) {
|
|
return dynamic_cast<VerilatedVpioInstanceIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
|
}
|
|
uint32_t type() const override { return vpiIterator; }
|
|
vpiHandle dovpi_scan() override {
|
|
while (true) {
|
|
if (m_it == m_vec->end()) {
|
|
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
|
return nullptr;
|
|
}
|
|
const VerilatedScope::Type itype = (*m_it)->type();
|
|
const VerilatedScope* const modp = *m_it++;
|
|
if (itype == VerilatedScope::SCOPE_MODULE) {
|
|
return (new VerilatedVpioModule{modp})->castVpiHandle();
|
|
}
|
|
if (itype == VerilatedScope::SCOPE_PACKAGE) {
|
|
return (new VerilatedVpioPackage{modp})->castVpiHandle();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
//======================================================================
|
|
|
|
using VerilatedPliCb = PLI_INT32 (*)(struct t_cb_data*);
|
|
|
|
class VerilatedVpiCbHolder final {
|
|
// Holds information needed to call a callback
|
|
uint64_t m_id; // Unique id/sequence number to find schedule's event, 0 = invalid
|
|
s_cb_data m_cbData;
|
|
s_vpi_value m_value;
|
|
VerilatedVpioVar m_varo; // If a cbValueChange callback, the object we will return
|
|
|
|
public:
|
|
// cppcheck-suppress uninitVar // m_value
|
|
VerilatedVpiCbHolder(uint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
|
|
: m_id{id}
|
|
, m_cbData{*cbDatap}
|
|
, m_varo{varop} {
|
|
m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
|
|
m_cbData.value = &m_value;
|
|
if (varop) {
|
|
m_cbData.obj = m_varo.castVpiHandle();
|
|
m_varo.createPrevDatap();
|
|
} else {
|
|
m_cbData.obj = nullptr;
|
|
}
|
|
}
|
|
~VerilatedVpiCbHolder() = default;
|
|
VerilatedPliCb cb_rtnp() const { return m_cbData.cb_rtn; }
|
|
s_cb_data* cb_datap() { return &m_cbData; }
|
|
uint64_t id() const { return m_id; }
|
|
bool invalid() const { return !m_id; }
|
|
void invalidate() { m_id = 0; }
|
|
};
|
|
|
|
class VerilatedVpiPutHolder final {
|
|
VerilatedVpioVar m_var;
|
|
s_vpi_value m_value;
|
|
union Storage {
|
|
char init = 0; // to ensure trivial constructor
|
|
std::string str;
|
|
std::vector<s_vpi_vecval> vec;
|
|
~Storage() noexcept {/* handled by VerilatedVpiPutHolder */};
|
|
} m_storage{};
|
|
|
|
public:
|
|
VerilatedVpiPutHolder(const VerilatedVpioVar* vop, p_vpi_value valuep)
|
|
: m_var{vop} {
|
|
m_value.format = valuep->format;
|
|
switch (valuep->format) {
|
|
case vpiBinStrVal: // FALLTHRU
|
|
case vpiOctStrVal: // FALLTHRU
|
|
case vpiDecStrVal: // FALLTHRU
|
|
case vpiHexStrVal: // FALLTHRU
|
|
case vpiStringVal: {
|
|
new (&m_storage.str) std::string{valuep->value.str};
|
|
m_value.value.str = const_cast<char*>(m_storage.str.c_str());
|
|
break;
|
|
}
|
|
case vpiScalarVal: {
|
|
m_value.value.scalar = valuep->value.scalar;
|
|
break;
|
|
}
|
|
case vpiIntVal: {
|
|
m_value.value.integer = valuep->value.integer;
|
|
break;
|
|
}
|
|
case vpiRealVal: {
|
|
m_value.value.real = valuep->value.real;
|
|
break;
|
|
}
|
|
case vpiVectorVal: {
|
|
size_t words = 0;
|
|
switch (vop->varp()->vltype()) {
|
|
case VLVT_UINT8:
|
|
case VLVT_UINT16:
|
|
case VLVT_UINT32: {
|
|
words = 1;
|
|
break;
|
|
}
|
|
case VLVT_UINT64: {
|
|
words = 2;
|
|
break;
|
|
}
|
|
case VLVT_WDATA: {
|
|
words = VL_WORDS_I(vop->varp()->entBits());
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
new (&m_storage.vec)
|
|
std::vector<s_vpi_vecval>{valuep->value.vector, &valuep->value.vector[words]};
|
|
m_value.value.vector = m_storage.vec.data();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
VerilatedVpiPutHolder(VerilatedVpiPutHolder const& o)
|
|
: m_var{o.m_var}
|
|
, m_value{o.m_value} {
|
|
switch (m_value.format) {
|
|
case vpiBinStrVal: // FALLTHRU
|
|
case vpiOctStrVal: // FALLTHRU
|
|
case vpiDecStrVal: // FALLTHRU
|
|
case vpiHexStrVal: // FALLTHRU
|
|
case vpiStringVal: {
|
|
new (&m_storage.str) std::string{o.m_storage.str};
|
|
break;
|
|
}
|
|
case vpiVectorVal: {
|
|
new (&m_storage.vec) std::vector<s_vpi_vecval>{o.m_storage.vec};
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
VerilatedVpiPutHolder(VerilatedVpiPutHolder&& o) noexcept
|
|
: m_var{std::move(o.m_var)}
|
|
, m_value{std::move(o.m_value)} {
|
|
switch (m_value.format) {
|
|
case vpiBinStrVal: // FALLTHRU
|
|
case vpiOctStrVal: // FALLTHRU
|
|
case vpiDecStrVal: // FALLTHRU
|
|
case vpiHexStrVal: // FALLTHRU
|
|
case vpiStringVal: {
|
|
new (&m_storage.str) std::string{std::move(o.m_storage.str)};
|
|
break;
|
|
}
|
|
case vpiVectorVal: {
|
|
new (&m_storage.vec) std::vector<s_vpi_vecval>{std::move(o.m_storage.vec)};
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
~VerilatedVpiPutHolder() noexcept {
|
|
switch (m_value.format) {
|
|
case vpiBinStrVal: // FALLTHRU
|
|
case vpiOctStrVal: // FALLTHRU
|
|
case vpiDecStrVal: // FALLTHRU
|
|
case vpiHexStrVal: // FALLTHRU
|
|
case vpiStringVal: m_storage.str.~basic_string(); break;
|
|
case vpiVectorVal: m_storage.vec.~vector(); break;
|
|
}
|
|
}
|
|
|
|
VerilatedVpioVar* varp() { return &m_var; }
|
|
p_vpi_value valuep() { return &m_value; }
|
|
|
|
static bool canInertialDelay(p_vpi_value valuep) {
|
|
switch (valuep->format) {
|
|
case vpiBinStrVal: // FALLTHRU
|
|
case vpiOctStrVal: // FALLTHRU
|
|
case vpiDecStrVal: // FALLTHRU
|
|
case vpiHexStrVal: // FALLTHRU
|
|
case vpiStringVal: {
|
|
if (VL_UNLIKELY(!valuep->value.str)) return false;
|
|
break;
|
|
}
|
|
case vpiScalarVal: // FALLTHRU
|
|
case vpiIntVal: // FALLTHRU
|
|
case vpiRealVal: break;
|
|
case vpiVectorVal: {
|
|
if (VL_UNLIKELY(!valuep->value.vector)) return false;
|
|
break;
|
|
}
|
|
default: {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
struct VerilatedVpiTimedCbsCmp final {
|
|
// Ordering sets keyed by time, then callback unique id
|
|
bool operator()(const std::pair<QData, uint64_t>& a,
|
|
const std::pair<QData, uint64_t>& b) const {
|
|
if (a.first < b.first) return true;
|
|
if (a.first > b.first) return false;
|
|
return a.second < b.second;
|
|
}
|
|
};
|
|
|
|
class VerilatedVpiError;
|
|
|
|
class VerilatedVpiImp final {
|
|
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maximum callback reason
|
|
using VpioCbList = std::list<VerilatedVpiCbHolder>;
|
|
using VpioFutureCbs = std::map<std::pair<QData, uint64_t>, VerilatedVpiCbHolder>;
|
|
|
|
// All only medium-speed, so use singleton function
|
|
// Callbacks that are past or at current timestamp
|
|
std::array<VpioCbList, CB_ENUM_MAX_VALUE> m_cbCurrentLists;
|
|
VpioCbList m_cbCallList; // List of callbacks currently being called by callCbs
|
|
VpioFutureCbs m_futureCbs; // Time based callbacks for future timestamps
|
|
VpioFutureCbs m_nextCbs; // cbNextSimTime callbacks
|
|
std::list<VerilatedVpiPutHolder> m_inertialPuts; // Pending vpi puts due to vpiInertialDelay
|
|
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
|
|
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
|
|
uint64_t m_nextCallbackId = 1; // Id to identify callback
|
|
bool m_evalNeeded = false; // Model has had signals updated via vpi_put_value()
|
|
|
|
static VerilatedVpiImp& s() { // Singleton
|
|
static VerilatedVpiImp s_s;
|
|
return s_s;
|
|
}
|
|
|
|
public:
|
|
static void assertOneCheck() { s().m_assertOne.check(); }
|
|
static uint64_t nextCallbackId() { return ++s().m_nextCallbackId; }
|
|
|
|
static void cbCurrentAdd(uint64_t id, const s_cb_data* cb_data_p) {
|
|
// The passed cb_data_p was property of the user, so need to recreate
|
|
if (VL_UNCOVERABLE(cb_data_p->reason >= CB_ENUM_MAX_VALUE)) {
|
|
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi bb reason too large");
|
|
}
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" PRId64 " obj=%p\n",
|
|
cb_data_p->reason, id, cb_data_p->obj););
|
|
VerilatedVpioVar* varop = nullptr;
|
|
if (cb_data_p->reason == cbValueChange) varop = VerilatedVpioVar::castp(cb_data_p->obj);
|
|
s().m_cbCurrentLists[cb_data_p->reason].emplace_back(id, cb_data_p, varop);
|
|
}
|
|
static void cbFutureAdd(uint64_t id, const s_cb_data* cb_data_p, QData time) {
|
|
// The passed cb_data_p was property of the user, so need to recreate
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" PRId64 " time=%" PRIu64
|
|
" obj=%p\n",
|
|
cb_data_p->reason, id, time, cb_data_p->obj););
|
|
s().m_futureCbs.emplace(std::piecewise_construct, std::forward_as_tuple(time, id),
|
|
std::forward_as_tuple(id, cb_data_p, nullptr));
|
|
}
|
|
static void cbNextAdd(uint64_t id, const s_cb_data* cb_data_p, QData time) {
|
|
// The passed cb_data_p was property of the user, so need to recreate
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d(NEXT) id=%" PRId64
|
|
" time=%" PRIu64 " obj=%p\n",
|
|
cb_data_p->reason, id, time, cb_data_p->obj););
|
|
s().m_nextCbs.emplace(std::piecewise_construct, std::forward_as_tuple(time, id),
|
|
std::forward_as_tuple(id, cb_data_p, nullptr));
|
|
}
|
|
static void cbReasonRemove(uint64_t id, uint32_t reason, QData time) {
|
|
// Id might no longer exist, if already removed due to call after event, or teardown
|
|
// We do not remove it now as we may be iterating the list,
|
|
// instead set to nullptr and will cleanup later
|
|
// Remove from cbCurrent queue
|
|
for (auto& ir : s().m_cbCurrentLists[reason]) {
|
|
if (ir.id() == id) {
|
|
ir.invalidate();
|
|
return; // Once found, it won't also be in m_cbCallList, m_futureCbs, or m_nextCbs
|
|
}
|
|
}
|
|
for (auto& ir : s().m_cbCallList) {
|
|
if (ir.id() == id) {
|
|
ir.invalidate();
|
|
return; // Once found, it won't also be in m_futureCbs or m_nextCbs
|
|
}
|
|
}
|
|
{ // Remove from cbFuture queue
|
|
const auto it = s().m_futureCbs.find(std::make_pair(time, id));
|
|
if (it != s().m_futureCbs.end()) {
|
|
it->second.invalidate();
|
|
return;
|
|
}
|
|
}
|
|
{ // Remove from cbNext
|
|
const auto it = s().m_nextCbs.find(std::make_pair(time, id));
|
|
if (it != s().m_nextCbs.end()) {
|
|
it->second.invalidate();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
static void moveFutureCbs() VL_MT_UNSAFE_ONE {
|
|
// For any events past current time, move from cbFuture queue to cbCurrent queue
|
|
if (s().m_futureCbs.empty() && s().m_nextCbs.empty()) return;
|
|
// VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: moveFutureCbs\n"); dumpCbs(); );
|
|
const QData time = VL_TIME_Q();
|
|
for (auto it = s().m_futureCbs.begin(); //
|
|
VL_UNLIKELY(it != s().m_futureCbs.end() && it->first.first <= time);) {
|
|
VerilatedVpiCbHolder& hor = it->second;
|
|
const auto last_it = it;
|
|
++it;
|
|
if (VL_UNLIKELY(!hor.invalid())) {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: moveFutureCbs id=%" PRId64 "\n", hor.id()););
|
|
s().m_cbCurrentLists[hor.cb_datap()->reason].emplace_back(hor);
|
|
}
|
|
s().m_futureCbs.erase(last_it);
|
|
}
|
|
for (auto it = s().m_nextCbs.begin(); //
|
|
VL_UNLIKELY(it != s().m_nextCbs.end() && it->first.first < time);) {
|
|
VerilatedVpiCbHolder& hor = it->second;
|
|
const auto last_it = it;
|
|
++it;
|
|
if (VL_UNLIKELY(!hor.invalid())) {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: moveFutureCbs id=%" PRId64 "\n", hor.id()););
|
|
s().m_cbCurrentLists[hor.cb_datap()->reason].emplace_back(hor);
|
|
}
|
|
s().m_nextCbs.erase(last_it);
|
|
}
|
|
}
|
|
static QData cbNextDeadline() {
|
|
const auto it = s().m_futureCbs.cbegin();
|
|
if (VL_LIKELY(it != s().m_futureCbs.cend())) return it->first.first;
|
|
return ~0ULL; // maxquad
|
|
}
|
|
static bool hasCbs(const uint32_t reason) VL_MT_UNSAFE_ONE {
|
|
return !s().m_cbCurrentLists[reason].empty();
|
|
}
|
|
static bool callCbs(const uint32_t reason) VL_MT_UNSAFE_ONE {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: callCbs reason=%u\n", reason););
|
|
assertOneCheck();
|
|
moveFutureCbs();
|
|
if (s().m_cbCurrentLists[reason].empty()) return false;
|
|
// Iterate on old list, making new list empty, to prevent looping over newly added elements
|
|
std::swap(s().m_cbCurrentLists[reason], s().m_cbCallList);
|
|
bool called = false;
|
|
for (VerilatedVpiCbHolder& ihor : s().m_cbCallList) {
|
|
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
|
|
if (VL_LIKELY(!ihor.invalid())) { // Not deleted earlier
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: reason_callback reason=%d id=%" PRId64 "\n",
|
|
reason, ihor.id()););
|
|
ihor.invalidate(); // Timed callbacks are one-shot
|
|
(ihor.cb_rtnp())(ihor.cb_datap());
|
|
called = true;
|
|
}
|
|
}
|
|
s().m_cbCallList.clear();
|
|
return called;
|
|
}
|
|
static bool callValueCbs() VL_MT_UNSAFE_ONE {
|
|
assertOneCheck();
|
|
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
|
|
bool called = false;
|
|
std::set<VerilatedVpioVar*> update; // set of objects to update after callbacks
|
|
if (cbObjList.empty()) return called;
|
|
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
|
|
for (auto it = cbObjList.begin(); true;) {
|
|
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
|
|
const bool was_last = it == last;
|
|
if (VL_UNLIKELY(it->invalid())) { // Deleted earlier, cleanup
|
|
it = cbObjList.erase(it);
|
|
if (was_last) break;
|
|
continue;
|
|
}
|
|
VerilatedVpiCbHolder& ho = *it++;
|
|
VerilatedVpioVar* const varop
|
|
= reinterpret_cast<VerilatedVpioVar*>(ho.cb_datap()->obj);
|
|
void* const newDatap = varop->varDatap();
|
|
void* const prevDatap = varop->prevDatap(); // Was malloced when we added the callback
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
|
|
varop->fullname(), *(static_cast<CData*>(newDatap)),
|
|
*(static_cast<CData*>(prevDatap)), newDatap, prevDatap););
|
|
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
|
|
ho.id(), varop->fullname(),
|
|
*(static_cast<CData*>(newDatap))););
|
|
update.insert(varop);
|
|
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
|
|
(ho.cb_rtnp())(ho.cb_datap());
|
|
called = true;
|
|
}
|
|
if (was_last) break;
|
|
}
|
|
for (const auto& ip : update) {
|
|
std::memcpy(ip->prevDatap(), ip->varDatap(), ip->entSize());
|
|
}
|
|
return called;
|
|
}
|
|
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
|
static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info
|
|
static bool evalNeeded() { return s().m_evalNeeded; }
|
|
static void evalNeeded(bool evalNeeded) { s().m_evalNeeded = evalNeeded; }
|
|
static void inertialDelay(const VerilatedVpioVar* vop, p_vpi_value valuep) {
|
|
s().m_inertialPuts.emplace_back(vop, valuep);
|
|
}
|
|
static void doInertialPuts() {
|
|
for (auto& it : s().m_inertialPuts) {
|
|
vpi_put_value(it.varp()->castVpiHandle(), it.valuep(), nullptr, vpiNoDelay);
|
|
}
|
|
s().m_inertialPuts.clear();
|
|
}
|
|
};
|
|
|
|
//======================================================================
|
|
// Statics
|
|
// Internal note: Globals may multi-construct, see verilated.cpp top.
|
|
|
|
thread_local uint8_t* VerilatedVpio::t_freeHeadp = nullptr;
|
|
|
|
//======================================================================
|
|
// VerilatedVpiError
|
|
// Internal container for vpi error info
|
|
|
|
class VerilatedVpiError final {
|
|
t_vpi_error_info m_errorInfo;
|
|
bool m_flag = false;
|
|
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::threadContextp()->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_buff[0] = '\0';
|
|
m_errorInfo.product = const_cast<PLI_BYTE8*>(Verilated::productName());
|
|
}
|
|
~VerilatedVpiError() = default;
|
|
static void selfTest() VL_MT_UNSAFE_ONE;
|
|
VerilatedVpiError* setMessage(PLI_INT32 level) {
|
|
m_flag = true;
|
|
m_errorInfo.level = level;
|
|
return this;
|
|
}
|
|
void setMessage(const std::string& file, PLI_INT32 line, const char* message, ...) {
|
|
// message cannot be a const string& as va_start cannot use a reference
|
|
static thread_local std::string t_filehold;
|
|
va_list args;
|
|
va_start(args, message);
|
|
VL_VSNPRINTF(m_buff, sizeof(m_buff), message, args);
|
|
va_end(args);
|
|
m_errorInfo.state = vpiPLI;
|
|
t_filehold = file;
|
|
setError(static_cast<PLI_BYTE8*>(m_buff), nullptr,
|
|
const_cast<PLI_BYTE8*>(t_filehold.c_str()), line);
|
|
}
|
|
p_vpi_error_info getError() {
|
|
if (m_flag) return &m_errorInfo;
|
|
return nullptr;
|
|
}
|
|
void resetError() { m_flag = false; }
|
|
static void vpi_unsupported() {
|
|
// Not supported yet
|
|
const 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) VL_PURE;
|
|
static const char* strFromVpiObjType(PLI_INT32 vpiVal) VL_PURE;
|
|
static const char* strFromVpiMethod(PLI_INT32 vpiVal) VL_PURE;
|
|
static const char* strFromVpiCallbackReason(PLI_INT32 vpiVal) VL_PURE;
|
|
static const char* strFromVpiProp(PLI_INT32 vpiVal) VL_PURE;
|
|
static const char* strFromVpiConstType(PLI_INT32 vpiVal) VL_PURE;
|
|
};
|
|
|
|
//======================================================================
|
|
// VerilatedVpi implementation
|
|
|
|
bool VerilatedVpi::callCbs(uint32_t reason) VL_MT_UNSAFE_ONE {
|
|
return VerilatedVpiImp::callCbs(reason);
|
|
}
|
|
|
|
bool VerilatedVpi::hasCbs(uint32_t reason) VL_MT_UNSAFE_ONE {
|
|
return VerilatedVpiImp::hasCbs(reason);
|
|
}
|
|
|
|
// Historical, before we had multiple kinds of timed callbacks
|
|
void VerilatedVpi::callTimedCbs() VL_MT_UNSAFE_ONE { VerilatedVpiImp::callCbs(cbAfterDelay); }
|
|
|
|
bool VerilatedVpi::callValueCbs() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::callValueCbs(); }
|
|
|
|
QData VerilatedVpi::cbNextDeadline() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::cbNextDeadline(); }
|
|
|
|
void VerilatedVpi::dumpCbs() VL_MT_UNSAFE_ONE { VerilatedVpiImp::dumpCbs(); }
|
|
|
|
PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
|
|
VerilatedVpiImp::cbReasonRemove(m_id, m_reason, m_time);
|
|
delete this; // IEEE 37.2.2 a vpi_remove_cb does a vpi_release_handle
|
|
return 1;
|
|
}
|
|
|
|
void VerilatedVpi::clearEvalNeeded() VL_MT_UNSAFE_ONE { VerilatedVpiImp::evalNeeded(false); }
|
|
bool VerilatedVpi::evalNeeded() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::evalNeeded(); }
|
|
|
|
void VerilatedVpi::doInertialPuts() VL_MT_UNSAFE_ONE { VerilatedVpiImp::doInertialPuts(); }
|
|
|
|
//======================================================================
|
|
// VerilatedVpiImp implementation
|
|
|
|
void VerilatedVpiImp::dumpCbs() VL_MT_UNSAFE_ONE {
|
|
assertOneCheck();
|
|
VL_DBG_MSGF("- vpi: dumpCbs\n");
|
|
for (uint32_t reason = 0; reason < CB_ENUM_MAX_VALUE; ++reason) {
|
|
VpioCbList& cbObjList = s().m_cbCurrentLists[reason];
|
|
for (auto& ho : cbObjList) {
|
|
if (VL_UNLIKELY(!ho.invalid())) {
|
|
VL_DBG_MSGF("- vpi: reason=%d=%s id=%" PRId64 "\n", reason,
|
|
VerilatedVpiError::strFromVpiCallbackReason(reason), ho.id());
|
|
}
|
|
}
|
|
}
|
|
for (auto& ifuture : s().m_nextCbs) {
|
|
const QData time = ifuture.first.first;
|
|
VerilatedVpiCbHolder& ho = ifuture.second;
|
|
if (VL_UNLIKELY(!ho.invalid())) {
|
|
VL_DBG_MSGF("- vpi: time=%" PRId64 "(NEXT) reason=%d=%s id=%" PRId64 "\n", time,
|
|
ho.cb_datap()->reason,
|
|
VerilatedVpiError::strFromVpiCallbackReason(ho.cb_datap()->reason),
|
|
ho.id());
|
|
}
|
|
}
|
|
for (auto& ifuture : s().m_futureCbs) {
|
|
const QData time = ifuture.first.first;
|
|
VerilatedVpiCbHolder& ho = ifuture.second;
|
|
if (VL_UNLIKELY(!ho.invalid())) {
|
|
VL_DBG_MSGF("- vpi: time=%" PRId64 " reason=%d=%s id=%" PRId64 "\n", time,
|
|
ho.cb_datap()->reason,
|
|
VerilatedVpiError::strFromVpiCallbackReason(ho.cb_datap()->reason),
|
|
ho.id());
|
|
}
|
|
}
|
|
}
|
|
|
|
VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE {
|
|
VerilatedVpiImp::assertOneCheck();
|
|
if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError;
|
|
return s().m_errorInfop;
|
|
}
|
|
|
|
//======================================================================
|
|
// VerilatedVpiError Methods
|
|
|
|
const char* VerilatedVpiError::strFromVpiVal(PLI_INT32 vpiVal) VL_PURE {
|
|
// clang-format off
|
|
static const char* const names[] = {
|
|
"*undefined*",
|
|
"vpiBinStrVal",
|
|
"vpiOctStrVal",
|
|
"vpiDecStrVal",
|
|
"vpiHexStrVal",
|
|
"vpiScalarVal",
|
|
"vpiIntVal",
|
|
"vpiRealVal",
|
|
"vpiStringVal",
|
|
"vpiVectorVal",
|
|
"vpiStrengthVal",
|
|
"vpiTimeVal",
|
|
"vpiObjTypeVal",
|
|
"vpiSuppressVal",
|
|
"vpiShortIntVal",
|
|
"vpiLongIntVal",
|
|
"vpiShortRealVal",
|
|
"vpiRawTwoStateVal",
|
|
"vpiRawFourStateVal",
|
|
};
|
|
// clang-format on
|
|
if (VL_UNCOVERABLE(vpiVal < 0)) return names[0];
|
|
return names[(vpiVal <= vpiRawFourStateVal) ? vpiVal : 0];
|
|
}
|
|
const char* VerilatedVpiError::strFromVpiObjType(PLI_INT32 vpiVal) VL_PURE {
|
|
// clang-format off
|
|
static const char* const names[] = {
|
|
"*undefined*",
|
|
"vpiAlways",
|
|
"vpiAssignStmt",
|
|
"vpiAssignment",
|
|
"vpiBegin",
|
|
"vpiCase",
|
|
"vpiCaseItem",
|
|
"vpiConstant",
|
|
"vpiContAssign",
|
|
"vpiDeassign",
|
|
"vpiDefParam",
|
|
"vpiDelayControl",
|
|
"vpiDisable",
|
|
"vpiEventControl",
|
|
"vpiEventStmt",
|
|
"vpiFor",
|
|
"vpiForce",
|
|
"vpiForever",
|
|
"vpiFork",
|
|
"vpiFuncCall",
|
|
"vpiFunction",
|
|
"vpiGate",
|
|
"vpiIf",
|
|
"vpiIfElse",
|
|
"vpiInitial",
|
|
"vpiIntegerVar",
|
|
"vpiInterModPath",
|
|
"vpiIterator",
|
|
"vpiIODecl",
|
|
"vpiMemory",
|
|
"vpiMemoryWord",
|
|
"vpiModPath",
|
|
"vpiModule",
|
|
"vpiNamedBegin",
|
|
"vpiNamedEvent",
|
|
"vpiNamedFork",
|
|
"vpiNet",
|
|
"vpiNetBit",
|
|
"vpiNullStmt",
|
|
"vpiOperation",
|
|
"vpiParamAssign",
|
|
"vpiParameter",
|
|
"vpiPartSelect",
|
|
"vpiPathTerm",
|
|
"vpiPort",
|
|
"vpiPortBit",
|
|
"vpiPrimTerm",
|
|
"vpiRealVar",
|
|
"vpiReg",
|
|
"vpiRegBit",
|
|
"vpiRelease",
|
|
"vpiRepeat",
|
|
"vpiRepeatControl",
|
|
"vpiSchedEvent",
|
|
"vpiSpecParam",
|
|
"vpiSwitch",
|
|
"vpiSysFuncCall",
|
|
"vpiSysTaskCall",
|
|
"vpiTableEntry",
|
|
"vpiTask",
|
|
"vpiTaskCall",
|
|
"vpiTchk",
|
|
"vpiTchkTerm",
|
|
"vpiTimeVar",
|
|
"vpiTimeQueue",
|
|
"vpiUdp",
|
|
"vpiUdpDefn",
|
|
"vpiUserSystf",
|
|
"vpiVarSelect",
|
|
"vpiWait",
|
|
"vpiWhile",
|
|
"vpiCondition",
|
|
"vpiDelay",
|
|
"vpiElseStmt",
|
|
"vpiForIncStmt",
|
|
"vpiForInitStmt",
|
|
"vpiHighConn",
|
|
"vpiLhs",
|
|
"vpiIndex",
|
|
"vpiLeftRange",
|
|
"vpiLowConn",
|
|
"vpiParent",
|
|
"vpiRhs",
|
|
"vpiRightRange",
|
|
"vpiScope",
|
|
"vpiSysTfCall",
|
|
"vpiTchkDataTerm",
|
|
"vpiTchkNotifier",
|
|
"vpiTchkRefTerm",
|
|
"vpiArgument",
|
|
"vpiBit",
|
|
"vpiDriver",
|
|
"vpiInternalScope",
|
|
"vpiLoad",
|
|
"vpiModDataPathIn",
|
|
"vpiModPathIn",
|
|
"vpiModPathOut",
|
|
"vpiOperand",
|
|
"vpiPortInst",
|
|
"vpiProcess",
|
|
"vpiVariables",
|
|
"vpiUse",
|
|
"vpiExpr",
|
|
"vpiPrimitive",
|
|
"vpiStmt",
|
|
"vpiAttribute",
|
|
"vpiBitSelect",
|
|
"vpiCallback",
|
|
"vpiDelayTerm",
|
|
"vpiDelayDevice",
|
|
"vpiFrame",
|
|
"vpiGateArray",
|
|
"vpiModuleArray",
|
|
"vpiPrimitiveArray",
|
|
"vpiNetArray",
|
|
"vpiRange",
|
|
"vpiRegArray",
|
|
"vpiSwitchArray",
|
|
"vpiUdpArray",
|
|
"vpiActiveTimeFormat",
|
|
"vpiInTerm",
|
|
"vpiInstanceArray",
|
|
"vpiLocalDriver",
|
|
"vpiLocalLoad",
|
|
"vpiOutTerm",
|
|
"vpiPorts",
|
|
"vpiSimNet",
|
|
"vpiTaskFunc",
|
|
"vpiContAssignBit",
|
|
"vpiNamedEventArray",
|
|
"vpiIndexedPartSelect",
|
|
"vpiBaseExpr",
|
|
"vpiWidthExpr",
|
|
"vpiGenScopeArray",
|
|
"vpiGenScope",
|
|
"vpiGenVar",
|
|
"vpiAutomatics"
|
|
};
|
|
static const char* const sv_names1[] = {
|
|
"vpiPackage",
|
|
"vpiInterface",
|
|
"vpiProgram",
|
|
"vpiInterfaceArray",
|
|
"vpiProgramArray",
|
|
"vpiTypespec",
|
|
"vpiModport",
|
|
"vpiInterfaceTfDecl",
|
|
"vpiRefObj",
|
|
"vpiTypeParameter",
|
|
"vpiLongIntVar",
|
|
"vpiShortIntVar",
|
|
"vpiIntVar",
|
|
"vpiShortRealVar",
|
|
"vpiByteVar",
|
|
"vpiClassVar",
|
|
"vpiStringVar",
|
|
"vpiEnumVar",
|
|
"vpiStructVar",
|
|
"vpiUnionVar",
|
|
"vpiBitVar",
|
|
"vpiClassObj",
|
|
"vpiChandleVar",
|
|
"vpiPackedArrayVar",
|
|
"*undefined*", // 624 is not defined for object types
|
|
"vpiLongIntTypespec",
|
|
"vpiShortRealTypespec",
|
|
"vpiByteTypespec",
|
|
"vpiShortIntTypespec",
|
|
"vpiIntTypespec",
|
|
"vpiClassTypespec",
|
|
"vpiStringTypespec",
|
|
"vpiChandleTypespec",
|
|
"vpiEnumTypespec",
|
|
"vpiEnumConst",
|
|
"vpiIntegerTypespec",
|
|
"vpiTimeTypespec",
|
|
"vpiRealTypespec",
|
|
"vpiStructTypespec",
|
|
"vpiUnionTypespec",
|
|
"vpiBitTypespec",
|
|
"vpiLogicTypespec",
|
|
"vpiArrayTypespec",
|
|
"vpiVoidTypespec",
|
|
"vpiTypespecMember",
|
|
"vpiDistItem",
|
|
"vpiAliasStmt",
|
|
"vpiThread",
|
|
"vpiMethodFuncCall",
|
|
"vpiMethodTaskCall",
|
|
"vpiClockingBlock",
|
|
"vpiClockingIODecl",
|
|
"vpiClassDefn",
|
|
"vpiConstraint",
|
|
"vpiConstraintOrdering",
|
|
"vpiPropertyDecl",
|
|
"vpiPropertySpec",
|
|
"vpiPropertyExpr",
|
|
"vpiMulticlockSequenceExpr",
|
|
"vpiClockedSeq",
|
|
"vpiPropertyInst",
|
|
"vpiSequenceDecl",
|
|
"vpiCaseProperty",
|
|
"*undefined*", // 663 is not defined for object types
|
|
"vpiSequenceInst",
|
|
"vpiImmediateAssert",
|
|
"vpiReturn",
|
|
"vpiAnyPattern",
|
|
"vpiTaggedPattern",
|
|
"vpiStructPattern",
|
|
"vpiDoWhile",
|
|
"vpiOrderedWait",
|
|
"vpiWaitFork",
|
|
"vpiDisableFork",
|
|
"vpiExpectStmt",
|
|
"vpiForeachStmt",
|
|
"vpiFinal",
|
|
"vpiExtends",
|
|
"vpiDistribution",
|
|
"vpiSeqFormalDecl",
|
|
"vpiEnumNet",
|
|
"vpiIntegerNet",
|
|
"vpiTimeNet",
|
|
"vpiStructNet",
|
|
"vpiBreak",
|
|
"vpiContinue",
|
|
"vpiAssert",
|
|
"vpiAssume",
|
|
"vpiCover",
|
|
"vpiDisableCondition",
|
|
"vpiClockingEvent",
|
|
"vpiReturnStmt",
|
|
"vpiPackedArrayTypespec",
|
|
"vpiPackedArrayNet",
|
|
"vpiImmediateAssume",
|
|
"vpiImmediateCover",
|
|
"vpiSequenceTypespec",
|
|
"vpiPropertyTypespec",
|
|
"vpiEventTypespec",
|
|
"vpiPropFormalDecl",
|
|
};
|
|
// clang-format on
|
|
if (VL_UNCOVERABLE(vpiVal < 0))
|
|
return names[0];
|
|
else if (vpiVal <= vpiAutomatics)
|
|
return names[vpiVal];
|
|
else if (vpiVal >= vpiPackage && vpiVal <= vpiPropFormalDecl)
|
|
return sv_names1[(vpiVal - vpiPackage)];
|
|
else
|
|
return names[0];
|
|
}
|
|
const char* VerilatedVpiError::strFromVpiMethod(PLI_INT32 vpiVal) VL_PURE {
|
|
// clang-format off
|
|
static const char* const names[] = {
|
|
"vpiCondition",
|
|
"vpiDelay",
|
|
"vpiElseStmt",
|
|
"vpiForIncStmt",
|
|
"vpiForInitStmt",
|
|
"vpiHighConn",
|
|
"vpiLhs",
|
|
"vpiIndex",
|
|
"vpiLeftRange",
|
|
"vpiLowConn",
|
|
"vpiParent",
|
|
"vpiRhs",
|
|
"vpiRightRange",
|
|
"vpiScope",
|
|
"vpiSysTfCall",
|
|
"vpiTchkDataTerm",
|
|
"vpiTchkNotifier",
|
|
"vpiTchkRefTerm",
|
|
"vpiArgument",
|
|
"vpiBit",
|
|
"vpiDriver",
|
|
"vpiInternalScope",
|
|
"vpiLoad",
|
|
"vpiModDataPathIn",
|
|
"vpiModPathIn",
|
|
"vpiModPathOut",
|
|
"vpiOperand",
|
|
"vpiPortInst",
|
|
"vpiProcess",
|
|
"vpiVariables",
|
|
"vpiUse",
|
|
"vpiExpr",
|
|
"vpiPrimitive",
|
|
"vpiStmt"
|
|
};
|
|
// clang-format on
|
|
if (vpiVal > vpiStmt || vpiVal < vpiCondition) return "*undefined*";
|
|
return names[vpiVal - vpiCondition];
|
|
}
|
|
|
|
const char* VerilatedVpiError::strFromVpiCallbackReason(PLI_INT32 vpiVal) VL_PURE {
|
|
// clang-format off
|
|
static const char* const names[] = {
|
|
"*undefined*",
|
|
"cbValueChange",
|
|
"cbStmt",
|
|
"cbForce",
|
|
"cbRelease",
|
|
"cbAtStartOfSimTime",
|
|
"cbReadWriteSynch",
|
|
"cbReadOnlySynch",
|
|
"cbNextSimTime",
|
|
"cbAfterDelay",
|
|
"cbEndOfCompile",
|
|
"cbStartOfSimulation",
|
|
"cbEndOfSimulation",
|
|
"cbError",
|
|
"cbTchkViolation",
|
|
"cbStartOfSave",
|
|
"cbEndOfSave",
|
|
"cbStartOfRestart",
|
|
"cbEndOfRestart",
|
|
"cbStartOfReset",
|
|
"cbEndOfReset",
|
|
"cbEnterInteractive",
|
|
"cbExitInteractive",
|
|
"cbInteractiveScopeChange",
|
|
"cbUnresolvedSystf",
|
|
"cbAssign",
|
|
"cbDeassign",
|
|
"cbDisable",
|
|
"cbPLIError",
|
|
"cbSignal",
|
|
"cbNBASynch",
|
|
"cbAtEndOfSimTime"
|
|
};
|
|
// clang-format on
|
|
if (VL_UNCOVERABLE(vpiVal < 0)) return names[0];
|
|
return names[(vpiVal <= cbAtEndOfSimTime) ? vpiVal : 0];
|
|
}
|
|
|
|
const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) VL_PURE {
|
|
// clang-format off
|
|
static const char* const names[] = {
|
|
"*undefined or other*",
|
|
"vpiType",
|
|
"vpiName",
|
|
"vpiFullName",
|
|
"vpiSize",
|
|
"vpiFile",
|
|
"vpiLineNo",
|
|
"vpiTopModule",
|
|
"vpiCellInstance",
|
|
"vpiDefName",
|
|
"vpiProtected",
|
|
"vpiTimeUnit",
|
|
"vpiTimePrecision",
|
|
"vpiDefNetType",
|
|
"vpiUnconnDrive",
|
|
"vpiDefFile",
|
|
"vpiDefLineNo",
|
|
"vpiScalar",
|
|
"vpiVector",
|
|
"vpiExplicitName",
|
|
"vpiDirection",
|
|
"vpiConnByName",
|
|
"vpiNetType",
|
|
"vpiExplicitScalared",
|
|
"vpiExplicitVectored",
|
|
"vpiExpanded",
|
|
"vpiImplicitDecl",
|
|
"vpiChargeStrength",
|
|
"vpiArray",
|
|
"vpiPortIndex",
|
|
"vpiTermIndex",
|
|
"vpiStrength0",
|
|
"vpiStrength1",
|
|
"vpiPrimType",
|
|
"vpiPolarity",
|
|
"vpiDataPolarity",
|
|
"vpiEdge",
|
|
"vpiPathType",
|
|
"vpiTchkType",
|
|
"vpiOpType",
|
|
"vpiConstType",
|
|
"vpiBlocking",
|
|
"vpiCaseType",
|
|
"vpiFuncType",
|
|
"vpiNetDeclAssign",
|
|
"vpiUserDefn",
|
|
"vpiScheduled",
|
|
"*undefined*",
|
|
"*undefined*",
|
|
"vpiActive",
|
|
"vpiAutomatic",
|
|
"vpiCell",
|
|
"vpiConfig",
|
|
"vpiConstantSelect",
|
|
"vpiDecompile",
|
|
"vpiDefAttribute",
|
|
"vpiDelayType",
|
|
"vpiIteratorType",
|
|
"vpiLibrary",
|
|
"*undefined*",
|
|
"vpiOffset",
|
|
"vpiResolvedNetType",
|
|
"vpiSaveRestartID",
|
|
"vpiSaveRestartLocation",
|
|
"vpiValid",
|
|
"vpiSigned",
|
|
"vpiStop",
|
|
"vpiFinish",
|
|
"vpiReset",
|
|
"vpiSetInteractiveScope",
|
|
"vpiLocalParam",
|
|
"vpiModPathHasIfNone",
|
|
"vpiIndexedPartSelectType",
|
|
"vpiIsMemory",
|
|
"vpiIsProtected"
|
|
};
|
|
// clang-format on
|
|
if (vpiVal == vpiUndefined) return "vpiUndefined";
|
|
return names[(vpiVal <= vpiIsProtected) ? vpiVal : 0];
|
|
}
|
|
const char* VerilatedVpiError::strFromVpiConstType(PLI_INT32 constType) VL_PURE {
|
|
// clang-format off
|
|
static const char* const names[] = {
|
|
"*undefined*",
|
|
"vpiDecConst",
|
|
"vpiRealConst",
|
|
"vpiBinaryConst",
|
|
"vpiOctConst",
|
|
"vpiHexConst",
|
|
"vpiStringConst",
|
|
"vpiIntConst",
|
|
"vpiTimeConst",
|
|
};
|
|
// clang-format on
|
|
if (VL_UNCOVERABLE(constType < 0)) return names[0];
|
|
return names[(constType <= vpiTimeConst) ? constType : 0];
|
|
}
|
|
|
|
#define SELF_CHECK_RESULT_CSTR(got, exp) \
|
|
if (0 != std::strcmp((got), (exp))) { \
|
|
const std::string msg = "%Error: GOT = '"s + (got) + "'" + " EXP = '" + (exp) + "'"; \
|
|
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); \
|
|
}
|
|
|
|
#define SELF_CHECK_ENUM_STR(fn, enumn) \
|
|
do { \
|
|
const char* const strVal = VerilatedVpiError::fn(enumn); \
|
|
SELF_CHECK_RESULT_CSTR(strVal, #enumn); \
|
|
} while (0)
|
|
|
|
void VerilatedVpi::selfTest() VL_MT_UNSAFE_ONE { VerilatedVpiError::selfTest(); }
|
|
void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
|
|
VerilatedVpiImp::assertOneCheck();
|
|
|
|
SELF_CHECK_ENUM_STR(strFromVpiVal, vpiBinStrVal);
|
|
SELF_CHECK_ENUM_STR(strFromVpiVal, vpiRawFourStateVal);
|
|
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAlways);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssignStmt);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssignment);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBegin);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCase);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCaseItem);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiConstant);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiContAssign);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDeassign);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDefParam);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDelayControl);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDisable);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEventControl);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEventStmt);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFor);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForce);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForever);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFork);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFuncCall);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFunction);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGate);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIf);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIfElse);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInitial);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntegerVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterModPath);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIterator);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIODecl);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMemory);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMemoryWord);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModPath);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModule);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNamedBegin);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNamedEvent);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNamedFork);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNet);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNetBit);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNullStmt);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiOperation);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiParamAssign);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiParameter);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPartSelect);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPathTerm);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPort);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPortBit);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPrimTerm);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRealVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiReg);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRegBit);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRelease);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRepeat);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRepeatControl);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSchedEvent);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSpecParam);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSwitch);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSysFuncCall);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSysTaskCall);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTableEntry);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTask);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTaskCall);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchk);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchkTerm);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeQueue);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUdp);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUdpDefn);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUserSystf);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiVarSelect);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiWait);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiWhile);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCondition);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDelay);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiElseStmt);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForIncStmt);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForInitStmt);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiHighConn);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLhs);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIndex);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLeftRange);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLowConn);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiParent);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRhs);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRightRange);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiScope);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSysTfCall);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchkDataTerm);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchkNotifier);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchkRefTerm);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiArgument);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBit);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDriver);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInternalScope);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLoad);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModDataPathIn);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModPathIn);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModPathOut);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiOperand);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPortInst);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiProcess);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiVariables);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUse);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiExpr);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPrimitive);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStmt);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAttribute);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitSelect);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCallback);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDelayTerm);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDelayDevice);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFrame);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGateArray);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModuleArray);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPrimitiveArray);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNetArray);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRange);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRegArray);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSwitchArray);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUdpArray);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiActiveTimeFormat);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInTerm);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInstanceArray);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLocalDriver);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLocalLoad);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiOutTerm);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPorts);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSimNet);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTaskFunc);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiContAssignBit);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNamedEventArray);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIndexedPartSelect);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBaseExpr);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiWidthExpr);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGenScopeArray);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGenScope);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGenVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAutomatics);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackage);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterface);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiProgram);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterfaceArray);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiProgramArray);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModport);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterfaceTfDecl);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRefObj);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTypeParameter);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLongIntVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortIntVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortRealVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiByteVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStringVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassObj);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiChandleVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackedArrayVar);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLongIntTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortRealTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiByteTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortIntTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStringTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiChandleTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumConst);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntegerTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRealTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLogicTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiArrayTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiVoidTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTypespecMember);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDistItem);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAliasStmt);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiThread);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMethodFuncCall);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMethodTaskCall);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockingBlock);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockingIODecl);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassDefn);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiConstraint);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiConstraintOrdering);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyDecl);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertySpec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyExpr);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMulticlockSequenceExpr);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockedSeq);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyInst);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSequenceDecl);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCaseProperty);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSequenceInst);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiImmediateAssert);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiReturn);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAnyPattern);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTaggedPattern);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructPattern);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDoWhile);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiOrderedWait);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiWaitFork);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDisableFork);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiExpectStmt);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForeachStmt);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFinal);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiExtends);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDistribution);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSeqFormalDecl);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumNet);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntegerNet);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeNet);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructNet);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBreak);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiContinue);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssert);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssume);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCover);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDisableCondition);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockingEvent);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiReturnStmt);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackedArrayTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackedArrayNet);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiImmediateAssume);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiImmediateCover);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSequenceTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEventTypespec);
|
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropFormalDecl);
|
|
|
|
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiCondition);
|
|
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiStmt);
|
|
|
|
SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbValueChange);
|
|
SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbAtEndOfSimTime);
|
|
|
|
SELF_CHECK_ENUM_STR(strFromVpiProp, vpiType);
|
|
SELF_CHECK_ENUM_STR(strFromVpiProp, vpiProtected);
|
|
SELF_CHECK_ENUM_STR(strFromVpiProp, vpiDirection);
|
|
SELF_CHECK_ENUM_STR(strFromVpiProp, vpiTermIndex);
|
|
SELF_CHECK_ENUM_STR(strFromVpiProp, vpiConstType);
|
|
SELF_CHECK_ENUM_STR(strFromVpiProp, vpiAutomatic);
|
|
SELF_CHECK_ENUM_STR(strFromVpiProp, vpiOffset);
|
|
SELF_CHECK_ENUM_STR(strFromVpiProp, vpiStop);
|
|
SELF_CHECK_ENUM_STR(strFromVpiProp, vpiIsProtected);
|
|
|
|
SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiDecConst);
|
|
SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiRealConst);
|
|
SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiBinaryConst);
|
|
SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiOctConst);
|
|
SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiHexConst);
|
|
SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiStringConst);
|
|
SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiIntConst);
|
|
SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiTimeConst);
|
|
}
|
|
|
|
#undef SELF_CHECK_ENUM_STR
|
|
#undef SELF_CHECK_RESULT_CSTR
|
|
|
|
//======================================================================
|
|
// callback related
|
|
|
|
vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
|
// Returns handle so user can remove the callback, user must vpi_release_handle it
|
|
// Don't confuse with the callback-activated t_cb_data object handle
|
|
// which is the object causing the callback rather than the callback itself
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
// cppcheck-suppress nullPointer
|
|
if (VL_UNLIKELY(!cb_data_p)) {
|
|
VL_VPI_WARNING_(__FILE__, __LINE__, "%s : callback data pointer is null", __func__);
|
|
return nullptr;
|
|
}
|
|
const PLI_INT32 reason = cb_data_p->reason;
|
|
switch (reason) {
|
|
case cbAfterDelay: // FALLTHRU // One-shot; time relative
|
|
case cbAtEndOfSimTime: // FALLTHRU // One-shot; time absolute; supported via vlt_main.cpp
|
|
case cbAtStartOfSimTime: // FALLTHRU // One-shot; time absolute; supported via vlt_main.cpp
|
|
case cbReadOnlySynch: // FALLTHRU // One-shot; time relative; supported via vlt_main.cpp
|
|
case cbReadWriteSynch: { // One-shot; time relative; supported via vlt_main.cpp
|
|
const bool abs = reason == cbAtStartOfSimTime || reason == cbAtEndOfSimTime;
|
|
const QData time = VL_TIME_Q();
|
|
QData abstime = 0;
|
|
if (cb_data_p->time) {
|
|
if (abs) {
|
|
abstime = VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
|
|
} else {
|
|
abstime = time + VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
|
|
}
|
|
}
|
|
const uint64_t id = VerilatedVpiImp::nextCallbackId();
|
|
VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, abstime, reason};
|
|
if (abstime <= time) {
|
|
VerilatedVpiImp::cbCurrentAdd(id, cb_data_p);
|
|
} else {
|
|
VerilatedVpiImp::cbFutureAdd(id, cb_data_p, abstime);
|
|
}
|
|
return vop->castVpiHandle();
|
|
}
|
|
case cbNextSimTime: { // One-shot; time always next; supported via vlt_main.cpp
|
|
const QData time = VL_TIME_Q();
|
|
const uint64_t id = VerilatedVpiImp::nextCallbackId();
|
|
VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, 0, reason};
|
|
VerilatedVpiImp::cbNextAdd(id, cb_data_p, time);
|
|
return vop->castVpiHandle();
|
|
}
|
|
case cbEndOfSimulation: // FALLTHRU // One-shot; time ignored; 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
|
|
case cbPLIError: // FALLTHRU // NOP, but need to return handle, so make object
|
|
case cbStartOfSimulation: // FALLTHRU // One-shot; time ignored; supported via vlt_main.cpp
|
|
case cbValueChange: { // Multi-shot; supported via vlt_main.cpp
|
|
const uint64_t id = VerilatedVpiImp::nextCallbackId();
|
|
VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, 0, reason};
|
|
VerilatedVpiImp::cbCurrentAdd(id, cb_data_p);
|
|
return vop->castVpiHandle();
|
|
}
|
|
default:
|
|
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported callback type %s", __func__,
|
|
VerilatedVpiError::strFromVpiCallbackReason(reason));
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
PLI_INT32 vpi_remove_cb(vpiHandle cb_obj) {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_remove_cb %p\n", cb_obj););
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
VerilatedVpio* const vop = VerilatedVpio::castp(cb_obj);
|
|
if (VL_UNLIKELY(!vop)) return 0;
|
|
return vop->dovpi_remove_cb();
|
|
}
|
|
|
|
void vpi_get_cb_info(vpiHandle /*object*/, p_cb_data /*cb_data_p*/) { VL_VPI_UNIMP_(); }
|
|
vpiHandle vpi_register_systf(p_vpi_systf_data /*systf_data_p*/) {
|
|
VL_VPI_UNIMP_();
|
|
return nullptr;
|
|
}
|
|
void vpi_get_systf_info(vpiHandle /*object*/, p_vpi_systf_data /*systf_data_p*/) {
|
|
VL_VPI_UNIMP_();
|
|
}
|
|
|
|
// for obtaining handles
|
|
|
|
vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
if (VL_UNLIKELY(!namep)) return nullptr;
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_name %s %p\n", namep, scope););
|
|
const VerilatedVar* varp = nullptr;
|
|
const VerilatedScope* scopep;
|
|
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
|
|
std::string scopeAndName = namep;
|
|
if (voScopep) {
|
|
const bool scopeIsPackage = VerilatedVpioPackage::castp(scope) != nullptr;
|
|
scopeAndName = std::string{voScopep->fullname()} + (scopeIsPackage ? "" : ".") + namep;
|
|
namep = const_cast<PLI_BYTE8*>(scopeAndName.c_str());
|
|
}
|
|
{
|
|
// This doesn't yet follow the hierarchy in the proper way
|
|
bool isPackage = false;
|
|
scopep = Verilated::threadContextp()->scopeFind(namep);
|
|
if (scopep) { // Whole thing found as a scope
|
|
if (scopep->type() == VerilatedScope::SCOPE_MODULE) {
|
|
return (new VerilatedVpioModule{scopep})->castVpiHandle();
|
|
} else if (scopep->type() == VerilatedScope::SCOPE_PACKAGE) {
|
|
return (new VerilatedVpioPackage{scopep})->castVpiHandle();
|
|
} else {
|
|
return (new VerilatedVpioScope{scopep})->castVpiHandle();
|
|
}
|
|
}
|
|
std::string basename = scopeAndName;
|
|
std::string scopename;
|
|
std::string::size_type prevpos = std::string::npos;
|
|
std::string::size_type pos = std::string::npos;
|
|
// Split hierarchical names at last '.' not inside escaped identifier
|
|
size_t i = 0;
|
|
while (i < scopeAndName.length()) {
|
|
if (scopeAndName[i] == '\\') {
|
|
while (i < scopeAndName.length() && scopeAndName[i] != ' ') ++i;
|
|
++i; // Proc ' ', it should always be there. Then grab '.' on next cycle
|
|
} else {
|
|
while (i < scopeAndName.length()
|
|
&& (scopeAndName[i] != '.'
|
|
&& (i + 1 >= scopeAndName.length() || scopeAndName[i] != ':'
|
|
|| scopeAndName[i + 1] != ':')))
|
|
++i;
|
|
if (i < scopeAndName.length()) {
|
|
prevpos = pos;
|
|
pos = i++;
|
|
if (scopeAndName[i - 1] == ':') isPackage = true;
|
|
}
|
|
}
|
|
}
|
|
// Do the split
|
|
if (VL_LIKELY(pos != std::string::npos)) {
|
|
basename.erase(0, pos + (isPackage ? 2 : 1));
|
|
scopename = scopeAndName.substr(0, pos);
|
|
if (scopename == "$unit") scopename = "\\$unit ";
|
|
}
|
|
if (prevpos == std::string::npos) {
|
|
// scopename is a toplevel (no '.' separator), so search in our TOP ports first.
|
|
scopep = Verilated::threadContextp()->scopeFind("TOP");
|
|
if (scopep) varp = scopep->varFind(basename.c_str());
|
|
}
|
|
if (!varp) {
|
|
scopep = Verilated::threadContextp()->scopeFind(scopename.c_str());
|
|
if (!scopep) return nullptr;
|
|
varp = scopep->varFind(basename.c_str());
|
|
}
|
|
}
|
|
if (!varp) return nullptr;
|
|
|
|
if (varp->isParam()) {
|
|
return (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
|
|
} else {
|
|
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_DBG_MSGF("- vpi: vpi_handle_by_index %p %d\n", object, indx););
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object);
|
|
if (VL_LIKELY(varop)) {
|
|
// Case: no dimensions left to index
|
|
if (VL_UNLIKELY(varop->indexedDim() + 1 > varop->varp()->dims() - 1)) return nullptr;
|
|
|
|
// Case: index out of range
|
|
if (VL_UNLIKELY(indx < varop->rangep()->low() || indx > varop->rangep()->high()))
|
|
return nullptr;
|
|
|
|
return varop->withIndex(indx)->castVpiHandle();
|
|
}
|
|
VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", __func__);
|
|
return nullptr;
|
|
}
|
|
|
|
// for traversing relationships
|
|
|
|
vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle %d %p\n", type, object););
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
switch (type) {
|
|
case vpiLeftRange: {
|
|
if (const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
|
|
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
|
|
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
|
|
} else if (const VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
|
|
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
|
|
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
|
|
}
|
|
VL_VPI_WARNING_(__FILE__, __LINE__,
|
|
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
|
|
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
|
|
return nullptr;
|
|
}
|
|
case vpiRightRange: {
|
|
if (const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
|
|
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
|
|
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
|
|
} else if (const VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
|
|
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
|
|
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
|
|
}
|
|
VL_VPI_WARNING_(__FILE__, __LINE__,
|
|
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
|
|
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
|
|
return nullptr;
|
|
}
|
|
case vpiIndex: {
|
|
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
|
if (VL_UNLIKELY(!vop)) return nullptr;
|
|
const int32_t val = vop->index().back();
|
|
return (new VerilatedVpioConst{val})->castVpiHandle();
|
|
}
|
|
case vpiScope: {
|
|
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
|
|
if (VL_UNLIKELY(!vop)) return nullptr;
|
|
return (new VerilatedVpioScope{vop->scopep()})->castVpiHandle();
|
|
}
|
|
case vpiParent: {
|
|
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
|
if (VL_UNLIKELY(!vop)) return nullptr;
|
|
return (new VerilatedVpioVar{vop->varp(), vop->scopep()})->castVpiHandle();
|
|
}
|
|
default:
|
|
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
|
|
__func__, VerilatedVpiError::strFromVpiMethod(type));
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
vpiHandle vpi_handle_multi(PLI_INT32 /*type*/, vpiHandle /*refHandle1*/, vpiHandle /*refHandle2*/,
|
|
...) {
|
|
VL_VPI_UNIMP_();
|
|
return nullptr;
|
|
}
|
|
|
|
vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_iterate %d %p\n", type, object););
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
switch (type) {
|
|
case vpiRange: {
|
|
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
|
if (VL_UNLIKELY(!vop)) return nullptr;
|
|
|
|
std::vector<VerilatedRange> ranges;
|
|
const int maxDim = vop->maxDim(vop->isIndexedDimUnpacked());
|
|
for (int dim = vop->indexedDim() + 1; dim <= maxDim; ++dim)
|
|
ranges.emplace_back(*vop->varp()->range(dim));
|
|
|
|
// allow one more range layer (regbit)
|
|
if (ranges.empty()) ranges.emplace_back(VerilatedRange(0, 0));
|
|
return ((new VerilatedVpioRangeIter{ranges})->castVpiHandle());
|
|
}
|
|
case vpiReg: {
|
|
const VerilatedVpioScope* const vscopep = VerilatedVpioScope::castp(object);
|
|
if (vscopep) return ((new VerilatedVpioVarIter{vscopep, false})->castVpiHandle());
|
|
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
|
if (vop) return ((new VerilatedVpioRegIter{vop})->castVpiHandle());
|
|
return nullptr;
|
|
}
|
|
case vpiParameter: {
|
|
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
|
if (VL_UNLIKELY(!vop)) return nullptr;
|
|
return ((new VerilatedVpioVarIter{vop, true})->castVpiHandle());
|
|
}
|
|
case vpiModule: {
|
|
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
|
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
|
|
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
|
|
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
|
|
if (it == map->end()) return nullptr;
|
|
return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
|
|
}
|
|
case vpiInternalScope: {
|
|
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
|
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
|
|
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
|
|
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
|
|
if (it == map->end()) return nullptr;
|
|
return ((new VerilatedVpioScopeIter{it->second})->castVpiHandle());
|
|
}
|
|
case vpiInstance: {
|
|
if (object) return nullptr;
|
|
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
|
|
const auto it = vlstd::as_const(map)->find(nullptr);
|
|
if (it == map->end()) return nullptr;
|
|
return ((new VerilatedVpioInstanceIter{it->second})->castVpiHandle());
|
|
}
|
|
default:
|
|
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
|
|
__func__, VerilatedVpiError::strFromVpiObjType(type));
|
|
return nullptr;
|
|
}
|
|
}
|
|
vpiHandle vpi_scan(vpiHandle object) {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_scan %p\n", object););
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
VerilatedVpio* const vop = VerilatedVpio::castp(object);
|
|
if (VL_UNLIKELY(!vop)) return nullptr;
|
|
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_DBG_MSGF("- vpi: vpi_get %d %p\n", property, object););
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
switch (property) {
|
|
case vpiTimePrecision: {
|
|
return Verilated::threadContextp()->timeprecision();
|
|
}
|
|
case vpiTimeUnit: {
|
|
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
|
if (!vop)
|
|
return Verilated::threadContextp()->timeunit(); // Null asks for global, not unlikely
|
|
return vop->scopep()->timeunit();
|
|
}
|
|
case vpiType: {
|
|
const VerilatedVpio* const vop = VerilatedVpio::castp(object);
|
|
if (VL_UNLIKELY(!vop)) return vpiUndefined;
|
|
return vop->type();
|
|
}
|
|
case vpiConstType: {
|
|
const VerilatedVpio* const vop = VerilatedVpio::castp(object);
|
|
if (VL_UNLIKELY(!vop)) return vpiUndefined;
|
|
return vop->constType();
|
|
}
|
|
case vpiDirection: {
|
|
// By forethought, the directions already are vpi enumerated
|
|
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
|
|
if (VL_UNLIKELY(!vop)) return vpiUndefined;
|
|
return vop->varp()->vldir();
|
|
}
|
|
case vpiScalar: // FALLTHRU
|
|
case vpiVector: {
|
|
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
|
|
if (VL_UNLIKELY(!vop)) return vpiUndefined;
|
|
return (property == vpiVector) ^ (vop->varp()->packedRanges().empty() || !vop->rangep());
|
|
}
|
|
case vpiSize: {
|
|
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
|
|
if (VL_UNLIKELY(!vop)) return vpiUndefined;
|
|
return vop->size();
|
|
}
|
|
default:
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported property %s, nothing will be returned",
|
|
__func__, VerilatedVpiError::strFromVpiProp(property));
|
|
return vpiUndefined;
|
|
}
|
|
}
|
|
|
|
PLI_INT64 vpi_get64(PLI_INT32 /*property*/, vpiHandle /*object*/) {
|
|
VL_VPI_UNIMP_();
|
|
return vpiUndefined;
|
|
}
|
|
|
|
PLI_BYTE8* vpi_get_str(PLI_INT32 property, vpiHandle object) {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_str %d %p\n", property, object););
|
|
VerilatedVpiImp::assertOneCheck();
|
|
const VerilatedVpio* const vop = VerilatedVpio::castp(object);
|
|
VL_VPI_ERROR_RESET_();
|
|
if (VL_UNLIKELY(!vop)) return nullptr;
|
|
switch (property) {
|
|
case vpiName: {
|
|
return const_cast<PLI_BYTE8*>(vop->name());
|
|
}
|
|
case vpiFullName: {
|
|
return const_cast<PLI_BYTE8*>(vop->fullname());
|
|
}
|
|
case vpiDefName: {
|
|
return const_cast<PLI_BYTE8*>(vop->defname());
|
|
}
|
|
case vpiType: {
|
|
return const_cast<PLI_BYTE8*>(VerilatedVpiError::strFromVpiObjType(vop->type()));
|
|
}
|
|
case vpiConstType: {
|
|
const PLI_INT32 constType = vpi_get(vpiConstType, object);
|
|
VL_VPI_ERROR_RESET_();
|
|
return const_cast<PLI_BYTE8*>(VerilatedVpiError::strFromVpiConstType(constType));
|
|
}
|
|
default:
|
|
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
|
|
__func__, VerilatedVpiError::strFromVpiProp(property));
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// delay processing
|
|
|
|
void vpi_get_delays(vpiHandle /*object*/, p_vpi_delay /*delay_p*/) { VL_VPI_UNIMP_(); }
|
|
void vpi_put_delays(vpiHandle /*object*/, p_vpi_delay /*delay_p*/) { VL_VPI_UNIMP_(); }
|
|
|
|
// value processing
|
|
bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const char* fullname,
|
|
bool isGetValue) {
|
|
bool status = true;
|
|
if ((valuep->format == vpiVectorVal) || (valuep->format == vpiBinStrVal)
|
|
|| (valuep->format == vpiOctStrVal) || (valuep->format == vpiHexStrVal)) {
|
|
switch (varp->vltype()) {
|
|
case VLVT_UINT8:
|
|
case VLVT_UINT16:
|
|
case VLVT_UINT32:
|
|
case VLVT_UINT64:
|
|
case VLVT_WDATA: return status;
|
|
default: status = false;
|
|
}
|
|
} else if (valuep->format == vpiDecStrVal) {
|
|
switch (varp->vltype()) {
|
|
case VLVT_UINT8:
|
|
case VLVT_UINT16:
|
|
case VLVT_UINT32:
|
|
case VLVT_UINT64: return status;
|
|
default: status = false;
|
|
}
|
|
} else if (valuep->format == vpiStringVal) {
|
|
switch (varp->vltype()) {
|
|
case VLVT_UINT8:
|
|
case VLVT_UINT16:
|
|
case VLVT_UINT32:
|
|
case VLVT_UINT64:
|
|
case VLVT_WDATA: return status;
|
|
case VLVT_STRING:
|
|
// string parameter values can't be changed
|
|
if (isGetValue || !varp->isParam()) {
|
|
return status;
|
|
} else {
|
|
status = false;
|
|
break;
|
|
}
|
|
default: status = false;
|
|
}
|
|
} else if (valuep->format == vpiIntVal) {
|
|
switch (varp->vltype()) {
|
|
case VLVT_UINT8:
|
|
case VLVT_UINT16:
|
|
case VLVT_UINT32:
|
|
case VLVT_UINT64:
|
|
case VLVT_WDATA: return status;
|
|
default: status = false;
|
|
}
|
|
} else if (valuep->format == vpiRealVal) {
|
|
switch (varp->vltype()) {
|
|
case VLVT_REAL: return status;
|
|
default: status = false;
|
|
}
|
|
} else if (valuep->format == vpiSuppressVal) {
|
|
return status;
|
|
} else {
|
|
status = false;
|
|
}
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", __func__,
|
|
VerilatedVpiError::strFromVpiVal(valuep->format), fullname);
|
|
return status;
|
|
}
|
|
|
|
static void vl_strprintf(std::string& buffer, char const* fmt, ...) {
|
|
va_list args, args_copy;
|
|
va_start(args, fmt);
|
|
buffer.clear();
|
|
// Make copy of args since we may need to call VL_VSNPRINTF more than once
|
|
va_copy(args_copy, args);
|
|
// Try VL_VSNPRINTF in existing buffer
|
|
const int result
|
|
= VL_VSNPRINTF(const_cast<char*>(buffer.data()), buffer.capacity(), fmt, args_copy);
|
|
va_end(args_copy);
|
|
const int required = result + 1; // Returned size doesn't include NUL terminator
|
|
// If there wasn't enough space, reallocate and try again
|
|
if (buffer.capacity() < required) {
|
|
buffer.reserve(required * 2);
|
|
VL_VSNPRINTF(const_cast<char*>(buffer.data()), buffer.capacity(), fmt, args);
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
// Information about how to access packed array data.
|
|
// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word
|
|
// boundaries, in which case m_maskHi != 0.
|
|
template <typename T>
|
|
struct VarAccessInfo final {
|
|
T* m_datap; // Typed pointer to packed array base address
|
|
size_t m_bitOffset; // Data start location (bit offset)
|
|
size_t m_wordOffset; // Data start location (word offset, VLVT_WDATA only)
|
|
T m_maskLo; // Access mask for m_datap[m_wordOffset]
|
|
T m_maskHi; // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only)
|
|
};
|
|
|
|
template <typename T>
|
|
VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount,
|
|
size_t addOffset) {
|
|
// VarAccessInfo generation
|
|
// vop - variable to access (already indexed)
|
|
// bitCount - how many bits to write/read
|
|
// addOffset - additional offset to apply (within the packed array element)
|
|
|
|
const size_t wordBits = sizeof(T) * 8;
|
|
uint32_t varBits = vop->bitSize();
|
|
|
|
if (vop->varp()->vltype() == VLVT_REAL) varBits *= sizeof(double) * 8;
|
|
|
|
// make sure we're not trying to write outside var bounds
|
|
assert(varBits > addOffset);
|
|
bitCount = std::min(bitCount, varBits - addOffset);
|
|
|
|
VarAccessInfo<T> info;
|
|
info.m_datap = reinterpret_cast<T*>(vop->varDatap());
|
|
if (vop->varp()->vltype() == VLVT_WDATA) {
|
|
assert(sizeof(T) == sizeof(EData));
|
|
assert(bitCount <= wordBits);
|
|
info.m_wordOffset = (vop->bitOffset() + addOffset) / wordBits;
|
|
info.m_bitOffset = (vop->bitOffset() + addOffset) % wordBits;
|
|
if (bitCount + info.m_bitOffset <= wordBits) {
|
|
// within single word
|
|
if (bitCount == wordBits)
|
|
info.m_maskLo = ~static_cast<T>(0);
|
|
else
|
|
info.m_maskLo = (static_cast<T>(1) << bitCount) - 1;
|
|
info.m_maskLo = info.m_maskLo << info.m_bitOffset;
|
|
info.m_maskHi = 0;
|
|
} else {
|
|
// straddles word boundary
|
|
info.m_maskLo = (static_cast<T>(1) << (wordBits - info.m_bitOffset)) - 1;
|
|
info.m_maskLo = info.m_maskLo << info.m_bitOffset;
|
|
info.m_maskHi = (static_cast<T>(1) << (bitCount + info.m_bitOffset - wordBits)) - 1;
|
|
}
|
|
} else {
|
|
info.m_wordOffset = 0;
|
|
info.m_bitOffset = vop->bitOffset() + addOffset;
|
|
assert(bitCount + info.m_bitOffset <= wordBits);
|
|
if (bitCount < wordBits) {
|
|
info.m_maskLo = (static_cast<T>(1) << bitCount) - 1;
|
|
info.m_maskLo = info.m_maskLo << info.m_bitOffset;
|
|
} else {
|
|
info.m_maskLo = ~static_cast<T>(0);
|
|
}
|
|
info.m_maskHi = 0;
|
|
}
|
|
return info;
|
|
}
|
|
|
|
template <typename T>
|
|
T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
|
|
const size_t wordBits = sizeof(T) * 8;
|
|
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
|
|
if (info.m_maskHi)
|
|
return ((info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset)
|
|
| ((info.m_datap[info.m_wordOffset + 1] & info.m_maskHi)
|
|
<< (wordBits - info.m_bitOffset));
|
|
else
|
|
return (info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset;
|
|
}
|
|
|
|
template <typename T>
|
|
void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) {
|
|
const size_t wordBits = sizeof(T) * 8;
|
|
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
|
|
|
|
if (info.m_maskHi) {
|
|
info.m_datap[info.m_wordOffset + 1]
|
|
= (info.m_datap[info.m_wordOffset + 1] & ~info.m_maskHi)
|
|
| ((word >> (wordBits - info.m_bitOffset)) & info.m_maskHi);
|
|
}
|
|
// cppcheck-has-bug-suppress unreadVariable
|
|
info.m_datap[info.m_wordOffset] = (info.m_datap[info.m_wordOffset] & ~info.m_maskLo)
|
|
| ((word << info.m_bitOffset) & info.m_maskLo);
|
|
}
|
|
|
|
// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds
|
|
// addOffset: additional read bitoffset
|
|
QData vl_vpi_get_word(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
|
|
switch (vop->varp()->vltype()) {
|
|
case VLVT_UINT8: return vl_vpi_get_word_gen<CData>(vop, bitCount, addOffset);
|
|
case VLVT_UINT16: return vl_vpi_get_word_gen<SData>(vop, bitCount, addOffset);
|
|
case VLVT_UINT32: return vl_vpi_get_word_gen<IData>(vop, bitCount, addOffset);
|
|
case VLVT_UINT64: return vl_vpi_get_word_gen<QData>(vop, bitCount, addOffset);
|
|
case VLVT_WDATA: return vl_vpi_get_word_gen<EData>(vop, bitCount, addOffset);
|
|
default:
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__,
|
|
vop->varp()->vltype());
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// word: data to be written
|
|
// bitCount: maximum number of bits to write, will stop earlier if it reaches the var bounds
|
|
// addOffset: additional write bitoffset
|
|
void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset) {
|
|
switch (vop->varp()->vltype()) {
|
|
case VLVT_UINT8: vl_vpi_put_word_gen<CData>(vop, word, bitCount, addOffset); break;
|
|
case VLVT_UINT16: vl_vpi_put_word_gen<SData>(vop, word, bitCount, addOffset); break;
|
|
case VLVT_UINT32: vl_vpi_put_word_gen<IData>(vop, word, bitCount, addOffset); break;
|
|
case VLVT_UINT64: vl_vpi_put_word_gen<QData>(vop, word, bitCount, addOffset); break;
|
|
case VLVT_WDATA: vl_vpi_put_word_gen<EData>(vop, word, bitCount, addOffset); break;
|
|
default:
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__,
|
|
vop->varp()->vltype());
|
|
}
|
|
}
|
|
|
|
void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
|
|
const VerilatedVar* const varp = vop->varp();
|
|
void* const varDatap = vop->varDatap();
|
|
const char* fullname = vop->fullname();
|
|
|
|
if (!vl_check_format(varp, valuep, fullname, true)) return;
|
|
// string data type is dynamic and may vary in size during simulation
|
|
static thread_local std::string t_outDynamicStr;
|
|
|
|
const int varBits = vop->bitSize();
|
|
|
|
// We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
|
|
// This may cause backward compatibility issues with older code.
|
|
if (valuep->format == vpiVectorVal) {
|
|
// Vector pointer must come from our memory pool
|
|
// It only needs to persist until the next vpi_get_value
|
|
static thread_local t_vpi_vecval t_out[VL_VALUE_STRING_MAX_WORDS * 2];
|
|
valuep->value.vector = t_out;
|
|
if (varp->vltype() == VLVT_WDATA) {
|
|
const int words = VL_WORDS_I(varBits);
|
|
if (VL_UNCOVERABLE(words >= VL_VALUE_STRING_MAX_WORDS)) {
|
|
VL_VPI_ERROR_(
|
|
__FILE__, __LINE__,
|
|
"vpi_get_value with more than VL_VALUE_STRING_MAX_WORDS; increase and "
|
|
"recompile");
|
|
return;
|
|
}
|
|
for (int i = 0; i < words; ++i) {
|
|
t_out[i].aval = vl_vpi_get_word(vop, 32, i * 32);
|
|
t_out[i].bval = 0;
|
|
}
|
|
return;
|
|
} else if (varp->vltype() == VLVT_UINT64 && varBits > 32) {
|
|
const QData data = vl_vpi_get_word(vop, 64, 0);
|
|
t_out[1].aval = static_cast<IData>(data >> 32ULL);
|
|
t_out[1].bval = 0;
|
|
t_out[0].aval = static_cast<IData>(data);
|
|
t_out[0].bval = 0;
|
|
return;
|
|
} else {
|
|
t_out[0].aval = vl_vpi_get_word(vop, 32, 0);
|
|
t_out[0].bval = 0;
|
|
return;
|
|
}
|
|
} else if (valuep->format == vpiBinStrVal) {
|
|
t_outDynamicStr.resize(varBits);
|
|
const CData* datap = reinterpret_cast<CData*>(varDatap);
|
|
for (size_t i = 0; i < varBits; ++i) {
|
|
const size_t pos = i + vop->bitOffset();
|
|
const char val = (datap[pos >> 3] >> (pos & 7)) & 1;
|
|
t_outDynamicStr[varBits - i - 1] = val ? '1' : '0';
|
|
}
|
|
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
|
return;
|
|
} else if (valuep->format == vpiOctStrVal) {
|
|
const int chars = (varBits + 2) / 3;
|
|
t_outDynamicStr.resize(chars);
|
|
for (size_t i = 0; i < chars; ++i) {
|
|
const char val = vl_vpi_get_word(vop, 3, i * 3);
|
|
t_outDynamicStr[chars - i - 1] = '0' + val;
|
|
}
|
|
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
|
return;
|
|
} else if (valuep->format == vpiDecStrVal) {
|
|
if (varp->vltype() == VLVT_UINT8) {
|
|
vl_strprintf(t_outDynamicStr, "%hhu",
|
|
static_cast<unsigned char>(vl_vpi_get_word(vop, 8, 0)));
|
|
} else if (varp->vltype() == VLVT_UINT16) {
|
|
vl_strprintf(t_outDynamicStr, "%hu",
|
|
static_cast<unsigned short>(vl_vpi_get_word(vop, 16, 0)));
|
|
} else if (varp->vltype() == VLVT_UINT32) {
|
|
vl_strprintf(t_outDynamicStr, "%u",
|
|
static_cast<unsigned int>(vl_vpi_get_word(vop, 32, 0)));
|
|
} else if (varp->vltype() == VLVT_UINT64) {
|
|
vl_strprintf(t_outDynamicStr, "%llu", // lintok-format-ll
|
|
static_cast<unsigned long long>(vl_vpi_get_word(vop, 64, 0)));
|
|
}
|
|
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
|
return;
|
|
} else if (valuep->format == vpiHexStrVal) {
|
|
const int chars = (varBits + 3) >> 2;
|
|
t_outDynamicStr.resize(chars);
|
|
for (size_t i = 0; i < chars; ++i) {
|
|
const char val = vl_vpi_get_word(vop, 4, i * 4);
|
|
t_outDynamicStr[chars - i - 1] = "0123456789abcdef"[static_cast<int>(val)];
|
|
}
|
|
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
|
return;
|
|
} else if (valuep->format == vpiStringVal) {
|
|
if (varp->vltype() == VLVT_STRING) {
|
|
if (varp->isParam()) {
|
|
valuep->value.str = reinterpret_cast<char*>(varDatap);
|
|
return;
|
|
} else {
|
|
t_outDynamicStr = *(vop->varStringDatap());
|
|
valuep->value.str = const_cast<char*>(t_outDynamicStr.c_str());
|
|
return;
|
|
}
|
|
} else {
|
|
const int chars = VL_BYTES_I(varBits);
|
|
t_outDynamicStr.resize(chars);
|
|
for (size_t i = 0; i < chars; ++i) {
|
|
const char val = vl_vpi_get_word(vop, 8, i * 8);
|
|
// other simulators replace [leading?] zero chars with spaces, replicate here.
|
|
t_outDynamicStr[chars - i - 1] = val ? val : ' ';
|
|
}
|
|
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
|
return;
|
|
}
|
|
} else if (valuep->format == vpiIntVal) {
|
|
valuep->value.integer = vl_vpi_get_word(vop, 32, 0);
|
|
return;
|
|
} else if (valuep->format == vpiRealVal) {
|
|
valuep->value.real = *(vop->varRealDatap());
|
|
return;
|
|
} else if (valuep->format == vpiSuppressVal) {
|
|
return;
|
|
}
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__,
|
|
VerilatedVpiError::strFromVpiVal(valuep->format), fullname);
|
|
}
|
|
|
|
void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_value %p\n", object););
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
if (VL_UNLIKELY(!valuep)) return;
|
|
|
|
if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
|
|
vl_vpi_get_value(vop, valuep);
|
|
return;
|
|
} else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
|
|
vl_vpi_get_value(vop, valuep);
|
|
return;
|
|
} else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
|
|
if (valuep->format == vpiIntVal) {
|
|
valuep->value.integer = vop->num();
|
|
return;
|
|
}
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", __func__,
|
|
VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
|
|
return;
|
|
}
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
|
|
}
|
|
|
|
vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/,
|
|
PLI_INT32 flags) {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n", object, valuep););
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
if (VL_UNLIKELY(!valuep)) {
|
|
VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer");
|
|
return nullptr;
|
|
}
|
|
const PLI_INT32 delay_mode = flags & 0xfff;
|
|
if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
|
|
VL_DEBUG_IF_PLI(
|
|
VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(),
|
|
valuep->format, valuep->value.integer);
|
|
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap()););
|
|
|
|
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
|
|
VL_VPI_ERROR_(__FILE__, __LINE__,
|
|
"vpi_put_value was used on signal marked read-only,"
|
|
" use public_flat_rw instead for %s : %s",
|
|
vop->fullname(), vop->scopep()->defname());
|
|
return nullptr;
|
|
}
|
|
if (!vl_check_format(vop->varp(), valuep, vop->fullname(), false)) return nullptr;
|
|
if (delay_mode == vpiInertialDelay) {
|
|
if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) {
|
|
VL_VPI_WARNING_(
|
|
__FILE__, __LINE__,
|
|
"%s: Unsupported p_vpi_value as requested for '%s' with vpiInertialDelay",
|
|
__func__, vop->fullname());
|
|
return nullptr;
|
|
}
|
|
VerilatedVpiImp::inertialDelay(vop, valuep);
|
|
return object;
|
|
}
|
|
VerilatedVpiImp::evalNeeded(true);
|
|
const int varBits = vop->bitSize();
|
|
if (valuep->format == vpiVectorVal) {
|
|
if (VL_UNLIKELY(!valuep->value.vector)) return nullptr;
|
|
if (vop->varp()->vltype() == VLVT_WDATA) {
|
|
const int words = VL_WORDS_I(varBits);
|
|
for (int i = 0; i < words; ++i)
|
|
vl_vpi_put_word(vop, valuep->value.vector[i].aval, 32, i * 32);
|
|
return object;
|
|
} else if (vop->varp()->vltype() == VLVT_UINT64 && varBits > 32) {
|
|
const QData val = (static_cast<QData>(valuep->value.vector[1].aval) << 32)
|
|
| static_cast<QData>(valuep->value.vector[0].aval);
|
|
vl_vpi_put_word(vop, val, 64, 0);
|
|
return object;
|
|
} else {
|
|
vl_vpi_put_word(vop, valuep->value.vector[0].aval, 32, 0);
|
|
return object;
|
|
}
|
|
} else if (valuep->format == vpiBinStrVal) {
|
|
const int len = std::strlen(valuep->value.str);
|
|
CData* const datap = reinterpret_cast<CData*>(vop->varDatap());
|
|
for (int i = 0; i < varBits; ++i) {
|
|
const bool set = (i < len) && (valuep->value.str[len - i - 1] == '1');
|
|
const size_t pos = vop->bitOffset() + i;
|
|
|
|
if (set)
|
|
datap[pos >> 3] |= 1 << (pos & 7);
|
|
else
|
|
datap[pos >> 3] &= ~(1 << (pos & 7));
|
|
}
|
|
return object;
|
|
} else if (valuep->format == vpiOctStrVal) {
|
|
const int len = std::strlen(valuep->value.str);
|
|
for (int i = 0; i < len; ++i) {
|
|
unsigned char digit = valuep->value.str[len - i - 1] - '0';
|
|
if (digit > 7) { // If str was < '0', then as unsigned, digit > 7
|
|
VL_VPI_WARNING_(__FILE__, __LINE__,
|
|
"%s: Non octal character '%c' in '%s' as value %s for %s",
|
|
__func__, digit + '0', valuep->value.str,
|
|
VerilatedVpiError::strFromVpiVal(valuep->format),
|
|
vop->fullname());
|
|
digit = 0;
|
|
}
|
|
vl_vpi_put_word(vop, digit, 3, i * 3);
|
|
}
|
|
return object;
|
|
} else if (valuep->format == vpiDecStrVal) {
|
|
char remainder[16];
|
|
unsigned long long val;
|
|
const int success = std::sscanf(valuep->value.str, "%30llu%15s", // lintok-format-ll
|
|
&val, remainder);
|
|
if (success < 1) {
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Parsing failed for '%s' as value %s for %s",
|
|
__func__, valuep->value.str,
|
|
VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
|
|
return nullptr;
|
|
}
|
|
if (success > 1) {
|
|
VL_VPI_WARNING_(__FILE__, __LINE__,
|
|
"%s: Trailing garbage '%s' in '%s' as value %s for %s", __func__,
|
|
remainder, valuep->value.str,
|
|
VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
|
|
}
|
|
vl_vpi_put_word(vop, val, 64, 0);
|
|
return object;
|
|
} else if (valuep->format == vpiHexStrVal) {
|
|
const int chars = (varBits + 3) >> 2;
|
|
const char* val = valuep->value.str;
|
|
// skip hex ident if one is detected at the start of the string
|
|
if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) val += 2;
|
|
const int len = std::strlen(val);
|
|
for (int i = 0; i < chars; ++i) {
|
|
char hex;
|
|
// compute hex digit value
|
|
if (i < len) {
|
|
const char digit = val[len - i - 1];
|
|
if (digit >= '0' && digit <= '9') {
|
|
hex = digit - '0';
|
|
} else if (digit >= 'a' && digit <= 'f') {
|
|
hex = digit - 'a' + 10;
|
|
} else if (digit >= 'A' && digit <= 'F') {
|
|
hex = digit - 'A' + 10;
|
|
} else {
|
|
VL_VPI_WARNING_(__FILE__, __LINE__,
|
|
"%s: Non hex character '%c' in '%s' as value %s for %s",
|
|
__func__, digit, valuep->value.str,
|
|
VerilatedVpiError::strFromVpiVal(valuep->format),
|
|
vop->fullname());
|
|
hex = 0;
|
|
}
|
|
} else {
|
|
hex = 0;
|
|
}
|
|
// assign hex digit value to destination
|
|
vl_vpi_put_word(vop, hex, 4, i * 4);
|
|
}
|
|
return object;
|
|
} else if (valuep->format == vpiStringVal) {
|
|
if (vop->varp()->vltype() == VLVT_STRING) {
|
|
*(vop->varStringDatap()) = valuep->value.str;
|
|
return object;
|
|
} else {
|
|
const int chars = VL_BYTES_I(varBits);
|
|
const int len = std::strlen(valuep->value.str);
|
|
for (int i = 0; i < chars; ++i) {
|
|
// prepend with 0 values before placing string the least significant bytes
|
|
const char c = (i < len) ? valuep->value.str[len - i - 1] : 0;
|
|
vl_vpi_put_word(vop, c, 8, i * 8);
|
|
}
|
|
}
|
|
return object;
|
|
} else if (valuep->format == vpiIntVal) {
|
|
vl_vpi_put_word(vop, valuep->value.integer, 64, 0);
|
|
return object;
|
|
} else if (valuep->format == vpiRealVal) {
|
|
if (vop->varp()->vltype() == VLVT_REAL) {
|
|
*(vop->varRealDatap()) = valuep->value.real;
|
|
return object;
|
|
}
|
|
}
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s",
|
|
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
|
|
return nullptr;
|
|
} else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
|
|
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiParameter: %s",
|
|
__func__, vop->fullname());
|
|
return nullptr;
|
|
} else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
|
|
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiConstant: %s",
|
|
__func__, vop->fullname());
|
|
return nullptr;
|
|
}
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
|
|
return nullptr;
|
|
}
|
|
|
|
bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arrayvalue_p,
|
|
const char* fullname) {
|
|
if (arrayvalue_p->format == vpiVectorVal) {
|
|
switch (varp->vltype()) {
|
|
case VLVT_UINT8:
|
|
case VLVT_UINT16:
|
|
case VLVT_UINT32:
|
|
case VLVT_UINT64:
|
|
case VLVT_WDATA: return true;
|
|
default:;
|
|
}
|
|
} else if (arrayvalue_p->format == vpiIntVal) {
|
|
switch (varp->vltype()) {
|
|
case VLVT_UINT8:
|
|
case VLVT_UINT16:
|
|
case VLVT_UINT32: return true;
|
|
default:;
|
|
}
|
|
} else if ((arrayvalue_p->format == vpiRawTwoStateVal)
|
|
|| (arrayvalue_p->format == vpiRawFourStateVal)) {
|
|
switch (varp->vltype()) {
|
|
case VLVT_UINT8:
|
|
case VLVT_UINT16:
|
|
case VLVT_UINT32:
|
|
case VLVT_UINT64:
|
|
case VLVT_WDATA: return true;
|
|
default:;
|
|
}
|
|
} else if (arrayvalue_p->format == vpiShortIntVal) {
|
|
switch (varp->vltype()) {
|
|
case VLVT_UINT8:
|
|
case VLVT_UINT16: return true;
|
|
default:;
|
|
}
|
|
} else if (arrayvalue_p->format == vpiLongIntVal) {
|
|
switch (varp->vltype()) {
|
|
case VLVT_UINT8:
|
|
case VLVT_UINT16:
|
|
case VLVT_UINT32:
|
|
case VLVT_UINT64: return true;
|
|
default:;
|
|
}
|
|
}
|
|
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__,
|
|
VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), fullname);
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename T, typename K>
|
|
void vl_get_value_array_integrals(unsigned index, const unsigned num, const unsigned size,
|
|
const unsigned packedSize, const bool leftIsLow, const T* src,
|
|
K* dst) {
|
|
static_assert(sizeof(K) >= sizeof(T), "size of type K is less than size of type T");
|
|
for (int i = 0; i < num; ++i) {
|
|
dst[i] = src[index];
|
|
index = leftIsLow ? index == (size - 1) ? 0 : index + 1
|
|
: index == 0 ? size - 1
|
|
: index - 1;
|
|
}
|
|
}
|
|
|
|
template <typename T, typename K>
|
|
void vl_put_value_array_integrals(unsigned index, const unsigned num, const unsigned size,
|
|
const unsigned packedSize, const bool leftIsLow, const T* src,
|
|
K* dst) {
|
|
static_assert(std::is_integral<T>::value, "type T is not an integral type");
|
|
static_assert(std::is_unsigned<T>::value, "type T is not unsigned");
|
|
static_assert(sizeof(T) >= sizeof(K), "size of type T is less than size of type K");
|
|
const unsigned element_size_bytes = VL_BYTES_I(packedSize);
|
|
const T mask = element_size_bytes == sizeof(T)
|
|
? static_cast<T>(-1)
|
|
: ~(static_cast<T>(-1) << (element_size_bytes * 8));
|
|
for (unsigned i = 0; i < num; ++i) {
|
|
dst[index] = src[i] & static_cast<T>(mask);
|
|
index = leftIsLow ? index == (size - 1) ? 0 : index + 1
|
|
: index == 0 ? size - 1
|
|
: index - 1;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void vl_get_value_array_vectors(unsigned index, const unsigned num, const unsigned size,
|
|
const unsigned packedSize, const bool leftIsLow, const T* src,
|
|
p_vpi_vecval dst) {
|
|
static_assert(std::is_unsigned<T>::value,
|
|
"type T is not unsigned"); // ensure logical right shift
|
|
const unsigned element_size_words = VL_WORDS_I(packedSize);
|
|
if (sizeof(T) == sizeof(QData)) {
|
|
for (unsigned i = 0; i < num; ++i) {
|
|
dst[i * 2].aval = static_cast<QData>(src[index]);
|
|
dst[i * 2].bval = 0;
|
|
dst[(i * 2) + 1].aval = static_cast<QData>(src[index]) >> 32;
|
|
dst[(i * 2) + 1].bval = 0;
|
|
index = leftIsLow ? index == (size - 1) ? 0 : index + 1
|
|
: index == 0 ? size - 1
|
|
: index - 1;
|
|
}
|
|
} else {
|
|
for (unsigned i = 0; i < num; ++i) {
|
|
const size_t dst_index = i * element_size_words;
|
|
const size_t src_index = index * element_size_words;
|
|
for (unsigned j = 0; j < element_size_words; ++j) {
|
|
dst[dst_index + j].aval = src[src_index + j];
|
|
dst[dst_index + j].bval = 0;
|
|
}
|
|
index = leftIsLow ? index == (size - 1) ? 0 : index + 1
|
|
: index == 0 ? size - 1
|
|
: index - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void vl_put_value_array_vectors(unsigned index, const unsigned num, const unsigned size,
|
|
const unsigned packedSize, const bool leftIsLow,
|
|
const bool fourState, const p_vpi_vecval src, T* dst) {
|
|
static_assert(std::is_unsigned<T>::value, "type T is not unsigned");
|
|
static_assert(std::is_integral<T>::value, "type T is not an integral type");
|
|
const unsigned element_size_bytes VL_BYTES_I(packedSize);
|
|
const unsigned element_size_words VL_WORDS_I(packedSize);
|
|
if (sizeof(T) == sizeof(QData)) { //destination is QDATA
|
|
const QData mask = element_size_bytes == sizeof(T)
|
|
? static_cast<QData>(-1)
|
|
: ~(static_cast<QData>(-1) << (element_size_bytes * 8));
|
|
for (unsigned i = 0; i < num; ++i) {
|
|
dst[index] = src[i * 2].aval;
|
|
dst[index]
|
|
|= (static_cast<QData>(src[(i * 2) + 1].aval) << (sizeof(PLI_UINT32) * 8)) & mask;
|
|
index = leftIsLow ? index == (size - 1) ? 0 : index + 1
|
|
: index == 0 ? size - 1
|
|
: index - 1;
|
|
}
|
|
} else {
|
|
for (unsigned i = 0; i < num; ++i) {
|
|
unsigned bytes_stored = 0;
|
|
for (unsigned j = 0; j < element_size_words; ++j) {
|
|
if (bytes_stored >= element_size_bytes) break;
|
|
const T mask
|
|
= (element_size_bytes - bytes_stored) >= sizeof(PLI_UINT32)
|
|
? static_cast<T>(-1)
|
|
: ~(static_cast<T>(-1) << ((element_size_bytes - bytes_stored) * 8));
|
|
dst[(index * element_size_words) + j]
|
|
= static_cast<T>(src[(i * element_size_words) + j].aval) & mask;
|
|
bytes_stored += sizeof(PLI_UINT32);
|
|
}
|
|
index = leftIsLow ? index == (size - 1) ? 0 : index + 1
|
|
: index == 0 ? size - 1
|
|
: index - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void vl_get_value_array_rawvals(unsigned index, unsigned num, const unsigned size,
|
|
const unsigned packedSize, const bool leftIsLow,
|
|
const bool fourState, const T* src, PLI_BYTE8* dst) {
|
|
static_assert(std::is_unsigned<T>::value,
|
|
"type T is not unsigned"); //ensure loigcal right shift
|
|
const unsigned element_size_bytes VL_BYTES_I(packedSize);
|
|
const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T);
|
|
size_t dst_index = 0;
|
|
while (num-- > 0) {
|
|
const size_t src_offset = index * element_size_repr;
|
|
unsigned bytes_copied = 0;
|
|
for (unsigned j = 0; j < element_size_repr; ++j) {
|
|
const T& src_data = src[src_offset + j];
|
|
for (unsigned k = 0; k < sizeof(T); ++k) {
|
|
if (bytes_copied++ == element_size_bytes) break;
|
|
dst[dst_index++] = src_data >> (k * 8);
|
|
}
|
|
}
|
|
if (fourState) {
|
|
std::fill(dst + dst_index, dst + dst_index + element_size_bytes, 0);
|
|
dst_index += element_size_bytes;
|
|
}
|
|
index = leftIsLow ? index == (size - 1) ? 0 : index + 1
|
|
: index == 0 ? size - 1
|
|
: index - 1;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void vl_put_value_array_rawvals(unsigned index, const unsigned num, const unsigned size,
|
|
const unsigned packedSize, const bool leftIsLow,
|
|
const bool fourState, const PLI_UBYTE8* src, T* dst) {
|
|
const unsigned element_size_bytes VL_BYTES_I(packedSize);
|
|
const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T);
|
|
for (unsigned i = 0; i < num; ++i) {
|
|
unsigned bytes_copied = 0;
|
|
const size_t dst_offset = index * element_size_repr;
|
|
const size_t src_offset = i * element_size_bytes;
|
|
for (unsigned j = 0; j < element_size_repr; ++j) {
|
|
T& dst_data = dst[dst_offset + j];
|
|
for (unsigned k = 0; k < sizeof(T); ++k) {
|
|
if (bytes_copied == element_size_bytes) break;
|
|
const unsigned src_index
|
|
= fourState ? (src_offset * 2) + bytes_copied : (src_offset) + bytes_copied;
|
|
dst_data &= ~((static_cast<T>(0xFF) & 0xFF) << (k * 8));
|
|
dst_data |= ((static_cast<T>(src[src_index]) & 0xFF) << (k * 8));
|
|
bytes_copied++;
|
|
}
|
|
}
|
|
index = leftIsLow ? index == (size - 1) ? 0 : index + 1
|
|
: index == 0 ? size - 1
|
|
: index - 1;
|
|
}
|
|
}
|
|
|
|
void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const PLI_INT32* index_p,
|
|
PLI_UINT32 num) {
|
|
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
|
if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return;
|
|
|
|
const VerilatedVar* const varp = vop->varp();
|
|
|
|
static thread_local EData out_data[VL_VALUE_STRING_MAX_WORDS * 2];
|
|
|
|
const unsigned size = vop->size();
|
|
if (VL_UNCOVERABLE(num > size)) {
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: requested elements (%u) exceed array size (%u)",
|
|
__func__, num, size);
|
|
return;
|
|
}
|
|
|
|
const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low();
|
|
const int index
|
|
= leftIsLow ? index_p[0] - vop->rangep()->left() : vop->rangep()->left() - index_p[0];
|
|
|
|
if (arrayvalue_p->format == vpiShortIntVal) {
|
|
if (VL_UNCOVERABLE((sizeof(PLI_INT16) * num) >= VL_VALUE_STRING_MAX_CHARS)) {
|
|
VL_FATAL_MT(__FILE__, __LINE__, "",
|
|
"vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
|
|
"increase and recompile");
|
|
}
|
|
|
|
PLI_INT16* shortintsp = (PLI_INT16*)out_data;
|
|
arrayvalue_p->value.shortints = shortintsp;
|
|
|
|
if (varp->vltype() == VLVT_UINT8) {
|
|
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varCDatap(), shortintsp);
|
|
} else if (varp->vltype() == VLVT_UINT16) {
|
|
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varSDatap(), shortintsp);
|
|
}
|
|
|
|
return;
|
|
} else if (arrayvalue_p->format == vpiIntVal) {
|
|
if (VL_UNCOVERABLE(num >= VL_VALUE_STRING_MAX_WORDS)) {
|
|
VL_FATAL_MT(__FILE__, __LINE__, "",
|
|
"vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
|
|
"increase and recompile");
|
|
}
|
|
|
|
PLI_INT32* integersp = (PLI_INT32*)out_data;
|
|
arrayvalue_p->value.integers = integersp;
|
|
|
|
if (varp->vltype() == VLVT_UINT8) {
|
|
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varCDatap(), integersp);
|
|
} else if (varp->vltype() == VLVT_UINT16) {
|
|
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varSDatap(), integersp);
|
|
} else if (varp->vltype() == VLVT_UINT32) {
|
|
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varIDatap(), integersp);
|
|
}
|
|
|
|
return;
|
|
} else if (arrayvalue_p->format == vpiLongIntVal) {
|
|
if (VL_UNCOVERABLE((sizeof(PLI_INT64) * num) >= VL_VALUE_STRING_MAX_CHARS)) {
|
|
VL_FATAL_MT(__FILE__, __LINE__, "",
|
|
"vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
|
|
"increase and recompile");
|
|
}
|
|
|
|
PLI_INT64* longintsp = (PLI_INT64*)out_data;
|
|
arrayvalue_p->value.longints = longintsp;
|
|
|
|
if (varp->vltype() == VLVT_UINT8) {
|
|
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varCDatap(), longintsp);
|
|
} else if (varp->vltype() == VLVT_UINT16) {
|
|
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varSDatap(), longintsp);
|
|
} else if (varp->vltype() == VLVT_UINT32) {
|
|
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varIDatap(), longintsp);
|
|
} else if (varp->vltype() == VLVT_UINT64) {
|
|
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varQDatap(), longintsp);
|
|
}
|
|
|
|
return;
|
|
} else if (arrayvalue_p->format == vpiVectorVal) {
|
|
if (VL_UNCOVERABLE((VL_WORDS_I(varp->entBits()) * 2 * num) >= VL_VALUE_STRING_MAX_WORDS)) {
|
|
VL_FATAL_MT(__FILE__, __LINE__, "",
|
|
"vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
|
|
"increase and recompile");
|
|
}
|
|
|
|
p_vpi_vecval vectorsp = (p_vpi_vecval)out_data;
|
|
arrayvalue_p->value.vectors = vectorsp;
|
|
|
|
if (varp->vltype() == VLVT_UINT8) {
|
|
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varCDatap(), vectorsp);
|
|
} else if (varp->vltype() == VLVT_UINT16) {
|
|
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varSDatap(), vectorsp);
|
|
} else if (varp->vltype() == VLVT_UINT32) {
|
|
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varIDatap(), vectorsp);
|
|
} else if (varp->vltype() == VLVT_UINT64) {
|
|
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varQDatap(), vectorsp);
|
|
} else if (varp->vltype() == VLVT_WDATA) {
|
|
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
|
vop->varEDatap(), vectorsp);
|
|
}
|
|
|
|
return;
|
|
} else if (arrayvalue_p->format == vpiRawFourStateVal) {
|
|
if (VL_UNCOVERABLE((VL_BYTES_I(varp->entBits()) * 2 * num) >= VL_VALUE_STRING_MAX_CHARS)) {
|
|
VL_FATAL_MT(__FILE__, __LINE__, "",
|
|
"vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
|
|
"increase and recompile");
|
|
}
|
|
|
|
PLI_BYTE8* valuep = (PLI_BYTE8*)out_data;
|
|
arrayvalue_p->value.rawvals = valuep;
|
|
|
|
if (varp->vltype() == VLVT_UINT8) {
|
|
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
|
vop->varCDatap(), valuep);
|
|
} else if (varp->vltype() == VLVT_UINT16) {
|
|
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
|
vop->varSDatap(), valuep);
|
|
} else if (varp->vltype() == VLVT_UINT32) {
|
|
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
|
vop->varIDatap(), valuep);
|
|
} else if (varp->vltype() == VLVT_UINT64) {
|
|
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
|
vop->varQDatap(), valuep);
|
|
} else if (varp->vltype() == VLVT_WDATA) {
|
|
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
|
vop->varEDatap(), valuep);
|
|
}
|
|
|
|
return;
|
|
} else if (arrayvalue_p->format == vpiRawTwoStateVal) {
|
|
if (VL_UNCOVERABLE((VL_BYTES_I(varp->entBits()) * num) >= VL_VALUE_STRING_MAX_CHARS)) {
|
|
VL_FATAL_MT(__FILE__, __LINE__, "",
|
|
"vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
|
|
"increase and recompile");
|
|
}
|
|
|
|
PLI_BYTE8* valuep = (PLI_BYTE8*)out_data;
|
|
arrayvalue_p->value.rawvals = valuep;
|
|
|
|
if (varp->vltype() == VLVT_UINT8) {
|
|
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
|
vop->varCDatap(), valuep);
|
|
} else if (varp->vltype() == VLVT_UINT16) {
|
|
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
|
vop->varSDatap(), valuep);
|
|
} else if (varp->vltype() == VLVT_UINT32) {
|
|
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
|
vop->varIDatap(), valuep);
|
|
} else if (varp->vltype() == VLVT_UINT64) {
|
|
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
|
vop->varQDatap(), valuep);
|
|
} else if (varp->vltype() == VLVT_WDATA) {
|
|
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
|
vop->varEDatap(), valuep);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__,
|
|
VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), vop->fullname());
|
|
}
|
|
|
|
void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p,
|
|
PLI_UINT32 num) {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_value_array %p\n", object););
|
|
VerilatedVpiImp::assertOneCheck();
|
|
|
|
VL_VPI_ERROR_RESET_();
|
|
if (VL_UNLIKELY(!object)) return;
|
|
|
|
if (VL_UNLIKELY(!arrayvalue_p)) {
|
|
VL_VPI_WARNING_(__FILE__, __LINE__,
|
|
"Ignoring vpi_get_value_array with null value pointer");
|
|
return;
|
|
}
|
|
|
|
if (VL_UNLIKELY(!index_p)) {
|
|
VL_VPI_WARNING_(__FILE__, __LINE__,
|
|
"Ignoring vpi_get_value_array with null index pointer");
|
|
return;
|
|
}
|
|
|
|
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
|
if (VL_UNLIKELY(!vop)) {
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
|
|
return;
|
|
}
|
|
|
|
if (vop->type() != vpiRegArray) {
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
|
|
VerilatedVpiError::strFromVpiObjType(vop->type()));
|
|
return;
|
|
}
|
|
|
|
const int lowRange = vop->rangep()->low();
|
|
const int highRange = vop->rangep()->high();
|
|
if ((index_p[0] > highRange) || (index_p[0] < lowRange)) {
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: index %u for object %s is out of bounds [%u,%u]",
|
|
__func__, index_p[0], vop->fullname(), lowRange, highRange);
|
|
return;
|
|
}
|
|
|
|
if (arrayvalue_p->flags & vpiUserAllocFlag) {
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiUserAllocFlag (%x)", __func__,
|
|
arrayvalue_p->flags);
|
|
return;
|
|
}
|
|
|
|
vl_get_value_array(object, arrayvalue_p, index_p, num);
|
|
}
|
|
|
|
void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const PLI_INT32* index_p,
|
|
PLI_UINT32 num) {
|
|
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
|
if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return;
|
|
|
|
const VerilatedVar* const varp = vop->varp();
|
|
|
|
const int size = vop->size();
|
|
if (VL_UNCOVERABLE(num > size)) {
|
|
VL_VPI_ERROR_(__FILE__, __LINE__,
|
|
"%s: requested elements to set (%u) exceed array size (%u)", __func__, num,
|
|
size);
|
|
return;
|
|
}
|
|
|
|
const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low();
|
|
const int index
|
|
= leftIsLow ? index_p[0] - vop->rangep()->left() : vop->rangep()->left() - index_p[0];
|
|
|
|
if (arrayvalue_p->format == vpiShortIntVal) {
|
|
const PLI_UINT16* shortintsp
|
|
= reinterpret_cast<PLI_UINT16*>(arrayvalue_p->value.shortints);
|
|
|
|
if (varp->vltype() == VLVT_UINT8) {
|
|
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
|
|
vop->varCDatap());
|
|
} else if (varp->vltype() == VLVT_UINT16) {
|
|
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
|
|
vop->varSDatap());
|
|
}
|
|
|
|
return;
|
|
} else if (arrayvalue_p->format == vpiIntVal) {
|
|
const PLI_UINT32* integersp = reinterpret_cast<PLI_UINT32*>(arrayvalue_p->value.integers);
|
|
|
|
if (varp->vltype() == VLVT_UINT8) {
|
|
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
|
|
vop->varCDatap());
|
|
} else if (varp->vltype() == VLVT_UINT16) {
|
|
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
|
|
vop->varSDatap());
|
|
} else if (varp->vltype() == VLVT_UINT32) {
|
|
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
|
|
vop->varIDatap());
|
|
}
|
|
|
|
return;
|
|
} else if (arrayvalue_p->format == vpiLongIntVal) {
|
|
const PLI_UINT64* longintsp = reinterpret_cast<PLI_UINT64*>(arrayvalue_p->value.longints);
|
|
|
|
if (varp->vltype() == VLVT_UINT8) {
|
|
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
|
|
vop->varCDatap());
|
|
} else if (varp->vltype() == VLVT_UINT16) {
|
|
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
|
|
vop->varSDatap());
|
|
} else if (varp->vltype() == VLVT_UINT32) {
|
|
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
|
|
vop->varIDatap());
|
|
} else if (varp->vltype() == VLVT_UINT64) {
|
|
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
|
|
vop->varQDatap());
|
|
}
|
|
|
|
return;
|
|
} else if (arrayvalue_p->format == vpiVectorVal) {
|
|
const p_vpi_vecval vectorsp = arrayvalue_p->value.vectors;
|
|
|
|
if (varp->vltype() == VLVT_UINT8) {
|
|
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
|
vectorsp, vop->varCDatap());
|
|
} else if (varp->vltype() == VLVT_UINT16) {
|
|
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
|
vectorsp, vop->varSDatap());
|
|
} else if (varp->vltype() == VLVT_UINT32) {
|
|
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
|
vectorsp, vop->varIDatap());
|
|
} else if (varp->vltype() == VLVT_UINT64) {
|
|
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
|
vectorsp, vop->varQDatap());
|
|
} else if (varp->vltype() == VLVT_WDATA) {
|
|
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
|
vectorsp, vop->varEDatap());
|
|
}
|
|
|
|
return;
|
|
} else if (arrayvalue_p->format == vpiRawFourStateVal) {
|
|
const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals);
|
|
|
|
if (varp->vltype() == VLVT_UINT8) {
|
|
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
|
vop->varCDatap());
|
|
} else if (varp->vltype() == VLVT_UINT16) {
|
|
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
|
vop->varSDatap());
|
|
} else if (varp->vltype() == VLVT_UINT32) {
|
|
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
|
vop->varIDatap());
|
|
} else if (varp->vltype() == VLVT_UINT64) {
|
|
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
|
vop->varQDatap());
|
|
} else if (varp->vltype() == VLVT_WDATA) {
|
|
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
|
vop->varEDatap());
|
|
}
|
|
|
|
return;
|
|
} else if (arrayvalue_p->format == vpiRawTwoStateVal) {
|
|
const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals);
|
|
|
|
if (varp->vltype() == VLVT_UINT8) {
|
|
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
|
vop->varCDatap());
|
|
} else if (varp->vltype() == VLVT_UINT16) {
|
|
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
|
vop->varSDatap());
|
|
} else if (varp->vltype() == VLVT_UINT32) {
|
|
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
|
vop->varIDatap());
|
|
} else if (varp->vltype() == VLVT_UINT64) {
|
|
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
|
vop->varQDatap());
|
|
} else if (varp->vltype() == VLVT_WDATA) {
|
|
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
|
vop->varEDatap());
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__,
|
|
VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), vop->fullname());
|
|
}
|
|
|
|
void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p,
|
|
PLI_UINT32 num) {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value_array %p\n", object););
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
|
|
if (VL_UNLIKELY(!arrayvalue_p)) {
|
|
VL_VPI_WARNING_(__FILE__, __LINE__,
|
|
"Ignoring vpi_put_value_array with null value pointer");
|
|
return;
|
|
}
|
|
|
|
if (VL_UNLIKELY(!index_p)) {
|
|
VL_VPI_WARNING_(__FILE__, __LINE__,
|
|
"Ignoring vpi_put_value_array with null index pointer");
|
|
return;
|
|
}
|
|
|
|
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
|
if (VL_UNLIKELY(!vop)) {
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
|
|
return;
|
|
}
|
|
|
|
if (vop->type() != vpiRegArray) {
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
|
|
VerilatedVpiError::strFromVpiObjType(vop->type()));
|
|
return;
|
|
}
|
|
|
|
const int lowRange = vop->rangep()->low();
|
|
const int highRange = vop->rangep()->high();
|
|
if ((index_p[0] > highRange) || (index_p[0] < lowRange)) {
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: index %u for object %s is out of bounds [%u,%u]",
|
|
__func__, index_p[0], vop->fullname(), lowRange, highRange);
|
|
return;
|
|
}
|
|
|
|
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
|
|
VL_VPI_ERROR_(__FILE__, __LINE__,
|
|
"Ignoring vpi_put_value_array to signal marked read-only,"
|
|
" use public_flat_rw instead: %s",
|
|
vop->fullname());
|
|
return;
|
|
}
|
|
|
|
if (arrayvalue_p->flags & (vpiPropagateOff | vpiOneValue)) {
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported flags (%x)", __func__,
|
|
arrayvalue_p->flags);
|
|
return;
|
|
}
|
|
|
|
vl_put_value_array(object, arrayvalue_p, index_p, num);
|
|
}
|
|
|
|
// time processing
|
|
|
|
void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
// cppcheck-suppress nullPointer
|
|
if (VL_UNLIKELY(!time_p)) {
|
|
VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_get_time with nullptr value pointer");
|
|
return;
|
|
}
|
|
if (time_p->type == vpiSimTime) {
|
|
const QData qtime = VL_TIME_Q();
|
|
VlWide<2> itime;
|
|
VL_SET_WQ(itime, qtime);
|
|
time_p->low = itime[0];
|
|
time_p->high = itime[1];
|
|
return;
|
|
} else if (time_p->type == vpiScaledRealTime) {
|
|
double dtime = VL_TIME_D();
|
|
if (const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object)) {
|
|
const int scalePow10
|
|
= Verilated::threadContextp()->timeprecision() - vop->scopep()->timeunit();
|
|
const double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001
|
|
dtime *= scale;
|
|
}
|
|
time_p->real = dtime;
|
|
return;
|
|
}
|
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%d)", __func__, time_p->type);
|
|
}
|
|
|
|
// I/O routines
|
|
|
|
PLI_UINT32 vpi_mcd_open(PLI_BYTE8* filenamep) {
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
return VL_FOPEN_NN(filenamep, "wb");
|
|
}
|
|
|
|
PLI_UINT32 vpi_mcd_close(PLI_UINT32 mcd) {
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
VL_FCLOSE_I(mcd);
|
|
return 0;
|
|
}
|
|
|
|
PLI_BYTE8* vpi_mcd_name(PLI_UINT32 /*mcd*/) {
|
|
VL_VPI_UNIMP_();
|
|
return nullptr;
|
|
}
|
|
|
|
PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, PLI_BYTE8* formatp, ...) {
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
va_list ap;
|
|
va_start(ap, formatp);
|
|
const int chars = vpi_mcd_vprintf(mcd, formatp, ap);
|
|
va_end(ap);
|
|
return chars;
|
|
}
|
|
|
|
PLI_INT32 vpi_printf(PLI_BYTE8* formatp, ...) {
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
va_list ap;
|
|
va_start(ap, formatp);
|
|
const int chars = vpi_vprintf(formatp, ap);
|
|
va_end(ap);
|
|
return chars;
|
|
}
|
|
|
|
PLI_INT32 vpi_vprintf(PLI_BYTE8* formatp, va_list ap) {
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
return VL_VPRINTF(formatp, ap);
|
|
}
|
|
|
|
PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8* format, va_list ap) {
|
|
VerilatedVpiImp::assertOneCheck();
|
|
FILE* const fp = VL_CVT_I_FP(mcd);
|
|
VL_VPI_ERROR_RESET_();
|
|
// cppcheck-suppress nullPointer
|
|
if (VL_UNLIKELY(!fp)) return 0;
|
|
const int chars = vfprintf(fp, format, ap);
|
|
return chars;
|
|
}
|
|
|
|
PLI_INT32 vpi_flush(void) {
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
Verilated::runFlushCallbacks();
|
|
return 0; // Gcc coverage bug // LCOV_EXCL_LINE
|
|
}
|
|
|
|
PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
|
|
VerilatedVpiImp::assertOneCheck();
|
|
FILE* const fp = VL_CVT_I_FP(mcd);
|
|
VL_VPI_ERROR_RESET_();
|
|
if (VL_UNLIKELY(!fp)) return 1;
|
|
std::fflush(fp);
|
|
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) {
|
|
// executing vpi_chk_error does not reset error
|
|
// error_info_p can be nullptr, so only return level in that case
|
|
VerilatedVpiImp::assertOneCheck();
|
|
p_vpi_error_info const _error_info_p = VerilatedVpiImp::error_info()->getError();
|
|
if (error_info_p && _error_info_p) *error_info_p = *_error_info_p;
|
|
if (!_error_info_p) return 0; // no error occurred
|
|
return _error_info_p->level; // return error severity level
|
|
}
|
|
|
|
#ifndef VL_NO_LEGACY
|
|
PLI_INT32 vpi_free_object(vpiHandle object) {
|
|
// vpi_free_object is IEEE deprecated, use vpi_release_handle
|
|
return vpi_release_handle(object);
|
|
}
|
|
#endif
|
|
|
|
PLI_INT32 vpi_release_handle(vpiHandle object) {
|
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_release_handle %p\n", object););
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VerilatedVpio* const vop = VerilatedVpio::castp(object);
|
|
VL_VPI_ERROR_RESET_();
|
|
if (VL_UNLIKELY(!vop)) return 0;
|
|
VL_DO_DANGLING(delete vop, vop);
|
|
return 1;
|
|
}
|
|
|
|
PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) {
|
|
// This is VL_MT_SAFE, but not marked as can't indicate it in the standardized header file
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
const auto argc_argv = Verilated::threadContextp()->impp()->argc_argv();
|
|
vlog_info_p->argc = argc_argv.first;
|
|
vlog_info_p->argv = argc_argv.second;
|
|
vlog_info_p->product = const_cast<PLI_BYTE8*>(Verilated::productName());
|
|
vlog_info_p->version = const_cast<PLI_BYTE8*>(Verilated::productVersion());
|
|
return 1;
|
|
}
|
|
|
|
// 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 nullptr;
|
|
}
|
|
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_DBG_MSGF("- vpi: vpi_control %d\n", operation););
|
|
VerilatedVpiImp::assertOneCheck();
|
|
VL_VPI_ERROR_RESET_();
|
|
switch (operation) {
|
|
case vpiFinish: {
|
|
VL_FINISH_MT("", 0, "*VPI*");
|
|
return 1;
|
|
}
|
|
case vpiStop: {
|
|
VL_STOP_MT("", 0, "*VPI*");
|
|
return 1; // LCOV_EXCL_LINE
|
|
}
|
|
default: {
|
|
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, ignoring", __func__,
|
|
VerilatedVpiError::strFromVpiProp(operation));
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
vpiHandle vpi_handle_by_multi_index(vpiHandle /*obj*/, PLI_INT32 /*num_index*/,
|
|
PLI_INT32* /*index_array*/) {
|
|
VL_VPI_UNIMP_();
|
|
return nullptr;
|
|
}
|