This commit is contained in:
Christian Hecken 2025-12-24 09:54:22 +05:30 committed by GitHub
commit 562e3a43a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 2146 additions and 55 deletions

View File

@ -1,4 +1,4 @@
Checks: '*,-hicpp*,-android-cloexec-fopen,-cert-dcl50-cpp,-cert-env33-c,-cert-err34-c,-cert-err58-cpp,-clang-analyzer-core.UndefinedBinaryOperatorResult,-clang-analyzer-security*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-no-malloc,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-special-member-functions,-fuchsia-*,-google-default-arguments,-google-readability-todo,-google-runtime-references,-llvm-header-guard,-llvm-include-order,-misc-string-integer-assignment,-misc-string-literal-with-embedded-nul,-modernize-use-auto,-modernize-use-trailing-return-type,-readability-braces-around-statements,-readability-container-size-empty,-readability-delete-null-pointer,-readability-else-after-return,-readability-implicit-bool-conversion,-readability-named-parameter,-readability-static-accessed-through-instance,-llvmlibc-*,-altera-*'
Checks: '*,-hicpp*,-android-cloexec-fopen,-cert-dcl50-cpp,-cert-env33-c,-cert-err34-c,-cert-err58-cpp,-clang-analyzer-core.UndefinedBinaryOperatorResult,-clang-analyzer-security*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-no-malloc,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-special-member-functions,-fuchsia-*,-google-default-arguments,-google-readability-todo,-google-runtime-references,-llvm-header-guard,-llvm-include-order,-misc-string-integer-assignment,-misc-string-literal-with-embedded-nul,-modernize-use-auto,-modernize-use-trailing-return-type,-readability-braces-around-statements,-readability-container-size-empty,-readability-delete-null-pointer,-readability-else-after-return,-readability-implicit-bool-conversion,-readability-named-parameter,-readability-static-accessed-through-instance,-llvmlibc-*,-altera-*,-boost-use-ranges'
WarningsAsErrors: ''
HeaderFilterRegex: ''
FormatStyle: none

View File

@ -154,7 +154,9 @@ enum VerilatedVarFlags {
// Flags
VLVF_PUB_RD = (1 << 8), // Public readable
VLVF_PUB_RW = (1 << 9), // Public writable
VLVF_DPI_CLAY = (1 << 10) // DPI compatible C standard layout
VLVF_DPI_CLAY = (1 << 10), // DPI compatible C standard layout
VLVF_FORCEABLE = (1 << 11), // Forceable
VLVF_CONTINUOUSLY = (1 << 12) // Is continously assigned
};
// IEEE 1800-2023 Table 20-6

View File

@ -156,6 +156,8 @@ public:
return bits;
}
bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); }
bool isForceable() const { return ((m_vlflags & VLVF_FORCEABLE) != 0); }
bool isContinuously() const { return ((m_vlflags & VLVF_CONTINUOUSLY) != 0); }
// DPI compatible C standard layout
bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); }
int udims() const VL_MT_SAFE { return m_unpacked.size(); }

View File

