diff --git a/include/verilated_random.h b/include/verilated_random.h index fb0c0b769..ad5ad131f 100644 --- a/include/verilated_random.h +++ b/include/verilated_random.h @@ -585,6 +585,17 @@ public: ++m_index; } + template + typename std::enable_if::value || VlIsVlWide::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(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 + void update_arr_table(VlUnpacked& 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 void record_arr_table(VlQueue& var, const std::string& name, int dimension, @@ -621,6 +641,15 @@ public: } } + // Recursively update pointers to all elements in a queue + template + void update_arr_table(VlQueue& 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 void record_arr_table(VlAssocArray& var, const std::string& name, @@ -651,6 +680,21 @@ public: } } + // Recursively update pointers to all elements in an associative array + template + void update_arr_table(VlAssocArray& 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 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 std::enable_if::value, void>::type @@ -666,6 +710,12 @@ public: oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL); } + template + typename std::enable_if::value, void>::type + update_struct_arr(T& var, const std::string& name) { + update_var(var, name.c_str()); + } + // Recursively process VlUnpacked of structs template void record_struct_arr(VlUnpacked& var, const std::string& name, int dimension, @@ -681,6 +731,17 @@ public: } } + template + void update_struct_arr(VlUnpacked& 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(i); + update_struct_arr(var[i], oss.str()); + } + } + // Recursively process VlQueue of structs template void record_struct_arr(VlQueue& var, const std::string& name, int dimension, @@ -695,6 +756,16 @@ public: } } + template + void update_struct_arr(VlQueue& 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(i); + update_struct_arr(var.atWrite(i), oss.str()); + } + } + // Recursively process associative arrays of structs template void record_struct_arr(VlAssocArray& var, const std::string& name, @@ -722,6 +793,27 @@ public: } } + template + void update_struct_arr(VlAssocArray& 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 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(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 + void updateMembers(T& obj, std::index_sequence, const std::string& baseName) { + (void)std::initializer_list{ + (update_var(std::get(obj.getMembers(obj)), + (baseName + "." + obj.memberNames()[I]).c_str()), + 0)...}; + } + + template + typename std::enable_if::value && !IsVlUnpacked::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( + name, it->second->width(), &var, it->second->dimension(), it->second->randModeIdx()); + } + + template + typename std::enable_if::value, void>::type update_var(T& var, + const char* name) { + updateMembers(var, var.memberIndices(), name); + } + + template + typename std::enable_if::value, void>::type + update_var(VlQueue& 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>>( + name, it->second->width(), &var, dimension, it->second->randModeIdx()); + m_index = 0; + update_arr_table(var, name, dimension); + } + + template + typename std::enable_if::value, void>::type + update_var(VlQueue& var, const char* name) { + update_struct_arr(var, name); + } + + template + typename std::enable_if::value, void>::type + update_var(VlUnpacked& 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>>( + name, it->second->width(), &var, dimension, it->second->randModeIdx()); + m_index = 0; + update_arr_table(var, name, dimension); + } + + template + typename std::enable_if::value, void>::type + update_var(VlUnpacked& var, const char* name) { + update_struct_arr(var, name); + } + + template + typename std::enable_if::value, void>::type + update_var(VlAssocArray& var, const char* name) { + auto it = m_vars.find(name); + assert(it != m_vars.end()); + const int dimension = it->second->dimension(); + std::vector keyWidths; + VlRandomAssocKeyWidths>::push(keyWidths); + it->second + = std::make_shared>>( + name, it->second->width(), &var, dimension, it->second->randModeIdx(), keyWidths); + m_index = 0; + update_arr_table(var, name, dimension); + } + + template + typename std::enable_if::value, void>::type + update_var(VlAssocArray& 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] == '\\') { diff --git a/src/V3AstAttr.h b/src/V3AstAttr.h index bd0867077..4d5dcab2e 100644 --- a/src/V3AstAttr.h +++ b/src/V3AstAttr.h @@ -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") \ diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index d1983695d..43f34081d 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -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; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index c689d4447..595cc7af4 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -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); diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 08e8d6a0c..dd8b28322 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -19,6 +19,7 @@ #include "V3EmitC.h" #include "V3EmitCConstInit.h" #include "V3File.h" +#include "V3MemberMap.h" #include "V3UniqueNames.h" #include @@ -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{}; "); diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 382de6c54..34fc5b0aa 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -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); diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 1d2c87025..a089d3942 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -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()) { diff --git a/test_regress/t/t_constraint_json_only.out b/test_regress/t/t_constraint_json_only.out index bc779fd38..8f8dea845 100644 --- a/test_regress/t/t_constraint_json_only.out +++ b/test_regress/t/t_constraint_json_only.out @@ -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)"} ]} ]} ]} diff --git a/test_regress/t/t_randomize_copy_constr.py b/test_regress/t/t_randomize_copy_constr.py new file mode 100755 index 000000000..db1adb3f9 --- /dev/null +++ b/test_regress/t/t_randomize_copy_constr.py @@ -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() diff --git a/test_regress/t/t_randomize_copy_constr.v b/test_regress/t/t_randomize_copy_constr.v new file mode 100644 index 000000000..f0b273643 --- /dev/null +++ b/test_regress/t/t_randomize_copy_constr.v @@ -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