diff --git a/include/verilated.cpp b/include/verilated.cpp index 16f8a6cee..eb31412b1 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -3785,20 +3785,49 @@ VerilatedVar* VerilatedScope::varInsert(const char* namep, void* datap, bool isP VerilatedVar* VerilatedScope::forceableVarInsert(const char* namep, void* datap, bool isParam, - VerilatedVarType vltype, int vlflags, + VerilatedVarType vltype, int vlflags, void* forceReadSignalData, + const char* const forceReadSignalName, std::pair forceControlSignals, int udims, int pdims...) VL_MT_UNSAFE { if (!m_varsp) m_varsp = new VerilatedVarNameMap; + // TODO: While the force read signal would be *expected* to have the same vltype and vlflags + // (except for forceable and public flags) as the base signal, this is not guaranteed. It would + // be a safer solution to adapt V3EmitCSyms to find the __VforceRd signal and give its vltype + // and vlflags to this function as arguments. + + // Use same flags as base signal, but remove forceable and public flags + const VerilatedVarFlags forceReadValueVlflags + = static_cast(vlflags & ~VLVF_FORCEABLE & ~VLVF_PUB_RW & ~VLVF_PUB_RD); + + VerilatedVar forceReadSignal{forceReadSignalName, + forceReadSignalData, + vltype, + forceReadValueVlflags, + udims, + pdims, + isParam}; + + va_list ap; + va_start(ap, pdims); + assert(udims == 0); // Forcing unpacked arrays is unsupported (#4735) and should have been + // checked in V3Force already. + for (int i = 0; i < pdims; ++i) { + const int msb = va_arg(ap, int); + const int lsb = va_arg(ap, int); + forceReadSignal.m_packed[i].m_left = msb; + forceReadSignal.m_packed[i].m_right = lsb; + } + va_end(ap); + std::unique_ptr verilatedForceControlSignalsp - = std::make_unique( - VerilatedForceControlSignals{forceControlSignals.first, forceControlSignals.second}); + = std::unique_ptr(new VerilatedForceControlSignals{ + forceControlSignals.first, forceControlSignals.second, std::move(forceReadSignal)}); VerilatedVar var(namep, datap, vltype, static_cast(vlflags), udims, pdims, isParam, std::move(verilatedForceControlSignalsp)); verilatedForceControlSignalsp = nullptr; - va_list ap; va_start(ap, pdims); assert(udims == 0); // Forcing unpacked arrays is unsupported (#4735) and should have been // checked in V3Force already. diff --git a/include/verilated.h b/include/verilated.h index b1c0bce15..ba37ff4bc 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -746,6 +746,7 @@ public: // But internals only - called from verilated modules, VerilatedSyms int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE; VerilatedVar* forceableVarInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype, int vlflags, + void* forceReadSignalData, const char* forceReadSignalName, std::pair forceControlSignals, int udims, int pdims...) VL_MT_UNSAFE; // ACCESSORS diff --git a/include/verilated_sym_props.h b/include/verilated_sym_props.h index da7199198..b31a4437a 100644 --- a/include/verilated_sym_props.h +++ b/include/verilated_sym_props.h @@ -247,18 +247,11 @@ public: } }; -//=========================================================================== -// Force control signals of a VerilatedVar - -struct VerilatedForceControlSignals final { - const VerilatedVar* forceEnableSignalp{nullptr}; // __VforceEn signal - const VerilatedVar* forceValueSignalp{nullptr}; // __VforceVal signal -}; - //=========================================================================== // Verilator variable // Thread safety: Assume is constructed only with model, then any number of readers +struct VerilatedForceControlSignals; class VerilatedVar final : public VerilatedVarProps { // MEMBERS void* const m_datap; // Location of data @@ -297,4 +290,13 @@ public: } }; +//=========================================================================== +// Force control signals of a VerilatedVar + +struct VerilatedForceControlSignals final { + const VerilatedVar* forceEnableSignalp{nullptr}; // __VforceEn signal + const VerilatedVar* forceValueSignalp{nullptr}; // __VforceVal signal + const VerilatedVar forceReadSignal; // __VforceRd signal +}; + #endif // Guard diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 412988cc0..56f291a38 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -1117,7 +1117,18 @@ public: } s().m_inertialPuts.clear(); } - static auto getForceControlSignals(const VerilatedVpioVar* vop); + using ForceControlSignalVops = struct { + std::unique_ptr forceEnable; + std::unique_ptr forceValue; + std::unique_ptr forceRead; + }; + static ForceControlSignalVops getForceControlSignals(const VerilatedVpioVar* vop); + + template + static T getReadDataWord(const VerilatedVpioVar* baseSignalVop, + const VerilatedVpioVar* forceEnableSignalVop, + const VerilatedVpioVar* forceValueSignalVop, size_t bitCount, + size_t bitOffset); static std::size_t vlTypeSize(VerilatedVarType vltype); static void setAllBitsToValue(const VerilatedVpioVar* vop, uint8_t bitValue) { @@ -1135,29 +1146,6 @@ public: 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 - static std::vector - createReadDataVector(const void* const baseSignalDatap, - const std::pair 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 readData(numWords); - for (std::size_t i{0}; i < numWords; ++i) { - const T forceEnableWord = reinterpret_cast(forceEnableDatap)[i]; - const T forceValueWord = reinterpret_cast(forceValueDatap)[i]; - const T baseSignalWord = reinterpret_cast(baseSignalDatap)[i]; - const T readDataWord - = (forceEnableWord & forceValueWord) | (~forceEnableWord & baseSignalWord); - readData[i] = readDataWord; - } - return readData; - } }; //====================================================================== @@ -1312,7 +1300,8 @@ VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE { return s().m_errorInfop; } -auto VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVar* const baseSignalVop) { +VerilatedVpiImp::ForceControlSignalVops +VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVar* const baseSignalVop) { const VerilatedForceControlSignals* const forceControlSignals = baseSignalVop->varp()->forceControlSignals(); // LCOV_EXCL_START - Would require a Verilation time error, so cannot test @@ -1322,28 +1311,19 @@ auto VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVar* const baseS "%s: VPI put or get requested for forceable signal '%s', but signal has no force " "control signals.", __func__, baseSignalVop->fullname()); - return std::pair, std::unique_ptr>{ - nullptr, nullptr}; + return {}; } // LCOV_EXCL_STOP const VerilatedVar* const forceEnableSignalVarp = forceControlSignals->forceEnableSignalp; const VerilatedVar* const forceValueSignalVarp = forceControlSignals->forceValueSignalp; + const VerilatedVar* const forceReadSignalVarp = &forceControlSignals->forceReadSignal; + // LCOV_EXCL_START - Would require a Verilation time error, so cannot test - if (VL_UNLIKELY(!forceEnableSignalVarp)) { + if (VL_UNLIKELY(!forceEnableSignalVarp || !forceValueSignalVarp || !forceReadSignalVarp)) { VL_VPI_ERROR_(__FILE__, __LINE__, - "%s: VPI put or get requested for forceable signal '%s', but force enable " - "signal could not be found.", + "%s: VPI put or get requested for forceable signal '%s', but force control " + "signals could not all be found.", __func__, baseSignalVop->fullname()); - return std::pair, std::unique_ptr>{ - nullptr, nullptr}; - } - if (VL_UNLIKELY(!forceValueSignalVarp)) { - VL_VPI_ERROR_( - __FILE__, __LINE__, - "%s: VPI put or get requested for forceable signal '%s', but force value signal could " - "not be found.", - __func__, baseSignalVop->fullname()); - return std::pair, std::unique_ptr>{ - nullptr, nullptr}; + return {}; } // LCOV_EXCL_STOP @@ -1353,37 +1333,38 @@ auto VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVar* const baseS = new VerilatedVpioVar{forceEnableSignalVarp, baseSignalVop->scopep()}; VerilatedVpioVar* forceValueSignalVop = new VerilatedVpioVar{forceValueSignalVarp, baseSignalVop->scopep()}; + VerilatedVpioVar* forceReadSignalVop + = new VerilatedVpioVar{forceReadSignalVarp, baseSignalVop->scopep()}; for (const int idx : baseSignalVop->index()) { VerilatedVpioVar* const nextForceEnableSignalVop = forceEnableSignalVop->withIndex(idx); VerilatedVpioVar* const nextForceValueSignalVop = forceValueSignalVop->withIndex(idx); + VerilatedVpioVar* const nextForceReadSignalVop = forceReadSignalVop->withIndex(idx); VL_DO_DANGLING(delete forceEnableSignalVop, forceEnableSignalVop); VL_DO_DANGLING(delete forceValueSignalVop, forceValueSignalVop); + VL_DO_DANGLING(delete forceReadSignalVop, forceReadSignalVop); forceEnableSignalVop = nextForceEnableSignalVop; forceValueSignalVop = nextForceValueSignalVop; - if (!forceEnableSignalVop || !forceValueSignalVop) break; // LCOV_EXCL_LINE + forceReadSignalVop = nextForceReadSignalVop; + // LCOV_EXCL_START + if (!forceEnableSignalVop || !forceValueSignalVop || !forceReadSignalVop) break; + // LCOV_EXCL_STOP } // LCOV_EXCL_START - Would require a Verilation time error, so cannot test - if (VL_UNLIKELY(!forceEnableSignalVop)) { + if (VL_UNLIKELY(!forceEnableSignalVop) || VL_UNLIKELY(!forceValueSignalVop) + || VL_UNLIKELY(!forceReadSignalVop)) { VL_VPI_ERROR_(__FILE__, __LINE__, - "%s: VPI put or get requested for forceable signal '%s', but force enable " - "signal could not be indexed to the same dimension as the base signal.", - __func__, baseSignalVop->fullname()); - if (VL_UNLIKELY(forceValueSignalVop)) - VL_DO_DANGLING(delete forceValueSignalVop, forceValueSignalVop); - return std::pair, std::unique_ptr>{ - nullptr, nullptr}; - } - if (VL_UNLIKELY(!forceValueSignalVop)) { - VL_VPI_ERROR_(__FILE__, __LINE__, - "%s: VPI put or get requested for forceable signal '%s', but force value " - "signal could not be indexed to the same dimension as the base signal.", + "%s: VPI put or get requested for forceable signal '%s', but force control " + "signals could not be indexed to the same dimension as the base signal.", __func__, baseSignalVop->fullname()); if (VL_UNLIKELY(forceEnableSignalVop)) VL_DO_DANGLING(delete forceEnableSignalVop, forceEnableSignalVop); - return std::pair, std::unique_ptr>{ - nullptr, nullptr}; + if (VL_UNLIKELY(forceValueSignalVop)) + VL_DO_DANGLING(delete forceValueSignalVop, forceValueSignalVop); + if (VL_UNLIKELY(forceReadSignalVop)) + VL_DO_DANGLING(delete forceReadSignalVop, forceReadSignalVop); + return {}; } // LCOV_EXCL_STOP @@ -1410,33 +1391,31 @@ auto VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVar* const baseS = forceEnableSignalVop->withPartSelect(partSelIndexHigh, partSelIndexLow); VerilatedVpioVar* partIndexedForceValueSignalVop = forceValueSignalVop->withPartSelect(partSelIndexHigh, partSelIndexLow); + VerilatedVpioVar* partIndexedForceReadSignalVop + = forceReadSignalVop->withPartSelect(partSelIndexHigh, partSelIndexLow); VL_DO_DANGLING(delete forceEnableSignalVop, forceEnableSignalVop); VL_DO_DANGLING(delete forceValueSignalVop, forceValueSignalVop); + VL_DO_DANGLING(delete forceReadSignalVop, forceReadSignalVop); forceEnableSignalVop = partIndexedForceEnableSignalVop; forceValueSignalVop = partIndexedForceValueSignalVop; + forceReadSignalVop = partIndexedForceReadSignalVop; } // LCOV_EXCL_START - Would require a Verilation time error, so cannot test - if (VL_UNLIKELY(!forceEnableSignalVop)) { + if (VL_UNLIKELY(!forceEnableSignalVop) || VL_UNLIKELY(!forceValueSignalVop) + || VL_UNLIKELY(!forceReadSignalVop)) { VL_VPI_ERROR_(__FILE__, __LINE__, "%s: VPI put or get requested for forceable signal '%s', but part selection " - "could not be applied to the force enable signal.", - __func__, baseSignalVop->fullname()); - if (VL_UNLIKELY(forceValueSignalVop)) - VL_DO_DANGLING(delete forceValueSignalVop, forceValueSignalVop); - return std::pair, std::unique_ptr>{ - nullptr, nullptr}; - } - if (VL_UNLIKELY(!forceValueSignalVop)) { - VL_VPI_ERROR_(__FILE__, __LINE__, - "%s: VPI put or get requested for forceable signal '%s', but part selection " - "could not be applied to the force value signal.", + "could not be applied to the force control signals.", __func__, baseSignalVop->fullname()); if (VL_UNLIKELY(forceEnableSignalVop)) VL_DO_DANGLING(delete forceEnableSignalVop, forceEnableSignalVop); - return std::pair, std::unique_ptr>{ - nullptr, nullptr}; + if (VL_UNLIKELY(forceValueSignalVop)) + VL_DO_DANGLING(delete forceValueSignalVop, forceValueSignalVop); + if (VL_UNLIKELY(forceReadSignalVop)) + VL_DO_DANGLING(delete forceReadSignalVop, forceReadSignalVop); + return {}; } // LCOV_EXCL_STOP @@ -1445,26 +1424,63 @@ auto VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVar* const baseS // the force control signals, so that they always refer to the same bits assert(forceEnableSignalVop->bitSize() == baseSignalVop->bitSize()); assert(forceValueSignalVop->bitSize() == baseSignalVop->bitSize()); + assert(forceReadSignalVop->bitSize() == baseSignalVop->bitSize()); assert(forceEnableSignalVop->indexedDim() == baseSignalVop->indexedDim()); assert(forceValueSignalVop->indexedDim() == baseSignalVop->indexedDim()); + assert(forceReadSignalVop->indexedDim() == baseSignalVop->indexedDim()); assert(forceEnableSignalVop->index() == baseSignalVop->index()); assert(forceValueSignalVop->index() == baseSignalVop->index()); + assert(forceReadSignalVop->index() == baseSignalVop->index()); assert(forceEnableSignalVop->bitOffset() == baseSignalVop->bitOffset()); assert(forceValueSignalVop->bitOffset() == baseSignalVop->bitOffset()); + assert(forceReadSignalVop->bitOffset() == baseSignalVop->bitOffset()); assert(forceEnableSignalVop->partselBits() == baseSignalVop->partselBits()); assert(forceValueSignalVop->partselBits() == baseSignalVop->partselBits()); + assert(forceReadSignalVop->partselBits() == baseSignalVop->partselBits()); // entSize can differ because the force enable signal can have a different vltyp than the base // signal (e.g. the base signal can be of type VLVT_REAL, while the force enable signal is // still of type VLVT_UINT8), but for now entSize is only used for unpacked arrays, which // cannot be forced through VPI yet // assert(forceEnableSignalVop->entSize() == baseSignalVop->entSize()); assert(forceValueSignalVop->entSize() == baseSignalVop->entSize()); + assert(forceReadSignalVop->entSize() == baseSignalVop->entSize()); assert(forceEnableSignalVop->varDatap() == forceEnableSignalVarp->datap()); assert(forceValueSignalVop->varDatap() == forceValueSignalVarp->datap()); + assert(forceReadSignalVop->varDatap() == forceReadSignalVarp->datap()); #endif // VL_DEBUG - return std::pair, std::unique_ptr>{ - forceEnableSignalVop, forceValueSignalVop}; + return VerilatedVpiImp::ForceControlSignalVops{ + .forceEnable{std::unique_ptr{forceEnableSignalVop}}, + .forceValue{std::unique_ptr{forceValueSignalVop}}, + .forceRead{std::unique_ptr{forceReadSignalVop}}}; +} + +QData vl_vpi_get_word(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset); +template +T VerilatedVpiImp::getReadDataWord(const VerilatedVpioVar* baseSignalVop, + const VerilatedVpioVar* forceEnableSignalVop, + const VerilatedVpioVar* forceValueSignalVop, size_t bitCount, + size_t bitOffset) { + // 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, bitOffset); + const QData forceEnableData = vl_vpi_get_word(forceEnableSignalVop, bitCount, bitOffset); + const QData forceValueData = vl_vpi_get_word(forceValueSignalVop, bitCount, bitOffset); + const QData readData + = (forceEnableData & forceValueData) | (~forceEnableData & baseSignalData); + return static_cast(readData); +} + +template <> +double VerilatedVpiImp::getReadDataWord(const VerilatedVpioVar* baseSignalVop, + const VerilatedVpioVar* forceEnableSignalVop, + const VerilatedVpioVar* forceValueSignalVop, + size_t /*bitCount*/, size_t /*bitOffset*/) { + const double baseSignalData = *baseSignalVop->varRealDatap(); + const bool forceEnableData = *forceEnableSignalVop->varCDatap(); + const double forceValueData = *forceValueSignalVop->varRealDatap(); + const double readData = forceEnableData ? forceValueData : baseSignalData; + return readData; } std::size_t VerilatedVpiImp::vlTypeSize(const VerilatedVarType vltype) { @@ -3017,66 +3033,6 @@ 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, will be removed entirely eventually (#7092), so its value has to - // be recreated using the __VforceEn and __VforceVal signals. - const auto forceControlSignals = [vop]() { - if (vop->varp()->isForceable()) { - const VerilatedVpioVar* varVop = dynamic_cast(vop); - assert(varVop); // VerilatedVpioParams cannot be forced - return VerilatedVpiImp::getForceControlSignals(varVop); - } - return std::pair, std::unique_ptr>{ - nullptr, nullptr}; - }(); - const VerilatedVpioVarBase* const forceEnableSignalVop = forceControlSignals.first.get(); - const VerilatedVpioVarBase* const forceValueSignalVop = forceControlSignals.second.get(); - 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 - // LCOV_EXCL_START - Cannot test, since getForceControlSignals can only fail when a Verilation - // time error causes force control signals to be missing - // NOLINTNEXTLINE(readability-simplify-boolean-expr); - if (VL_UNLIKELY( - (errorOccurred && getForceControlSignalsError.level >= vpiError) - || (vop->varp()->isForceable() && (!forceEnableSignalVop || !forceValueSignalVop)))) { - - // Check if getForceControlSignals provided any additional error info - 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; - } // LCOV_EXCL_STOP - - const std::function 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 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) { @@ -3088,7 +3044,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { t_out.resize(words); valuep->value.vector = t_out.data(); for (int i = 0; i < words; ++i) { - t_out[i].aval = get_word(vop, 32, i * 32); + t_out[i].aval = vl_vpi_get_word(vop, 32, i * 32); t_out[i].bval = 0; } return; @@ -3096,7 +3052,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { if (varp->vltype() == VLVT_UINT64 && varBits > 32) { t_out.resize(2); valuep->value.vector = t_out.data(); - const QData data = get_word(vop, 64, 0); + const QData data = vl_vpi_get_word(vop, 64, 0); t_out[1].aval = static_cast(data >> 32ULL); t_out[1].bval = 0; t_out[0].aval = static_cast(data); @@ -3105,25 +3061,12 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { } t_out.resize(1); valuep->value.vector = t_out.data(); - t_out[0].aval = get_word(vop, 32, 0); + t_out[0].aval = vl_vpi_get_word(vop, 32, 0); t_out[0].bval = 0; return; } else if (valuep->format == vpiBinStrVal) { t_outDynamicStr.resize(varBits); - - static thread_local std::vector forceReadCData; - forceReadCData - = vop->varp()->isForceable() - ? VerilatedVpiImp::createReadDataVector( - varDatap, - {forceEnableSignalVop->varDatap(), forceValueSignalVop->varDatap()}, - vop->bitSize()) - : std::vector{}; - const uint8_t* const varCDatap = vop->varp()->isForceable() - ? forceReadCData.data() - : reinterpret_cast(varDatap); - - const CData* datap = varCDatap; + const CData* datap = reinterpret_cast(varDatap); for (size_t i = 0; i < varBits; ++i) { const size_t pos = i + vop->bitOffset(); const char val = (datap[pos >> 3] >> (pos & 7)) & 1; @@ -3135,22 +3078,24 @@ 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 = get_word(vop, 3, i * 3); + const char val = vl_vpi_get_word(vop, 3, i * 3); t_outDynamicStr[chars - i - 1] = '0' + val; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } else if (valuep->format == vpiDecStrVal) { if (varp->vltype() == VLVT_UINT8) { - vl_strprintf(t_outDynamicStr, "%hhu", static_cast(get_word(vop, 8, 0))); + vl_strprintf(t_outDynamicStr, "%hhu", + static_cast(vl_vpi_get_word(vop, 8, 0))); } else if (varp->vltype() == VLVT_UINT16) { vl_strprintf(t_outDynamicStr, "%hu", - static_cast(get_word(vop, 16, 0))); + static_cast(vl_vpi_get_word(vop, 16, 0))); } else if (varp->vltype() == VLVT_UINT32) { - vl_strprintf(t_outDynamicStr, "%u", static_cast(get_word(vop, 32, 0))); + vl_strprintf(t_outDynamicStr, "%u", + static_cast(vl_vpi_get_word(vop, 32, 0))); } else if (varp->vltype() == VLVT_UINT64) { vl_strprintf(t_outDynamicStr, "%llu", // lintok-format-ll - static_cast(get_word(vop, 64, 0))); + static_cast(vl_vpi_get_word(vop, 64, 0))); } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; @@ -3158,7 +3103,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 = get_word(vop, 4, i * 4); + const char val = vl_vpi_get_word(vop, 4, i * 4); t_outDynamicStr[chars - i - 1] = "0123456789abcdef"[static_cast(val)]; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); @@ -3176,7 +3121,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 = get_word(vop, 8, i * 8); + const char val = vl_vpi_get_word(vop, 8, i * 8); // other simulators replace [leading?] zero chars with spaces, replicate here. t_outDynamicStr[chars - i - 1] = val ? val : ' '; } @@ -3184,19 +3129,13 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { return; } } else if (valuep->format == vpiIntVal) { - valuep->value.integer = get_word(vop, 32, 0); + valuep->value.integer = vl_vpi_get_word(vop, 32, 0); return; } else if (valuep->format == vpiRealVal) { - // Only cover the scalar case, since reals cannot be packed (IEEE 1800-2023 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(); - + valuep->value.real = *(vop->varRealDatap()); return; } else if (valuep->format == vpiScalarVal) { - valuep->value.scalar = get_word(vop, 32, 0) ? vpi1 : vpi0; + valuep->value.scalar = vl_vpi_get_word(vop, 32, 0) ? vpi1 : vpi0; return; } else if (valuep->format == vpiSuppressVal) { return; @@ -3211,8 +3150,45 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) { VL_VPI_ERROR_RESET_(); if (VL_UNLIKELY(!valuep)) return; - if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) { - vl_vpi_get_value(vop, valuep); + if (const VerilatedVpioVar* const baseSignalVop = VerilatedVpioVar::castp(object)) { + // If the signal is forceable, read the value from the __VforceRd signal instead of the + // base signal + + const std::unique_ptr forceReadSignalVop + = baseSignalVop->varp()->isForceable() + ? std::move(VerilatedVpiImp::getForceControlSignals(baseSignalVop).forceRead) + : 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 + // LCOV_EXCL_START - Cannot test, since getForceControlSignals can only fail when a + // Verilation time error causes force control signals to be missing + // NOLINTNEXTLINE(readability-simplify-boolean-expr); + if (VL_UNLIKELY((errorOccurred && getForceControlSignalsError.level >= vpiError) + || (baseSignalVop->varp()->isForceable() && (!forceReadSignalVop)))) { + + // Check if getForceControlSignals provided any additional error info + const bool gotErrorMessage = vpi_chk_error(&getForceControlSignalsError); + const std::string previousErrorMessage + = gotErrorMessage ? " Error message: "s + getForceControlSignalsError.message : ""; + + VL_VPI_ERROR_(__FILE__, __LINE__, + "%s: Signal '%s' is marked forceable, but force " + "read signal could not be retrieved.%s", + __func__, baseSignalVop->fullname(), + gotErrorMessage ? previousErrorMessage.c_str() : ""); + return; + } // LCOV_EXCL_STOP + + const VerilatedVpioVarBase* const valueVop + = baseSignalVop->varp()->isForceable() ? forceReadSignalVop.get() : baseSignalVop; + + vl_vpi_get_value(valueVop, valuep); return; } if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) { @@ -3282,12 +3258,11 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ const auto forceControlSignals = baseSignalVop->varp()->isForceable() - && (forceFlag == vpiForceFlag || forceFlag == vpiReleaseFlag) ? VerilatedVpiImp::getForceControlSignals(baseSignalVop) - : std::pair, - std::unique_ptr>{nullptr, nullptr}; - const VerilatedVpioVar* const forceEnableSignalVop = forceControlSignals.first.get(); - const VerilatedVpioVar* const forceValueSignalVop = forceControlSignals.second.get(); + : VerilatedVpiImp::ForceControlSignalVops{}; + const VerilatedVpioVar* const forceEnableSignalVop = forceControlSignals.forceEnable.get(); + const VerilatedVpioVar* const forceValueSignalVop = forceControlSignals.forceValue.get(); + const VerilatedVpioVar* const forceReadSignalVop = forceControlSignals.forceRead.get(); t_vpi_error_info getForceControlSignalsError{}; bool errorOccurred = vpi_chk_error(&getForceControlSignalsError); // LCOV_EXCL_START - Cannot test, since getForceControlSignals does not (currently) produce @@ -3299,9 +3274,9 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ // LCOV_EXCL_START - Cannot test, since getForceControlSignals can only fail when a // Verilation time error causes force control signals to be missing // NOLINTNEXTLINE(readability-simplify-boolean-expr); - if (VL_UNLIKELY(baseSignalVop->varp()->isForceable() - && (forceFlag == vpiForceFlag || forceFlag == vpiReleaseFlag) - && (!forceEnableSignalVop || !forceValueSignalVop))) { + if (VL_UNLIKELY( + baseSignalVop->varp()->isForceable() + && (!forceEnableSignalVop || !forceValueSignalVop || !forceReadSignalVop))) { // Check if getForceControlSignals provided any additional error info const bool gotErrorMessage = vpi_chk_error(&getForceControlSignalsError); @@ -3321,6 +3296,52 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ const VerilatedVpioVar* const valueVop = (forceFlag == vpiForceFlag) ? forceValueSignalVop : baseSignalVop; + const auto updateVforceRd + = [forceEnableSignalVop, forceValueSignalVop, forceReadSignalVop, baseSignalVop]() { + if (baseSignalVop->varp()->vltype() == VLVT_REAL) { + const double readData = VerilatedVpiImp::getReadDataWord( + baseSignalVop, forceEnableSignalVop, forceValueSignalVop, 64, 0); + *forceReadSignalVop->varRealDatap() = readData; + return; + } + + const std::size_t wordSize + = 8ULL * VerilatedVpiImp::vlTypeSize(forceReadSignalVop->varp()->vltype()); + assert(wordSize > 0); + const uint32_t varBits = baseSignalVop->bitSize(); + const std::size_t numChunks = (varBits / wordSize); + for (std::size_t i{0}; i < numChunks; ++i) { + const QData readData = VerilatedVpiImp::getReadDataWord( + baseSignalVop, forceEnableSignalVop, forceValueSignalVop, wordSize, + i * wordSize); + vl_vpi_put_word(forceReadSignalVop, readData, 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) { + const QData readData = VerilatedVpiImp::getReadDataWord( + baseSignalVop, forceEnableSignalVop, forceValueSignalVop, + varBits % wordSize, numChunks * wordSize); + vl_vpi_put_word(forceReadSignalVop, readData, varBits % wordSize, + numChunks * wordSize); + } + }; + + const std::function + putForceableSignalWord + = [forceEnableSignalVop, forceValueSignalVop, forceReadSignalVop, + baseSignalVop](const VerilatedVpioVar* valueVop, QData word, size_t bitCount, + size_t addOffset) -> void { + vl_vpi_put_word(valueVop, word, bitCount, addOffset); + const QData readData = VerilatedVpiImp::getReadDataWord( + baseSignalVop, forceEnableSignalVop, forceValueSignalVop, bitCount, addOffset); + vl_vpi_put_word(forceReadSignalVop, readData, bitCount, addOffset); + return; + }; + + const std::function put_word + = baseSignalVop->varp()->isForceable() ? putForceableSignalWord : vl_vpi_put_word; + if (forceFlag == vpiForceFlag) { // Enable __VforceEn VerilatedVpiImp::setAllBitsToValue(forceEnableSignalVop, 1); @@ -3330,22 +3351,26 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ // (non-forced) value. Else, get the (still forced) value first, then clear the force // enable bits. - if (baseSignalVop->varp()->isContinuously()) + if (baseSignalVop->varp()->isContinuously()) { VerilatedVpiImp::setAllBitsToValue(forceEnableSignalVop, 0); + updateVforceRd(); + } // For writing back the forced value to a non-continuously assigned base signal in the // release case, need to get the forced signal's raw data, since valuep could have // a format that modifies the data (e.g. vpiStringVal which pads with spaces) const s_vpi_value forcedValue - = baseSignalVop->varp()->isContinuously() ? s_vpi_value{} : [&baseSignalVop]() { - s_vpi_value val{.format - = vl_get_vltype_format(baseSignalVop->varp()->vltype()), - .value{}}; - vl_vpi_get_value(baseSignalVop, &val); - return val; - }(); + = baseSignalVop->varp()->isContinuously() + ? s_vpi_value{} + : [&forceReadSignalVop]() { + s_vpi_value val{.format = vl_get_vltype_format( + forceReadSignalVop->varp()->vltype()), + .value{}}; + vl_vpi_get_value(forceReadSignalVop, &val); + return val; + }(); - vl_vpi_get_value(baseSignalVop, valuep); + vl_vpi_get_value(forceReadSignalVop, valuep); t_vpi_error_info baseValueGetError{}; errorOccurred = vpi_chk_error(&baseValueGetError); @@ -3385,15 +3410,15 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ if (valueVop->varp()->vltype() == VLVT_WDATA) { const int words = VL_WORDS_I(varBits); for (int i = 0; i < words; ++i) - vl_vpi_put_word(valueVop, valuep->value.vector[i].aval, 32, i * 32); + put_word(valueVop, valuep->value.vector[i].aval, 32, i * 32); return object; } else if (valueVop->varp()->vltype() == VLVT_UINT64 && varBits > 32) { const QData val = (static_cast(valuep->value.vector[1].aval) << 32) | static_cast(valuep->value.vector[0].aval); - vl_vpi_put_word(valueVop, val, 64, 0); + put_word(valueVop, val, 64, 0); return object; } else { - vl_vpi_put_word(valueVop, valuep->value.vector[0].aval, 32, 0); + put_word(valueVop, valuep->value.vector[0].aval, 32, 0); return object; } } else if (valuep->format == vpiBinStrVal) { @@ -3408,6 +3433,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ else datap[pos >> 3] &= ~(1 << (pos & 7)); } + if (baseSignalVop->varp()->isForceable()) updateVforceRd(); return object; } else if (valuep->format == vpiOctStrVal) { const int len = std::strlen(valuep->value.str); @@ -3421,7 +3447,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ valueVop->fullname()); digit = 0; } - vl_vpi_put_word(valueVop, digit, 3, i * 3); + put_word(valueVop, digit, 3, i * 3); } return object; } else if (valuep->format == vpiDecStrVal) { @@ -3442,7 +3468,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ __func__, remainder, valuep->value.str, VerilatedVpiError::strFromVpiVal(valuep->format), valueVop->fullname()); } - vl_vpi_put_word(valueVop, val, 64, 0); + put_word(valueVop, val, 64, 0); return object; } else if (valuep->format == vpiHexStrVal) { const int chars = (varBits + 3) >> 2; @@ -3473,7 +3499,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ hex = 0; } // assign hex digit value to destination - vl_vpi_put_word(valueVop, hex, 4, i * 4); + put_word(valueVop, hex, 4, i * 4); } return object; } else if (valuep->format == vpiStringVal) { @@ -3487,20 +3513,21 @@ 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(valueVop, c, 8, i * 8); + put_word(valueVop, c, 8, i * 8); } return object; } else if (valuep->format == vpiIntVal) { - vl_vpi_put_word(valueVop, valuep->value.integer, 64, 0); + put_word(valueVop, valuep->value.integer, 64, 0); return object; } else if (valuep->format == vpiRealVal) { if (valueVop->varp()->vltype() == VLVT_REAL) { *(valueVop->varRealDatap()) = valuep->value.real; + if (baseSignalVop->varp()->isForceable()) updateVforceRd(); return object; } } else if (valuep->format == vpiScalarVal) { - vl_vpi_put_word(valueVop, (valuep->value.scalar == vpi1 ? 1 : 0), 1, 0); + put_word(valueVop, (valuep->value.scalar == vpi1 ? 1 : 0), 1, 0); return object; } VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for '%s'", diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 31a125414..e954a6fe4 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -208,7 +208,7 @@ class EmitCSyms final : EmitCBaseVisitorConst { static std::pair isForceControlSignal(const AstVar* const signalVarp) { // __VforceRd should not show up here because it is never public, but just in case it does, - // it should be skipped because the VPI code lazily re-creates its value + // it should be skipped because forceableVarInsert creates its VerilatedVar. for (const std::string forceControlSuffix : {"__VforceEn", "__VforceVal", "__VforceRd"}) { const std::size_t suffixPos = signalVarp->name().find(forceControlSuffix); const bool suffixFound = suffixPos != std::string::npos; @@ -277,6 +277,10 @@ class EmitCSyms final : EmitCBaseVisitorConst { stmt += varp->vlEnumType(); // VLVT_UINT32 etc stmt += ", "; stmt += varp->vlEnumDir(); // VLVD_IN etc + stmt += ", &("; + stmt += varName + "__VforceRd"; + stmt += "), \"" + V3OutFormatter::quoteNameControls(protect(svd.m_varBasePretty)) + + "__VforceRd" + '"'; stmt += ", {"; // Find __VforceEn diff --git a/test_regress/t/t_vpi_force.cpp b/test_regress/t/t_vpi_force.cpp index 8049f3909..8e4b790de 100644 --- a/test_regress/t/t_vpi_force.cpp +++ b/test_regress/t/t_vpi_force.cpp @@ -99,9 +99,9 @@ enum class Direction : uint8_t { }; #ifndef IVERILOG -const std::array TestSignals = { +const std::array TestSignals = { #else // Multidimensional packed arrays aren't tested in Icarus -const std::array TestSignals = { +const std::array TestSignals = { #endif TestSignal{"onebit", vpiIntVal, @@ -111,6 +111,14 @@ const std::array TestSignals = { false, {}, // Can't partially force just one bit {}}, // Can't index into a single bit + TestSignal{"scalarbit", + vpiScalarVal, + {}, + {.integer = vpi1}, + {.integer = vpi0}, + false, + {}, // Can't partially force just one bit + {}}, // Can't index into a single bit TestSignal{"intval", vpiIntVal, {}, @@ -710,6 +718,7 @@ bool vpiValuesEqual(const std::size_t bitCount, const s_vpi_value& first, if (first.format != second.format) return false; switch (first.format) { case vpiIntVal: return first.value.integer == second.value.integer; break; + case vpiScalarVal: return first.value.scalar == second.value.scalar; break; case vpiVectorVal: { const t_vpi_vecval* const firstVecval = first.value.vector; const t_vpi_vecval* const secondVecval = second.value.vector; @@ -746,6 +755,7 @@ std::unique_ptr vpiValueWithFormat(const PLI_INT32 signalFormat, switch (signalFormat) { case vpiIntVal: value_sp->value = {.integer = value.integer}; break; + case vpiScalarVal: value_sp->value = {.scalar = value.integer}; break; case vpiVectorVal: value_sp->value = {.vector = const_cast(value.vector)}; break; case vpiRealVal: value_sp->value = {.real = value.real}; break; case vpiStringVal: diff --git a/test_regress/t/t_vpi_force.v b/test_regress/t/t_vpi_force.v index e9060ff71..ae4913926 100644 --- a/test_regress/t/t_vpi_force.v +++ b/test_regress/t/t_vpi_force.v @@ -140,6 +140,9 @@ typedef enum byte { logic onebit `PUBLIC_FORCEABLE; // CData logic [ 31:0] intval `PUBLIC_FORCEABLE; // IData + // Force with vpiScalarVal + logic scalarbit `PUBLIC_FORCEABLE; // CData + // Force with vpiVectorVal logic [ 7:0] vectorC `PUBLIC_FORCEABLE; // CData logic [ 61:0] vectorQ `PUBLIC_FORCEABLE; // QData @@ -218,6 +221,9 @@ typedef enum byte { wire onebitContinuously `PUBLIC_FORCEABLE; // CData wire [ 31:0] intvalContinuously `PUBLIC_FORCEABLE; // IData + // Force with vpiScalarVal + wire scalarbitContinuously `PUBLIC_FORCEABLE; // CData + // Force with vpiVectorVal wire [ 7:0] vectorCContinuously `PUBLIC_FORCEABLE; // CData wire [ 61:0] vectorQContinuously `PUBLIC_FORCEABLE; // QData @@ -293,6 +299,8 @@ typedef enum byte { onebit <= 1; intval <= 32'hAAAAAAAA; + scalarbit <= 1; + vectorC <= 8'hAA; vectorQ <= 62'h2AAAAAAA_AAAAAAAA; vectorW <= 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA; @@ -368,6 +376,8 @@ typedef enum byte { assign onebitContinuously = 1; assign intvalContinuously = 32'hAAAAAAAA; + assign scalarbitContinuously = 1; + assign vectorCContinuously = 8'hAA; assign vectorQContinuously = 62'h2AAAAAAA_AAAAAAAA; assign vectorWContinuously = 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA; @@ -442,6 +452,7 @@ typedef enum byte { force forcedNonForceable = 8'h55; force onebit = 0; force intval = 32'h55555555; + force scalarbit = 0; force vectorC = 8'h55; force vectorQ = 62'h15555555_55555555; force vectorW = 128'h55555555_55555555_55555555_55555555; @@ -503,6 +514,7 @@ typedef enum byte { force forcedNonForceableContinuously = 8'h55; force onebitContinuously = 0; force intvalContinuously = 32'h55555555; + force scalarbitContinuously = 0; force vectorCContinuously = 8'h55; force vectorQContinuously = 62'h15555555_55555555; force vectorWContinuously = 128'h55555555_55555555_55555555_55555555; @@ -880,6 +892,7 @@ typedef enum byte { release forcedNonForceable; release onebit; release intval; + release scalarbit; release vectorC; release vectorQ; release vectorW; @@ -919,6 +932,7 @@ typedef enum byte { release forcedNonForceableContinuously; release onebitContinuously; release intvalContinuously; + release scalarbitContinuously; release vectorCContinuously; release vectorQContinuously; release vectorWContinuously; @@ -1251,6 +1265,7 @@ typedef enum byte { `checkh(forcedNonForceableContinuously, 8'h55); `checkh(onebitContinuously, 0); `checkh(intvalContinuously, 32'h55555555); + `checkh(scalarbitContinuously, 0); `checkh(vectorCContinuously, 8'h55); `checkh(vectorQContinuously, 62'h15555555_55555555); `checkh(vectorWContinuously, 128'h55555555_55555555_55555555_55555555); @@ -1318,6 +1333,7 @@ typedef enum byte { `checkh(forcedNonForceable, 8'h55); `checkh(onebit, 0); `checkh(intval, 32'h55555555); + `checkh(scalarbit, 0); `checkh(vectorC, 8'h55); `checkh(vectorQ, 62'h15555555_55555555); `checkh(vectorW, 128'h55555555_55555555_55555555_55555555); @@ -1384,6 +1400,7 @@ typedef enum byte { `checkh(forcedNonForceableContinuously, 8'hAA); `checkh(onebitContinuously, 1); `checkh(intvalContinuously, 32'hAAAAAAAA); + `checkh(scalarbitContinuously, 1); `checkh(vectorCContinuously, 8'hAA); `checkh(vectorQContinuously, 62'h2AAAAAAA_AAAAAAAA); `checkh(vectorWContinuously, 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA); @@ -1682,6 +1699,7 @@ typedef enum byte { `checkh(forcedNonForceable, 8'hAA); `checkh(onebit, 1); `checkh(intval, 32'hAAAAAAAA); + `checkh(scalarbit, 1); `checkh(vectorC, 8'hAA); `checkh(vectorQ, 62'h2AAAAAAA_AAAAAAAA); `checkh(vectorW, 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA); @@ -2011,10 +2029,11 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Forcing with %s, releasing with %s", forceDimIndexingMethod.name(), releaseDimIndexingMethod.name()); vpiForceValues(forceDimIndexingMethod); - // 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 - #8 vpiCheckValuesForced(); + // Check *immediately* after forcing (no eval) + vpiCheckValuesForced(); + svCheckValuesForced(); + // Wait until after positive clock edge to ensure signals did not get overwritten + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); // Wait until negedge, then release @(negedge clk) vpiReleaseValues(releaseDimIndexingMethod); @@ -2022,12 +2041,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); // should still have their forced value, because the posedge re-assigning // the non-continuously assigned signals has not happened yet, but // continuously assigned signals should have their non-forced value again + + // Check *immediately* after releasing (no eval) + vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesReleased(); + + // Conduct an `eval` and check that the values are still correct #1 vpiCheckNonContinuousValuesForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesReleased(); - #8; // All signals should be released by now - vpiCheckValuesReleased(); + // Non-continuous signals should have their non-forced values back now + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); releaseDimIndexingMethod = releaseDimIndexingMethod.next(); @@ -2042,14 +2069,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Forcing with %s, releasing through Verilog", forceDimIndexingMethod.name()); #8 vpiForceValues(forceDimIndexingMethod); - #8 vpiCheckValuesForced(); + vpiCheckValuesForced(); + svCheckValuesForced(); + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); @(negedge clk) svReleaseValues(); + vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesReleased(); #1 vpiCheckNonContinuousValuesForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); forceDimIndexingMethod = forceDimIndexingMethod.next(); end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); @@ -2060,14 +2093,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Forcing through Verilog, releasing with %s", releaseDimIndexingMethod.name()); #8 svForceValues(); - #8 vpiCheckValuesForced(); + vpiCheckValuesForced(); + svCheckValuesForced(); + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); @(negedge clk) vpiReleaseValues(releaseDimIndexingMethod); + vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesReleased(); #1 vpiCheckNonContinuousValuesForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); releaseDimIndexingMethod = releaseDimIndexingMethod.next(); end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); @@ -2076,14 +2115,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); // VPI) if (`verbose) $display("*** Forcing through Verilog, releasing through Verilog ***"); #8 svForceValues(); - #8 vpiCheckValuesForced(); + vpiCheckValuesForced(); + svCheckValuesForced(); + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); @(negedge clk) svReleaseValues(); + vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesReleased(); #1 vpiCheckNonContinuousValuesForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); // Partial forcing tests obtain partial handles through @@ -2101,14 +2146,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Partially forcing with through VPI, releasing with %s", releaseDimIndexingMethod.name()); #8 vpiPartiallyForceValues(Descending); - #8 vpiCheckValuesPartiallyForced(); + vpiCheckValuesPartiallyForced(); + svCheckValuesPartiallyForced(); + @(posedge clk) #1 vpiCheckValuesPartiallyForced(); svCheckValuesPartiallyForced(); @(negedge clk) vpiReleasePartiallyForcedValues(releaseDimIndexingMethod); + vpiCheckNonContinuousValuesPartiallyForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesPartiallyForced(); + svCheckContinuousValuesReleased(); #1 vpiCheckNonContinuousValuesPartiallyForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousValuesPartiallyForced(); svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); releaseDimIndexingMethod = releaseDimIndexingMethod.next(); end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); @@ -2116,14 +2167,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); // Partially force through VPI, release through Verilog if (`verbose) $display("*** Partially forcing through VPI, releasing through Verilog ***"); #8 vpiPartiallyForceValues(Descending); - #8 vpiCheckValuesPartiallyForced(); + vpiCheckValuesPartiallyForced(); + svCheckValuesPartiallyForced(); + @(posedge clk) #1 vpiCheckValuesPartiallyForced(); svCheckValuesPartiallyForced(); @(negedge clk) svReleaseValues(); + vpiCheckNonContinuousValuesPartiallyForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesPartiallyForced(); + svCheckContinuousValuesReleased(); #1 vpiCheckNonContinuousValuesPartiallyForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousValuesPartiallyForced(); svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); // Xrun doesn't support ascending bit ranges in vpi_handle_by_name @@ -2136,14 +2193,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Partially forcing through VPI, releasing with %s", releaseDimIndexingMethod.name()); #8 vpiPartiallyForceValues(Ascending); - #8 vpiCheckValuesPartiallyForced(); + vpiCheckValuesPartiallyForced(); + svCheckValuesPartiallyForced(); + @(posedge clk) #1 vpiCheckValuesPartiallyForced(); svCheckValuesPartiallyForced(); @(negedge clk) vpiReleasePartiallyForcedValues(releaseDimIndexingMethod); + vpiCheckNonContinuousValuesPartiallyForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesPartiallyForced(); + svCheckContinuousValuesReleased(); #1 vpiCheckNonContinuousValuesPartiallyForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousValuesPartiallyForced(); svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); releaseDimIndexingMethod = releaseDimIndexingMethod.next(); end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); @@ -2151,14 +2214,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); // Partially force through VPI, release through Verilog if (`verbose) $display("*** Partially forcing through VPI, releasing through Verilog ***"); #8 vpiPartiallyForceValues(Ascending); - #8 vpiCheckValuesPartiallyForced(); + vpiCheckValuesPartiallyForced(); + svCheckValuesPartiallyForced(); + @(posedge clk) #1 vpiCheckValuesPartiallyForced(); svCheckValuesPartiallyForced(); @(negedge clk) svReleaseValues(); + vpiCheckNonContinuousValuesPartiallyForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesPartiallyForced(); + svCheckContinuousValuesReleased(); #1 vpiCheckNonContinuousValuesPartiallyForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousValuesPartiallyForced(); svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); `endif @@ -2170,14 +2239,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Partially forcing through Verilog, releasing with %s", releaseDimIndexingMethod.name()); #8 svPartiallyForceValues(); - #8 vpiCheckValuesPartiallyForced(); + vpiCheckValuesPartiallyForced(); + svCheckValuesPartiallyForced(); + @(posedge clk) #1 vpiCheckValuesPartiallyForced(); svCheckValuesPartiallyForced(); @(negedge clk) vpiReleasePartiallyForcedValues(releaseDimIndexingMethod); + vpiCheckNonContinuousValuesPartiallyForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesPartiallyForced(); + svCheckContinuousValuesReleased(); #1 vpiCheckNonContinuousValuesPartiallyForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousValuesPartiallyForced(); svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); releaseDimIndexingMethod = releaseDimIndexingMethod.next(); end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); @@ -2185,27 +2260,39 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); // Partially force through Verilog, release through Verilog if (`verbose) $display("*** Partially forcing through Verilog, releasing through Verilog ***"); #8 svPartiallyForceValues(); - #8 vpiCheckValuesPartiallyForced(); + vpiCheckValuesPartiallyForced(); + svCheckValuesPartiallyForced(); + @(posedge clk) #1 vpiCheckValuesPartiallyForced(); svCheckValuesPartiallyForced(); @(negedge clk) svReleaseValues(); + vpiCheckNonContinuousValuesPartiallyForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesPartiallyForced(); + svCheckContinuousValuesReleased(); #1 vpiCheckNonContinuousValuesPartiallyForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousValuesPartiallyForced(); svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); // Force through Verilog, partially release through Verilog if (`verbose) $display("*** Forcing through Verilog, partially releasing through Verilog ***"); #8 svForceValues(); - #8 vpiCheckValuesForced(); + vpiCheckValuesForced(); + svCheckValuesForced(); + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); @(negedge clk) svPartiallyReleaseValues(); + vpiCheckNonContinuousValuesForced; + vpiCheckContinuousValuesPartiallyReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesPartiallyReleased(); #1 vpiCheckNonContinuousValuesForced; vpiCheckContinuousValuesPartiallyReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesPartiallyReleased(); - #8 vpiCheckValuesPartiallyReleased(); + @(posedge clk) #1 vpiCheckValuesPartiallyReleased(); svCheckValuesPartiallyReleased(); `ifndef IVERILOG @@ -2220,14 +2307,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Forcing with %s, partially releasing through VPI", forceDimIndexingMethod.name()); #8 vpiForceValues(forceDimIndexingMethod); - #8 vpiCheckValuesForced(); + vpiCheckValuesForced(); + svCheckValuesForced(); + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); @(negedge clk) vpiPartiallyReleaseValues(Descending); + vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesPartiallyReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesPartiallyReleased(); #1 vpiCheckNonContinuousValuesForced(); vpiCheckContinuousValuesPartiallyReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesPartiallyReleased(); - #8 vpiCheckValuesPartiallyReleased(); + @(posedge clk) #1 vpiCheckValuesPartiallyReleased(); svCheckValuesPartiallyReleased(); forceDimIndexingMethod = forceDimIndexingMethod.next(); end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); @@ -2235,14 +2328,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); // Force through Verilog, partially release through VPI if (`verbose) $display("*** Forcing through Verilog, partially releasing through VPI ***"); #8 svForceValues(); - #8 vpiCheckValuesForced(); + vpiCheckValuesForced(); + svCheckValuesForced(); + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); @(negedge clk) vpiPartiallyReleaseValues(Descending); + vpiCheckNonContinuousValuesForced; + vpiCheckContinuousValuesPartiallyReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesPartiallyReleased(); #1 vpiCheckNonContinuousValuesForced; vpiCheckContinuousValuesPartiallyReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesPartiallyReleased(); - #8 vpiCheckValuesPartiallyReleased(); + @(posedge clk) #1 vpiCheckValuesPartiallyReleased(); svCheckValuesPartiallyReleased(); // Ascending @@ -2255,14 +2354,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Forcing through VPI, partially releasing with %s", forceDimIndexingMethod.name()); #8 vpiForceValues(forceDimIndexingMethod); - #8 vpiCheckValuesForced(); + vpiCheckValuesForced(); + svCheckValuesForced(); + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); @(negedge clk) vpiPartiallyReleaseValues(Ascending); + vpiCheckNonContinuousValuesForced; + vpiCheckContinuousValuesPartiallyReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesPartiallyReleased(); #1 vpiCheckNonContinuousValuesForced; vpiCheckContinuousValuesPartiallyReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesPartiallyReleased(); - #8 vpiCheckValuesPartiallyReleased(); + @(posedge clk) #1 vpiCheckValuesPartiallyReleased(); svCheckValuesPartiallyReleased(); forceDimIndexingMethod = forceDimIndexingMethod.next(); end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); @@ -2270,14 +2375,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); // Force through Verilog, partially release through VPI if (`verbose) $display("*** Forcing through Verilog, partially releasing through VPI ***"); #8 svForceValues(); - #8 vpiCheckValuesForced(); + vpiCheckValuesForced(); + svCheckValuesForced(); + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); @(negedge clk) vpiPartiallyReleaseValues(Ascending); + vpiCheckNonContinuousValuesForced; + vpiCheckContinuousValuesPartiallyReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesPartiallyReleased(); #1 vpiCheckNonContinuousValuesForced; vpiCheckContinuousValuesPartiallyReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesPartiallyReleased(); - #8 vpiCheckValuesPartiallyReleased(); + @(posedge clk) #1 vpiCheckValuesPartiallyReleased(); svCheckValuesPartiallyReleased(); `endif @@ -2290,14 +2401,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Forcing with %s, partially releasing through Verilog", forceDimIndexingMethod.name()); #8 vpiForceValues(forceDimIndexingMethod); - #8 vpiCheckValuesForced(); + vpiCheckValuesForced(); + svCheckValuesForced(); + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); @(negedge clk) svPartiallyReleaseValues(); + vpiCheckNonContinuousValuesForced; + vpiCheckContinuousValuesPartiallyReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesPartiallyReleased(); #1 vpiCheckNonContinuousValuesForced; vpiCheckContinuousValuesPartiallyReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesPartiallyReleased(); - #8 vpiCheckValuesPartiallyReleased(); + @(posedge clk) #1 vpiCheckValuesPartiallyReleased(); svCheckValuesPartiallyReleased(); end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); @@ -2320,14 +2437,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Forcing single bit with bit indexing method %s and dimension indexing method %s, releasing with dimension indexing method %s", bitIndexingMethod.name(), forceDimIndexingMethod.name(), releaseDimIndexingMethod.name()); #8 vpiForceSingleBit(bitIndexingMethod, forceDimIndexingMethod); - #8 vpiCheckSingleBitForced(); + vpiCheckSingleBitForced(); + svCheckSingleBitForced(); + @(posedge clk) #1 vpiCheckSingleBitForced(); svCheckSingleBitForced(); @(negedge clk) vpiReleaseSingleBitForcedValues(releaseDimIndexingMethod); + vpiCheckNonContinuousSingleBitForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousSingleBitForced(); + svCheckContinuousValuesReleased(); #1 vpiCheckNonContinuousSingleBitForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousSingleBitForced(); svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); releaseDimIndexingMethod = releaseDimIndexingMethod.next(); end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); @@ -2344,14 +2467,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Forcing single bit with bit indexing method %s and dimension indexing method %s, releasing through Verilog", bitIndexingMethod.name(), forceDimIndexingMethod.name()); #8 vpiForceSingleBit(bitIndexingMethod, forceDimIndexingMethod); - #8 vpiCheckSingleBitForced(); + vpiCheckSingleBitForced(); + svCheckSingleBitForced(); + @(posedge clk) #1 vpiCheckSingleBitForced(); svCheckSingleBitForced(); @(negedge clk) svReleaseValues(); + vpiCheckNonContinuousSingleBitForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousSingleBitForced(); + svCheckContinuousValuesReleased(); #1 vpiCheckNonContinuousSingleBitForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousSingleBitForced(); svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); forceDimIndexingMethod = forceDimIndexingMethod.next(); end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); @@ -2364,14 +2493,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Forcing single bit through Verilog, releasing with %s", releaseDimIndexingMethod.name()); #8 svForceSingleBit(); - #8 vpiCheckSingleBitForced(); + vpiCheckSingleBitForced(); + svCheckSingleBitForced(); + @(posedge clk) #1 vpiCheckSingleBitForced(); svCheckSingleBitForced(); @(negedge clk) vpiReleaseSingleBitForcedValues(releaseDimIndexingMethod); + vpiCheckNonContinuousSingleBitForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousSingleBitForced(); + svCheckContinuousValuesReleased(); #1 vpiCheckNonContinuousSingleBitForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousSingleBitForced(); svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); releaseDimIndexingMethod = releaseDimIndexingMethod.next(); end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); @@ -2379,14 +2514,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); // Force single bit through Verilog, release through Verilog if (`verbose) $display("*** Forcing single bit through Verilog, releasing through Verilog ***"); #8 svForceSingleBit(); - #8 vpiCheckSingleBitForced(); + vpiCheckSingleBitForced(); + svCheckSingleBitForced(); + @(posedge clk) #1 vpiCheckSingleBitForced(); svCheckSingleBitForced(); @(negedge clk) svReleaseValues(); + vpiCheckNonContinuousSingleBitForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousSingleBitForced(); + svCheckContinuousValuesReleased(); #1 vpiCheckNonContinuousSingleBitForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousSingleBitForced(); svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); + @(posedge clk) #1 vpiCheckValuesReleased(); svCheckValuesReleased(); // Force through VPI, release single bit through VPI @@ -2399,14 +2540,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Forcing with %s, releasing single bit with bit indexing method %s and dimension indexing method %s", forceDimIndexingMethod.name(), bitIndexingMethod.name(), releaseDimIndexingMethod.name()); #8 vpiForceValues(forceDimIndexingMethod); - #8 vpiCheckValuesForced(); + vpiCheckValuesForced(); + svCheckValuesForced(); + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); @(negedge clk) vpiReleaseSingleBit(bitIndexingMethod, releaseDimIndexingMethod); + vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesSingleBitReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesSingleBitReleased(); #1 vpiCheckNonContinuousValuesForced(); vpiCheckContinuousValuesSingleBitReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesSingleBitReleased(); - #8 vpiCheckSingleBitReleased(); + @(posedge clk) #1 vpiCheckSingleBitReleased(); svCheckSingleBitReleased(); releaseDimIndexingMethod = releaseDimIndexingMethod.next(); end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); @@ -2421,14 +2568,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Forcing with %s, releasing single bit through Verilog", forceDimIndexingMethod.name()); #8 vpiForceValues(forceDimIndexingMethod); - #8 vpiCheckValuesForced(); + vpiCheckValuesForced(); + svCheckValuesForced(); + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); @(negedge clk) svReleaseSingleBit(); + vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesSingleBitReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesSingleBitReleased(); #1 vpiCheckNonContinuousValuesForced(); vpiCheckContinuousValuesSingleBitReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesSingleBitReleased(); - #8 vpiCheckSingleBitReleased(); + @(posedge clk) #1 vpiCheckSingleBitReleased(); svCheckSingleBitReleased(); forceDimIndexingMethod = forceDimIndexingMethod.next(); end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); @@ -2441,14 +2594,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); do begin if (`verbose) $display("Forcing through Verilog, releasing single bit with bit indexing method %s and dimension indexing method %s", bitIndexingMethod.name(), releaseDimIndexingMethod.name()); #8 svForceValues(); - #8 vpiCheckValuesForced(); + vpiCheckValuesForced(); + svCheckValuesForced(); + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); @(negedge clk) vpiReleaseSingleBit(bitIndexingMethod, releaseDimIndexingMethod); + vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesSingleBitReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesSingleBitReleased(); #1 vpiCheckNonContinuousValuesForced(); vpiCheckContinuousValuesSingleBitReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesSingleBitReleased(); - #8 vpiCheckSingleBitReleased(); + @(posedge clk) #1 vpiCheckSingleBitReleased(); svCheckSingleBitReleased(); releaseDimIndexingMethod = releaseDimIndexingMethod.next(); end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); @@ -2458,14 +2617,20 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); // Force through Verilog, release single bit through Verilog if (`verbose) $display("*** Forcing through Verilog, releasing single bit through Verilog ***"); #8 svForceValues(); - #8 vpiCheckValuesForced(); + vpiCheckValuesForced(); + svCheckValuesForced(); + @(posedge clk) #1 vpiCheckValuesForced(); svCheckValuesForced(); @(negedge clk) svReleaseSingleBit(); + vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesSingleBitReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesSingleBitReleased(); #1 vpiCheckNonContinuousValuesForced(); vpiCheckContinuousValuesSingleBitReleased(); svCheckNonContinuousValuesForced(); svCheckContinuousValuesSingleBitReleased(); - #8 vpiCheckSingleBitReleased(); + @(posedge clk) #1 vpiCheckSingleBitReleased(); svCheckSingleBitReleased(); `endif `endif @@ -2486,6 +2651,7 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); $display("onebit: %x", onebit); $display("intval: %x", intval); + $display("scalarbit: %x", scalarbit); $display("vectorC: %x", vectorC); $display("vectorQ: %x", vectorQ); $display("vectorW: %x", vectorW); @@ -2524,6 +2690,7 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); $display("forcedNonForceableContinuously: %x", forcedNonForceableContinuously); $display("onebitContinuously: %x", onebitContinuously); $display("intvalContinuously: %x", intvalContinuously); + $display("scalarbitContinuously: %x", scalarbitContinuously); $display("vectorCContinuously: %x", vectorCContinuously); $display("vectorQContinuously: %x", vectorQContinuously); $display("vectorWContinuously: %x", vectorWContinuously); diff --git a/test_regress/t/t_vpi_forceable_var.py b/test_regress/t/t_vpi_forceable_var.py new file mode 100755 index 000000000..0232be9e0 --- /dev/null +++ b/test_regress/t/t_vpi_forceable_var.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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-FileCopyrightText: 2024 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') +test.pli_filename = "t/t_vpi_var.cpp" +test.top_filename = "t/t_vpi_var.v" + +test.compile(make_top_shell=False, + make_main=False, + make_pli=True, + sim_time=2100, + iv_flags2=["-DT_VPI_FORCEABLE_VAR"], + v_flags2=["+define+USE_VPI_NOT_DPI", "+define+T_VPI_FORCEABLE_VAR"], + verilator_flags2=[ + "-Wno-SYMRSVDWORD --exe --timing --vpi --no-l2name", test.pli_filename, + test.t_dir + "/t_vpi_forceable_var.vlt" + ]) + +test.execute(use_libvpi=True, all_run_flags=['+PLUS +INT=1234 +STRSTR']) + +test.passes() diff --git a/test_regress/t/t_vpi_forceable_var.vlt b/test_regress/t/t_vpi_forceable_var.vlt new file mode 100644 index 000000000..ae3035c4e --- /dev/null +++ b/test_regress/t/t_vpi_forceable_var.vlt @@ -0,0 +1,55 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Christian Hecken +// SPDX-License-Identifier: CC0-1.0 + +`verilator_config +forceable -module "t" -var "onebit" +forceable -module "t" -var "twoone" +// forceable -module "t" -var "LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND" // TODO: Causes Verilation error +forceable -module "t" -var "rev" +forceable -module "t" -var "count" +forceable -module "t" -var "half_count" +forceable -module "t" -var "delayed" +forceable -module "t" -var "\\escaped_with_brackets[3]" // TODO: Does not set signal as forceable +forceable -module "t" -var "text_byte" +forceable -module "t" -var "text_half" +forceable -module "t" -var "text_word" +forceable -module "t" -var "text_long" +forceable -module "t" -var "text" +forceable -module "t" -var "big" +forceable -module "t" -var "too_big" +forceable -module "t" -var "bit1" +forceable -module "t" -var "integer1" +forceable -module "t" -var "byte1" +forceable -module "t" -var "short1" +forceable -module "t" -var "int1" +forceable -module "t" -var "long1" +forceable -module "t" -var "real1" +forceable -module "t" -var "gen_sig" + +// forceable -module "sub" -var "subsig1" // TODO: Forceable signals break vpi_scan +// forceable -module "sub" -var "subsig2" // TODO: Forceable signals break vpi_scan + +forceable -module "subsub" -var "subsig1" +forceable -module "subsub" -var "subsig2" + +forceable -module "arr" -var "sig" +forceable -module "arr" -var "rfr" +forceable -module "arr" -var "\\escaped_sig[1]" // TODO: Does not set signal as forceable +forceable -module "arr" -var "check" +forceable -module "arr" -var "verbose" + +// The following signals have unpacked dimensions and are thus unsupported for forcing: +// fourthreetwoone +// quads +// delayed_mem +// mem_2d +// mem_3d +// multi_packed +// negative_multi_packed +// unpacked_only +// some_mem + +// str1 is not set as forceable because strings cannot be forced. diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index bd345a825..3afaf92ca 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -26,6 +26,9 @@ #elif defined(T_VPI_VAR3) #include "Vt_vpi_var3.h" #include "Vt_vpi_var3__Dpi.h" +#elif defined(T_VPI_FORCEABLE_VAR) +#include "Vt_vpi_forceable_var.h" +#include "Vt_vpi_forceable_var__Dpi.h" #else #include "Vt_vpi_var.h" #include "Vt_vpi_var__Dpi.h" diff --git a/test_regress/t/t_vpi_var.v b/test_regress/t/t_vpi_var.v index 78a27cbf4..e115a1c4d 100644 --- a/test_regress/t/t_vpi_var.v +++ b/test_regress/t/t_vpi_var.v @@ -138,6 +138,10 @@ extern "C" int mon_check(); end end +`ifdef T_VPI_FORCEABLE_VAR + #0; // TODO: Workaround to force signal initialization, else `gen_sig` stays at 0 +`endif + `ifdef VERILATOR status = $c32("mon_check()"); `endif