@ -24,12 +24,12 @@
///
//=========================================================================
#include "verilatedos.h"
#define VERILATOR_VERILATED_VPI_CPP_
#include "verilated_vpi.h"
#include "verilated.h"
#include "verilated_imp.h"
#include "verilated_vpi.h"
#include "vltstd/vpi_user.h"
@ -880,6 +880,7 @@ struct VerilatedVpiTimedCbsCmp final {
};
class VerilatedVpiError;
void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset);
class VerilatedVpiImp final {
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maximum callback reason
@ -1075,6 +1076,47 @@ public:
}
s().m_inertialPuts.clear();
}
static std::pair<vpiHandle, vpiHandle> getForceControlSignals(const VerilatedVpioVarBase* vop);
static std::size_t vlTypeSize(VerilatedVarType vltype);
static void setAllBitsToValue(const VerilatedVpioVar* vop, uint8_t bitValue) {
assert(bitValue == 0 || bitValue == 1);
const uint64_t word = (bitValue == 1) ? -1ULL : 0ULL;
const std::size_t wordSize = vlTypeSize(vop->varp()->vltype());
assert(wordSize > 0);
const uint32_t varBits = vop->bitSize();
const std::size_t numChunks = (varBits / wordSize);
for (std::size_t i{0}; i < numChunks; ++i) {
vl_vpi_put_word(vop, word, wordSize, i * wordSize);
}
// addOffset == varBits would trigger assertion in vl_vpi_var_access_info even if
// bitCount == 0, so first check if there is a remainder
if (varBits % wordSize != 0)
vl_vpi_put_word(vop, word, varBits % wordSize, numChunks * wordSize);
}
// Recreates the __VforceRd signal's data vector, since __VforceRd is not publicly accessible
// in Verilated code.
template <typename T>
static std::vector<T>
createReadDataVector(const void* const baseSignalDatap,
const std::pair<const void*, const void*> forceControlDatap,
const std::size_t bitCount) {
const void* const forceEnableDatap = forceControlDatap.first;
const void* const forceValueDatap = forceControlDatap.second;
assert(bitCount > 0);
const std::size_t numWords = (bitCount + (8 * sizeof(T)) - 1) / (8 * sizeof(T)); // Ceil
std::vector<T> readData(numWords);
for (std::size_t i{0}; i < numWords; ++i) {
const T forceEnableWord = reinterpret_cast<const T*>(forceEnableDatap)[i];
const T forceValueWord = reinterpret_cast<const T*>(forceValueDatap)[i];
const T baseSignalWord = reinterpret_cast<const T*>(baseSignalDatap)[i];
const T readDataWord
= (forceEnableWord & forceValueWord) | (~forceEnableWord & baseSignalWord);
readData[i] = readDataWord;
}
return readData;
}
};
//======================================================================
@ -1229,6 +1271,47 @@ VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE {
return s().m_errorInfop;
}
std::pair<vpiHandle, vpiHandle>
VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVarBase* const vop) {
const std::string signalName = vop->fullname();
const std::string forceEnableSignalName = signalName + "__VforceEn";
const std::string forceValueSignalName = signalName + "__VforceVal";
vpiHandle const forceEnableSignalp // NOLINT(misc-misplaced-const)
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(forceEnableSignalName.c_str()), nullptr);
vpiHandle const forceValueSignalp // NOLINT(misc-misplaced-const)
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(forceValueSignalName.c_str()), nullptr);
if (VL_UNLIKELY(!VerilatedVpioVar::castp(forceEnableSignalp))) {
VL_VPI_ERROR_(__FILE__, __LINE__,
"%s: vpi force or release requested for '%s', but vpiHandle '%p' of enable "
"signal '%s' could not be cast to VerilatedVpioVar*. Ensure signal is "
"marked as forceable",
__func__, signalName.c_str(), forceEnableSignalp,
forceEnableSignalName.c_str());
}
if (VL_UNLIKELY(!VerilatedVpioVar::castp(forceValueSignalp))) {
VL_VPI_ERROR_(__FILE__, __LINE__,
"%s: vpi force or release requested for '%s', but vpiHandle '%p' of value "
"signal '%s' could not be cast to VerilatedVpioVar*. Ensure signal is "
"marked as forceable",
__func__, signalName.c_str(), forceValueSignalp,
forceValueSignalName.c_str());
}
return {forceEnableSignalp, forceValueSignalp};
};
std::size_t VerilatedVpiImp::vlTypeSize(const VerilatedVarType vltype) {
switch (vltype) {
case VLVT_UINT8: return sizeof(CData); break;
case VLVT_UINT16: return sizeof(SData); break;
case VLVT_UINT32: return sizeof(IData); break;
case VLVT_UINT64: return sizeof(QData); break;
case VLVT_WDATA: return sizeof(EData); break;
default: // LCOV_EXCL_START
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__, vltype);
return 0;
} // LCOV_EXCL_STOP
}
//======================================================================
// VerilatedVpiError Methods
@ -2604,6 +2687,66 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
const int varBits = vop->bitSize();
// __VforceRd already has the correct value, but that signal is not public and thus not
// present in the scope's m_varsp map, so its value has to be recreated using the __VforceEn
// and __VforceVal signals.
// TODO: Implement a way to retrieve __VforceRd, rather than needing to recreate it.
const auto forceControlSignals = vop->varp()->isForceable()
? VerilatedVpiImp::getForceControlSignals(vop)
: std::pair<vpiHandle, vpiHandle>{nullptr, nullptr};
const vpiHandle& forceEnableSignalp = forceControlSignals.first;
const vpiHandle& forceValueSignalp = forceControlSignals.second;
const VerilatedVpioVarBase* const forceEnableSignalVop
= vop->varp()->isForceable() ? VerilatedVpioVar::castp(forceEnableSignalp) : nullptr;
const VerilatedVpioVarBase* const forceValueSignalVop
= vop->varp()->isForceable() ? VerilatedVpioVar::castp(forceValueSignalp) : nullptr;
t_vpi_error_info getForceControlSignalsError{};
const bool errorOccurred = vpi_chk_error(&getForceControlSignalsError);
// LCOV_EXCL_START - Cannot test, since getForceControlSignals does not (currently) produce
// any notices or warnings.
if (errorOccurred && getForceControlSignalsError.level < vpiError) {
vpi_printf(getForceControlSignalsError.message);
VL_VPI_ERROR_RESET_();
} // LCOV_EXCL_STOP
// NOLINTNEXTLINE(readability-simplify-boolean-expr);
if (VL_UNLIKELY((errorOccurred && getForceControlSignalsError.level >= vpiError)
|| (vop->varp()->isForceable()
&& (!forceEnableSignalp || !forceEnableSignalVop || !forceValueSignalp
|| !forceValueSignalVop)))) {
// Check if getForceControlSignals provided any additional error info
t_vpi_error_info getForceControlSignalsError{};
const bool gotErrorMessage = vpi_chk_error(&getForceControlSignalsError);
const std::string previousErrorMessage
= gotErrorMessage
? std::string{" Error message: "} + getForceControlSignalsError.message
: "";
VL_VPI_ERROR_(__FILE__, __LINE__,
"%s: Signal '%s' is marked forceable, but force "
"control signals could not be retrieved.%s",
__func__, vop->fullname(),
gotErrorMessage ? previousErrorMessage.c_str() : "");
return;
}
const std::function<QData(const VerilatedVpioVarBase*, size_t, size_t)> getForceableSignalWord
= [forceEnableSignalVop, forceValueSignalVop](const VerilatedVpioVarBase* baseSignalVop,
size_t bitCount, size_t addOffset) -> QData {
// variables are QData, even though signals may have different representation, because any
// extraneous bits are simply truncated upon implicit casting when this function is called.
const QData baseSignalData = vl_vpi_get_word(baseSignalVop, bitCount, addOffset);
const QData forceEnableData = vl_vpi_get_word(forceEnableSignalVop, bitCount, addOffset);
const QData forceValueData = vl_vpi_get_word(forceValueSignalVop, bitCount, addOffset);
const QData readData
= (forceEnableData & forceValueData) | (~forceEnableData & baseSignalData);
return readData;
};
const std::function<QData(const VerilatedVpioVarBase*, size_t, size_t)> get_word
= vop->varp()->isForceable() ? getForceableSignalWord : vl_vpi_get_word;
// 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) {
@ -2621,25 +2764,38 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
return;
}
for (int i = 0; i < words; ++i) {
t_out[i].aval = vl_vpi_get_word(vop, 32, i * 32);
t_out[i].aval = 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);
const QData data = 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].aval = 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);
static thread_local std::vector<uint8_t> forceReadCData;
forceReadCData
= vop->varp()->isForceable()
? VerilatedVpiImp::createReadDataVector<uint8_t>(
varDatap,
{forceEnableSignalVop->varDatap(), forceValueSignalVop->varDatap()},
vop->bitSize())
: std::vector<uint8_t>{};
const uint8_t* const varCDatap = vop->varp()->isForceable()
? forceReadCData.data()
: reinterpret_cast<CData*>(varDatap);
const CData* datap = varCDatap;
for (size_t i = 0; i < varBits; ++i) {
const size_t pos = i + vop->bitOffset();
const char val = (datap[pos >> 3] >> (pos & 7)) & 1;
@ -2651,24 +2807,22 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
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);
const char val = 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)));
vl_strprintf(t_outDynamicStr, "%hhu", static_cast<unsigned char>(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)));
static_cast<unsigned short>(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)));
vl_strprintf(t_outDynamicStr, "%u", static_cast<unsigned int>(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)));
static_cast<unsigned long long>(get_word(vop, 64, 0)));
}
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
@ -2676,7 +2830,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
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);
const char val = 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());
@ -2687,7 +2841,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
valuep->value.str = reinterpret_cast<char*>(varDatap);
return;
} else {
t_outDynamicStr = *(vop->varStringDatap());
t_outDynamicStr = *vop->varStringDatap();
valuep->value.str = const_cast<char*>(t_outDynamicStr.c_str());
return;
}
@ -2695,7 +2849,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
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);
const char val = get_word(vop, 8, i * 8);
// other simulators replace [leading?] zero chars with spaces, replicate here.
t_outDynamicStr[chars - i - 1] = val ? val : ' ';
}
@ -2703,10 +2857,16 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
return;
}
} else if (valuep->format == vpiIntVal) {
valuep->value.integer = vl_vpi_get_word(vop, 32, 0);
valuep->value.integer = get_word(vop, 32, 0);
return;
} else if (valuep->format == vpiRealVal) {
valuep->value.real = *(vop->varRealDatap());
// Only cover the scalar case, since reals cannot be packed (IEEE 1800, section 7.4.1), and
// unpacked arrays are not supported for forcing in Verilator (#4735).
if (vop->varp()->isForceable() && *forceEnableSignalVop->varCDatap())
valuep->value.real = *forceValueSignalVop->varRealDatap();
else
valuep->value.real = *vop->varRealDatap();
return;
} else if (valuep->format == vpiSuppressVal) {
return;
@ -2749,55 +2909,158 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
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()););
const PLI_INT32 forceFlag = flags & 0xfff;
if (const VerilatedVpioVar* const baseSignalVop = VerilatedVpioVar::castp(object)) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n",
baseSignalVop->fullname(), valuep->format,
valuep->value.integer);
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n",
baseSignalVop->varp()->datap(), baseSignalVop->varDatap()););
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
if (VL_UNLIKELY(!baseSignalVop->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());
baseSignalVop->fullname(), baseSignalVop->scopep()->defname());
return nullptr;
}
if (!vl_check_format(vop->varp(), valuep, vop->fullname(), false)) return nullptr;
// NOLINTNEXTLINE(readability-simplify-boolean-expr);
if (VL_UNLIKELY((forceFlag == vpiForceFlag || forceFlag == vpiReleaseFlag)
&& !baseSignalVop->varp()->isForceable())) {
VL_VPI_ERROR_(__FILE__, __LINE__,
"vpi_put_value was used with %s on non-forceable signal '%s' : '%s'",
forceFlag == vpiForceFlag ? "vpiForceFlag" : "vpiReleaseFlag",
baseSignalVop->fullname(), baseSignalVop->scopep()->defname());
return nullptr;
}
if (!vl_check_format(baseSignalVop->varp(), valuep, baseSignalVop->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());
__func__, baseSignalVop->fullname());
return nullptr;
}
VerilatedVpiImp::inertialDelay(vop, valuep);
VerilatedVpiImp::inertialDelay(baseSignalVop, valuep);
return object;
}
VerilatedVpiImp::evalNeeded(true);
const int varBits = vop->bitSize();
const int varBits = baseSignalVop->bitSize();
const auto forceControlSignals
= baseSignalVop->varp()->isForceable()
? VerilatedVpiImp::getForceControlSignals(baseSignalVop)
: std::pair<vpiHandle, vpiHandle>{nullptr, nullptr};
const vpiHandle& forceEnableSignalp = forceControlSignals.first;
const vpiHandle& forceValueSignalp = forceControlSignals.second;
const VerilatedVpioVar* const forceEnableSignalVop
= baseSignalVop->varp()->isForceable() ? VerilatedVpioVar::castp(forceEnableSignalp)
: nullptr;
const VerilatedVpioVar* const forceValueSignalVop
= baseSignalVop->varp()->isForceable() ? VerilatedVpioVar::castp(forceValueSignalp)
: nullptr;
t_vpi_error_info getForceControlSignalsError{};
const bool errorOccurred = vpi_chk_error(&getForceControlSignalsError);
// LCOV_EXCL_START - Cannot test, since getForceControlSignals does not (currently) produce
// any notices or warnings.
if (errorOccurred && getForceControlSignalsError.level < vpiError) {
vpi_printf(getForceControlSignalsError.message);
VL_VPI_ERROR_RESET_();
} // LCOV_EXCL_STOP
// NOLINTNEXTLINE(readability-simplify-boolean-expr);
if (VL_UNLIKELY(baseSignalVop->varp()->isForceable()
&& (!forceEnableSignalp || !forceEnableSignalVop || !forceValueSignalp
|| !forceValueSignalVop))) {
// Check if getForceControlSignals provided any additional error info
t_vpi_error_info getForceControlSignalsError{};
const bool gotErrorMessage = vpi_chk_error(&getForceControlSignalsError);
const std::string previousErrorMessage
= gotErrorMessage
? std::string{" Error message: "} + getForceControlSignalsError.message
: "";
VL_VPI_ERROR_(__FILE__, __LINE__,
"%s: Signal '%s' with vpiHandle '%p' is marked forceable, but force "
"control signals could not be retrieved.%s",
__func__, baseSignalVop->fullname(), object,
gotErrorMessage ? previousErrorMessage.c_str() : "");
return nullptr;
}
const VerilatedVpioVar* const valueVop = (forceFlag == vpiForceFlag)
? VerilatedVpioVar::castp(forceValueSignalp)
: baseSignalVop;
if (forceFlag == vpiForceFlag) {
// Enable __VforceEn
VerilatedVpiImp::setAllBitsToValue(forceEnableSignalVop, 1);
}
if (forceFlag == vpiReleaseFlag) {
// If signal is continuously assigned, first clear the force enable bits, then get the
// (non-forced) value. Else, get the (still forced) value first, then clear the force
// enable bits.
if (valueVop->varp()->isContinuously())
VerilatedVpiImp::setAllBitsToValue(forceEnableSignalVop, 0);
vl_vpi_get_value(baseSignalVop, valuep);
t_vpi_error_info baseValueGetError{};
const bool errorOccurred = vpi_chk_error(&baseValueGetError);
// LCOV_EXCL_START - Cannot test, because missing signal would already trigger error
// earlier, at the getForceControlSignals stage
// NOLINTNEXTLINE(readability-simplify-boolean-expr);
if (VL_UNLIKELY(errorOccurred && baseValueGetError.level >= vpiError)) {
const std::string baseValueSignalName = baseSignalVop->fullname();
const std::string previousErrorMessage = baseValueGetError.message;
VL_VPI_ERROR_(__FILE__, __LINE__,
"%s: Could not retrieve value of signal '%s' with "
"vpiHandle '%p'. Error message: %s",
__func__, baseValueSignalName.c_str(), object,
previousErrorMessage.c_str());
return nullptr;
}
// NOLINTNEXTLINE(readability-simplify-boolean-expr);
if (VL_UNCOVERABLE(errorOccurred && baseValueGetError.level < vpiError)) {
vpi_printf(baseValueGetError.message);
VL_VPI_ERROR_RESET_();
} // LCOV_EXCL_STOP
if (!valueVop->varp()->isContinuously())
VerilatedVpiImp::setAllBitsToValue(forceEnableSignalVop, 0);
// TODO: According to the SystemVerilog specification,
// vpi_put_value should return a handle to the scheduled event
// if the vpiReturnEvent flag is selected, NULL otherwise.
return object;
}
if (valuep->format == vpiVectorVal) {
if (VL_UNLIKELY(!valuep->value.vector)) return nullptr;
if (vop->varp()->vltype() == VLVT_WDATA) {
if (valueVop->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);
vl_vpi_put_word(valueVop, valuep->value.vector[i].aval, 32, i * 32);
return object;
} else if (vop->varp()->vltype() == VLVT_UINT64 && varBits > 32) {
} else if (valueVop->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);
vl_vpi_put_word(valueVop, val, 64, 0);
return object;
} else {
vl_vpi_put_word(vop, valuep->value.vector[0].aval, 32, 0);
vl_vpi_put_word(valueVop, 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());
CData* const datap = reinterpret_cast<CData*>(valueVop->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;
const size_t pos = valueVop->bitOffset() + i;
if (set)
datap[pos >> 3] |= 1 << (pos & 7);
@ -2814,10 +3077,10 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
"%s: Non octal character '%c' in '%s' as value %s for %s",
__func__, digit + '0', valuep->value.str,
VerilatedVpiError::strFromVpiVal(valuep->format),
vop->fullname());
valueVop->fullname());
digit = 0;
}
vl_vpi_put_word(vop, digit, 3, i * 3);
vl_vpi_put_word(valueVop, digit, 3, i * 3);
}
return object;
} else if (valuep->format == vpiDecStrVal) {
@ -2828,16 +3091,17 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
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());
VerilatedVpiError::strFromVpiVal(valuep->format),
valueVop->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_WARNING_(
__FILE__, __LINE__, "%s: Trailing garbage '%s' in '%s' as value %s for %s",
__func__, remainder, valuep->value.str,
VerilatedVpiError::strFromVpiVal(valuep->format), valueVop->fullname());
}
vl_vpi_put_word(vop, val, 64, 0);
vl_vpi_put_word(valueVop, val, 64, 0);
return object;
} else if (valuep->format == vpiHexStrVal) {
const int chars = (varBits + 3) >> 2;
@ -2861,19 +3125,20 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
"%s: Non hex character '%c' in '%s' as value %s for %s",
__func__, digit, valuep->value.str,
VerilatedVpiError::strFromVpiVal(valuep->format),
vop->fullname());
valueVop->fullname());
hex = 0;
}
} else {
hex = 0;
}
// assign hex digit value to destination
vl_vpi_put_word(vop, hex, 4, i * 4);
vl_vpi_put_word(valueVop, hex, 4, i * 4);
}
return object;
} else if (valuep->format == vpiStringVal) {
if (vop->varp()->vltype() == VLVT_STRING) {
*(vop->varStringDatap()) = valuep->value.str;
if (valueVop->varp()->vltype() == VLVT_STRING) {
// Does not use valueVop, because strings are not forceable anyway
*(baseSignalVop->varStringDatap()) = valuep->value.str;
return object;
} else {
const int chars = VL_BYTES_I(varBits);
@ -2881,21 +3146,22 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
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);
vl_vpi_put_word(valueVop, c, 8, i * 8);
}
}
return object;
} else if (valuep->format == vpiIntVal) {
vl_vpi_put_word(vop, valuep->value.integer, 64, 0);
vl_vpi_put_word(valueVop, valuep->value.integer, 64, 0);
return object;
} else if (valuep->format == vpiRealVal) {
if (vop->varp()->vltype() == VLVT_REAL) {
*(vop->varRealDatap()) = valuep->value.real;
if (valueVop->varp()->vltype() == VLVT_REAL) {
*(valueVop->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());
__func__, VerilatedVpiError::strFromVpiVal(valuep->format),
valueVop->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",

View File

@ -630,6 +630,8 @@ string AstVar::vlEnumDir() const {
} else if (isSigUserRdPublic()) {
out += "|VLVF_PUB_RD";
}
if (isForceable()) out += "|VLVF_FORCEABLE";
if (isContinuously()) out += "|VLVF_CONTINUOUSLY";
//
if (const AstBasicDType* const bdtypep = basicp()) {
if (bdtypep->keyword().isDpiCLayout()) out += "|VLVF_DPI_CLAY";

View File

@ -457,6 +457,20 @@ class ForceConvertVisitor final : public VNVisitor {
void visit(AstVarScope* nodep) override {
// If this signal is marked externally forceable, create the public force signals
if (nodep->varp()->isForceable()) {
if (VN_IS(nodep->varp()->dtypeSkipRefp(), UnpackArrayDType)) {
nodep->varp()->v3warn(
E_UNSUPPORTED,
"Unsupported: Forcing unpacked arrays: " << nodep->varp()->name()); // (#4735)
return;
}
const AstBasicDType* const bdtypep = nodep->varp()->basicp();
const bool strtype = bdtypep && bdtypep->keyword() == VBasicDTypeKwd::STRING;
if (strtype) {
nodep->varp()->v3error(
"Forcing strings is not permitted: " << nodep->varp()->name());
}
const ForceState::ForceComponentsVarScope& fc = m_state.getForceComponents(nodep);
fc.m_enVscp->varp()->sigUserRWPublic(true);
fc.m_valVscp->varp()->sigUserRWPublic(true);

View File

@ -0,0 +1,5 @@
%Error: t/t_forceable_string_bad.v:8:10: Forcing strings is not permitted: t__DOT__str
8 | string str /*verilator forceable*/;
| ^~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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
import vltest_bootstrap
test.scenarios('vlt')
test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,9 @@
// ======================================================================
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty.
// SPDX-License-Identifier: CC0-1.0
// ======================================================================
module t;
string str /*verilator forceable*/;
endmodule

View File

@ -0,0 +1,5 @@
%Error-UNSUPPORTED: t/t_forceable_unpacked_bad.v:8:9: Unsupported: Forcing unpacked arrays: t__DOT__unpacked
8 | logic unpacked[1] /*verilator forceable*/;
| ^~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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
import vltest_bootstrap
test.scenarios('vlt')
test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,9 @@
// ======================================================================
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty.
// SPDX-License-Identifier: CC0-1.0
// ======================================================================
module t;
logic unpacked[1] /*verilator forceable*/;
endmodule

View File

@ -0,0 +1,877 @@
// ======================================================================
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty.
// SPDX-License-Identifier: CC0-1.0
// ======================================================================
// DESCRIPTION: vpi force and release test
//
// This test checks that forcing a signal using vpi_put_value with vpiForceFlag
// sets it to the correct value, and then releasing it with vpiReleaseFlag
// returns it to the initial state.
#include "verilated.h" // For VL_PRINTF
#include "verilated_sym_props.h" // For VerilatedVar
#include "verilated_syms.h" // For VerilatedVarNameMap
#include "verilated_vpi.h" // For VerilatedVpi::doInertialPuts();
#include "TestSimulator.h" // For is_verilator()
#include "TestVpi.h" // For CHECK_RESULT_NZ
#include "vpi_user.h"
#include <algorithm>
#include <memory> // For std::unique_ptr
namespace {
constexpr int maxAllowedErrorLevel = vpiWarning;
const std::string scopeName = "t.test";
using signalValueTypes = union {
const char* str;
PLI_INT32 integer;
double real;
const struct t_vpi_vecval* vector;
};
using TestSignal = const struct {
const char* signalName;
PLI_INT32 valueType;
signalValueTypes releaseValue;
signalValueTypes forceValue;
std::pair<signalValueTypes, bool>
partialForceValue; // No std::optional on C++14, so the bool inside the pair is used to
// specify if a partial force should be tested for this signal. For a
// partial force, the first part of the signal is left at the release
// value, while the second part is forced to the force value.
};
constexpr std::array<TestSignal, 16> TestSignals = {
TestSignal{"onebit",
vpiIntVal,
{.integer = 1},
{.integer = 0},
{{}, false}}, // Can't partially force just one bit
TestSignal{"intval",
vpiIntVal,
{.integer = -1431655766}, // 1010...1010
{.integer = 0x55555555}, // 0101...0101
{{.integer = -1431677611}, true}}, // 1010...010_010...0101
TestSignal{"vectorC",
vpiVectorVal,
// NOLINTBEGIN (cppcoreguidelines-avoid-c-arrays)
{.vector = (t_vpi_vecval[]){{0b10101010, 0}}},
{.vector = (t_vpi_vecval[]){{0b01010101, 0}}},
{{.vector = (t_vpi_vecval[]){{0b10100101, 0}}}, true}},
// NOLINTEND (cppcoreguidelines-avoid-c-arrays)
TestSignal{
"vectorQ",
vpiVectorVal,
// NOTE: This is a 62 bit signal, so the first two bits of the MSBs (*second* vecval,
// since the LSBs come first) are set to 0, hence the 0x2 and 0x1, respectively.
// NOLINTBEGIN (cppcoreguidelines-avoid-c-arrays)
{.vector = (t_vpi_vecval[]){{0xAAAAAAAAUL, 0}, {0x2AAAAAAAUL, 0}}}, // (00)1010...1010
{.vector = (t_vpi_vecval[]){{0x55555555UL, 0}, {0x15555555UL, 0}}}, // (00)0101...0101
{{.vector = (t_vpi_vecval[]){{0xD5555555UL, 0}, {0x2AAAAAAAUL, 0}}},
true}}, // 1010...010_010...0101
// NOLINTEND (cppcoreguidelines-avoid-c-arrays)
TestSignal{"vectorW",
vpiVectorVal,
// NOLINTBEGIN (cppcoreguidelines-avoid-c-arrays)
{.vector = (t_vpi_vecval[]){{0xAAAAAAAAUL, 0}, // 1010...1010
{0xAAAAAAAAUL, 0},
{0xAAAAAAAAUL, 0},
{0xAAAAAAAAUL, 0}}},
{.vector = (t_vpi_vecval[]){{0x55555555UL, 0}, // 0101...0101
{0x55555555UL, 0},
{0x55555555UL, 0},
{0x55555555UL, 0}}},
{{.vector = (t_vpi_vecval[]){{0x55555555UL, 0}, // 1010...010_010...0101
{0x55555555UL, 0},
{0xAAAAAAAAUL, 0},
{0xAAAAAAAAUL, 0}}},
true}},
// NOLINTEND (cppcoreguidelines-avoid-c-arrays)
TestSignal{"real1",
vpiRealVal,
{.real = 1.0},
{.real = 123456.789},
{{}, false}}, // reals cannot be packed and individual bits cannot be accessed, so
// there is no way to partially force a real signal.
TestSignal{"textHalf", vpiStringVal, {.str = "Hf"}, {.str = "T2"}, {{.str = "H2"}, true}},
TestSignal{"textLong",
vpiStringVal,
{.str = "Long64b"},
{.str = "44Four44"},
{{.str = "Lonur44"}, true}},
TestSignal{"text",
vpiStringVal,
{.str = "Verilog Test module"},
{.str = "lorem ipsum"},
{{.str = "Verilog Tesem ipsum"}, true}},
TestSignal{"binString",
vpiBinStrVal,
{.str = "10101010"},
{.str = "01010101"},
{{.str = "10100101"}, true}},
TestSignal{"octString",
vpiOctStrVal,
{.str = "25252"}, // 010101010101010
{.str = "52525"}, // 101010101010101
{{.str = "25325"}, true}}, // 010101011010101
TestSignal{"hexString",
vpiHexStrVal,
{.str = "aaaaaaaaaaaaaaaa"}, // 1010...1010
{.str = "5555555555555555"}, // 0101...0101
{{.str = "aaaaaaaa55555555"}, true}}, // 1010...010_010...0101
TestSignal{"decStringC",
vpiDecStrVal,
{.str = "170"}, // 10101010
{.str = "85"}, // 01010101
{{.str = "165"}, true}}, // 10100101
TestSignal{"decStringS",
vpiDecStrVal,
{.str = "43690"}, // 1010...1010
{.str = "21845"}, // 0101...0101
{{.str = "43605"}, true}}, // 1010...010_010...0101
TestSignal{"decStringI",
vpiDecStrVal,
{.str = "2863311530"}, // 1010...1010
{.str = "1431655765"}, // 0101...0101
{{.str = "2863289685"}, true}}, // 1010...010_010...0101
TestSignal{"decStringQ",
vpiDecStrVal,
{.str = "12297829382473034410"}, // 1010...1010
{.str = "6148914691236517205"}, // 0101...0101
{{.str = "12297829381041378645"}, true}}, // 1010...010_010...0101
};
bool vpiCheckErrorLevel(const int maxAllowedErrorLevel) {
t_vpi_error_info errorInfo{};
const bool errorOccured = vpi_chk_error(&errorInfo);
if (VL_UNLIKELY(errorOccured)) {
VL_PRINTF("%s", errorInfo.message);
return errorInfo.level > maxAllowedErrorLevel;
}
return false;
}
std::pair<const std::string, const bool> vpiGetErrorMessage() {
t_vpi_error_info errorInfo{};
const bool errorOccured = vpi_chk_error(&errorInfo);
return {errorOccured ? errorInfo.message : std::string{}, errorOccured};
}
#ifdef VERILATOR // m_varsp is Verilator-specific and does not make sense for other simulators
std::unique_ptr<const VerilatedVar> removeSignalFromScope(const std::string& scopeName,
const std::string& signalName) {
const VerilatedScope* const scopep = Verilated::threadContextp()->scopeFind(scopeName.c_str());
if (!scopep) return nullptr;
VerilatedVarNameMap* const varsp = scopep->varsp();
const VerilatedVarNameMap::const_iterator foundSignalIt = varsp->find(signalName.c_str());
if (foundSignalIt == varsp->end()) return nullptr;
VerilatedVar foundSignal = foundSignalIt->second;
varsp->erase(foundSignalIt);
return std::make_unique<const VerilatedVar>(foundSignal);
}
bool insertSignalIntoScope(const std::pair<std::string, std::string>& scopeAndSignalNames,
const std::unique_ptr<const VerilatedVar> signal) {
const std::string& scopeName = scopeAndSignalNames.first;
const std::string& signalName = scopeAndSignalNames.second;
const VerilatedScope* const scopep = Verilated::threadContextp()->scopeFind(scopeName.c_str());
if (!scopep) return false;
VerilatedVarNameMap* const varsp = scopep->varsp();
// NOTE: The lifetime of the name inserted into varsp must be the same as the scopep, i.e. the
// same as threadContextp. Otherwise, the key in the m_varsp map will be a stale pointer.
// Hence, names of signals being inserted are stored in the static set, and it is assumed that
// the set's lifetime is the same as the threadContextp.
static std::set<std::string> insertedSignalNames;
const auto insertedSignalName = insertedSignalNames.insert(signalName);
varsp->insert(
std::pair<const char*, VerilatedVar>{insertedSignalName.first->c_str(), *signal});
return true;
}
int tryVpiGetWithMissingSignal(const TestVpiHandle& signalToGet, // NOLINT(misc-misplaced-const)
const PLI_INT32 signalFormat,
const std::pair<std::string, std::string>& scopeAndSignalNames,
const std::string& expectedErrorMessage) {
const std::string& scopeName = scopeAndSignalNames.first;
const std::string& signalNameToRemove = scopeAndSignalNames.second;
std::unique_ptr<const VerilatedVar> removedSignal
= removeSignalFromScope(scopeName, signalNameToRemove);
CHECK_RESULT_NZ(removedSignal); // NOLINT(concurrency-mt-unsafe)
s_vpi_value value_s{.format = signalFormat, .value = {}};
// Prevent program from terminating, so error message can be collected
Verilated::fatalOnVpiError(false);
vpi_get_value(signalToGet, &value_s);
// Re-enable so tests that should pass properly terminate the simulation on failure
Verilated::fatalOnVpiError(true);
std::pair<const std::string, const bool> receivedError = vpiGetErrorMessage();
const bool errorOccurred = receivedError.second;
const std::string receivedErrorMessage = receivedError.first;
CHECK_RESULT_NZ(errorOccurred); // NOLINT(concurrency-mt-unsafe)
// NOLINTNEXTLINE(concurrency-mt-unsafe,performance-avoid-endl)
CHECK_RESULT(receivedErrorMessage, expectedErrorMessage);
bool insertSuccess
= insertSignalIntoScope({scopeName, signalNameToRemove}, std::move(removedSignal));
CHECK_RESULT_NZ(insertSuccess); // NOLINT(concurrency-mt-unsafe)
return 0;
}
int tryVpiPutWithMissingSignal(const s_vpi_value value_s,
const TestVpiHandle& signalToPut, // NOLINT(misc-misplaced-const)
const int flag, const std::string& scopeName,
const std::string& signalNameToRemove,
const std::vector<std::string>& expectedErrorMessageSubstrings) {
std::unique_ptr<const VerilatedVar> removedSignal
= removeSignalFromScope(scopeName, signalNameToRemove);
CHECK_RESULT_NZ(removedSignal); // NOLINT(concurrency-mt-unsafe)
// Prevent program from terminating, so error message can be collected
Verilated::fatalOnVpiError(false);
vpi_put_value(signalToPut, const_cast<p_vpi_value>(&value_s), nullptr, flag);
// Re-enable so tests that should pass properly terminate the simulation on failure
Verilated::fatalOnVpiError(true);
std::pair<const std::string, const bool> receivedError = vpiGetErrorMessage();
const bool errorOccurred = receivedError.second;
const std::string receivedErrorMessage = receivedError.first;
CHECK_RESULT_NZ(errorOccurred); // NOLINT(concurrency-mt-unsafe)
const bool allExpectedErrorSubstringsFound
= std::all_of(expectedErrorMessageSubstrings.begin(), expectedErrorMessageSubstrings.end(),
[receivedErrorMessage](const std::string& expectedSubstring) {
return receivedErrorMessage.find(expectedSubstring) != std::string::npos;
});
CHECK_RESULT_NZ(allExpectedErrorSubstringsFound); // NOLINT(concurrency-mt-unsafe)
bool insertSuccess
= insertSignalIntoScope({scopeName, signalNameToRemove}, std::move(removedSignal));
CHECK_RESULT_NZ(insertSuccess); // NOLINT(concurrency-mt-unsafe)
return 0;
}
// Simpler function that expects an exact string instead of a number of substrings, and just a
// signalName instead of a handle.
int expectVpiPutError(const std::string& signalName, s_vpi_value value_s, const int flag,
const std::string& expectedErrorMessage) {
const std::string fullSignalName = std::string{scopeName} + "." + signalName;
TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const)
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(fullSignalName.c_str()), nullptr);
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
// Prevent program from terminating, so error message can be collected
Verilated::fatalOnVpiError(false);
vpi_put_value(signalHandle, &value_s, nullptr, flag);
// Re-enable so tests that should pass properly terminate the simulation on failure
Verilated::fatalOnVpiError(true);
std::pair<const std::string, const bool> receivedError = vpiGetErrorMessage();
const bool errorOccurred = receivedError.second;
const std::string receivedErrorMessage = receivedError.first;
CHECK_RESULT_NZ(errorOccurred); // NOLINT(concurrency-mt-unsafe)
// NOLINTNEXTLINE(concurrency-mt-unsafe,performance-avoid-endl)
CHECK_RESULT(receivedErrorMessage, expectedErrorMessage);
return 0;
}
#endif
bool vpiValuesEqual(const std::size_t bitCount, const s_vpi_value& first,
const s_vpi_value& second) {
if (first.format != second.format) return false;
switch (first.format) {
case vpiIntVal: return first.value.integer == second.value.integer; break;
case vpiVectorVal: {
const t_vpi_vecval* const firstVecval = first.value.vector;
const t_vpi_vecval* const secondVecval = second.value.vector;
const std::size_t vectorElements = (bitCount + 31) / 32; // Ceil
for (std::size_t i{0}; i < vectorElements; ++i) {
if (firstVecval[i].aval != secondVecval[i].aval) return false;
}
return true;
}
case vpiRealVal:
return std::abs(first.value.real - second.value.real)
< std::numeric_limits<double>::epsilon();
break;
case vpiStringVal:
case vpiBinStrVal:
case vpiOctStrVal:
case vpiDecStrVal:
case vpiHexStrVal: {
// Same as CHECK_RESULT_CSTR_STRIP, but should return true when equal, false otherwise
const std::string firstUnpadded = first.value.str + std::strspn(first.value.str, " ");
return std::string{firstUnpadded} == std::string{second.value.str};
break;
}
default:
VL_PRINTF("Unsupported value format %i passed to vpiValuesEqual\n", first.format);
return false;
}
}
std::unique_ptr<s_vpi_value> vpiValueWithFormat(const PLI_INT32 signalFormat,
const signalValueTypes value) {
std::unique_ptr<s_vpi_value> value_sp = std::make_unique<s_vpi_value>();
value_sp->format = signalFormat;
switch (signalFormat) {
case vpiIntVal: value_sp->value = {.integer = value.integer}; break;
case vpiVectorVal: value_sp->value = {.vector = const_cast<p_vpi_vecval>(value.vector)}; break;
case vpiRealVal: value_sp->value = {.real = value.real}; break;
case vpiStringVal:
case vpiBinStrVal:
case vpiOctStrVal:
case vpiDecStrVal:
case vpiHexStrVal: value_sp->value = {.str = const_cast<PLI_BYTE8*>(value.str)}; break;
default:
VL_PRINTF("Unsupported value format %i passed to vpiValueWithFormat\n", signalFormat);
return nullptr;
}
return value_sp;
}
int checkValue(const std::string& scopeName, const std::string& testSignalName,
const PLI_INT32 signalFormat, const signalValueTypes expectedValue) {
const std::string testSignalFullName
= std::string{scopeName} + "." + std::string{testSignalName};
TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const)
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(testSignalFullName.c_str()), nullptr);
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
#ifdef VERILATOR
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(tryVpiGetWithMissingSignal(
signalHandle, signalFormat, {scopeName, testSignalName + "__VforceEn"},
"vl_vpi_get_value: Signal '" + testSignalFullName
+ "' is marked forceable, but force control signals could not be retrieved. Error "
"message: getForceControlSignals: vpi force or release requested for '"
+ testSignalFullName + "', but vpiHandle '(nil)' of enable signal '"
+ testSignalFullName
+ "__VforceEn' could not be cast to VerilatedVpioVar*. Ensure signal is marked as "
"forceable"));
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(tryVpiGetWithMissingSignal(
signalHandle, signalFormat, {scopeName, testSignalName + "__VforceVal"},
"vl_vpi_get_value: Signal '" + testSignalFullName
+ "' is marked forceable, but force control signals could not be retrieved. Error "
"message: getForceControlSignals: vpi force or release requested for '"
+ testSignalFullName + "', but vpiHandle '(nil)' of value signal '"
+ testSignalFullName
+ "__VforceVal' could not be cast to VerilatedVpioVar*. Ensure signal is marked "
"as "
"forceable"));
#endif
std::unique_ptr<s_vpi_value> receivedValueSp = vpiValueWithFormat(signalFormat, {});
CHECK_RESULT_NZ(receivedValueSp); // NOLINT(concurrency-mt-unsafe)
vpi_get_value(signalHandle, receivedValueSp.get());
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
const std::unique_ptr<s_vpi_value> expectedValueSp
= vpiValueWithFormat(signalFormat, expectedValue);
CHECK_RESULT_NZ(expectedValueSp); // NOLINT(concurrency-mt-unsafe)
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_NZ(
vpiValuesEqual(vpi_get(vpiSize, signalHandle), *receivedValueSp, *expectedValueSp));
return 0;
}
int forceSignal(const std::string& scopeName, const std::string& testSignalName,
const PLI_INT32 signalFormat, const signalValueTypes forceValue) {
const std::string testSignalFullName
= std::string{scopeName} + "." + std::string{testSignalName};
TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const)
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(testSignalFullName.c_str()), nullptr);
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
std::unique_ptr<s_vpi_value> value_sp = vpiValueWithFormat(signalFormat, forceValue);
CHECK_RESULT_NZ(value_sp); // NOLINT(concurrency-mt-unsafe)
#ifdef VERILATOR
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(tryVpiPutWithMissingSignal(
*value_sp, signalHandle, vpiForceFlag, scopeName, testSignalName + "__VforceEn",
{"vpi_put_value: Signal '" + testSignalFullName + "' with vpiHandle ",
// Exact handle address does not matter
" is marked forceable, but force control signals could not be retrieved. Error "
"message: getForceControlSignals: vpi force or release requested for '"
+ testSignalFullName + "', but vpiHandle '(nil)' of enable signal '"
+ testSignalFullName
+ "__VforceEn' could not be cast to VerilatedVpioVar*. Ensure signal is marked "
"as "
"forceable"}));
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(tryVpiPutWithMissingSignal(
*value_sp, signalHandle, vpiForceFlag, scopeName, testSignalName + "__VforceVal",
{"vpi_put_value: Signal '" + testSignalFullName + "' with vpiHandle ",
// Exact handle address does not matter
" is marked forceable, but force control signals could not be retrieved. Error "
"message: getForceControlSignals: vpi force or release requested for '"
+ testSignalFullName + "', but vpiHandle '(nil)' of value signal '"
+ testSignalFullName
+ "__VforceVal' could not be cast to VerilatedVpioVar*. Ensure signal is marked "
"as "
"forceable"}));
#endif
vpi_put_value(signalHandle, value_sp.get(), nullptr, vpiForceFlag);
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
return 0;
}
int releaseSignal(const std::string& scopeName, const std::string& testSignalName,
const PLI_INT32 signalFormat,
const std::pair<signalValueTypes, signalValueTypes> releaseValue) {
const signalValueTypes expectedReleaseValueInit = releaseValue.first;
const signalValueTypes expectedReleaseValue = releaseValue.second;
const std::string testSignalFullName
= std::string{scopeName} + "." + std::string{testSignalName};
TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const)
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(testSignalFullName.c_str()), nullptr);
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
// initialize value_sp to the value that is *not* expected (i.e. forceValue for continuously
// assigned signals, and releaseValue for clocked signals) to ensure the test fails if value_sp
// is not updated
std::unique_ptr<s_vpi_value> value_sp
= vpiValueWithFormat(signalFormat, expectedReleaseValueInit);
CHECK_RESULT_NZ(value_sp); //NOLINT(concurrency-mt-unsafe)
#ifdef VERILATOR
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(tryVpiPutWithMissingSignal(
*value_sp, signalHandle, vpiReleaseFlag, scopeName, testSignalName + "__VforceEn",
{"vpi_put_value: Signal '" + testSignalFullName + "' with vpiHandle ",
// Exact handle address does not matter
" is marked forceable, but force control signals could not be retrieved. Error "
"message: getForceControlSignals: vpi force or release requested for '"
+ testSignalFullName + "', but vpiHandle '(nil)' of enable signal '"
+ testSignalFullName
+ "__VforceEn' could not be cast to VerilatedVpioVar*. Ensure signal is marked "
"as "
"forceable"}));
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(tryVpiPutWithMissingSignal(
*value_sp, signalHandle, vpiReleaseFlag, scopeName, testSignalName + "__VforceVal",
{"vpi_put_value: Signal '" + testSignalFullName + "' with vpiHandle ",
// Exact handle address does not matter
" is marked forceable, but force control signals could not be retrieved. Error "
"message: getForceControlSignals: vpi force or release requested for '"
+ testSignalFullName + "', but vpiHandle '(nil)' of value signal '"
+ testSignalFullName
+ "__VforceVal' could not be cast to VerilatedVpioVar*. Ensure signal is marked "
"as "
"forceable"}));
#endif
vpi_put_value(signalHandle, value_sp.get(), nullptr, vpiReleaseFlag);
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
#ifdef XRUN
// Special case: real1Continuously is continuously assigned, but in xrun the value for the
// s_vpi_value output parameter upon releasing using vpi_put_value is *not* the releaseValue as
// expected, but the forceValue. This ifdef ensures the test still passes on xrun.
const std::unique_ptr<s_vpi_value> expectedValueSp = vpiValueWithFormat(
signalFormat, testSignalName == "real1Continuously" ? signalValueTypes{.real = 123456.789}
: expectedReleaseValue);
#else
const std::unique_ptr<s_vpi_value> expectedValueSp
= vpiValueWithFormat(signalFormat, expectedReleaseValue);
#endif
CHECK_RESULT_NZ(expectedValueSp); // NOLINT(concurrency-mt-unsafe)
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, signalHandle), *value_sp, *expectedValueSp));
return 0;
}
} // namespace
extern "C" int checkValuesForced(void) {
// Clocked signals
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
checkValue(scopeName, signal.signalName, signal.valueType, signal.forceValue));
return 0;
}));
// Continuously assigned signals
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
const std::string continouslyAssignedSignal
= std::string{signal.signalName} + "Continuously";
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
checkValue(scopeName, continouslyAssignedSignal, signal.valueType,
signal.forceValue));
return 0;
}));
return 0;
}
extern "C" int checkValuesPartiallyForced(void) {
// Clocked signals
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
if (signal.partialForceValue.second)
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
checkValue(scopeName, signal.signalName, signal.valueType,
signal.partialForceValue.first));
return 0;
}));
// Continuously assigned signals
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
if (signal.partialForceValue.second)
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
checkValue(scopeName, std::string{signal.signalName} + "Continuously",
signal.valueType, signal.partialForceValue.first));
return 0;
}));
return 0;
}
extern "C" int checkValuesReleased(void) {
// Clocked signals
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
checkValue(scopeName, signal.signalName, signal.valueType, signal.releaseValue));
return 0;
}));
// Continuously assigned signals
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
checkValue(scopeName, std::string{signal.signalName} + "Continuously",
signal.valueType, signal.releaseValue));
return 0;
}));
return 0;
}
#ifdef VERILATOR
// This function only makes sense with Verilator, because its purpose is testing error messages
// emitted from verilated_vpi.
extern "C" int tryInvalidPutOperations() {
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
"str1", {.format = vpiStringVal, .value = {.str = const_cast<PLI_BYTE8*>("foo")}},
vpiForceFlag,
"vpi_put_value was used with vpiForceFlag on non-forceable signal 't.test.str1' : "
"'Test'"));
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
"octString", {.format = vpiOctStrVal, .value = {.str = const_cast<PLI_BYTE8*>("123A")}},
vpiForceFlag,
"vpi_put_value: Non octal character 'A' in '123A' as value "
"vpiOctStrVal for t.test.octString__VforceVal"));
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
"decStringC", {.format = vpiDecStrVal, .value = {.str = const_cast<PLI_BYTE8*>("A123")}},
vpiForceFlag,
"vpi_put_value: Parsing failed for 'A123' as value vpiDecStrVal for "
"t.test.decStringC__VforceVal"));
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
"decStringC", {.format = vpiDecStrVal, .value = {.str = const_cast<PLI_BYTE8*>("123A")}},
vpiForceFlag,
"vpi_put_value: Trailing garbage 'A' in '123A' as value vpiDecStrVal for "
"t.test.decStringC__VforceVal"));
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
"hexString", {.format = vpiHexStrVal, .value = {.str = const_cast<PLI_BYTE8*>("12AG")}},
vpiForceFlag,
"vpi_put_value: Non hex character 'G' in '12AG' as value vpiHexStrVal for "
"t.test.hexString__VforceVal"));
// vop was replaced with baseSignalVop in vpi_put_value, so these tests are required to hit the
// test coverage target and ensure the error messages still work.
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
"onebit", {.format = vpiRawFourStateVal, .value = {}}, vpiForceFlag,
"vl_check_format: Unsupported format (vpiRawFourStateVal) for t.test.onebit"));
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
"onebit", {.format = vpiSuppressVal, .value = {}}, vpiForceFlag,
"vpi_put_value: Unsupported format (vpiSuppressVal) as "
"requested for t.test.onebit__VforceVal"));
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
"onebit", {.format = vpiStringVal, .value = {}}, vpiInertialDelay,
"vpi_put_value: Unsupported p_vpi_value as requested for 't.test.onebit' with "
"vpiInertialDelay"));
return 0;
}
// This function is just needed for hitting the test coverage target for verilated_vpi.cpp and
// ensuring that vpi_put_value to a string without vpiForceFlag still works.
extern "C" int putString() {
const std::string stringName = std::string{scopeName} + ".str1";
TestVpiHandle const stringSignalHandle //NOLINT(misc-misplaced-const)
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(stringName.c_str()), nullptr);
CHECK_RESULT_NZ(stringSignalHandle); // NOLINT(concurrency-mt-unsafe)
s_vpi_value value_s{.format = vpiStringVal, .value = {.str = const_cast<PLI_BYTE8*>("foo")}};
vpi_put_value(stringSignalHandle, &value_s, nullptr, vpiNoDelay);
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
value_s.value.str
= const_cast<PLI_BYTE8*>("bar"); // Ensure that test fails if value_s is not updated
vpi_get_value(stringSignalHandle, &value_s);
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
s_vpi_value expectedValueS{.format = vpiStringVal,
.value = {.str = const_cast<PLI_BYTE8*>("foo")}};
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, stringSignalHandle), value_s, expectedValueS));
return 0;
}
// This function is just needed for hitting the test coverage target for verilated_vpi.cpp and
// ensuring that vpiInertialDelay still works after renaming vop to baseSignalVop.
extern "C" int putInertialDelay() {
const std::string fullSignalName = std::string{scopeName} + ".delayed";
TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const)
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(fullSignalName.c_str()), nullptr);
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
constexpr int delayedValue = 123;
s_vpi_value value_s{.format = vpiIntVal, .value = {.integer = delayedValue}};
s_vpi_time time{.type = vpiSimTime, .high = 0, .low = 0, .real = {}};
vpi_put_value(signalHandle, &value_s, &time, vpiInertialDelay);
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
// Check that the put had no immediate effect
vpi_get_value(signalHandle, &value_s);
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
s_vpi_value expectedValueS{.format = vpiIntVal, .value = {.integer = 0}};
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, signalHandle), value_s, expectedValueS));
// Check that the put is executed upon doInertialPuts
VerilatedVpi::doInertialPuts();
value_s.value.integer = 0; // Ensure that test fails if value_s is not updated
vpi_get_value(signalHandle, &value_s);
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
expectedValueS.value.integer = delayedValue;
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, signalHandle), value_s, expectedValueS));
return 0;
}
#endif
extern "C" int forceValues(void) {
if (!TestSimulator::is_verilator()) {
#ifdef VERILATOR
printf("TestSimulator indicating not verilator, but VERILATOR macro is defined\n");
return 1;
#endif
}
// Clocked signals
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
forceSignal(scopeName, signal.signalName, signal.valueType, signal.forceValue));
return 0;
}));
// Continuously assigned signals
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
forceSignal(scopeName, std::string{signal.signalName} + "Continuously",
signal.valueType, signal.forceValue));
return 0;
}));
return 0;
}
extern "C" int releaseValues(void) {
// Clocked signals
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
releaseSignal(scopeName, signal.signalName, signal.valueType,
{signal.releaseValue, signal.forceValue}));
return 0;
}));
// Continuously assigned signals
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
releaseSignal(scopeName, std::string{signal.signalName} + "Continuously",
signal.valueType, {signal.forceValue, signal.releaseValue}));
return 0;
}));
return 0;
}
extern "C" int releasePartiallyForcedValues(void) {
// Skip any values that cannot be partially forced. Can't just reuse releaseValues, because the
// output argument s_vpi_value of vpi_put_value with vpiReleaseFlag differs depending on
// whether or not a signal was forced before, and not all signals are forced in the partial
// forcing test.
// Clocked signals
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
if (signal.partialForceValue.second)
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
releaseSignal(scopeName, signal.signalName, signal.valueType,
{signal.releaseValue, signal.partialForceValue.first}));
return 0;
}));
// Continuously assigned signals
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
if (signal.partialForceValue.second)
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
releaseSignal(scopeName, std::string{signal.signalName} + "Continuously",
signal.valueType,
{signal.partialForceValue.first, signal.releaseValue}));
return 0;
}));
return 0;
}
#ifdef IS_VPI
static int checkValuesForcedVpi() {
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
s_vpi_value vpi_value;
vpi_value.format = vpiIntVal;
vpi_value.value.integer = checkValuesForced();
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
return 0;
}
static int checkValuesPartiallyForcedVpi() {
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
s_vpi_value vpi_value;
vpi_value.format = vpiIntVal;
vpi_value.value.integer = checkValuesPartiallyForced();
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
return 0;
}
static int checkValuesReleasedVpi() {
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
s_vpi_value vpi_value;
vpi_value.format = vpiIntVal;
vpi_value.value.integer = checkValuesReleased();
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
return 0;
}
static int forceValuesVpi() {
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
s_vpi_value vpi_value;
vpi_value.format = vpiIntVal;
vpi_value.value.integer = forceValues();
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
return 0;
}
static int releaseValuesVpi() {
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
s_vpi_value vpiValue;
vpiValue.format = vpiIntVal;
vpiValue.value.integer = releaseValues();
vpi_put_value(href, &vpiValue, NULL, vpiNoDelay);
return 0;
}
static int releasePartiallyForcedValuesVpi() {
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
s_vpi_value vpiValue;
vpiValue.format = vpiIntVal;
vpiValue.value.integer = releasePartiallyForcedValues();
vpi_put_value(href, &vpiValue, NULL, vpiNoDelay);
return 0;
}
std::array<s_vpi_systf_data, 6> vpi_systf_data
= {s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$forceValues",
(PLI_INT32(*)(PLI_BYTE8*))forceValuesVpi, 0, 0, 0},
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$releaseValues",
(PLI_INT32(*)(PLI_BYTE8*))releaseValuesVpi, 0, 0, 0},
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$releasePartiallyForcedValues",
(PLI_INT32(*)(PLI_BYTE8*))releasePartiallyForcedValuesVpi, 0, 0, 0},
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkValuesForced",
(PLI_INT32(*)(PLI_BYTE8*))checkValuesForcedVpi, 0, 0, 0},
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkValuesPartiallyForced",
(PLI_INT32(*)(PLI_BYTE8*))checkValuesPartiallyForcedVpi, 0, 0, 0},
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkValuesReleased",
(PLI_INT32(*)(PLI_BYTE8*))checkValuesReleasedVpi, 0, 0, 0}};
// cver entry
extern "C" void vpi_compat_bootstrap(void) {
for (s_vpi_systf_data& systf : vpi_systf_data) vpi_register_systf(&systf);
}
// icarus entry
void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0};
#endif

