Internals: Extend VerilatedVar to hold force control signal pointers (#7217)
This commit is contained in:
parent
0019c12242
commit
9c5f4e2483
6
Changes
6
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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue