Merge f39e56111e into 1b93033690
This commit is contained in:
commit
562e3a43a4
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
%Error: .../verilated_vpi.cpp:2931: vpi_put_value was used with vpiForceFlag on non-forceable signal 't.nonForceableSignal' : 't'
|
||||
Aborting...
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue