Internals: Extend VerilatedVar to hold force control signal pointers (#7217)

This commit is contained in:
Christian Hecken 2026-03-08 20:53:32 +01:00 committed by GitHub
parent 0019c12242
commit 9c5f4e2483
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 463 additions and 319 deletions

View File

@ -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]

View File

@ -50,7 +50,10 @@
#include "verilated_config.h"
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_imp.h"
#include "verilated_sym_props.h"
#include <algorithm>
#include <cctype>
@ -59,6 +62,7 @@
#include <iostream>
#include <limits>
#include <list>
#include <memory>
#include <sstream>
#include <utility>
@ -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<VerilatedVar*, VerilatedVar*> forceControlSignals,
int udims, int pdims...) VL_MT_UNSAFE {
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
std::unique_ptr<VerilatedForceControlSignals> verilatedForceControlSignalsp
= std::make_unique<VerilatedForceControlSignals>(
VerilatedForceControlSignals{forceControlSignals.first, forceControlSignals.second});
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(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

View File

@ -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<VerilatedVar*, VerilatedVar*> 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; }

View File

@ -28,6 +28,8 @@
#include "verilatedos.h"
#include "verilated.h"
#include <vector>
//===========================================================================
@ -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<const VerilatedForceControlSignals>
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<const VerilatedForceControlSignals> 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

View File

@ -25,6 +25,8 @@
//=========================================================================
#include "verilatedos.h"
#include <memory>
#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<VerilatedVpioVar, decltype(&VerilatedVpiImp::releaseVop)>;
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<PLI_BYTE8*>(forceEnableSignalName.c_str()), nullptr);
vpiHandle const forceValueSignalp // NOLINT(misc-misplaced-const)
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(forceValueSignalName.c_str()), nullptr);
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<VerilatedVpioVar>, std::unique_ptr<VerilatedVpioVar>>{
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<VerilatedVpioVar>, std::unique_ptr<VerilatedVpioVar>>{
nullptr, nullptr};
}
return std::pair<vopGuard_t, vopGuard_t>{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<VerilatedVpioVar>, std::unique_ptr<VerilatedVpioVar>>{
nullptr, nullptr};
}
// LCOV_EXCL_STOP
VerilatedVpioVar forceEnableSignal{forceEnableSignalVarp, baseSignalVop->scopep()};
VerilatedVpioVar forceValueSignal{forceValueSignalVarp, baseSignalVop->scopep()};
return std::pair<std::unique_ptr<VerilatedVpioVar>, std::unique_ptr<VerilatedVpioVar>>{
std::make_unique<VerilatedVpioVar>(forceEnableSignal),
std::make_unique<VerilatedVpioVar>(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, VerilatedVpiImp::vopGuard_t>{
VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop},
VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop}};
: std::pair<std::unique_ptr<VerilatedVpioVar>, std::unique_ptr<VerilatedVpioVar>>{
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<QData(const VerilatedVpioVarBase*, size_t, size_t)> 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, VerilatedVpiImp::vopGuard_t>{
VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop},
VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop}};
: std::pair<std::unique_ptr<VerilatedVpioVar>,
std::unique_ptr<VerilatedVpioVar>>{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;

View File

@ -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):

View File

@ -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;
}

View File

