diff --git a/Changes b/Changes index 79cbea2af..199946ef1 100644 --- a/Changes +++ b/Changes @@ -18,7 +18,7 @@ Verilator 5.047 devel * Support constraint imperfect distributions (#6811) (#7168). [Yilou Wang] * Support procedural concurrent assertion simple cases (#6944). * Support force assignments to array elements of real type (#7048). [Ryszard Rozak, Antmicro Ltd.] -* Support VPI array indexing in signal names (#7097) (#7187). [Christian Hecken] +* Support VPI array indexing in signal names (#7097) (#7187). [Christian Hecken, Heidelberg University] * Support soft constraint solving (#7124) (#7166). [Yilou Wang] * Support constraints on fixed-size array of class object members (#7170) (#7183). [Yilou Wang] * Support Z non-blocking assignment (#7192) (#496) (#7197). [Nick Brereton] @@ -71,7 +71,7 @@ Verilator 5.046 2026-02-28 * Support solve..before constraints (#5647) (#7123). [Yilou Wang] * Support structure initial values (#6130). * Support proper automatic/static initialization, and remove STATICVAR warning (#6405) (#7086). -* Support vpi_put/vpi_get forcing of signals (#5933) (#6704). [Christian Hecken] +* Support vpi_put/vpi_get forcing of signals (#5933) (#6704). [Christian Hecken, Heidelberg University] * Support detailed failure info for constraint violations (#6617) (#6883). [Yilou Wang] * Support `unique` constraints (on 1D static arrays) (#6810) (#6878). [Srinivasan Venkataramanan] * Support complex expressions as std::randomize arguments (#6860). [Jakub Wasilewski, Antmicro Ltd.] @@ -134,7 +134,7 @@ Verilator 5.046 2026-02-28 * Fix `foreach` with mid-index empty commas (#6910). * Fix internal error when fork under always expression (#6911). * Fix error when calling non-static method (#6916). [Artur Bieniek, Antmicro Ltd.] -* Fix memory leak in vpi_put_value and vpi_get_value (#6917). [Christian Hecken] +* Fix memory leak in vpi_put_value and vpi_get_value (#6917). [Christian Hecken, Heidelberg University] * Fix interface internal type reference (#6920) (#6966). [Todd Strader] * Fix segfault after assignment pattern XOR error (#6928) (#6931). [emmettifelts] * Fix delayed initial assignment (#6929). [Todd Strader] diff --git a/include/verilated.cpp b/include/verilated.cpp index 86a891df8..4511b37af 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -50,7 +50,10 @@ #include "verilated_config.h" #include "verilatedos.h" +#include "verilated.h" + #include "verilated_imp.h" +#include "verilated_sym_props.h" #include #include @@ -59,6 +62,7 @@ #include #include #include +#include #include #include @@ -3590,9 +3594,9 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_ } } -void VerilatedScope::varInsert(const char* namep, void* datap, bool isParam, - VerilatedVarType vltype, int vlflags, int udims, - int pdims...) VL_MT_UNSAFE { +VerilatedVar* VerilatedScope::varInsert(const char* namep, void* datap, bool isParam, + VerilatedVarType vltype, int vlflags, int udims, + int pdims...) VL_MT_UNSAFE { // Grab dimensions // In the future we may just create a large table at emit time and // statically construct from that. @@ -3617,7 +3621,39 @@ void VerilatedScope::varInsert(const char* namep, void* datap, bool isParam, } va_end(ap); - m_varsp->emplace(namep, var); + m_varsp->emplace(namep, std::move(var)); + return &(m_varsp->find(namep)->second); +} + +VerilatedVar* +VerilatedScope::forceableVarInsert(const char* namep, void* datap, bool isParam, + VerilatedVarType vltype, int vlflags, + std::pair forceControlSignals, + int udims, int pdims...) VL_MT_UNSAFE { + if (!m_varsp) m_varsp = new VerilatedVarNameMap; + + std::unique_ptr verilatedForceControlSignalsp + = std::make_unique( + VerilatedForceControlSignals{forceControlSignals.first, forceControlSignals.second}); + + 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. + for (int i = 0; i < pdims; ++i) { + const int msb = va_arg(ap, int); + const int lsb = va_arg(ap, int); + var.m_packed[i].m_left = msb; + var.m_packed[i].m_right = lsb; + } + va_end(ap); + + m_varsp->emplace(namep, std::move(var)); + return &(m_varsp->find(namep)->second); } // cppcheck-suppress unusedFunction // Used by applications diff --git a/include/verilated.h b/include/verilated.h index 15fdab267..6369ccdaa 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -731,8 +731,12 @@ public: // But internals only - called from verilated modules, VerilatedSyms ~VerilatedScope(); void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE; - void varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype, - int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE; + VerilatedVar* varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype, + int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE; + VerilatedVar* forceableVarInsert(const char* namep, void* datap, bool isParam, + VerilatedVarType vltype, int vlflags, + std::pair forceControlSignals, + int udims, int pdims...) VL_MT_UNSAFE; // ACCESSORS const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; } const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; } diff --git a/include/verilated_sym_props.h b/include/verilated_sym_props.h index 1cdd3a990..d88a38cb0 100644 --- a/include/verilated_sym_props.h +++ b/include/verilated_sym_props.h @@ -28,6 +28,8 @@ #include "verilatedos.h" +#include "verilated.h" + #include //=========================================================================== @@ -248,6 +250,14 @@ 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 @@ -256,6 +266,9 @@ class VerilatedVar final : public VerilatedVarProps { // MEMBERS void* const m_datap; // Location of data const char* const m_namep; // Name - slowpath + std::unique_ptr + m_forceControlSignals; // Force control signals + protected: const bool m_isParam; friend class VerilatedScope; @@ -266,13 +279,25 @@ protected: , m_datap{datap} , m_namep{namep} , m_isParam{isParam} {} + VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype, + VerilatedVarFlags vlflags, int udims, int pdims, bool isParam, + std::unique_ptr forceControlSignals) + : VerilatedVarProps{vltype, vlflags, udims, pdims} + , m_datap{datap} + , m_namep{namep} + , m_forceControlSignals{std::move(forceControlSignals)} + , m_isParam{isParam} {} public: ~VerilatedVar() = default; + VerilatedVar(VerilatedVar&&) = default; // ACCESSORS void* datap() const { return m_datap; } const char* name() const { return m_namep; } bool isParam() const { return m_isParam; } + const VerilatedForceControlSignals* forceControlSignals() const { + return m_forceControlSignals.get(); + } }; #endif // Guard diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index e4273ba30..ce0efb84e 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -25,6 +25,8 @@ //========================================================================= #include "verilatedos.h" + +#include #define VERILATOR_VERILATED_VPI_CPP_ #include "verilated.h" @@ -1127,25 +1129,6 @@ public: } static auto getForceControlSignals(const VerilatedVpioVarBase* vop); - // Used in the deleter of vopGuard_t, which is invoked upon - // destruction of the return value of getForceControlSignals. - // This means that it is called at the end of vpi_get_value whenever the signal - // is forceable and at the end of vpi_put_value whenever the signal is both forceable and - // either vpiForceFlag or vpiReleaseFlag is used. - // Because it is always automatically called at the end, it should not - // erase any previously issued errors or warnings. - static void releaseWithoutErrorReset(vpiHandle object) { - VerilatedVpiImp::assertOneCheck(); - VerilatedVpio* const vop = VerilatedVpio::castp(object); - VL_DO_DANGLING(delete vop, vop); - } - - static void releaseVop(VerilatedVpioVar* vop) { - releaseWithoutErrorReset(vop->castVpiHandle()); - } - - using vopGuard_t = std::unique_ptr; - static std::size_t vlTypeSize(VerilatedVarType vltype); static void setAllBitsToValue(const VerilatedVpioVar* vop, uint8_t bitValue) { assert(bitValue == 0 || bitValue == 1); @@ -1339,35 +1322,43 @@ VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE { return s().m_errorInfop; } -auto 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(forceEnableSignalName.c_str()), nullptr); - vpiHandle const forceValueSignalp // NOLINT(misc-misplaced-const) - = vpi_handle_by_name(const_cast(forceValueSignalName.c_str()), nullptr); - VerilatedVpioVar* forceEnableSignalVop = VerilatedVpioVar::castp(forceEnableSignalp); - VerilatedVpioVar* forceValueSignalVop = VerilatedVpioVar::castp(forceValueSignalp); - if (VL_UNLIKELY(!forceEnableSignalVop)) { +auto VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVarBase* const baseSignalVop) { + const VerilatedForceControlSignals* const forceControlSignals + = baseSignalVop->varp()->forceControlSignals(); + // LCOV_EXCL_START - Would require a Verilation time error, so cannot test + if (VL_UNLIKELY(!forceControlSignals)) { 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(!forceValueSignalVop)) { + "%s: VPI force or release requested for '%s', but signal has no force " + "control signals. Ensure signal is marked as forceable", + __func__, baseSignalVop->fullname()); + return std::pair, std::unique_ptr>{ + nullptr, nullptr}; + } // LCOV_EXCL_STOP + const VerilatedVar* const forceEnableSignalVarp = forceControlSignals->forceEnableSignalp; + const VerilatedVar* const forceValueSignalVarp = forceControlSignals->forceValueSignalp; + // LCOV_EXCL_START - Would require a Verilation time error, so cannot test + if (VL_UNLIKELY(!forceEnableSignalVarp)) { 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()); + "%s: VPI force or release requested for '%s', but force enable signal could " + "not be found. Ensure signal is marked as forceable", + __func__, baseSignalVop->fullname()); + return std::pair, std::unique_ptr>{ + nullptr, nullptr}; } - return std::pair{vopGuard_t{forceEnableSignalVop, releaseVop}, - vopGuard_t{forceValueSignalVop, releaseVop}}; + if (VL_UNLIKELY(!forceValueSignalVarp)) { + VL_VPI_ERROR_(__FILE__, __LINE__, + "%s: VPI force or release requested for '%s', but force value signal could " + "not be found. Ensure signal is marked as forceable", + __func__, baseSignalVop->fullname()); + return std::pair, std::unique_ptr>{ + nullptr, nullptr}; + } + // LCOV_EXCL_STOP + VerilatedVpioVar forceEnableSignal{forceEnableSignalVarp, baseSignalVop->scopep()}; + VerilatedVpioVar forceValueSignal{forceValueSignalVarp, baseSignalVop->scopep()}; + return std::pair, std::unique_ptr>{ + std::make_unique(forceEnableSignal), + std::make_unique(forceValueSignal)}; } std::size_t VerilatedVpiImp::vlTypeSize(const VerilatedVarType vltype) { @@ -2885,16 +2876,14 @@ 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. + // __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->varp()->isForceable() ? VerilatedVpiImp::getForceControlSignals(vop) - : std::pair{ - VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop}, - VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop}}; + : 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{}; @@ -2905,6 +2894,8 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { 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) @@ -2923,7 +2914,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { __func__, vop->fullname(), gotErrorMessage ? previousErrorMessage.c_str() : ""); return; - } + } // LCOV_EXCL_STOP const std::function getForceableSignalWord = [forceEnableSignalVop, forceValueSignalVop](const VerilatedVpioVarBase* baseSignalVop, @@ -3149,9 +3140,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ = baseSignalVop->varp()->isForceable() && (forceFlag == vpiForceFlag || forceFlag == vpiReleaseFlag) ? VerilatedVpiImp::getForceControlSignals(baseSignalVop) - : std::pair{ - VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop}, - VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop}}; + : std::pair, + std::unique_ptr>{nullptr, nullptr}; const VerilatedVpioVar* const forceEnableSignalVop = forceControlSignals.first.get(); const VerilatedVpioVar* const forceValueSignalVop = forceControlSignals.second.get(); t_vpi_error_info getForceControlSignalsError{}; @@ -3162,6 +3152,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ 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(baseSignalVop->varp()->isForceable() && (forceFlag == vpiForceFlag || forceFlag == vpiReleaseFlag) @@ -3180,7 +3172,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ __func__, baseSignalVop->fullname(), object, gotErrorMessage ? previousErrorMessage.c_str() : ""); return nullptr; - } + } // LCOV_EXCL_STOP const VerilatedVpioVar* const valueVop = (forceFlag == vpiForceFlag) ? forceValueSignalVop : baseSignalVop; diff --git a/nodist/log_changes b/nodist/log_changes index 293fb5c50..7690ebdd1 100755 --- a/nodist/log_changes +++ b/nodist/log_changes @@ -58,6 +58,8 @@ def process() -> None: author = am.group(1) if re.search(r'antmicro', email): author += ", Antmicro Ltd." + if re.search(r'stud\.uni-heidelberg', email): + author += ", Heidelberg University" if re.search(r'tenstorrent', email): author += ", Testorrent USA, Inc." if re.search(r'github action', author): diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 3af4938a3..3b55c4ee8 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -682,6 +682,12 @@ string AstVar::vlEnumDir() const { if (const AstBasicDType* const bdtypep = basicp()) { if (bdtypep->keyword().isDpiCLayout()) out += "|VLVF_DPI_CLAY"; } + // + if (dtypep()->skipRefp()->isSigned()) out += "|VLVF_SIGNED"; + // + if (AstBasicDType* const basicp = dtypep()->skipRefp()->basicp()) { + if (basicp->keyword() == VBasicDTypeKwd::BIT) out += "|VLVF_BITVAR"; + } return out; } diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 5a9c6398f..58bc6c621 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -24,6 +24,7 @@ #include "V3Stats.h" #include +#include #include #include @@ -171,6 +172,259 @@ class EmitCSyms final : EmitCBaseVisitorConst { return pos != std::string::npos ? scpname.substr(pos + 1) : scpname; } + static std::tuple getDimensions(const AstVar* const varp) { + int pdim = 0; + int udim = 0; + std::string bounds; + if (const AstBasicDType* const basicp = varp->basicp()) { + // Range is always first, it's not in "C" order + for (AstNodeDType* dtypep = varp->dtypep(); dtypep;) { + // Skip AstRefDType/AstTypedef, or return same node + dtypep = dtypep->skipRefp(); + if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) { + bounds += " ,"; + bounds += std::to_string(adtypep->left()); + bounds += ","; + bounds += std::to_string(adtypep->right()); + if (VN_IS(dtypep, PackArrayDType)) + pdim++; + else + udim++; + dtypep = adtypep->subDTypep(); + } else { + if (basicp->isRanged()) { + bounds += " ,"; + bounds += std::to_string(basicp->left()); + bounds += ","; + bounds += std::to_string(basicp->right()); + pdim++; + } + break; // AstBasicDType - nothing below, 1 + } + } + } + return {pdim, udim, bounds}; + } + + 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 + for (const std::string forceControlSuffix : {"__VforceEn", "__VforceVal", "__VforceRd"}) { + const std::size_t suffixPos = signalVarp->name().find(forceControlSuffix); + const bool suffixFound = suffixPos != std::string::npos; + const bool suffixIsAtEnd + = suffixPos + forceControlSuffix.length() == signalVarp->name().length(); + if (!(suffixFound && suffixIsAtEnd)) continue; + const std::string baseSignalName = signalVarp->name().substr(0, suffixPos); + return std::pair{true, baseSignalName}; + } + return std::pair{false, ""}; + } + + static std::string insertVarStatement(const ScopeVarData& svd, const AstScope* const scopep, + const AstVar* const varp, const int udim, const int pdim, + const std::string& bounds) { + std::string stmt; + stmt += protect("__Vscopep_" + svd.m_scopeName) + "->varInsert(\""; + stmt += V3OutFormatter::quoteNameControls(protect(svd.m_varBasePretty)) + '"'; + + const std::string varName = VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + + "." + protect(varp->name()); + + if (!varp->isParam()) { + stmt += ", &("; + stmt += varName; + stmt += "), false, "; + } else if (varp->vlEnumType() == "VLVT_STRING" + && !VN_IS(varp->subDTypep(), UnpackArrayDType)) { + stmt += ", const_cast(static_cast("; + stmt += varName; + stmt += ".c_str())), true, "; + } else { + stmt += ", const_cast(static_cast(&("; + stmt += varName; + stmt += "))), true, "; + } + + stmt += varp->vlEnumType(); // VLVT_UINT32 etc + stmt += ", "; + stmt += varp->vlEnumDir(); // VLVD_IN etc + stmt += ", "; + stmt += std::to_string(udim); + stmt += ", "; + stmt += std::to_string(pdim); + stmt += bounds; + stmt += ")"; + return stmt; + } + + std::string insertForceableVarStatement(const ScopeVarData& svd, const AstScope* const scopep, + const AstVar* const varp, const int udim, + const int pdim, const std::string& bounds) { + std::string stmt; + + stmt += protect("__Vscopep_" + svd.m_scopeName) + "->forceableVarInsert(\""; + stmt += V3OutFormatter::quoteNameControls(protect(svd.m_varBasePretty)) + '"'; + + const std::string varName = VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + + "." + protect(varp->name()); + + assert(!varp->isParam()); // Forceable params do not make sense + stmt += ", &("; + stmt += varName; + stmt += "), false, "; + + stmt += varp->vlEnumType(); // VLVT_UINT32 etc + stmt += ", "; + stmt += varp->vlEnumDir(); // VLVD_IN etc + stmt += ", {"; + + // Find __VforceEn + { + const std::string enableSignalKey = getKeyName(scopep, varp->name() + "__VforceEn"); + const std::map::const_iterator itpair + = m_scopeVars.find(enableSignalKey); + + if (itpair == m_scopeVars.end()) { + varp->v3fatalSrc("Signal " << varp->prettyNameQ() + << " is marked forceable, but the force enable signal '" + << varp->name() << "__VforceEn" + << "' can not be found in m_scopeVars with key '" + << enableSignalKey << "'."); + } + + const ScopeVarData& svd = itpair->second; + const AstScope* const scopep = svd.m_scopep; + const AstVar* const varp = svd.m_varp; + const std::tuple dimensions = getDimensions(varp); + const int pdim = std::get<0>(dimensions); + const int udim = std::get<1>(dimensions); + const std::string bounds = std::get<2>(dimensions); + stmt += insertVarStatement(svd, scopep, varp, udim, pdim, bounds); + } + stmt += ","; + // Find __VforceVal + { + const std::string valueSignalKey = getKeyName(scopep, varp->name() + "__VforceVal"); + const std::map::const_iterator itpair + = m_scopeVars.find(valueSignalKey); + + if (itpair == m_scopeVars.end()) { + varp->v3fatalSrc("Signal " << varp->prettyNameQ() + << " is marked forceable, but the force value signal '" + << varp->name() << "__VforceVal" + << "' can not be found in m_scopeVars with key '" + << valueSignalKey << "'."); + } + + const ScopeVarData& svd = itpair->second; + const AstScope* const scopep = svd.m_scopep; + const AstVar* const varp = svd.m_varp; + const std::tuple dimensions = getDimensions(varp); + const int pdim = std::get<0>(dimensions); + const int udim = std::get<1>(dimensions); + const std::string bounds = std::get<2>(dimensions); + stmt += insertVarStatement(svd, scopep, varp, udim, pdim, bounds); + } + + stmt += "}"; + stmt += ", "; + stmt += std::to_string(udim); + stmt += ", "; + stmt += std::to_string(pdim); + stmt += bounds; + stmt += ")"; + return stmt; + } + + static std::string getKeyName(const AstScope* const scopep, const std::string& signal_name) { + // Copies the process from `varsExpand` which created the keys in the first place, in order + // signal can be found. + std::string whole = scopep->name() + "__DOT__" + signal_name; + std::string scpName; + if (VString::startsWith(whole, "__DOT__TOP")) whole.replace(0, 10, ""); + const std::string::size_type dpos = whole.rfind("__DOT__"); + if (dpos != std::string::npos) { scpName = whole.substr(0, dpos); } + const std::string scpSym = scopeSymString(VName::dehash(scpName)); + return scpSym + " " + signal_name; + } + + bool baseSignalIsValid(const AstScope* const scopep, const AstVar* const controlSignalVarp, + const std::string& baseSignalName) const { + const std::string baseSignalKey = getKeyName(scopep, baseSignalName); + const std::map::const_iterator baseSignalIt + = m_scopeVars.find(baseSignalKey); + if (baseSignalIt == m_scopeVars.end()) { + // This means that the signal is forceable, but not public, so only the force control + // signals show up in the m_scopeVars, but not the base signal itself. + // Expect this branch not to be hit, because `baseSignalIsPublic` is checked before + // this, so this function will not get called in that case. + return false; + } else { + const AstVar* const baseSignalVarp = baseSignalIt->second.m_varp; + if (m_scopeVars.count(baseSignalName) > 1) { + baseSignalVarp->v3fatalSrc( + "Found Signal " << baseSignalName + << ", but also found at least one other signal with name" + << baseSignalName << " occurring in the same scope."); + return false; + } + if (!baseSignalVarp->isForceable()) { + controlSignalVarp->v3fatalSrc( + "Found signal " << controlSignalVarp->prettyNameQ() + << " which is a force control signal, but the base signal " + << baseSignalVarp->prettyNameQ() + << " is not marked as forceable."); + return false; + } + } + return true; + } + + bool baseSignalIsPublic(const AstScope* const scopep, + const std::string& baseSignalName) const { + const std::string baseSignalKey = getKeyName(scopep, baseSignalName); + const std::map::const_iterator baseSignalIt + = m_scopeVars.find(baseSignalKey); + if (baseSignalIt == m_scopeVars.end()) { + return false; + } else { + const AstVar* const baseSignalVarp = baseSignalIt->second.m_varp; + // Should not actually occur if the variable is in the m_scopeVars + if (!baseSignalVarp->isSigPublic()) return false; + } + return true; + } + + bool forceControlSignalsAreValid(const AstScope* const scopep, + const AstVar* const baseSignalVarp) const { + constexpr std::array forceControlSuffixes = {"__VforceEn", "__VforceVal"}; + return std::all_of( + forceControlSuffixes.begin(), forceControlSuffixes.end(), + [scopep, baseSignalVarp, this](const std::string& forceControlSuffix) { + const std::string controlSignalName = baseSignalVarp->name() + forceControlSuffix; + const std::string controlSignalKey = getKeyName(scopep, controlSignalName); + const std::size_t controlSignalCount = m_scopeVars.count(controlSignalKey); + if (controlSignalCount == 0) { + baseSignalVarp->v3fatalSrc("Signal " + << baseSignalVarp->prettyNameQ() + << " is marked forceable, but the control signal '" + << controlSignalName + << "' can not be found in m_scopeVars with key '" + << controlSignalKey << "'."); + return false; + } + if (controlSignalCount > 1) { + baseSignalVarp->v3fatalSrc("The control signal '" + << controlSignalName << "' for forceable signal " + << baseSignalVarp->prettyNameQ() + << " occurs several times within the same scope."); + return false; + } + return true; + }); + } + /// (scp, m_vpiScopeCandidates, m_scopeNames) -> m_scopeNames /// Look for parent scopes of scp in m_vpiScopeCandidates (separated by __DOT__ or ".") /// Then add/update entry in m_scopeNames if not already there @@ -770,74 +1024,28 @@ std::vector EmitCSyms::getSymCtorStmts() { const ScopeVarData& svd = itpair.second; const AstScope* const scopep = svd.m_scopep; const AstVar* const varp = svd.m_varp; - int pdim = 0; - int udim = 0; - std::string bounds; - if (const AstBasicDType* const basicp = varp->basicp()) { - // Range is always first, it's not in "C" order - for (AstNodeDType* dtypep = varp->dtypep(); dtypep;) { - // Skip AstRefDType/AstTypedef, or return same node - dtypep = dtypep->skipRefp(); - if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) { - bounds += " ,"; - bounds += std::to_string(adtypep->left()); - bounds += ","; - bounds += std::to_string(adtypep->right()); - if (VN_IS(dtypep, PackArrayDType)) - pdim++; - else - udim++; - dtypep = adtypep->subDTypep(); - } else { - if (basicp->isRanged()) { - bounds += " ,"; - bounds += std::to_string(basicp->left()); - bounds += ","; - bounds += std::to_string(basicp->right()); - pdim++; - } - break; // AstBasicDType - nothing below, 1 - } - } + const std::tuple dimensions = getDimensions(varp); + const int pdim = std::get<0>(dimensions); + const int udim = std::get<1>(dimensions); + const std::string bounds = std::get<2>(dimensions); + + const std::pair isForceControlResult = isForceControlSignal(varp); + const bool currentSignalIsForceControlSignal = isForceControlResult.first; + const std::string baseSignalName = isForceControlResult.second; + if (currentSignalIsForceControlSignal && baseSignalIsPublic(scopep, baseSignalName) + && baseSignalIsValid(scopep, varp, baseSignalName)) { + continue; } - std::string stmt; - stmt += protect("__Vscopep_" + svd.m_scopeName) + "->varInsert(\""; - stmt += V3OutFormatter::quoteNameControls(protect(svd.m_varBasePretty)) + '"'; - - const std::string varName - = VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "." - + protect(varp->name()); - - if (!varp->isParam()) { - stmt += ", &("; - stmt += varName; - stmt += "), false, "; - } else if (varp->vlEnumType() == "VLVT_STRING" - && !VN_IS(varp->subDTypep(), UnpackArrayDType)) { - stmt += ", const_cast(static_cast("; - stmt += varName; - stmt += ".c_str())), true, "; + if (varp->isForceable() && forceControlSignalsAreValid(scopep, varp)) { + const std::string stmt + = insertForceableVarStatement(svd, scopep, varp, udim, pdim, bounds) + ";"; + add(stmt); } else { - stmt += ", const_cast(static_cast(&("; - stmt += varName; - stmt += "))), true, "; + const std::string stmt + = insertVarStatement(svd, scopep, varp, udim, pdim, bounds) + ";"; + add(stmt); } - - stmt += varp->vlEnumType(); // VLVT_UINT32 etc - stmt += ", "; - stmt += varp->vlEnumDir(); // VLVD_IN etc - if (varp->dtypep()->skipRefp()->isSigned()) stmt += "|VLVF_SIGNED"; - if (AstBasicDType* const basicp = varp->dtypep()->skipRefp()->basicp()) { - if (basicp->keyword() == VBasicDTypeKwd::BIT) stmt += "|VLVF_BITVAR"; - } - stmt += ", "; - stmt += std::to_string(udim); - stmt += ", "; - stmt += std::to_string(pdim); - stmt += bounds; - stmt += ");"; - add(stmt); } } diff --git a/test_regress/t/t_vpi_force.cpp b/test_regress/t/t_vpi_force.cpp index 949f0d075..1ac7c3dd7 100644 --- a/test_regress/t/t_vpi_force.cpp +++ b/test_regress/t/t_vpi_force.cpp @@ -11,8 +11,6 @@ // 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() @@ -170,105 +168,7 @@ std::pair vpiGetErrorMessage() { 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 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(foundSignal); -} - -bool insertSignalIntoScope(const std::pair& scopeAndSignalNames, - const std::unique_ptr 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 insertedSignalNames; - const auto insertedSignalName = insertedSignalNames.insert(signalName); - - varsp->insert( - std::pair{insertedSignalName.first->c_str(), *signal}); - return true; -} - -int tryVpiGetWithMissingSignal(const TestVpiHandle& signalToGet, // NOLINT(misc-misplaced-const) - const PLI_INT32 signalFormat, - const std::pair& scopeAndSignalNames, - const std::string& expectedErrorMessage) { - const std::string& scopeName = scopeAndSignalNames.first; - const std::string& signalNameToRemove = scopeAndSignalNames.second; - std::unique_ptr 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 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& expectedErrorMessageSubstrings) { - std::unique_ptr 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(&value_s), nullptr, flag); - // Re-enable so tests that should pass properly terminate the simulation on failure - Verilated::fatalOnVpiError(true); - - std::pair 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. +#ifdef VERILATOR 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; @@ -358,31 +258,6 @@ int checkValue(const std::string& scopeName, const std::string& testSignalName, = vpi_handle_by_name(const_cast(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 receivedValueSp = vpiValueWithFormat(signalFormat, {}); CHECK_RESULT_NZ(receivedValueSp); // NOLINT(concurrency-mt-unsafe) vpi_get_value(signalHandle, receivedValueSp.get()); @@ -411,34 +286,6 @@ int forceSignal(const std::string& scopeName, const std::string& testSignalName, std::unique_ptr 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) @@ -465,34 +312,6 @@ int releaseSignal(const std::string& scopeName, const std::string& testSignalNam = 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) @@ -666,8 +485,7 @@ extern "C" int putString() { 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. +// This function is needed to ensure that vpiInertialDelay also works with forceable signals. extern "C" int putInertialDelay() { const std::string fullSignalName = std::string{scopeName} + ".delayed"; TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const) @@ -691,15 +509,31 @@ extern "C" int putInertialDelay() { // NOLINTNEXTLINE(concurrency-mt-unsafe) CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, signalHandle), value_s, expectedValueS)); - // Check that the put is executed upon doInertialPuts + // NOTE: Because __VforceRd will only be updated in `eval_act`, `doInertialPuts` does not + // update the value read by `vpi_get_value` - that is only visible after another `eval`. Hence, + // `checkInertialDelay` must be called later to check success. VerilatedVpi::doInertialPuts(); - value_s.value.integer = 0; // Ensure that test fails if value_s is not updated + return 0; +} + +extern "C" int checkInertialDelay() { + const std::string fullSignalName = std::string{scopeName} + ".delayed"; + TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const) + = vpi_handle_by_name(const_cast(fullSignalName.c_str()), nullptr); + CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe) + + s_vpi_value value_s{.format = vpiIntVal, + .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; + constexpr int delayedValue = 123; + s_vpi_value expectedValueS{.format = vpiIntVal, .value = {.integer = delayedValue}}; + // NOLINTNEXTLINE(concurrency-mt-unsafe) CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, signalHandle), value_s, expectedValueS)); diff --git a/test_regress/t/t_vpi_force.v b/test_regress/t/t_vpi_force.v index f6041846e..d4f04c15e 100644 --- a/test_regress/t/t_vpi_force.v +++ b/test_regress/t/t_vpi_force.v @@ -37,6 +37,7 @@ module Test ( extern "C" int putString(); extern "C" int tryInvalidPutOperations(); extern "C" int putInertialDelay(); + extern "C" int checkInertialDelay(); extern "C" int forceValues(); extern "C" int releaseValues(); extern "C" int releasePartiallyForcedValues(); @@ -50,6 +51,7 @@ module Test ( import "DPI-C" context function int putString(); import "DPI-C" context function int tryInvalidPutOperations(); import "DPI-C" context function int putInertialDelay(); + import "DPI-C" context function int checkInertialDelay(); `endif import "DPI-C" context function int forceValues(); import "DPI-C" context function int releaseValues(); @@ -62,6 +64,11 @@ module Test ( // Verify that vpi_put_value still works for strings string str1 /*verilator public_flat_rw*/; // std::string + // Verify that EmitCSyms changes still allow for forceable, but not + // public_flat_rw signals. This signal is only forced and checked in this + // SystemVerilog testbench, but not through VPI. + logic nonPublic /*verilator forceable*/; // CData + // Verify that vpi_put_value still works with vpiInertialDelay logic [ 31:0] delayed `PUBLIC_FORCEABLE; // IData @@ -131,6 +138,8 @@ module Test ( wire [ 63:0] decStringQContinuously `PUBLIC_FORCEABLE; // QData always @(posedge clk) begin + nonPublic <= 1; + onebit <= 1; intval <= 32'hAAAAAAAA; @@ -315,6 +324,27 @@ module Test ( end endtask + task automatic vpiCheckInertialDelay(); + 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("checkInertialDelay()"); +`else + vpiStatus = checkInertialDelay(); +`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_get_value to check result of previous vpi_put_value with vpiInertialDelay failed)"); + $stop; + end + endtask + task automatic vpiForceValues(); integer vpiStatus = 1; `ifdef VERILATOR @@ -603,10 +633,15 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); vpiPutString(); vpiTryInvalidPutOperations(); vpiPutInertialDelay(); + #1 vpiCheckInertialDelay(); + // Force and check non-public, but forceable signal + force nonPublic = 0; + #4 if(nonPublic != 0) $stop; + release nonPublic; + #4 if (nonPublic != 1) $stop; `endif - // Wait a bit before triggering the force to see a change in the traces - #4 vpiForceValues(); + 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 @@ -660,6 +695,8 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); always @(posedge clk or negedge clk) begin $display("time: %0t\tclk:%b", $time, clk); + $display("nonPublic: %x", nonPublic); + $display("str1: %s", str1); $display("delayed: %x", delayed);