22
test_regress/t/t_vpi_force.py Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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
import vltest_bootstrap
test.scenarios('simulator')
test.compile(make_top_shell=False,
make_main=False,
make_pli=True,
verilator_flags2=["--binary --vpi", test.pli_filename],
v_flags2=["+define+USE_VPI_NOT_DPI +define+VERILATOR_COMMENTS"])
test.execute(xrun_flags2=["+define+USE_VPI_NOT_DPI"], use_libvpi=True, check_finished=True)
test.passes()

View File

@ -0,0 +1,704 @@
// ======================================================================
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty.
// SPDX-License-Identifier: CC0-1.0
// ======================================================================
`define STRINGIFY(x) `"x`"
`ifdef VERILATOR_COMMENTS
`define PUBLIC_FORCEABLE /*verilator public_flat_rw*/ /*verilator forceable*/
`else
`define PUBLIC_FORCEABLE
`endif
module t;
reg clk;
initial begin
clk = 0;
forever #1 clk = ~clk;
end
Test test (.clk(clk));
endmodule
module Test (
input clk
);
`ifdef IVERILOG
`elsif USE_VPI_NOT_DPI
`ifdef VERILATOR
`systemc_header
extern "C" int putString();
extern "C" int tryInvalidPutOperations();
extern "C" int putInertialDelay();
extern "C" int forceValues();
extern "C" int releaseValues();
extern "C" int releasePartiallyForcedValues();
extern "C" int checkValuesForced();
extern "C" int checkValuesPartiallyForced();
extern "C" int checkValuesReleased();
`verilog
`endif
`else
`ifdef VERILATOR
import "DPI-C" context function int putString();
import "DPI-C" context function int tryInvalidPutOperations();
import "DPI-C" context function int putInertialDelay();
`endif
import "DPI-C" context function int forceValues();
import "DPI-C" context function int releaseValues();
import "DPI-C" context function int releasePartiallyForcedValues();
import "DPI-C" context function int checkValuesPartiallyForced();
import "DPI-C" context function int checkValuesForced();
import "DPI-C" context function int checkValuesReleased();
`endif
// Verify that vpi_put_value still works for strings
string str1 /*verilator public_flat_rw*/; // std::string
// Verify that vpi_put_value still works with vpiInertialDelay
logic [ 31:0] delayed `PUBLIC_FORCEABLE; // IData
// Clocked signals
// Force with vpiIntVal
logic onebit `PUBLIC_FORCEABLE; // CData
logic [ 31:0] intval `PUBLIC_FORCEABLE; // IData
// Force with vpiVectorVal
logic [ 7:0] vectorC `PUBLIC_FORCEABLE; // CData
logic [ 61:0] vectorQ `PUBLIC_FORCEABLE; // QData
logic [127:0] vectorW `PUBLIC_FORCEABLE; // VlWide
// Force with vpiRealVal
real real1 `PUBLIC_FORCEABLE; // double
// Force with vpiStringVal
logic [ 15:0] textHalf `PUBLIC_FORCEABLE; // SData
logic [ 63:0] textLong `PUBLIC_FORCEABLE; // QData
logic [511:0] text `PUBLIC_FORCEABLE; // VlWide
// Force with vpiBinStrVal, vpiOctStrVal, vpiHexStrVal
logic [ 7:0] binString `PUBLIC_FORCEABLE; // CData
logic [ 14:0] octString `PUBLIC_FORCEABLE; // SData
logic [ 63:0] hexString `PUBLIC_FORCEABLE; // QData
// Force with vpiDecStrVal
logic [ 7:0] decStringC `PUBLIC_FORCEABLE; // CData
logic [ 15:0] decStringS `PUBLIC_FORCEABLE; // SData
logic [ 31:0] decStringI `PUBLIC_FORCEABLE; // IData
logic [ 63:0] decStringQ `PUBLIC_FORCEABLE; // QData
// Continuously assigned signals:
// Force with vpiIntVal
wire onebitContinuously `PUBLIC_FORCEABLE; // CData
wire [ 31:0] intvalContinuously `PUBLIC_FORCEABLE; // IData
// Force with vpiVectorVal
wire [ 7:0] vectorCContinuously `PUBLIC_FORCEABLE; // CData
wire [ 61:0] vectorQContinuously `PUBLIC_FORCEABLE; // QData
wire [127:0] vectorWContinuously `PUBLIC_FORCEABLE; // VlWide
// Force with vpiRealVal
`ifdef IVERILOG
// Need wreal with Icarus for forcing continuously assigned real
wreal real1Continuously `PUBLIC_FORCEABLE; // double
`else
real real1Continuously `PUBLIC_FORCEABLE; // double
`endif
// Force with vpiStringVal
wire [ 15:0] textHalfContinuously `PUBLIC_FORCEABLE; // SData
wire [ 63:0] textLongContinuously `PUBLIC_FORCEABLE; // QData
wire [511:0] textContinuously `PUBLIC_FORCEABLE; // VlWide
// Force with vpiBinStrVal, vpiOctStrVal, vpiHexStrVal
wire [ 7:0] binStringContinuously `PUBLIC_FORCEABLE; // CData
wire [ 14:0] octStringContinuously `PUBLIC_FORCEABLE; // SData
wire [ 63:0] hexStringContinuously `PUBLIC_FORCEABLE; // QData
// Force with vpiDecStrVal
wire [ 7:0] decStringCContinuously `PUBLIC_FORCEABLE; // CData
wire [ 15:0] decStringSContinuously `PUBLIC_FORCEABLE; // SData
wire [ 31:0] decStringIContinuously `PUBLIC_FORCEABLE; // IData
wire [ 63:0] decStringQContinuously `PUBLIC_FORCEABLE; // QData
always @(posedge clk) begin
onebit <= 1;
intval <= 32'hAAAAAAAA;
vectorC <= 8'hAA;
vectorQ <= 62'h2AAAAAAA_AAAAAAAA;
vectorW <= 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA;
real1 <= 1.0;
textHalf <= "Hf";
textLong <= "Long64b";
text <= "Verilog Test module";
binString <= 8'b10101010;
octString <= 15'o25252; // 0b1010...
hexString <= 64'hAAAAAAAAAAAAAAAA; // 0b1010...
decStringC <= 8'hAA;
decStringS <= 16'hAAAA;
decStringI <= 32'hAAAAAAAA;
decStringQ <= 64'd12297829382473034410; // 0b1010...
end
assign onebitContinuously = 1;
assign intvalContinuously = 32'hAAAAAAAA;
assign vectorCContinuously = 8'hAA;
assign vectorQContinuously = 62'h2AAAAAAA_AAAAAAAA;
assign vectorWContinuously = 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA;
assign real1Continuously = 1.0;
assign textHalfContinuously = "Hf";
assign textLongContinuously = "Long64b";
assign textContinuously = "Verilog Test module";
assign binStringContinuously = 8'b10101010;
assign octStringContinuously = 15'o25252; // 0b1010...
assign hexStringContinuously = 64'hAAAAAAAAAAAAAAAA; // 0b1010...
assign decStringCContinuously = 8'hAA;
assign decStringSContinuously = 16'hAAAA;
assign decStringIContinuously = 32'hAAAAAAAA;
assign decStringQContinuously = 64'd12297829382473034410; // 0b1010...
task automatic svForceValues();
force onebit = 0;
force intval = 32'h55555555;
force vectorC = 8'h55;
force vectorQ = 62'h15555555_55555555;
force vectorW = 128'h55555555_55555555_55555555_55555555;
force real1 = 123456.789;
force textHalf = "T2";
force textLong = "44Four44";
force text = "lorem ipsum";
force binString = 8'b01010101;
force octString = 15'o52525;
force hexString = 64'h5555555555555555;
force decStringC = 8'h55;
force decStringS = 16'h5555;
force decStringI = 32'h55555555;
force decStringQ = 64'd6148914691236517205;
force onebitContinuously = 0;
force intvalContinuously = 32'h55555555;
force vectorCContinuously = 8'h55;
force vectorQContinuously = 62'h15555555_55555555;
force vectorWContinuously = 128'h55555555_55555555_55555555_55555555;
force real1Continuously = 123456.789;
force textHalfContinuously = "T2";
force textLongContinuously = "44Four44";
force textContinuously = "lorem ipsum";
force binStringContinuously = 8'b01010101;
force octStringContinuously = 15'o52525;
force hexStringContinuously = 64'h5555555555555555;
force decStringCContinuously = 8'h55;
force decStringSContinuously = 16'h5555;
force decStringIContinuously = 32'h55555555;
force decStringQContinuously = 64'd6148914691236517205;
endtask
task automatic svPartiallyForceValues();
force intval[15:0] = 16'h5555;
force vectorC[3:0] = 4'h5;
force vectorQ[30:0] = 31'h55555555;
force vectorW[63:0] = 64'h55555555_55555555;
force textHalf[7:0] = "2";
force textLong[31:0] = "ur44";
force text[63:0] = "em ipsum";
force binString[3:0] = 4'b0101;
force octString[6:0] = 7'o125;
force hexString[31:0] = 32'h55555555;
force decStringC[3:0] = 4'h5;
force decStringS[7:0] = 8'h55;
force decStringI[15:0] = 16'h5555;
force decStringQ[31:0] = 32'd1431655765;
force intvalContinuously[15:0] = 16'h5555;
force vectorCContinuously[3:0] = 4'h5;
force vectorQContinuously[30:0] = 31'h55555555;
force vectorWContinuously[63:0] = 64'h55555555_55555555;
force textHalfContinuously[7:0] = "2";
force textLongContinuously[31:0] = "ur44";
force textContinuously[63:0] = "em ipsum";
force binStringContinuously[3:0] = 4'b0101;
force octStringContinuously[6:0] = 7'o125;
force hexStringContinuously[31:0] = 32'h55555555;
force decStringCContinuously[3:0] = 4'h5;
force decStringSContinuously[7:0] = 8'h55;
force decStringIContinuously[15:0] = 16'h5555;
force decStringQContinuously[31:0] = 32'd1431655765;
endtask
task automatic vpiPutString();
integer vpiStatus = 1; // Default to failed status to ensure that a function *not* getting
// called also causes simulation termination
`ifdef VERILATOR
`ifdef USE_VPI_NOT_DPI
vpiStatus = $c32("putString()");
`else
vpiStatus = putString();
`endif
`else
$stop; // This task only makes sense with Verilator, since other simulators ignore the "verilator forceable" metacomment.
`endif
if (vpiStatus != 0) begin
$write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus);
$display(
"C Test failed (vpi_put_value failed for string)");
$stop;
end
endtask
task automatic vpiTryInvalidPutOperations();
integer vpiStatus = 1; // Default to failed status to ensure that a function *not* getting
// called also causes simulation termination
`ifdef VERILATOR
`ifdef USE_VPI_NOT_DPI
vpiStatus = $c32("tryInvalidPutOperations()");
`else
vpiStatus = tryInvalidPutOperations();
`endif
`else
$stop; // This task only makes sense with Verilator, since it tests verilated_vpi.cpp
`endif
if (vpiStatus != 0) begin
$write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus);
$display(
"C Test failed (invalid vpi_put_value operation either succeeded, even though it should have failed, or produced an unexpected error message.)");
$stop;
end
endtask
task automatic vpiPutInertialDelay();
integer vpiStatus = 1; // Default to failed status to ensure that a function *not* getting
// called also causes simulation termination
`ifdef VERILATOR
`ifdef USE_VPI_NOT_DPI
vpiStatus = $c32("putInertialDelay()");
`else
vpiStatus = putInertialDelay();
`endif
`else
$stop; // This task only makes sense with Verilator, since it tests verilated_vpi.cpp
`endif
if (vpiStatus != 0) begin
$write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus);
$display(
"C Test failed (vpi_put_value with vpiInertialDelay failed)");
$stop;
end
endtask
task automatic vpiForceValues();
integer vpiStatus = 1;
`ifdef VERILATOR
`ifdef USE_VPI_NOT_DPI
vpiStatus = $c32("forceValues()");
`else
vpiStatus = forceValues();
`endif
`elsif IVERILOG
vpiStatus = $forceValues;
`elsif USE_VPI_NOT_DPI
vpiStatus = $forceValues;
`else
vpiStatus = forceValues();
`endif
if (vpiStatus != 0) begin
$write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus);
$display("C Test failed (could not force value)");
$stop;
end
endtask
task automatic svReleaseValues();
release onebit;
release intval;
release vectorC;
release vectorQ;
release vectorW;
release real1;
release textHalf;
release textLong;
release text;
release binString;
release octString;
release hexString;
release decStringC;
release decStringS;
release decStringI;
release decStringQ;
release onebitContinuously;
release intvalContinuously;
release vectorCContinuously;
release vectorQContinuously;
release vectorWContinuously;
release real1Continuously;
release textHalfContinuously;
release textLongContinuously;
release textContinuously;
release binStringContinuously;
release octStringContinuously;
release hexStringContinuously;
release decStringCContinuously;
release decStringSContinuously;
release decStringIContinuously;
release decStringQContinuously;
endtask
task automatic vpiReleaseValues();
integer vpiStatus = 1;
`ifdef VERILATOR
`ifdef USE_VPI_NOT_DPI
vpiStatus = $c32("releaseValues()");
`else
vpiStatus = releaseValues();
`endif
`elsif IVERILOG
vpiStatus = $releaseValues;
`elsif USE_VPI_NOT_DPI
vpiStatus = $releaseValues;
`else
vpiStatus = releaseValues();
`endif
if (vpiStatus != 0) begin
$write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus);
$display("C Test failed (could not release value)");
$stop;
end
endtask
task automatic vpiReleasePartiallyForcedValues();
integer vpiStatus = 1;
`ifdef VERILATOR
`ifdef USE_VPI_NOT_DPI
vpiStatus = $c32("releasePartiallyForcedValues()");
`else
vpiStatus = releasePartiallyForcedValues();
`endif
`elsif IVERILOG
vpiStatus = $releasePartiallyForcedValues;
`elsif USE_VPI_NOT_DPI
vpiStatus = $releasePartiallyForcedValues;
`else
vpiStatus = releasePartiallyForcedValues();
`endif
if (vpiStatus != 0) begin
$write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus);
$display("C Test failed (could not release value)");
$stop;
end
endtask
task automatic svCheckValuesForced();
if(onebit != 0) $stop;
if(intval != 32'h55555555) $stop;
if(vectorC != 8'h55) $stop;
if(vectorQ != 62'h15555555_55555555) $stop;
if(vectorW != 128'h55555555_55555555_55555555_55555555) $stop;
if(real1 != 123456.789) $stop;
if(textHalf != "T2") $stop;
if(textLong != "44Four44") $stop;
if(text != "lorem ipsum") $stop;
if(binString != 8'b01010101) $stop;
if(octString != 15'o52525) $stop;
if(hexString != 64'h5555555555555555) $stop;
if(decStringC != 8'h55) $stop;
if(decStringS != 16'h5555) $stop;
if(decStringI != 32'h55555555) $stop;
if(decStringQ != 64'd6148914691236517205) $stop;
if(onebitContinuously != 0) $stop;
if(intvalContinuously != 32'h55555555) $stop;
if(vectorCContinuously != 8'h55) $stop;
if(vectorQContinuously != 62'h15555555_55555555) $stop;
if(vectorWContinuously != 128'h55555555_55555555_55555555_55555555) $stop;
if(real1Continuously != 123456.789) $stop;
if(textHalfContinuously != "T2") $stop;
if(textLongContinuously != "44Four44") $stop;
if(textContinuously != "lorem ipsum") $stop;
if(binStringContinuously != 8'b01010101) $stop;
if(octStringContinuously != 15'o52525) $stop;
if(hexStringContinuously != 64'h5555555555555555) $stop;
if(decStringCContinuously != 8'h55) $stop;
if(decStringSContinuously != 16'h5555) $stop;
if(decStringIContinuously != 32'h55555555) $stop;
if(decStringQContinuously != 64'd6148914691236517205) $stop;
endtask
task automatic vpiCheckValuesForced();
integer vpiStatus = 1;
`ifdef VERILATOR
`ifdef USE_VPI_NOT_DPI
vpiStatus = $c32("checkValuesForced()");
`else
vpiStatus = checkValuesForced();
`endif
`elsif IVERILOG
vpiStatus = $checkValuesForced;
`elsif USE_VPI_NOT_DPI
vpiStatus = $checkValuesForced;
`else
vpiStatus = checkValuesForced();
`endif
if (vpiStatus != 0) begin
$write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus);
$display("C Test failed (value after forcing does not match expectation)");
$stop;
end
endtask
task automatic svCheckValuesPartiallyForced();
if (intval != 32'hAAAA_5555) $stop;
if (vectorC != 8'h A5) $stop;
if (vectorQ != 62'h2AAAAAAAD5555555) $stop;
if (vectorW != 128'hAAAAAAAA_AAAAAAAA_55555555_55555555) $stop;
if (textHalf != "H2") $stop;
if (textLong != "Lonur44") $stop;
if (text != "Verilog Tesem ipsum") $stop;
if (binString != 8'b1010_0101) $stop;
if (octString != 15'b01010101_1010101) $stop;
if (hexString != 64'hAAAAAAAA_55555555) $stop;
if (decStringC != 8'hA5) $stop;
if (decStringS != 16'hAA55) $stop;
if (decStringI != 32'hAAAA_5555) $stop;
if (decStringQ != 64'hAAAAAAAA_55555555) $stop;
if (intvalContinuously != 32'hAAAA_5555) $stop;
if (vectorCContinuously != 8'h A5) $stop;
if (vectorQContinuously != 62'h2AAAAAAAD5555555) $stop;
if (vectorWContinuously != 128'hAAAAAAAA_AAAAAAAA_55555555_55555555) $stop;
if (textHalfContinuously != "H2") $stop;
if (textLongContinuously != "Lonur44") $stop;
if (textContinuously != "Verilog Tesem ipsum") $stop;
if (binStringContinuously != 8'b1010_0101) $stop;
if (octStringContinuously != 15'b01010101_1010101) $stop;
if (hexStringContinuously != 64'hAAAAAAAA_55555555) $stop;
if (decStringCContinuously != 8'hA5) $stop;
if (decStringSContinuously != 16'hAA55) $stop;
if (decStringIContinuously != 32'hAAAA_5555) $stop;
if (decStringQContinuously != 64'hAAAAAAAA_55555555) $stop;
endtask
task automatic vpiCheckValuesPartiallyForced();
integer vpiStatus = 1;
`ifdef VERILATOR
`ifdef USE_VPI_NOT_DPI
vpiStatus = $c32("checkValuesPartiallyForced()");
`else
vpiStatus = checkValuesPartiallyForced();
`endif
`elsif IVERILOG
vpiStatus = $checkValuesPartiallyForced;
`elsif USE_VPI_NOT_DPI
vpiStatus = $checkValuesPartiallyForced;
`else
vpiStatus = checkValuesPartiallyForced();
`endif
if (vpiStatus != 0) begin
$write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus);
$display("C Test failed (value after partial forcing does not match expectation)");
$stop;
end
endtask
task automatic svCheckValuesReleased();
if (onebit != 1) $stop;
if (intval != 32'hAAAAAAAA) $stop;
if (vectorC != 8'hAA) $stop;
if (vectorQ != 62'h2AAAAAAA_AAAAAAAA) $stop;
if (vectorW != 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA) $stop;
if (real1 != 1.0) $stop;
if (textHalf != "Hf") $stop;
if (textLong != "Long64b") $stop;
if (text != "Verilog Test module") $stop;
if (binString != 8'b10101010) $stop;
if (octString != 15'o25252) $stop;
if (hexString != 64'hAAAAAAAAAAAAAAAA) $stop;
if (decStringC != 8'hAA) $stop;
if (decStringS != 16'hAAAA) $stop;
if (decStringI != 32'hAAAAAAAA) $stop;
if (decStringQ != 64'd12297829382473034410) $stop;
if (onebitContinuously != 1) $stop;
if (intvalContinuously != 32'hAAAAAAAA) $stop;
if (vectorCContinuously != 8'hAA) $stop;
if (vectorQContinuously != 62'h2AAAAAAA_AAAAAAAA) $stop;
if (vectorWContinuously != 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA) $stop;
if (real1Continuously != 1.0) $stop;
if (textHalfContinuously != "Hf") $stop;
if (textLongContinuously != "Long64b") $stop;
if (textContinuously != "Verilog Test module") $stop;
if (binStringContinuously != 8'b10101010) $stop;
if (octStringContinuously != 15'o25252) $stop;
if (hexStringContinuously != 64'hAAAAAAAAAAAAAAAA) $stop;
if (decStringCContinuously != 8'hAA) $stop;
if (decStringSContinuously != 16'hAAAA) $stop;
if (decStringIContinuously != 32'hAAAAAAAA) $stop;
if (decStringQContinuously != 64'd12297829382473034410) $stop;
endtask
task automatic vpiCheckValuesReleased();
integer vpiStatus = 1;
`ifdef VERILATOR
`ifdef USE_VPI_NOT_DPI
vpiStatus = $c32("checkValuesReleased()");
`else
vpiStatus = checkValuesReleased();
`endif
`elsif IVERILOG
vpiStatus = $checkValuesReleased;
`elsif USE_VPI_NOT_DPI
vpiStatus = $checkValuesReleased;
`else
vpiStatus = checkValuesReleased();
`endif
if (vpiStatus != 0) begin
$write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus);
$display("C Test failed (value after releasing does not match expectation)");
$stop;
end
endtask
initial begin
`ifdef WAVES
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
$dumpvars();
`endif
`ifdef VERILATOR
vpiPutString();
vpiTryInvalidPutOperations();
vpiPutInertialDelay();
`endif
// Wait a bit before triggering the force to see a change in the traces
#4 vpiForceValues();
// Time delay to ensure setting and checking values does not happen
// at the same time, so that the signals can have their values overwritten
// by other processes
#4 vpiCheckValuesForced();
svCheckValuesForced();
#4 vpiReleaseValues();
#4 vpiCheckValuesReleased();
svCheckValuesReleased();
// Force through VPI, release through Verilog
#4 vpiForceValues();
#4 vpiCheckValuesForced();
svCheckValuesForced();
#4 svReleaseValues();
#4 vpiCheckValuesReleased();
svCheckValuesReleased();
// Force through Verilog, release through VPI
#4 svForceValues();
#4 vpiCheckValuesForced();
svCheckValuesForced();
#4 vpiReleaseValues();
#4 vpiCheckValuesReleased();
svCheckValuesReleased();
// Force only some bits, check if __VforceRd yields correct signal,
// release through VPI
#4 svPartiallyForceValues();
#4 vpiCheckValuesPartiallyForced();
svCheckValuesPartiallyForced();
#4 vpiReleasePartiallyForcedValues();
#4 vpiCheckValuesReleased();
svCheckValuesReleased();
// Force only some bits, check if __VforceRd yields correct signal,
// release through Verilog
#4 svPartiallyForceValues();
#4 vpiCheckValuesPartiallyForced();
svCheckValuesPartiallyForced();
#4 svReleaseValues();
#4 vpiCheckValuesReleased();
svCheckValuesReleased();
#5 $display("*-* All Finished *-*");
$finish;
end
`ifdef TEST_VERBOSE
always @(posedge clk or negedge clk) begin
$display("time: %0t\tclk:%b", $time, clk);
$display("str1: %s", str1);
$display("delayed: %x", delayed);
$display("onebit: %x", onebit);
$display("intval: %x", intval);
$display("vectorC: %x", vectorC);
$display("vectorQ: %x", vectorQ);
$display("vectorW: %x", vectorW);
$display("real1: %f", real1);
$display("textHalf: %s", textHalf);
$display("textLong: %s", textLong);
$display("text: %s", text);
$display("binString: %x", binString);
$display("octString: %x", octString);
$display("hexString: %x", hexString);
$display("decStringC: %x", decStringC);
$display("decStringS: %x", decStringS);
$display("decStringI: %x", decStringI);
$display("decStringQ: %x", decStringQ);
$display("onebitContinuously: %x", onebitContinuously);
$display("intvalContinuously: %x", intvalContinuously);
$display("vectorCContinuously: %x", vectorCContinuously);
$display("vectorQContinuously: %x", vectorQContinuously);
$display("vectorWContinuously: %x", vectorWContinuously);
$display("real1Continuously: %f", real1Continuously);
$display("textHalfContinuously: %s", textHalfContinuously);
$display("textLongContinuously: %s", textLongContinuously);
$display("textContinuously: %s", textContinuously);
$display("binStringContinuously: %x", binStringContinuously);
$display("octStringContinuously: %x", octStringContinuously);
$display("hexStringContinuously: %x", hexStringContinuously);
$display("decStringCContinuously: %x", decStringCContinuously);
$display("decStringSContinuously: %x", decStringSContinuously);
$display("decStringIContinuously: %x", decStringIContinuously);
$display("decStringQContinuously: %x", decStringQContinuously);
$display("========================\n");
end
`endif
endmodule

View File

@ -0,0 +1,65 @@
// ======================================================================
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty.
// SPDX-License-Identifier: CC0-1.0
// ======================================================================
// DESCRIPTION: Test failure of trying to force a non-forceable signal
//
// This test checks that attempting to force a signal that is not marked as
// forceable causes an error under Verilator, and does not cause an error in
// other simulators that do not need this metacomment to be able to force
// signals.
#include "verilated.h"
#include "TestSimulator.h" // For is_verilator()
#include "TestVpi.h" // For CHECK_RESULT_NZ
#include "vpi_user.h"
extern "C" int forceValue(void) {
if (!TestSimulator::is_verilator()) {
#ifdef VERILATOR
printf("TestSimulator indicating not verilator, but VERILATOR macro is defined\n");
return 1;
#endif
}
PLI_BYTE8 testSignalName[] = "t.nonForceableSignal";
vpiHandle signal = vpi_handle_by_name(testSignalName, nullptr);
CHECK_RESULT_NZ(signal); // NOLINT(concurrency-mt-unsafe)
s_vpi_value value_s;
value_s.format = vpiIntVal;
value_s.value.integer = 0;
vpi_put_value(signal, &value_s, nullptr, vpiForceFlag);
// NOLINTNEXTLINE(concurrency-mt-unsafe);
CHECK_RESULT_Z(vpi_chk_error(nullptr))
return 0;
}
#ifdef IS_VPI
static int force_value_vpi() {
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
s_vpi_value vpi_value;
vpi_value.format = vpiIntVal;
vpi_value.value.integer = forceValue();
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
return 0;
}
std::array<s_vpi_systf_data, 1> vpi_systf_data
= {s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$forceValue",
(PLI_INT32(*)(PLI_BYTE8*))force_value_vpi, 0, 0, 0}};
// cver entry
extern "C" void vpi_compat_bootstrap(void) {
for (s_vpi_systf_data& systf : vpi_systf_data) vpi_register_systf(&systf);
}
// icarus entry
void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0};
#endif

View File

@ -0,0 +1,2 @@
%Error: .../verilated_vpi.cpp:2931: vpi_put_value was used with vpiForceFlag on non-forceable signal 't.nonForceableSignal' : 't'
Aborting...

View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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
import vltest_bootstrap
test.scenarios('simulator')
test.compile(make_top_shell=False,
make_main=False,
make_pli=True,
verilator_flags2=["--binary --vpi", test.pli_filename])
test.execute(use_libvpi=True,
fails=test.vlt_all,
expect_filename=test.golden_filename,
check_finished=test.iv) # or check_finished=test.xrun
test.passes()

View File

@ -0,0 +1,51 @@
// ======================================================================
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty.
// SPDX-License-Identifier: CC0-1.0
// ======================================================================
module t;
`ifdef IVERILOG
`elsif USE_VPI_NOT_DPI
`ifdef VERILATOR
`systemc_header
extern "C" int forceValue();
`verilog
`endif
`else
import "DPI-C" context function int forceValue();
`endif
wire nonForceableSignal /*verilator public_flat_rw*/ = 1'b0;
integer vpiStatus = 1;
initial begin
`ifdef VERILATOR
`ifdef USE_VPI_NOT_DPI
vpiStatus = $c32("forceValue()");
`else
vpiStatus = forceValue();
`endif
`elsif IVERILOG
vpiStatus = $forceValue;
`elsif USE_VPI_NOT_DPI
vpiStatus = $forceValue;
`else
vpiStatus = forceValue();
`endif
if (vpiStatus != 0) begin
$write("%%Error: t_vpi_forceable_bad.cpp:%0d:", vpiStatus);
$display("C Test failed (could not force value)");
$stop;
end
vpiStatus = 1; // Reset status to ensure that a function *not* getting
// called also causes failure
$display("*-* All Finished *-*");
$finish;
end
endmodule