@ -24,6 +24,7 @@
#include "V3Stats.h"
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
@ -171,6 +172,259 @@ class EmitCSyms final : EmitCBaseVisitorConst {
return pos != std::string::npos ? scpname.substr(pos + 1) : scpname;
}
static std::tuple<int, int, std::string> 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<bool, std::string> 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<bool, std::string>{true, baseSignalName};
}
return std::pair<bool, std::string>{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<void*>(static_cast<const void*>(";
stmt += varName;
stmt += ".c_str())), true, ";
} else {
stmt += ", const_cast<void*>(static_cast<const void*>(&(";
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 std::string, ScopeVarData>::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<int, int, std::string> 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 std::string, ScopeVarData>::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<int, int, std::string> 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 std::string, ScopeVarData>::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 std::string, ScopeVarData>::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<const char*, 2> 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<std::string> 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<int, int, std::string> 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<bool, std::string> 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<void*>(static_cast<const void*>(";
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<void*>(static_cast<const void*>(&(";
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);
}
}

View File

@ -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<const std::string, const bool> 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<const VerilatedVar> removeSignalFromScope(const std::string& scopeName,
const std::string& signalName) {
const VerilatedScope* const scopep = Verilated::threadContextp()->scopeFind(scopeName.c_str());
if (!scopep) return nullptr;
VerilatedVarNameMap* const varsp = scopep->varsp();
const VerilatedVarNameMap::const_iterator foundSignalIt = varsp->find(signalName.c_str());
if (foundSignalIt == varsp->end()) return nullptr;
VerilatedVar foundSignal = foundSignalIt->second;
varsp->erase(foundSignalIt);
return std::make_unique<const VerilatedVar>(foundSignal);
}
bool insertSignalIntoScope(const std::pair<std::string, std::string>& scopeAndSignalNames,
const std::unique_ptr<const VerilatedVar> signal) {
const std::string& scopeName = scopeAndSignalNames.first;
const std::string& signalName = scopeAndSignalNames.second;
const VerilatedScope* const scopep = Verilated::threadContextp()->scopeFind(scopeName.c_str());
if (!scopep) return false;
VerilatedVarNameMap* const varsp = scopep->varsp();
// NOTE: The lifetime of the name inserted into varsp must be the same as the scopep, i.e. the
// same as threadContextp. Otherwise, the key in the m_varsp map will be a stale pointer.
// Hence, names of signals being inserted are stored in the static set, and it is assumed that
// the set's lifetime is the same as the threadContextp.
static std::set<std::string> insertedSignalNames;
const auto insertedSignalName = insertedSignalNames.insert(signalName);
varsp->insert(
std::pair<const char*, VerilatedVar>{insertedSignalName.first->c_str(), *signal});
return true;
}
int tryVpiGetWithMissingSignal(const TestVpiHandle& signalToGet, // NOLINT(misc-misplaced-const)
const PLI_INT32 signalFormat,
const std::pair<std::string, std::string>& scopeAndSignalNames,
const std::string& expectedErrorMessage) {
const std::string& scopeName = scopeAndSignalNames.first;
const std::string& signalNameToRemove = scopeAndSignalNames.second;
std::unique_ptr<const VerilatedVar> removedSignal
= removeSignalFromScope(scopeName, signalNameToRemove);
CHECK_RESULT_NZ(removedSignal); // NOLINT(concurrency-mt-unsafe)
s_vpi_value value_s{.format = signalFormat, .value = {}};
// Prevent program from terminating, so error message can be collected
Verilated::fatalOnVpiError(false);
vpi_get_value(signalToGet, &value_s);
// Re-enable so tests that should pass properly terminate the simulation on failure
Verilated::fatalOnVpiError(true);
std::pair<const std::string, const bool> receivedError = vpiGetErrorMessage();
const bool errorOccurred = receivedError.second;
const std::string receivedErrorMessage = receivedError.first;
CHECK_RESULT_NZ(errorOccurred); // NOLINT(concurrency-mt-unsafe)
// NOLINTNEXTLINE(concurrency-mt-unsafe,performance-avoid-endl)
CHECK_RESULT(receivedErrorMessage, expectedErrorMessage);
bool insertSuccess
= insertSignalIntoScope({scopeName, signalNameToRemove}, std::move(removedSignal));
CHECK_RESULT_NZ(insertSuccess); // NOLINT(concurrency-mt-unsafe)
return 0;
}
int tryVpiPutWithMissingSignal(const s_vpi_value value_s,
const TestVpiHandle& signalToPut, // NOLINT(misc-misplaced-const)
const int flag, const std::string& scopeName,
const std::string& signalNameToRemove,
const std::vector<std::string>& expectedErrorMessageSubstrings) {
std::unique_ptr<const VerilatedVar> removedSignal
= removeSignalFromScope(scopeName, signalNameToRemove);
CHECK_RESULT_NZ(removedSignal); // NOLINT(concurrency-mt-unsafe)
// Prevent program from terminating, so error message can be collected
Verilated::fatalOnVpiError(false);
vpi_put_value(signalToPut, const_cast<p_vpi_value>(&value_s), nullptr, flag);
// Re-enable so tests that should pass properly terminate the simulation on failure
Verilated::fatalOnVpiError(true);
std::pair<const std::string, const bool> receivedError = vpiGetErrorMessage();
const bool errorOccurred = receivedError.second;
const std::string receivedErrorMessage = receivedError.first;
CHECK_RESULT_NZ(errorOccurred); // NOLINT(concurrency-mt-unsafe)
const bool allExpectedErrorSubstringsFound
= std::all_of(expectedErrorMessageSubstrings.begin(), expectedErrorMessageSubstrings.end(),
[receivedErrorMessage](const std::string& expectedSubstring) {
return receivedErrorMessage.find(expectedSubstring) != std::string::npos;
});
CHECK_RESULT_NZ(allExpectedErrorSubstringsFound); // NOLINT(concurrency-mt-unsafe)
bool insertSuccess
= insertSignalIntoScope({scopeName, signalNameToRemove}, std::move(removedSignal));
CHECK_RESULT_NZ(insertSuccess); // NOLINT(concurrency-mt-unsafe)
return 0;
}
// Simpler function that expects an exact string instead of a number of substrings, and just a
// signalName instead of a handle.
#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<PLI_BYTE8*>(testSignalFullName.c_str()), nullptr);
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
#ifdef VERILATOR
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(tryVpiGetWithMissingSignal(
signalHandle, signalFormat, {scopeName, testSignalName + "__VforceEn"},
"vl_vpi_get_value: Signal '" + testSignalFullName
+ "' is marked forceable, but force control signals could not be retrieved. Error "
"message: getForceControlSignals: VPI force or release requested for '"
+ testSignalFullName + "', but vpiHandle '(nil)' of enable signal '"
+ testSignalFullName
+ "__VforceEn' could not be cast to VerilatedVpioVar*. Ensure signal is marked as "
"forceable"));
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(tryVpiGetWithMissingSignal(
signalHandle, signalFormat, {scopeName, testSignalName + "__VforceVal"},
"vl_vpi_get_value: Signal '" + testSignalFullName
+ "' is marked forceable, but force control signals could not be retrieved. Error "
"message: getForceControlSignals: VPI force or release requested for '"
+ testSignalFullName + "', but vpiHandle '(nil)' of value signal '"
+ testSignalFullName
+ "__VforceVal' could not be cast to VerilatedVpioVar*. Ensure signal is marked "
"as "
"forceable"));
#endif
std::unique_ptr<s_vpi_value> receivedValueSp = vpiValueWithFormat(signalFormat, {});
CHECK_RESULT_NZ(receivedValueSp); // NOLINT(concurrency-mt-unsafe)
vpi_get_value(signalHandle, receivedValueSp.get());
@ -411,34 +286,6 @@ int forceSignal(const std::string& scopeName, const std::string& testSignalName,
std::unique_ptr<s_vpi_value> value_sp = vpiValueWithFormat(signalFormat, forceValue);
CHECK_RESULT_NZ(value_sp); // NOLINT(concurrency-mt-unsafe)
#ifdef VERILATOR
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(tryVpiPutWithMissingSignal(
*value_sp, signalHandle, vpiForceFlag, scopeName, testSignalName + "__VforceEn",
{"vpi_put_value: Signal '" + testSignalFullName + "' with vpiHandle ",
// Exact handle address does not matter
" is marked forceable, but force control signals could not be retrieved. Error "
"message: getForceControlSignals: VPI force or release requested for '"
+ testSignalFullName + "', but vpiHandle '(nil)' of enable signal '"
+ testSignalFullName
+ "__VforceEn' could not be cast to VerilatedVpioVar*. Ensure signal is marked "
"as "
"forceable"}));
// NOLINTNEXTLINE(concurrency-mt-unsafe)
CHECK_RESULT_Z(tryVpiPutWithMissingSignal(
*value_sp, signalHandle, vpiForceFlag, scopeName, testSignalName + "__VforceVal",
{"vpi_put_value: Signal '" + testSignalFullName + "' with vpiHandle ",
// Exact handle address does not matter
" is marked forceable, but force control signals could not be retrieved. Error "
"message: getForceControlSignals: VPI force or release requested for '"
+ testSignalFullName + "', but vpiHandle '(nil)' of value signal '"
+ testSignalFullName
+ "__VforceVal' could not be cast to VerilatedVpioVar*. Ensure signal is marked "
"as "
"forceable"}));
#endif
vpi_put_value(signalHandle, value_sp.get(), nullptr, vpiForceFlag);
// NOLINTNEXTLINE(concurrency-mt-unsafe)
@ -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<PLI_BYTE8*>(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));

View File

@ -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);