mirror of
https://github.com/verilator/verilator.git
synced 2026-10-02 16:13:34 +02:00
Fix copy constructor of classes with constraints (#8381)
This commit is contained in:
@@ -585,6 +585,17 @@ public:
|
||||
++m_index;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<!std::is_class<T>::value || VlIsVlWide<T>::value, void>::type
|
||||
update_arr_table(T& var, const std::string& name, int /*dimension*/) {
|
||||
const std::string key = generateKey(name, m_index++);
|
||||
const auto it = m_arr_vars.find(key);
|
||||
assert(it != m_arr_vars.end());
|
||||
const ArrayInfo& arrayInfo = *it->second;
|
||||
it->second = std::make_shared<const ArrayInfo>(arrayInfo.m_name, &var, arrayInfo.m_index,
|
||||
arrayInfo.m_indices, arrayInfo.m_idxWidths);
|
||||
}
|
||||
|
||||
// This is the "Sender" API for the generated code.
|
||||
// The elements to make distinct are taken from the array element table at
|
||||
// solve time, so a container resized by the solver is handled correctly.
|
||||
@@ -606,6 +617,15 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively update pointers to all elements in an unpacked array
|
||||
template <typename T, std::size_t N_Depth>
|
||||
void update_arr_table(VlUnpacked<T, N_Depth>& var, const std::string& name, int dimension) {
|
||||
assert(dimension > 0);
|
||||
for (size_t i = 0; i < N_Depth; ++i) {
|
||||
update_arr_table(var[i], name + "[" + std::to_string(i) + "]", dimension - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively record all elements in a queue
|
||||
template <typename T, size_t N_MaxSize>
|
||||
void record_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
|
||||
@@ -621,6 +641,15 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively update pointers to all elements in a queue
|
||||
template <typename T, size_t N_MaxSize>
|
||||
void update_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension) {
|
||||
assert(dimension > 0);
|
||||
for (size_t i = 0; i < var.size(); ++i) {
|
||||
update_arr_table(var.atWrite(i), name + "[" + std::to_string(i) + "]", dimension - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively record all elements in an associative array
|
||||
template <typename T_Key, typename T_Value>
|
||||
void record_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string& name,
|
||||
@@ -651,6 +680,21 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively update pointers to all elements in an associative array
|
||||
template <typename T_Key, typename T_Value>
|
||||
void update_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string& name,
|
||||
int dimension) {
|
||||
assert(dimension > 0);
|
||||
for (auto it = var.begin(); it != var.end(); ++it) {
|
||||
const T_Key& key = it->first;
|
||||
std::string indexed_name;
|
||||
std::vector<size_t> integral_index;
|
||||
size_t idx_width = 0;
|
||||
process_key(key, indexed_name, integral_index, name, idx_width);
|
||||
update_arr_table(var.atWrite(key), indexed_name, dimension - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Register a single structArray element via write_var
|
||||
template <typename T>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
@@ -666,6 +710,12 @@ public:
|
||||
oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
update_struct_arr(T& var, const std::string& name) {
|
||||
update_var(var, name.c_str());
|
||||
}
|
||||
|
||||
// Recursively process VlUnpacked of structs
|
||||
template <typename T, std::size_t N_Depth>
|
||||
void record_struct_arr(VlUnpacked<T, N_Depth>& var, const std::string& name, int dimension,
|
||||
@@ -681,6 +731,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N_Depth>
|
||||
void update_struct_arr(VlUnpacked<T, N_Depth>& var, const std::string& name) {
|
||||
constexpr size_t idx_width = 1 << VL_CLOG2_CE_Q(VL_CLOG2_CE_Q(N_Depth) + 1);
|
||||
for (size_t i = 0; i < N_Depth; ++i) {
|
||||
std::ostringstream oss;
|
||||
oss << name << "." << std::hex << std::setw(int(idx_width / 4)) << std::setfill('0')
|
||||
<< static_cast<int>(i);
|
||||
update_struct_arr(var[i], oss.str());
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively process VlQueue of structs
|
||||
template <typename T, size_t N_MaxSize>
|
||||
void record_struct_arr(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
|
||||
@@ -695,6 +756,16 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, size_t N_MaxSize>
|
||||
void update_struct_arr(VlQueue<T, N_MaxSize>& var, const std::string& name) {
|
||||
for (size_t i = 0; i < var.size(); ++i) {
|
||||
std::ostringstream oss;
|
||||
oss << name << "." << std::hex << std::setw(8) << std::setfill('0')
|
||||
<< static_cast<int>(i);
|
||||
update_struct_arr(var.atWrite(i), oss.str());
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively process associative arrays of structs
|
||||
template <typename T_Key, typename T_Value>
|
||||
void record_struct_arr(VlAssocArray<T_Key, T_Value>& var, const std::string& name,
|
||||
@@ -722,6 +793,27 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T_Key, typename T_Value>
|
||||
void update_struct_arr(VlAssocArray<T_Key, T_Value>& var, const std::string& name) {
|
||||
for (auto it = var.begin(); it != var.end(); ++it) {
|
||||
const T_Key& key = it->first;
|
||||
|
||||
std::string indexed_name;
|
||||
std::vector<size_t> integral_index;
|
||||
size_t idx_width = 0;
|
||||
|
||||
process_key(key, indexed_name, integral_index, name, idx_width);
|
||||
std::ostringstream oss;
|
||||
for (size_t i = 0; i < integral_index.size(); ++i) {
|
||||
oss << std::hex << static_cast<int>(integral_index[i]);
|
||||
}
|
||||
|
||||
std::string result = oss.str();
|
||||
result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
|
||||
update_struct_arr(var.atWrite(key), name + "." + result);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Helper functions ---
|
||||
|
||||
// Helper: Register all members of a user-defined struct
|
||||
@@ -734,6 +826,87 @@ public:
|
||||
0)...};
|
||||
}
|
||||
|
||||
template <typename T, std::size_t... I>
|
||||
void updateMembers(T& obj, std::index_sequence<I...>, const std::string& baseName) {
|
||||
(void)std::initializer_list<int>{
|
||||
(update_var(std::get<I>(obj.getMembers(obj)),
|
||||
(baseName + "." + obj.memberNames()[I]).c_str()),
|
||||
0)...};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value && !IsVlUnpacked<T>::value,
|
||||
void>::type
|
||||
update_var(T& var, const char* name) {
|
||||
auto it = m_vars.find(name);
|
||||
assert(it != m_vars.end());
|
||||
it->second = std::make_shared<const VlRandomVar>(
|
||||
name, it->second->width(), &var, it->second->dimension(), it->second->randModeIdx());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<VlIsCustomStruct<T>::value, void>::type update_var(T& var,
|
||||
const char* name) {
|
||||
updateMembers(var, var.memberIndices(), name);
|
||||
}
|
||||
|
||||
template <typename T, size_t N_MaxSize>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
|
||||
update_var(VlQueue<T, N_MaxSize>& var, const char* name) {
|
||||
auto it = m_vars.find(name);
|
||||
assert(it != m_vars.end());
|
||||
const int dimension = it->second->dimension();
|
||||
it->second = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T, N_MaxSize>>>(
|
||||
name, it->second->width(), &var, dimension, it->second->randModeIdx());
|
||||
m_index = 0;
|
||||
update_arr_table(var, name, dimension);
|
||||
}
|
||||
|
||||
template <typename T, size_t N_MaxSize>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
update_var(VlQueue<T, N_MaxSize>& var, const char* name) {
|
||||
update_struct_arr(var, name);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N_Depth>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
|
||||
update_var(VlUnpacked<T, N_Depth>& var, const char* name) {
|
||||
auto it = m_vars.find(name);
|
||||
assert(it != m_vars.end());
|
||||
const int dimension = it->second->dimension();
|
||||
it->second = std::make_shared<const VlRandomArrayVarTemplate<VlUnpacked<T, N_Depth>>>(
|
||||
name, it->second->width(), &var, dimension, it->second->randModeIdx());
|
||||
m_index = 0;
|
||||
update_arr_table(var, name, dimension);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N_Depth>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
update_var(VlUnpacked<T, N_Depth>& var, const char* name) {
|
||||
update_struct_arr(var, name);
|
||||
}
|
||||
|
||||
template <typename T_Key, typename T_Value>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T_Value>::value, void>::type
|
||||
update_var(VlAssocArray<T_Key, T_Value>& var, const char* name) {
|
||||
auto it = m_vars.find(name);
|
||||
assert(it != m_vars.end());
|
||||
const int dimension = it->second->dimension();
|
||||
std::vector<size_t> keyWidths;
|
||||
VlRandomAssocKeyWidths<VlAssocArray<T_Key, T_Value>>::push(keyWidths);
|
||||
it->second
|
||||
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
|
||||
name, it->second->width(), &var, dimension, it->second->randModeIdx(), keyWidths);
|
||||
m_index = 0;
|
||||
update_arr_table(var, name, dimension);
|
||||
}
|
||||
|
||||
template <typename T_Key, typename T_Value>
|
||||
typename std::enable_if<VlContainsCustomStruct<T_Value>::value, void>::type
|
||||
update_var(VlAssocArray<T_Key, T_Value>& var, const char* name) {
|
||||
update_struct_arr(var, name);
|
||||
}
|
||||
|
||||
// Helper: Generate unique variable key from name and index
|
||||
static std::string generateKey(const std::string& name, int idx) {
|
||||
if (!name.empty() && name[0] == '\\') {
|
||||
|
||||
@@ -932,6 +932,7 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
|
||||
macro(RANDOMIZER_MARK_RANDC, "markRandc", false, "r") \
|
||||
macro(RANDOMIZER_SOLVE_BEFORE, "solveBefore", false, "rr") \
|
||||
macro(RANDOMIZER_PIN_VAR, "pin_var", false, "rrr") \
|
||||
macro(RANDOMIZER_UPDATE_VAR, "update_var", false, "TODO") \
|
||||
macro(RANDOMIZER_WRITE_VAR, "write_var", false, "TODO") \
|
||||
macro(RANDOMIZER_SET_VAR_DISABLED, "set_var_disabled", false, "r") \
|
||||
macro(RANDOMIZER_CLEAR_VAR_DISABLED, "clear_var_disabled", false, "r") \
|
||||
|
||||
@@ -114,6 +114,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
||||
bool m_isHideProtected : 1; // Verilog protected
|
||||
bool m_dpiPure : 1; // DPI import pure (vs. virtual pure)
|
||||
bool m_keepAlive : 1; // Disable dead function elimination
|
||||
bool m_needsSyms : 1; // Requires vlSymsp argument
|
||||
bool m_pureVirtual : 1; // Pure virtual
|
||||
bool m_recursive : 1; // Recursive or part of recursion
|
||||
bool m_static : 1; // Static method in class
|
||||
@@ -147,6 +148,7 @@ protected:
|
||||
, m_isHideProtected{false}
|
||||
, m_dpiPure{false}
|
||||
, m_keepAlive{false}
|
||||
, m_needsSyms{true}
|
||||
, m_pureVirtual{false}
|
||||
, m_recursive{false}
|
||||
, m_static{false}
|
||||
@@ -216,6 +218,8 @@ public:
|
||||
void dpiPure(bool flag) { m_dpiPure = flag; }
|
||||
bool keepAlive() const { return m_keepAlive; }
|
||||
void keepAlive(bool flag) { m_keepAlive = flag; }
|
||||
bool needsSyms() const { return m_needsSyms; }
|
||||
void needsSyms(bool flag) { m_needsSyms = flag; }
|
||||
bool pureVirtual() const { return m_pureVirtual; }
|
||||
void pureVirtual(bool flag) { m_pureVirtual = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
@@ -3010,6 +3014,8 @@ class AstClass final : public AstNodeModule {
|
||||
// @astgen ptr := m_covergroupEnclosingClassp : Optional[AstClass] // Lexical enclosing class
|
||||
uint32_t m_declTokenNum; // Declaration token number
|
||||
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||
bool m_hasRandVarsUpdate = false; // Has updateRandVars method,
|
||||
// which updates pointers to rand variables in clone()
|
||||
bool m_covergroup = false; // Is covergroup (TODO perhaps make a new Ast node type for CG?)
|
||||
bool m_extended = false; // Is extension or extended by other classes
|
||||
bool m_interfaceClass = false; // Interface class
|
||||
@@ -3038,6 +3044,8 @@ public:
|
||||
void covergroupEnclosingClassp(AstClass* classp) { m_covergroupEnclosingClassp = classp; }
|
||||
AstNode* membersp() const VL_MT_STABLE { return stmtsp(); }
|
||||
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
|
||||
bool hasRandVarsUpdate() const { return m_hasRandVarsUpdate; }
|
||||
void hasRandVarsUpdate(bool flag) { m_hasRandVarsUpdate = flag; }
|
||||
bool isCovergroup() const { return m_covergroup; }
|
||||
void isCovergroup(bool flag) { m_covergroup = flag; }
|
||||
bool isExtended() const { return m_extended; }
|
||||
|
||||
@@ -735,6 +735,7 @@ void AstClass::dump(std::ostream& str) const {
|
||||
if (isPrintedFrom()) str << " [PRINTED]";
|
||||
if (isVirtual()) str << " [VIRT]";
|
||||
if (needRNG()) str << " [NRNG]";
|
||||
if (hasRandVarsUpdate()) str << "[RANDVARUPD]";
|
||||
if (useVirtualPublic()) str << " [VIRPUB]";
|
||||
if (baseOverride().isAny()) str << " [" << baseOverride().ascii() << "]";
|
||||
if (cgAutoBinMax()) str << " cost=" << cgAutoBinMax();
|
||||
@@ -747,6 +748,7 @@ void AstClass::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isPrintedFrom);
|
||||
dumpJsonBoolFuncIf(str, isVirtual);
|
||||
dumpJsonBoolFuncIf(str, needRNG);
|
||||
dumpJsonBoolFuncIf(str, hasRandVarsUpdate);
|
||||
dumpJsonBoolFuncIf(str, useVirtualPublic);
|
||||
if (baseOverride().isAny()) dumpJsonStr(str, "baseOverride", baseOverride().ascii());
|
||||
dumpJsonNumFunc(str, cgAutoBinMax);
|
||||
|
||||
+10
-1
@@ -19,6 +19,7 @@
|
||||
#include "V3EmitC.h"
|
||||
#include "V3EmitCConstInit.h"
|
||||
#include "V3File.h"
|
||||
#include "V3MemberMap.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -34,6 +35,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
class EmitCHeader final : public EmitCConstInit {
|
||||
V3UniqueNames m_names;
|
||||
VMemberMap m_memberMap;
|
||||
// METHODS
|
||||
|
||||
class CoverCountVisitor final : public VNVisitorConst {
|
||||
@@ -270,12 +272,19 @@ class EmitCHeader final : public EmitCConstInit {
|
||||
}
|
||||
});
|
||||
const string className = EmitCUtil::prefixNameProtect(classp);
|
||||
if (embeddedCovergroupVars.empty()) {
|
||||
if (embeddedCovergroupVars.empty() && !classp->hasRandVarsUpdate()) {
|
||||
putns(classp,
|
||||
"VlClass* clone() const { return new " + className + "(*this); }\n");
|
||||
} else {
|
||||
putns(classp, "VlClass* clone() const { " + className + "* const clonep = new "
|
||||
+ className + "(*this); ");
|
||||
if (classp->hasRandVarsUpdate()) {
|
||||
const string updateName = "__VnoInFunc___VupdateRandVars";
|
||||
AstCFunc* const updatep
|
||||
= VN_AS(m_memberMap.findMember(classp, updateName), CFunc);
|
||||
UASSERT_OBJ(updatep, classp, "Missing updateRandVars method");
|
||||
puts("clonep->" + updatep->nameProtect() + "();\n");
|
||||
}
|
||||
for (const EmbeddedCovergroupVar& item : embeddedCovergroupVars) {
|
||||
puts("clonep->" + EmitCUtil::prefixNameProtect(item.first)
|
||||
+ "::" + item.second->nameProtect() + " = VlNull{}; ");
|
||||
|
||||
+44
-3
@@ -4583,6 +4583,43 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
return resExprp;
|
||||
}
|
||||
|
||||
void addUpdateRandVarsBody(AstClass* const nodep, AstFunc* const randomizep) {
|
||||
UASSERT_OBJ(!nodep->hasRandVarsUpdate(), nodep, "__VupdateRandVars already exists");
|
||||
AstFunc* const updatep
|
||||
= new AstFunc{nodep->fileline(), "__VupdateRandVars", nullptr, nullptr};
|
||||
updatep->classMethod(true);
|
||||
updatep->isVirtual(true);
|
||||
updatep->keepAlive(true);
|
||||
updatep->needsSyms(false);
|
||||
nodep->addMembersp(updatep);
|
||||
nodep->hasRandVarsUpdate(true);
|
||||
|
||||
for (AstClass* classp = nodep; classp;
|
||||
classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr) {
|
||||
AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask);
|
||||
for (AstNode* stmtp = newp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
AstStmtExpr* const stmtExprp = VN_CAST(stmtp, StmtExpr);
|
||||
if (!stmtExprp) continue;
|
||||
AstCMethodHard* const methodp = VN_CAST(stmtExprp->exprp(), CMethodHard);
|
||||
if (!methodp || methodp->method() != VCMethod::RANDOMIZER_WRITE_VAR) continue;
|
||||
|
||||
AstNodeVarRef* const varRefp = VN_AS(methodp->pinsp(), NodeVarRef);
|
||||
if (varRefp->varp()->lifetime().isStatic()) {
|
||||
// There is no need to update pointer to a static variable
|
||||
continue;
|
||||
}
|
||||
AstCMethodHard* const updateMethodp = new AstCMethodHard{
|
||||
methodp->fileline(), methodp->fromp()->cloneTreePure(false),
|
||||
VCMethod::RANDOMIZER_UPDATE_VAR, varRefp->cloneTreePure(false)};
|
||||
updateMethodp->dtypeSetVoid();
|
||||
AstNodeExpr* const namep = VN_AS(varRefp->nextp()->nextp(), NodeExpr);
|
||||
updateMethodp->addPinsp(namep->cloneTreePure(false));
|
||||
updateMethodp->method(VCMethod::RANDOMIZER_UPDATE_VAR);
|
||||
updatep->addStmtsp(updateMethodp->makeStmt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addBasicRandomizeBody(AstFunc* const basicRandomizep, AstClass* const nodep,
|
||||
AstVar* randModeVarp) {
|
||||
UINFO(9, "addBasicRTB " << nodep);
|
||||
@@ -5782,6 +5819,8 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
beginValp = new AstConst{fl, AstConst::WidthedValue{}, 32, 1};
|
||||
}
|
||||
|
||||
addUpdateRandVarsBody(nodep, randomizep);
|
||||
|
||||
AstFunc* const basicRandomizep
|
||||
= V3Randomize::newRandomizeFunc(m_memberMap, nodep, BASIC_RANDOMIZE_FUNC_NAME);
|
||||
addBasicRandomizeBody(basicRandomizep, nodep, randModeVarp);
|
||||
@@ -6337,9 +6376,10 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
= newResizeConstrainedArrayTask(classp, m_constraintp->name());
|
||||
m_constraintp->user3p(resizerTaskp);
|
||||
}
|
||||
AstVarRef* const sizeVarRefp = new AstVarRef{
|
||||
fl, VN_AS(sizeVarp->user2p(), NodeModule), sizeVarp, VAccess::READ};
|
||||
AstCMethodHard* const resizep = new AstCMethodHard{
|
||||
fl, nodep->fromp()->unlinkFrBack(), VCMethod::DYN_RESIZE,
|
||||
new AstVarRef{fl, sizeVarp, VAccess::READ}};
|
||||
fl, nodep->fromp()->unlinkFrBack(), VCMethod::DYN_RESIZE, sizeVarRefp};
|
||||
resizep->dtypep(nodep->findVoidDType());
|
||||
resizerTaskp->addStmtsp(new AstStmtExpr{fl, resizep});
|
||||
}
|
||||
@@ -6348,7 +6388,8 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
// to make sure it is always >= 0.
|
||||
m_constraintp->addItemsp(createSizeGteZeroConstraint(fl, sizeVarp));
|
||||
}
|
||||
AstVarRef* const sizeVarRefp = new AstVarRef{fl, sizeVarp, VAccess::READ};
|
||||
AstVarRef* const sizeVarRefp = new AstVarRef{fl, VN_AS(sizeVarp->user2p(), NodeModule),
|
||||
sizeVarp, VAccess::READ};
|
||||
sizeVarRefp->user1(true);
|
||||
nodep->replaceWith(sizeVarRefp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
|
||||
+2
-1
@@ -1383,7 +1383,8 @@ class TaskVisitor final : public VNVisitor {
|
||||
if (cfuncp->dpiImportWrapper()) cfuncp->cname(nodep->cname());
|
||||
|
||||
const bool needSyms
|
||||
= (!nodep->dpiImport() && !nodep->taskPublic()) || v3Global.opt.profExec();
|
||||
= nodep->needsSyms()
|
||||
&& ((!nodep->dpiImport() && !nodep->taskPublic()) || v3Global.opt.profExec());
|
||||
if (needSyms) cfuncp->argTypes(EmitCUtil::symClassVar());
|
||||
|
||||
if (!nodep->dpiImport() && !nodep->taskPublic()) {
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
]},
|
||||
{"type":"PACKAGE","name":"$unit","addr":"(E)","loc":"a,0:0,0:0","origName":"__024unit","verilogName":"\\$unit ","level":2,"depth":3,"inLibrary":true,"unconnectedDrive":"DEFAULT_FALSE","lifetime":"NONE","timeunit":"1ps",
|
||||
"stmtsp": [
|
||||
{"type":"CLASS","name":"Packet","addr":"(N)","loc":"d,7:1,7:6","needRNG":true,"cgAutoBinMax":-1,"origName":"Packet","verilogName":"Packet","level":3,"depth":4,"unconnectedDrive":"DEFAULT_FALSE","lifetime":"NONE","timeunit":"1ps",
|
||||
{"type":"CLASS","name":"Packet","addr":"(N)","loc":"d,7:1,7:6","needRNG":true,"hasRandVarsUpdate":true,"cgAutoBinMax":-1,"origName":"Packet","verilogName":"Packet","level":3,"depth":4,"unconnectedDrive":"DEFAULT_FALSE","lifetime":"NONE","timeunit":"1ps",
|
||||
"stmtsp": [
|
||||
{"type":"VAR","name":"header","addr":"(JB)","loc":"d,8:12,8:18","dtypep":"(KB)","origName":"header","verilogName":"header","direction":"NONE","declDirection":"NONE","lifetime":"VAUTOMI","varType":"MEMBER","dtypeName":"int","rand":"RAND"},
|
||||
{"type":"VAR","name":"length","addr":"(LB)","loc":"d,9:12,9:18","dtypep":"(KB)","origName":"length","verilogName":"length","direction":"NONE","declDirection":"NONE","lifetime":"VAUTOMI","varType":"MEMBER","dtypeName":"int","rand":"RAND"},
|
||||
@@ -763,17 +763,118 @@
|
||||
{"type":"TASKREF","name":"meth_setup_constraint","addr":"(NN)","loc":"d,56:14,56:18","dtypep":"(FC)","taskp":"(KM)","classOrPackagep":"(N)"}
|
||||
]}
|
||||
]},
|
||||
{"type":"FUNC","name":"__VupdateRandVars","addr":"(ON)","loc":"d,7:1,7:6","method":true,"isVirtual":true,"keepAlive":true,"lifetime":"NONE","cname":"__VupdateRandVars",
|
||||
"stmtsp": [
|
||||
{"type":"STMTEXPR","addr":"(PN)","loc":"d,8:12,8:18",
|
||||
"exprp": [
|
||||
{"type":"CMETHODHARD","name":"update_var","addr":"(QN)","loc":"d,8:12,8:18","dtypep":"(FC)",
|
||||
"fromp": [
|
||||
{"type":"VARREF","name":"constraint","addr":"(RN)","loc":"d,8:12,8:18","dtypep":"(JC)","access":"RW","varp":"(KC)","classOrPackagep":"(N)"}
|
||||
],
|
||||
"pinsp": [
|
||||
{"type":"VARREF","name":"header","addr":"(SN)","loc":"d,8:12,8:18","dtypep":"(KB)","access":"WR","varp":"(JB)","classOrPackagep":"(N)"},
|
||||
{"type":"CEXPR","addr":"(TN)","loc":"d,8:12,8:18","dtypep":"(KB)","pure":true,
|
||||
"nodesp": [
|
||||
{"type":"TEXT","addr":"(UN)","loc":"d,8:12,8:18","text":"\"header\""}
|
||||
]}
|
||||
]}
|
||||
]},
|
||||
{"type":"STMTEXPR","addr":"(VN)","loc":"d,9:12,9:18",
|
||||
"exprp": [
|
||||
{"type":"CMETHODHARD","name":"update_var","addr":"(WN)","loc":"d,9:12,9:18","dtypep":"(FC)",
|
||||
"fromp": [
|
||||
{"type":"VARREF","name":"constraint","addr":"(XN)","loc":"d,9:12,9:18","dtypep":"(JC)","access":"RW","varp":"(KC)","classOrPackagep":"(N)"}
|
||||
],
|
||||
"pinsp": [
|
||||
{"type":"VARREF","name":"length","addr":"(YN)","loc":"d,9:12,9:18","dtypep":"(KB)","access":"WR","varp":"(LB)","classOrPackagep":"(N)"},
|
||||
{"type":"CEXPR","addr":"(ZN)","loc":"d,9:12,9:18","dtypep":"(KB)","pure":true,
|
||||
"nodesp": [
|
||||
{"type":"TEXT","addr":"(AO)","loc":"d,9:12,9:18","text":"\"length\""}
|
||||
]}
|
||||
]}
|
||||
]},
|
||||
{"type":"STMTEXPR","addr":"(BO)","loc":"d,11:12,11:16",
|
||||
"exprp": [
|
||||
{"type":"CMETHODHARD","name":"update_var","addr":"(CO)","loc":"d,11:12,11:16","dtypep":"(FC)",
|
||||
"fromp": [
|
||||
{"type":"VARREF","name":"constraint","addr":"(DO)","loc":"d,11:12,11:16","dtypep":"(JC)","access":"RW","varp":"(KC)","classOrPackagep":"(N)"}
|
||||
],
|
||||
"pinsp": [
|
||||
{"type":"VARREF","name":"if_4","addr":"(EO)","loc":"d,11:12,11:16","dtypep":"(X)","access":"WR","varp":"(NB)","classOrPackagep":"(N)"},
|
||||
{"type":"CEXPR","addr":"(FO)","loc":"d,11:12,11:16","dtypep":"(X)","pure":true,
|
||||
"nodesp": [
|
||||
{"type":"TEXT","addr":"(GO)","loc":"d,11:12,11:16","text":"\"if_4\""}
|
||||
]}
|
||||
]}
|
||||
]},
|
||||
{"type":"STMTEXPR","addr":"(HO)","loc":"d,12:12,12:19",
|
||||
"exprp": [
|
||||
{"type":"CMETHODHARD","name":"update_var","addr":"(IO)","loc":"d,12:12,12:19","dtypep":"(FC)",
|
||||
"fromp": [
|
||||
{"type":"VARREF","name":"constraint","addr":"(JO)","loc":"d,12:12,12:19","dtypep":"(JC)","access":"RW","varp":"(KC)","classOrPackagep":"(N)"}
|
||||
],
|
||||
"pinsp": [
|
||||
{"type":"VARREF","name":"iff_5_6","addr":"(KO)","loc":"d,12:12,12:19","dtypep":"(X)","access":"WR","varp":"(OB)","classOrPackagep":"(N)"},
|
||||
{"type":"CEXPR","addr":"(LO)","loc":"d,12:12,12:19","dtypep":"(X)","pure":true,
|
||||
"nodesp": [
|
||||
{"type":"TEXT","addr":"(MO)","loc":"d,12:12,12:19","text":"\"iff_5_6\""}
|
||||
]}
|
||||
]}
|
||||
]},
|
||||
{"type":"STMTEXPR","addr":"(NO)","loc":"d,15:12,15:17",
|
||||
"exprp": [
|
||||
{"type":"CMETHODHARD","name":"update_var","addr":"(OO)","loc":"d,15:12,15:17","dtypep":"(FC)",
|
||||
"fromp": [
|
||||
{"type":"VARREF","name":"constraint","addr":"(PO)","loc":"d,15:12,15:17","dtypep":"(JC)","access":"RW","varp":"(KC)","classOrPackagep":"(N)"}
|
||||
],
|
||||
"pinsp": [
|
||||
{"type":"VARREF","name":"array","addr":"(QO)","loc":"d,15:12,15:17","dtypep":"(RB)","access":"WR","varp":"(QB)","classOrPackagep":"(N)"},
|
||||
{"type":"CEXPR","addr":"(RO)","loc":"d,15:12,15:17","dtypep":"(RB)","pure":true,
|
||||
"nodesp": [
|
||||
{"type":"TEXT","addr":"(SO)","loc":"d,15:12,15:17","text":"\"array\""}
|
||||
]}
|
||||
]}
|
||||
]},
|
||||
{"type":"STMTEXPR","addr":"(TO)","loc":"d,10:12,10:21",
|
||||
"exprp": [
|
||||
{"type":"CMETHODHARD","name":"update_var","addr":"(UO)","loc":"d,10:12,10:21","dtypep":"(FC)",
|
||||
"fromp": [
|
||||
{"type":"VARREF","name":"constraint","addr":"(VO)","loc":"d,10:12,10:21","dtypep":"(JC)","access":"RW","varp":"(KC)","classOrPackagep":"(N)"}
|
||||
],
|
||||
"pinsp": [
|
||||
{"type":"VARREF","name":"sublength","addr":"(WO)","loc":"d,10:12,10:21","dtypep":"(KB)","access":"WR","varp":"(MB)","classOrPackagep":"(N)"},
|
||||
{"type":"CEXPR","addr":"(XO)","loc":"d,10:12,10:21","dtypep":"(KB)","pure":true,
|
||||
"nodesp": [
|
||||
{"type":"TEXT","addr":"(YO)","loc":"d,10:12,10:21","text":"\"sublength\""}
|
||||
]}
|
||||
]}
|
||||
]},
|
||||
{"type":"STMTEXPR","addr":"(ZO)","loc":"d,13:12,13:23",
|
||||
"exprp": [
|
||||
{"type":"CMETHODHARD","name":"update_var","addr":"(AP)","loc":"d,13:12,13:23","dtypep":"(FC)",
|
||||
"fromp": [
|
||||
{"type":"VARREF","name":"constraint","addr":"(BP)","loc":"d,13:12,13:23","dtypep":"(JC)","access":"RW","varp":"(KC)","classOrPackagep":"(N)"}
|
||||
],
|
||||
"pinsp": [
|
||||
{"type":"VARREF","name":"if_state_ok","addr":"(CP)","loc":"d,13:12,13:23","dtypep":"(X)","access":"WR","varp":"(PB)","classOrPackagep":"(N)"},
|
||||
{"type":"CEXPR","addr":"(DP)","loc":"d,13:12,13:23","dtypep":"(X)","pure":true,
|
||||
"nodesp": [
|
||||
{"type":"TEXT","addr":"(EP)","loc":"d,13:12,13:23","text":"\"if_state_ok\""}
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
]},
|
||||
{"type":"FUNC","name":"__VBasicRand","addr":"(DF)","loc":"d,7:1,7:6","dtypep":"(T)","method":true,"lifetime":"NONE","cname":"__VBasicRand",
|
||||
"fvarp": [
|
||||
{"type":"VAR","name":"__VBasicRand","addr":"(ON)","loc":"d,7:1,7:6","dtypep":"(T)","origName":"__VBasicRand","verilogName":"__VBasicRand","direction":"OUTPUT","declDirection":"NONE","isFuncReturn":true,"isFuncLocal":true,"lifetime":"VAUTOM","varType":"MEMBER","dtypeName":"bit"}
|
||||
{"type":"VAR","name":"__VBasicRand","addr":"(FP)","loc":"d,7:1,7:6","dtypep":"(T)","origName":"__VBasicRand","verilogName":"__VBasicRand","direction":"OUTPUT","declDirection":"NONE","isFuncReturn":true,"isFuncLocal":true,"lifetime":"VAUTOM","varType":"MEMBER","dtypeName":"bit"}
|
||||
],
|
||||
"stmtsp": [
|
||||
{"type":"ASSIGN","addr":"(PN)","loc":"d,7:1,7:6","dtypep":"(T)",
|
||||
{"type":"ASSIGN","addr":"(GP)","loc":"d,7:1,7:6","dtypep":"(T)",
|
||||
"rhsp": [
|
||||
{"type":"CONST","name":"32'h1","addr":"(QN)","loc":"d,7:1,7:6","dtypep":"(EB)"}
|
||||
{"type":"CONST","name":"32'h1","addr":"(HP)","loc":"d,7:1,7:6","dtypep":"(EB)"}
|
||||
],
|
||||
"lhsp": [
|
||||
{"type":"VARREF","name":"__VBasicRand","addr":"(RN)","loc":"d,7:1,7:6","dtypep":"(T)","access":"WR","varp":"(ON)"}
|
||||
{"type":"VARREF","name":"__VBasicRand","addr":"(IP)","loc":"d,7:1,7:6","dtypep":"(T)","access":"WR","varp":"(FP)"}
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
@@ -789,12 +890,12 @@
|
||||
{"type":"BASICDTYPE","name":"bit","addr":"(X)","loc":"d,11:8,11:11","dtypep":"(X)","keyword":"bit","generic":true},
|
||||
{"type":"UNPACKARRAYDTYPE","addr":"(RB)","loc":"d,15:17,15:18","dtypep":"(RB)","declRange":"[0:1]","signed":true,"refDTypep":"(KB)",
|
||||
"rangep": [
|
||||
{"type":"RANGE","addr":"(SN)","loc":"d,15:17,15:18","ascending":true,"fromBracket":true,
|
||||
{"type":"RANGE","addr":"(JP)","loc":"d,15:17,15:18","ascending":true,"fromBracket":true,
|
||||
"leftp": [
|
||||
{"type":"CONST","name":"32'h0","addr":"(TN)","loc":"d,15:18,15:19","dtypep":"(EB)"}
|
||||
{"type":"CONST","name":"32'h0","addr":"(KP)","loc":"d,15:18,15:19","dtypep":"(EB)"}
|
||||
],
|
||||
"rightp": [
|
||||
{"type":"CONST","name":"32'h1","addr":"(UN)","loc":"d,15:18,15:19","dtypep":"(EB)"}
|
||||
{"type":"CONST","name":"32'h1","addr":"(LP)","loc":"d,15:18,15:19","dtypep":"(EB)"}
|
||||
]}
|
||||
]},
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(CK)","loc":"d,32:16,32:18","dtypep":"(CK)","keyword":"logic","generic":true,"signed":true},
|
||||
@@ -808,9 +909,9 @@
|
||||
]},
|
||||
{"type":"CONSTPOOL","addr":"(D)","loc":"a,0:0,0:0",
|
||||
"modulep": [
|
||||
{"type":"MODULE","name":"@CONST-POOL@","addr":"(VN)","loc":"a,0:0,0:0","origName":"@CONST-POOL@","verilogName":"@CONST-POOL@","unconnectedDrive":"DEFAULT_FALSE","lifetime":"NONE","timeunit":"NONE",
|
||||
{"type":"MODULE","name":"@CONST-POOL@","addr":"(MP)","loc":"a,0:0,0:0","origName":"@CONST-POOL@","verilogName":"@CONST-POOL@","unconnectedDrive":"DEFAULT_FALSE","lifetime":"NONE","timeunit":"NONE",
|
||||
"stmtsp": [
|
||||
{"type":"SCOPE","name":"@CONST-POOL@","addr":"(WN)","loc":"a,0:0,0:0","modp":"(VN)"}
|
||||
{"type":"SCOPE","name":"@CONST-POOL@","addr":"(NP)","loc":"a,0:0,0:0","modp":"(MP)"}
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
if not test.have_solver:
|
||||
test.skip("No constraint solver installed")
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,165 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
|
||||
// verilog_format: off
|
||||
`define check_rand(cl, field, cond) \
|
||||
begin \
|
||||
automatic longint prev_result; \
|
||||
automatic int ok = 0; \
|
||||
if (!bit'(cl.randomize())) $stop; \
|
||||
prev_result = longint'(field); \
|
||||
if (!(cond)) $stop; \
|
||||
repeat(20) begin \
|
||||
longint result; \
|
||||
if (!bit'(cl.randomize())) $stop; \
|
||||
result = longint'(field); \
|
||||
if (!(cond)) $stop; \
|
||||
if (result != prev_result) ok = 1; \
|
||||
prev_result = result; \
|
||||
end \
|
||||
if (ok != 1) $stop; \
|
||||
end
|
||||
// verilog_format: on
|
||||
|
||||
class Base;
|
||||
typedef struct {
|
||||
rand bit [6:0] lo;
|
||||
rand bit [8:0] hi;
|
||||
} pair_t;
|
||||
|
||||
rand bit [1:0] x;
|
||||
rand bit [1:0] y;
|
||||
rand pair_t pair;
|
||||
rand bit [6:0] fixed_arr[3];
|
||||
rand pair_t pair_fixed_arr[3];
|
||||
rand int queue[$];
|
||||
rand pair_t pair_queue[$];
|
||||
rand int assoc[string];
|
||||
rand pair_t pair_assoc[string];
|
||||
|
||||
rand static bit [5:0] stat_int;
|
||||
|
||||
function new();
|
||||
queue = '{1, 1};
|
||||
pair_queue = '{'{default: 0}, '{default: 0}};
|
||||
assoc["a"] = 0;
|
||||
assoc["b"] = 0;
|
||||
pair_assoc["a"] = '{default: 0};
|
||||
pair_assoc["b"] = '{default: 0};
|
||||
endfunction
|
||||
|
||||
constraint constr1 {
|
||||
x != 0;
|
||||
stat_int % 5 == 1;
|
||||
pair.lo != 0;
|
||||
pair.hi != 0;
|
||||
foreach (fixed_arr[i]) fixed_arr[i] != 0;
|
||||
foreach (pair_fixed_arr[i]) {
|
||||
pair_fixed_arr[i].lo != 0;
|
||||
}
|
||||
queue.size() inside {[3:5]};
|
||||
unique {queue};
|
||||
foreach (pair_queue[i]) {
|
||||
pair_queue[i].lo != 0;
|
||||
}
|
||||
foreach (assoc[key]) {assoc[key] inside {[50 : 70]};}
|
||||
foreach (pair_assoc[key]) {
|
||||
pair_assoc[key].hi != 0;
|
||||
}
|
||||
}
|
||||
endclass
|
||||
|
||||
class Derv extends Base;
|
||||
rand int z;
|
||||
constraint constr2 {
|
||||
x != 1;
|
||||
y % 2 == 1;
|
||||
z == 1;
|
||||
}
|
||||
endclass
|
||||
|
||||
class Derv2 extends Derv;
|
||||
endclass
|
||||
|
||||
|
||||
class Empty;
|
||||
endclass
|
||||
|
||||
class ClsRandC extends Empty;
|
||||
randc bit [3:0] x;
|
||||
endclass
|
||||
|
||||
module t;
|
||||
Base copied;
|
||||
Base base_for_copy;
|
||||
Derv derv;
|
||||
Derv2 derv2;
|
||||
|
||||
task test;
|
||||
`check_rand(copied, copied.x, copied.x > 1 && derv.x == 0);
|
||||
`check_rand(copied, copied.stat_int,
|
||||
copied.stat_int % 5 == 1 && copied.stat_int == derv.stat_int);
|
||||
`check_rand(copied, copied.y, copied.y % 2 == 1 && derv.y == 0);
|
||||
`check_rand(copied, copied.pair.lo, copied.pair.lo != 0 && derv.pair.lo == 0);
|
||||
`check_rand(copied, copied.fixed_arr[1], copied.fixed_arr[1] != 0 && derv.fixed_arr[1] == 0);
|
||||
`check_rand(copied, copied.pair_fixed_arr[2].lo,
|
||||
copied.pair_fixed_arr[2].lo != 0 && derv.pair_fixed_arr[2].lo == 0);
|
||||
`check_rand(copied, copied.queue.size(),
|
||||
copied.queue.size() inside {[3 : 5]} && derv.queue.size() == 2);
|
||||
foreach (copied.queue[i]) begin
|
||||
foreach (copied.queue[j]) begin
|
||||
if (i != j && copied.queue[i] == copied.queue[j]) $stop;
|
||||
end
|
||||
end
|
||||
if (derv.queue[0] != 1 || derv.queue[1] != 1) $stop;
|
||||
`check_rand(copied, copied.pair_queue[1].lo,
|
||||
copied.pair_queue[1].lo != 0 && derv.pair_queue[1].lo == 0);
|
||||
`check_rand(copied, copied.assoc["a"],
|
||||
copied.assoc["a"] >= 50 && copied.assoc["a"] <= 70 && derv.assoc["a"] == 0);
|
||||
`check_rand(copied, copied.pair_assoc["a"].hi,
|
||||
copied.pair_assoc["a"].hi != 0 && derv.pair_assoc["a"].hi == 0);
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
ClsRandC cr1, cr2;
|
||||
Empty empty1, empty2;
|
||||
bit [15:0] visited;
|
||||
|
||||
derv = new;
|
||||
base_for_copy = derv;
|
||||
copied = new base_for_copy;
|
||||
test();
|
||||
|
||||
derv2 = new;
|
||||
derv = derv2;
|
||||
base_for_copy = derv;
|
||||
copied = new base_for_copy;
|
||||
test();
|
||||
|
||||
if(derv.z != 0) $stop;
|
||||
$cast(derv, copied);
|
||||
void'(derv.randomize());
|
||||
if (derv.z != 1) $stop;
|
||||
|
||||
cr1 = new;
|
||||
repeat(8) begin
|
||||
cr1.randomize();
|
||||
visited[cr1.x] = 1;
|
||||
end
|
||||
empty1 = cr1;
|
||||
empty2 = new empty1;
|
||||
$cast(cr2, empty2);
|
||||
repeat(8) begin
|
||||
cr2.randomize();
|
||||
if (visited[cr2.x]) $stop;
|
||||
visited[cr2.x] = 1;
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Reference in New Issue
Block a user