Support inline random variable control (#5317)

Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
This commit is contained in:
Krzysztof Bieganski 2024-08-13 20:20:31 +02:00 committed by GitHub
parent 124e3463fc
commit 6cb0a41857
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 758 additions and 85 deletions

View File

@ -1680,6 +1680,36 @@ public:
bool operator==(const VSelfPointerText& other) const { return *m_strp == *other.m_strp; }
};
// ######################################################################
// Defines what kind of randomization is done on a variable
class VRandAttr final {
public:
enum en : uint8_t {
NONE, // Not randomizable
RAND, // Has a rand modifier
RAND_CYCLIC, // Has a randc modifier
RAND_INLINE // Not rand/randc, but used in inline random variable control
};
enum en m_e;
const char* ascii() const {
static const char* const names[] = {"NONE", "RAND", "RANDC", "RAND-INLINE"};
return names[m_e];
}
VRandAttr()
: m_e{NONE} {}
// cppcheck-suppress noExplicitConstructor
constexpr VRandAttr(en _e)
: m_e{_e} {}
constexpr operator en() const { return m_e; }
bool isRandomizable() const { return m_e != NONE; }
bool isRand() const { return m_e == RAND || m_e == RAND_CYCLIC; }
bool isRandC() const { return m_e == RAND_CYCLIC; }
};
inline std::ostream& operator<<(std::ostream& os, const VRandAttr& rhs) {
return os << rhs.ascii();
}
//######################################################################
// AstNUser - Generic base class for AST User nodes.
// - Also used to allow parameter passing up/down iterate calls

View File

@ -1762,6 +1762,7 @@ class AstVar final : public AstNode {
VDirection m_declDirection; // Declared direction input/output etc
VLifetime m_lifetime; // Lifetime
VVarAttrClocker m_attrClocker;
VRandAttr m_rand; // Randomizability of this variable (rand, randc, etc)
int m_pinNum = 0; // For XML, if non-zero the connection pin number
bool m_ansi : 1; // Params or pins declared in the module header, rather than the body
bool m_declTyped : 1; // Declared as type (for dedup check)
@ -1785,8 +1786,6 @@ class AstVar final : public AstNode {
bool m_attrSFormat : 1; // User sformat attribute
bool m_attrSplitVar : 1; // declared with split_var metacomment
bool m_fileDescr : 1; // File descriptor
bool m_isRand : 1; // Random variable
bool m_isRandC : 1; // Random cyclic variable (isRand also set)
bool m_isConst : 1; // Table contains constant data
bool m_isContinuously : 1; // Ever assigned continuously (for force/release)
bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier
@ -1831,8 +1830,6 @@ class AstVar final : public AstNode {
m_attrSFormat = false;
m_attrSplitVar = false;
m_fileDescr = false;
m_isRand = false;
m_isRandC = false;
m_isConst = false;
m_isContinuously = false;
m_hasStrengthAssignment = false;
@ -1957,6 +1954,7 @@ public:
void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; }
void attrSFormat(bool flag) { m_attrSFormat = flag; }
void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
void rand(const VRandAttr flag) { m_rand = flag; }
void usedClock(bool flag) { m_usedClock = flag; }
void usedParam(bool flag) { m_usedParam = flag; }
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
@ -1973,11 +1971,6 @@ public:
void sc(bool flag) { m_sc = flag; }
void scSensitive(bool flag) { m_scSensitive = flag; }
void primaryIO(bool flag) { m_primaryIO = flag; }
void isRand(bool flag) { m_isRand = flag; }
void isRandC(bool flag) {
m_isRandC = flag;
if (flag) isRand(true);
}
void isConst(bool flag) { m_isConst = flag; }
void isContinuously(bool flag) { m_isContinuously = flag; }
void isStatic(bool flag) { m_isStatic = flag; }
@ -2066,8 +2059,8 @@ public:
bool isSigUserRdPublic() const { return m_sigUserRdPublic; }
bool isSigUserRWPublic() const { return m_sigUserRWPublic; }
bool isTrace() const { return m_trace; }
bool isRand() const { return m_isRand; }
bool isRandC() const { return m_isRandC; }
bool isRand() const { return m_rand.isRand(); }
bool isRandC() const { return m_rand.isRandC(); }
bool isConst() const VL_MT_SAFE { return m_isConst; }
bool isStatic() const VL_MT_SAFE { return m_isStatic; }
bool isLatched() const { return m_isLatched; }
@ -2084,6 +2077,7 @@ public:
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
AstIface* sensIfacep() const { return m_sensIfacep; }
VVarAttrClocker attrClocker() const { return m_attrClocker; }
VRandAttr rand() const { return m_rand; }
string verilogKwd() const override;
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VLifetime lifetime() const { return m_lifetime; }

View File

@ -2395,11 +2395,7 @@ void AstVar::dump(std::ostream& str) const {
if (isInternal()) str << " [INTERNAL]";
if (isLatched()) str << " [LATCHED]";
if (isUsedLoopIdx()) str << " [LOOP]";
if (isRandC()) {
str << " [RANDC]";
} else if (isRand()) {
str << " [RAND]";
}
if (rand().isRandomizable()) str << rand();
if (noReset()) str << " [!RST]";
if (attrIsolateAssign()) str << " [aISO]";
if (attrFileDescr()) str << " [aFD]";

View File

@ -759,6 +759,7 @@ class LinkDotFindVisitor final : public VNVisitor {
bool m_explicitNew = false; // Hit a "new" function
int m_modBlockNum = 0; // Begin block number in module, 0=none seen
int m_modWithNum = 0; // With block number, 0=none seen
int m_modArgNum = 0; // Arg block number for randomize(), 0=none seen
// METHODS
void makeImplicitNew(AstClass* nodep) {
@ -871,6 +872,7 @@ class LinkDotFindVisitor final : public VNVisitor {
VL_RESTORER(m_paramNum);
VL_RESTORER(m_modBlockNum);
VL_RESTORER(m_modWithNum);
VL_RESTORER(m_modArgNum);
if (doit && nodep->user2()) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Identically recursive module (module instantiates "
@ -897,6 +899,7 @@ class LinkDotFindVisitor final : public VNVisitor {
m_paramNum = 0;
m_modBlockNum = 0;
m_modWithNum = 0;
m_modArgNum = 0;
// m_modSymp/m_curSymp for non-packages set by AstCell above this module
// Iterate
nodep->user2(true);
@ -932,6 +935,7 @@ class LinkDotFindVisitor final : public VNVisitor {
VL_RESTORER(m_paramNum);
VL_RESTORER(m_modBlockNum);
VL_RESTORER(m_modWithNum);
VL_RESTORER(m_modArgNum);
{
UINFO(4, " Link Class: " << nodep << endl);
VSymEnt* const upperSymp = m_curSymp;
@ -945,6 +949,7 @@ class LinkDotFindVisitor final : public VNVisitor {
m_paramNum = 0;
m_modBlockNum = 0;
m_modWithNum = 0;
m_modArgNum = 0;
m_explicitNew = false;
// m_modSymp/m_curSymp for non-packages set by AstCell above this module
// Iterate
@ -2056,8 +2061,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
const AstCell* m_cellp = nullptr; // Current cell
AstNodeModule* m_modp = nullptr; // Current module
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
AstMethodCall* m_randMethodCallp = nullptr; // Current randomize() method call
int m_modportNum = 0; // Uniqueify modport numbers
bool m_inSens = false; // True if in senitem
bool m_inWith = false; // True if in with
std::map<std::string, AstNode*> m_ifClassImpNames; // Names imported from interface class
std::set<AstClass*> m_extendsParam; // Classes that have a parameterized super class
// (except the default instances)
@ -2682,6 +2689,21 @@ class LinkDotResolveVisitor final : public VNVisitor {
if (m_fromSymp) {
foundp = m_fromSymp->findIdFlat(nodep->name());
if (foundp) {
if (!m_inWith) {
UASSERT_OBJ(m_randMethodCallp, nodep, "Expected to be under randomize()");
// This will start failing once complex expressions are allowed on the LHS
// of randomize() with args
UASSERT_OBJ(VN_IS(m_randMethodCallp->fromp(), VarRef), m_randMethodCallp,
"Expected simple randomize target");
// A ParseRef is used here so that the dot RHS gets resolved
nodep->replaceWith(new AstMemberSel{
nodep->fileline(),
new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT,
m_randMethodCallp->fromp()->name()},
VFlagChildDType{}, nodep->name()});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
UINFO(9, " randomize-with fromSym " << foundp->nodep() << endl);
if (m_ds.m_dotPos != DP_NONE) m_ds.m_dotPos = DP_MEMBER;
AstLambdaArgRef* const lambdaRefp
@ -3152,9 +3174,11 @@ class LinkDotResolveVisitor final : public VNVisitor {
// Created here so should already be resolved.
VL_RESTORER(m_ds);
VL_RESTORER(m_fromSymp);
VL_RESTORER(m_randMethodCallp);
{
m_ds.init(m_curSymp);
if (nodep->name() == "randomize" && nodep->pinsp()) {
m_randMethodCallp = nodep;
const AstNodeDType* fromDtp = nodep->fromp()->dtypep();
if (!fromDtp) {
if (const AstNodeVarRef* const varRefp = VN_CAST(nodep->fromp(), NodeVarRef)) {
@ -3171,9 +3195,17 @@ class LinkDotResolveVisitor final : public VNVisitor {
fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep();
}
}
if (!fromDtp)
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: 'randomize() with' on complex expressions");
if (!fromDtp) {
if (VN_IS(nodep->pinsp(), With)) {
nodep->v3warn(
E_UNSUPPORTED,
"Unsupported: 'randomize() with' on complex expressions");
} else {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Inline random variable control with "
"'randomize()' called on complex expressions");
}
}
}
if (m_statep->forPrimary() && isParamedClassRefDType(fromDtp)) {
m_ds.m_unresolvedClass = true;
@ -3186,14 +3218,6 @@ class LinkDotResolveVisitor final : public VNVisitor {
else
m_fromSymp = m_statep->getNodeSym(classDtp->classp());
}
AstNode* pinsp = nodep->pinsp();
if (VN_IS(pinsp, With)) {
iterate(pinsp);
pinsp = pinsp->nextp();
}
m_fromSymp = nullptr;
iterateAndNextNull(pinsp);
return;
}
iterateChildren(nodep);
}
@ -3323,7 +3347,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
if (m_fromSymp) {
VSymEnt* const foundp = m_fromSymp->findIdFlat(nodep->name());
if (foundp) {
if (foundp && m_inWith) {
UINFO(9, " randomize-with fromSym " << foundp->nodep() << endl);
AstNodeExpr* argsp = nullptr;
if (nodep->pinsp()) {
@ -3577,8 +3601,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
UINFO(5, " " << nodep << endl);
checkNoDot(nodep);
VL_RESTORER(m_curSymp);
VL_RESTORER(m_inWith);
{
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
m_inWith = true;
iterateChildren(nodep);
}
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);

View File

@ -82,8 +82,8 @@ struct VMemberQualifiers final {
}
void applyToNodes(AstVar* nodesp) const {
for (AstVar* nodep = nodesp; nodep; nodep = VN_AS(nodep->nextp(), Var)) {
if (m_rand) nodep->isRand(true);
if (m_randc) nodep->isRandC(true);
if (m_rand) nodep->rand(VRandAttr::RAND);
if (m_randc) nodep->rand(VRandAttr::RAND_CYCLIC);
if (m_local) nodep->isHideLocal(true);
if (m_protected) nodep->isHideProtected(true);
if (m_static) nodep->lifetime(VLifetime::STATIC);

View File

@ -48,6 +48,15 @@
VL_DEFINE_DEBUG_FUNCTIONS;
// ######################################################################
// Determines if a class is used with randomization
enum ClassRandom : uint8_t {
NONE, // randomize() is not called
IS_RANDOMIZED, // randomize() is called
IS_RANDOMIZED_INLINE, // randomize() with args is called
};
// ######################################################################
// Establishes the target of a rand_mode() call
@ -130,18 +139,26 @@ class RandomizeMarkVisitor final : public VNVisitor {
void markMembers(const AstClass* nodep) {
for (const AstClass* classp = nodep; classp;
classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr) {
for (const AstNode* memberp = classp->stmtsp(); memberp; memberp = memberp->nextp()) {
// If member is rand and of class type, mark its class
if (VN_IS(memberp, Var) && VN_AS(memberp, Var)->isRand()) {
for (AstNode* memberp = classp->stmtsp(); memberp; memberp = memberp->nextp()) {
AstVar* const varp = VN_CAST(memberp, Var);
if (!varp) continue;
// If member is randomizable and of class type, mark its class
if (varp->rand().isRandomizable()) {
if (const AstClassRefDType* const classRefp
= VN_CAST(memberp->dtypep()->skipRefp(), ClassRefDType)) {
= VN_CAST(varp->dtypep()->skipRefp(), ClassRefDType)) {
AstClass* const rclassp = classRefp->classp();
if (!rclassp->user1()) {
rclassp->user1(true);
rclassp->user1(IS_RANDOMIZED);
markMembers(rclassp);
markDerived(rclassp);
}
}
// If the class is randomized inline, all members use rand mode
if (nodep->user1() == IS_RANDOMIZED_INLINE) {
VarRandMode randMode = {};
randMode.usesRandMode = true;
varp->user1(randMode.asInt);
}
}
}
}
@ -150,8 +167,8 @@ class RandomizeMarkVisitor final : public VNVisitor {
const auto it = m_baseToDerivedMap.find(nodep);
if (it != m_baseToDerivedMap.end()) {
for (auto* classp : it->second) {
if (!classp->user1()) {
classp->user1(true);
if (classp->user1() < nodep->user1()) {
classp->user1(nodep->user1());
markMembers(classp);
markDerived(classp);
}
@ -277,9 +294,42 @@ class RandomizeMarkVisitor final : public VNVisitor {
}
}
if (classp) {
classp->user1(true);
if (!classp->user1()) classp->user1(IS_RANDOMIZED);
markMembers(classp);
}
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
AstArg* const argp = VN_CAST(pinp, Arg);
if (!argp) continue;
classp->user1(IS_RANDOMIZED_INLINE);
AstNodeExpr* exprp = argp->exprp();
AstVar* fromVarp = nullptr; // If nodep is a method call, this is its receiver
if (AstMethodCall* methodCallp = VN_CAST(nodep, MethodCall)) {
if (AstMemberSel* const memberSelp = VN_CAST(methodCallp->fromp(), MemberSel)) {
fromVarp = memberSelp->varp();
} else {
AstVarRef* const varrefp = VN_AS(methodCallp->fromp(), VarRef);
fromVarp = varrefp->varp();
}
}
while (exprp) {
AstVar* randVarp = nullptr;
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
randVarp = memberSelp->varp();
exprp = memberSelp->fromp();
} else {
AstVarRef* const varrefp = VN_AS(exprp, VarRef);
randVarp = varrefp->varp();
exprp = nullptr;
}
if (randVarp == fromVarp) break;
AstNode* backp = randVarp;
while (backp && !VN_IS(backp, Class)) backp = backp->backp();
VarRandMode randMode = {};
randMode.usesRandMode = true;
randVarp->user1(randMode.asInt);
VN_AS(backp, Class)->user1(IS_RANDOMIZED_INLINE);
}
}
}
void visit(AstConstraintExpr* nodep) override {
VL_RESTORER(m_constraintExprp);
@ -300,7 +350,7 @@ class RandomizeMarkVisitor final : public VNVisitor {
if (nodep->varp()->lifetime().isStatic()) m_staticRefs.emplace(nodep);
if (!nodep->varp()->isRand()) return;
if (!nodep->varp()->rand().isRandomizable()) return;
for (AstNode* backp = nodep; backp != m_constraintExprp && !backp->user1();
backp = backp->backp())
backp->user1(true);
@ -308,7 +358,7 @@ class RandomizeMarkVisitor final : public VNVisitor {
void visit(AstMemberSel* nodep) override {
if (!m_constraintExprp) return;
if (VN_IS(nodep->fromp(), LambdaArgRef)) {
if (!nodep->varp()->isRand()) return;
if (!nodep->varp()->rand().isRandomizable()) return;
for (AstNode* backp = nodep; backp != m_constraintExprp && !backp->user1();
backp = backp->backp())
backp->user1(true);
@ -974,6 +1024,8 @@ class RandomizeVisitor final : public VNVisitor {
// STATE
V3UniqueNames m_inlineUniqueNames; // For generating unique function names
V3UniqueNames m_randModeUniqueNames{"__Vrandmode"}; // For generating unique rand mode state
// var names
VMemberMap m_memberMap; // Member names cached for fast lookup
AstNodeModule* m_modp = nullptr; // Current module
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
@ -1079,8 +1131,8 @@ class RandomizeVisitor final : public VNVisitor {
new AstVarRef{fl, randModeModp, randModeVarp, VAccess::READ});
dynarrayNewp->dtypeSetVoid();
AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask);
fl = classp->fileline();
UASSERT_OBJ(newp, classp, "No new() in class");
fl = classp->fileline();
newp->addStmtsp(dynarrayNewp->makeStmt());
newp->addStmtsp(makeRandModeInitLoop(
fl, new AstVarRef{fl, randModeModp, randModeVarp, VAccess::WRITE},
@ -1180,8 +1232,7 @@ class RandomizeVisitor final : public VNVisitor {
if (basicp->width() > 32) {
varp->v3error("Maximum implemented width for randc is 32 bits, "
<< varp->prettyNameQ() << " is " << basicp->width() << " bits");
varp->isRandC(false);
varp->isRand(true);
varp->rand(VRandAttr::RAND);
return nullptr;
}
items = 1ULL << basicp->width();
@ -1286,16 +1337,119 @@ class RandomizeVisitor final : public VNVisitor {
clearp->dtypeSetVoid();
return clearp->makeStmt();
}
AstVar* getVarFromRef(AstNodeExpr* const exprp) {
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
return memberSelp->varp();
} else if (AstVarRef* const varrefp = VN_CAST(exprp, VarRef)) {
return varrefp->varp();
}
exprp->v3fatalSrc("Not a MemberSel nor VarRef");
return nullptr; // LCOV_EXCL_LINE
}
AstNodeExpr* makeSiblingRefp(AstNodeExpr* const exprp, AstVar* const varp,
const VAccess access) {
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
return new AstMemberSel{exprp->fileline(), memberSelp->fromp()->cloneTree(false),
varp};
}
UASSERT_OBJ(VN_IS(exprp, VarRef), exprp, "Should be a VarRef");
return new AstVarRef{exprp->fileline(), VN_AS(varp->user2p(), Class), varp, access};
}
AstNodeExpr* getFromp(AstNodeExpr* const exprp) {
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
return memberSelp->fromp();
} else if (AstMethodCall* const methodCallp = VN_CAST(exprp, MethodCall)) {
return methodCallp->fromp();
}
return nullptr;
}
AstVar* makeTmpRandModeVar(AstNodeExpr* siblingExprp, AstVar* randModeVarp,
AstNode*& storeStmtspr, AstNodeStmt*& restoreStmtspr) {
FileLine* const fl = randModeVarp->fileline();
AstVar* const randModeTmpVarp
= new AstVar{fl, VVarType::BLOCKTEMP, m_randModeUniqueNames.get(randModeVarp),
randModeVarp->dtypep()};
randModeTmpVarp->funcLocal(m_ftaskp);
randModeTmpVarp->lifetime(VLifetime::AUTOMATIC);
storeStmtspr = AstNode::addNext(
storeStmtspr,
new AstAssign{fl, new AstVarRef{fl, randModeTmpVarp, VAccess::WRITE},
makeSiblingRefp(siblingExprp, randModeVarp, VAccess::READ)});
storeStmtspr = AstNode::addNext(
storeStmtspr,
makeRandModeInitLoop(fl, makeSiblingRefp(siblingExprp, randModeVarp, VAccess::WRITE),
new AstConst{fl, 0}, m_ftaskp));
restoreStmtspr = AstNode::addNext(
restoreStmtspr,
new AstAssign{fl, makeSiblingRefp(siblingExprp, randModeVarp, VAccess::WRITE),
new AstVarRef{fl, randModeTmpVarp, VAccess::READ}});
return randModeTmpVarp;
}
// Returns the common prefix of two hierarchical accesses, or nullptr if there is none
// e.g. a.b.c and a.b.d -> a.b
AstNodeExpr* sliceToCommonPrefix(AstNodeExpr* thisp, AstNodeExpr* otherp) {
static std::vector<AstNodeExpr*> thisHier, otherHier; // Keep around
// to avoid reallocations
thisHier.clear();
otherHier.clear();
while (thisp) {
thisHier.push_back(thisp);
thisp = getFromp(thisp);
}
while (otherp) {
otherHier.push_back(otherp);
otherp = getFromp(otherp);
}
AstNodeExpr* commonp = nullptr;
for (auto thisIt = thisHier.rbegin(), otherIt = otherHier.rbegin();
thisIt != thisHier.rend() && otherIt != otherHier.rend(); ++thisIt, ++otherIt) {
if ((*thisIt)->type() != (*otherIt)->type()) break;
if (AstMemberSel* memberSelp = VN_CAST(*thisIt, MemberSel)) {
AstMemberSel* otherMemberSelp = VN_AS(*otherIt, MemberSel);
if (memberSelp->varp() == otherMemberSelp->varp()) {
commonp = memberSelp;
continue;
}
} else if (AstMethodCall* thisMethodCallp = VN_CAST(*thisIt, MethodCall)) {
AstMethodCall* otherMethodCallp = VN_AS(*otherIt, MethodCall);
if (thisMethodCallp->taskp() == otherMethodCallp->taskp()) {
commonp = thisMethodCallp;
continue;
}
} else if (AstVarRef* firstVarRefp = VN_CAST(*thisIt, VarRef)) {
AstVarRef* secondVarRefp = VN_AS(*otherIt, VarRef);
if (firstVarRefp->varp() == secondVarRefp->varp()) {
commonp = firstVarRefp;
continue;
}
}
break;
}
return commonp;
}
void addBasicRandomizeBody(AstFunc* const basicRandomizep, AstClass* const nodep) {
void addBasicRandomizeBody(AstFunc* const basicRandomizep, AstClass* const nodep,
AstVar* randModeVarp) {
FileLine* const fl = nodep->fileline();
AstVar* const basicFvarp = VN_AS(basicRandomizep->fvarp(), Var);
AstVarRef* const basicFvarRefp = new AstVarRef{fl, basicFvarp, VAccess::WRITE};
AstConst* const beginBasicValp = new AstConst{fl, AstConst::WidthedValue{}, 32, 1};
basicRandomizep->addStmtsp(new AstAssign{fl, basicFvarRefp, beginBasicValp});
AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(nodep, "new"), NodeFTask);
UASSERT_OBJ(newp, nodep, "No new() in class");
nodep->foreachMember([&](AstClass* classp, AstVar* memberVarp) {
if (!memberVarp->isRand() || memberVarp->user3()) return;
if (!memberVarp->rand().isRandomizable()) return;
const VarRandMode randMode = {.asInt = memberVarp->user1()};
if (randMode.usesRandMode && !memberVarp->isRand()) { // Not randomizable by default
AstCMethodHard* atp = new AstCMethodHard{
nodep->fileline(),
new AstVarRef{fl, VN_AS(randModeVarp->user2p(), NodeModule), randModeVarp,
VAccess::WRITE},
"at", new AstConst{nodep->fileline(), randMode.index}};
atp->dtypeSetUInt32();
newp->addStmtsp(new AstAssign{fl, atp, new AstConst{fl, 0}});
}
if (memberVarp->user3()) return; // Handled in constraints
const AstNodeDType* const dtypep = memberVarp->dtypep()->skipRefp();
if (VN_IS(dtypep, BasicDType) || VN_IS(dtypep, StructDType)) {
AstVar* const randcVarp = newRandcVarsp(memberVarp);
@ -1332,6 +1486,90 @@ class RandomizeVisitor final : public VNVisitor {
});
}
// Handle inline random variable control. After this, the randomize() call has no args
void handleRandomizeArgs(AstNodeFTaskRef* const nodep) {
if (!nodep->pinsp()) return;
// This assumes arguments to always be a member sel from nodep->fromp(), if applicable
// e.g. LinkDot transformed a.randomize(b, a.c) -> a.randomize(a.b, a.c)
// Merge pins with common prefixes so that setting their rand mode doesn't interfere
// with each other.
// e.g. a.randomize(a.b, a.c, a.b.d) -> a.randomize(a.b, a.c)
for (AstNode *pinp = nodep->pinsp(), *nextp = nullptr; pinp; pinp = nextp) {
nextp = pinp->nextp();
AstArg* const argp = VN_CAST(pinp, Arg);
if (!argp) continue;
AstNode* otherNextp = nullptr;
for (AstNode* otherPinp = nextp; otherPinp; otherPinp = otherNextp) {
otherNextp = otherPinp->nextp();
AstArg* const otherArgp = VN_CAST(otherPinp, Arg);
if (!otherArgp) continue;
if (AstNodeExpr* const prefixp
= sliceToCommonPrefix(argp->exprp(), otherArgp->exprp())) {
if (prefixp == argp->exprp()) {
if (nextp == otherPinp) nextp = nextp->nextp();
VL_DO_DANGLING(otherPinp->unlinkFrBack()->deleteTree(), otherPinp);
continue;
}
}
if (AstNodeExpr* const prefixp
= sliceToCommonPrefix(otherArgp->exprp(), argp->exprp())) {
if (prefixp == otherArgp->exprp()) {
VL_DO_DANGLING(pinp->unlinkFrBack()->deleteTree(), pinp);
break;
}
}
}
}
// Construct temp vars, and store and restore statements
std::set<AstVar*> savedRandModeVarps;
AstVar* tmpVarps = nullptr;
AstNode* storeStmtsp = nullptr;
AstNode* setStmtsp = nullptr;
AstNodeStmt* restoreStmtsp = nullptr;
for (AstNode *pinp = nodep->pinsp(), *nextp = nullptr; pinp; pinp = nextp) {
nextp = pinp->nextp();
AstArg* const argp = VN_CAST(pinp, Arg);
if (!argp) continue;
AstNodeExpr* exprp = VN_AS(pinp, Arg)->exprp();
AstNodeExpr* const commonPrefixp = sliceToCommonPrefix(exprp, nodep);
UASSERT_OBJ(commonPrefixp != exprp, nodep,
"Common prefix should be different than pin");
FileLine* const fl = argp->fileline();
while (exprp) {
if (commonPrefixp == exprp) break;
AstVar* const randVarp = getVarFromRef(exprp);
AstClass* const classp = VN_AS(randVarp->user2p(), Class);
AstVar* const randModeVarp = getRandModeVar(classp);
if (savedRandModeVarps.find(randModeVarp) == savedRandModeVarps.end()) {
AstVar* const randModeTmpVarp
= makeTmpRandModeVar(exprp, randModeVarp, storeStmtsp, restoreStmtsp);
savedRandModeVarps.insert(randModeVarp);
tmpVarps = AstNode::addNext(tmpVarps, randModeTmpVarp);
}
const VarRandMode randMode = {.asInt = randVarp->user1()};
AstCMethodHard* atp
= new AstCMethodHard{fl, makeSiblingRefp(exprp, randModeVarp, VAccess::WRITE),
"at", new AstConst{fl, randMode.index}};
atp->dtypeSetUInt32();
setStmtsp
= AstNode::addNext(setStmtsp, new AstAssign{fl, atp, new AstConst{fl, 1}});
exprp = getFromp(exprp);
}
pinp->unlinkFrBack()->deleteTree();
}
if (tmpVarps) {
UASSERT_OBJ(storeStmtsp && setStmtsp && restoreStmtsp, nodep, "Should have stmts");
VNRelinker relinker;
m_stmtp->unlinkFrBack(&relinker);
AstNode* const stmtsp = tmpVarps;
stmtsp->addNext(storeStmtsp);
stmtsp->addNext(setStmtsp);
stmtsp->addNext(m_stmtp);
stmtsp->addNext(restoreStmtsp);
relinker.relink(new AstBegin{nodep->fileline(), "", stmtsp, false, true});
}
}
// VISITORS
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
@ -1411,12 +1649,9 @@ class RandomizeVisitor final : public VNVisitor {
AstVarRef* const fvarRefp = new AstVarRef{fl, fvarp, VAccess::WRITE};
randomizep->addStmtsp(new AstAssign{fl, fvarRefp, beginValp});
AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(nodep, "new"), NodeFTask);
UASSERT_OBJ(newp, nodep, "No new() in class");
AstFunc* const basicRandomizep
= V3Randomize::newRandomizeFunc(m_memberMap, nodep, "__Vbasic_randomize");
addBasicRandomizeBody(basicRandomizep, nodep);
addBasicRandomizeBody(basicRandomizep, nodep, randModeVarp);
AstFuncRef* const basicRandomizeCallp = new AstFuncRef{fl, "__Vbasic_randomize", nullptr};
basicRandomizeCallp->taskp(basicRandomizep);
basicRandomizeCallp->dtypep(basicRandomizep->dtypep());
@ -1539,9 +1774,13 @@ class RandomizeVisitor final : public VNVisitor {
return;
}
if (nodep->name() != "randomize") return;
handleRandomizeArgs(nodep);
AstWith* const withp = VN_CAST(nodep->pinsp(), With);
if (!(nodep->name() == "randomize") || !withp) {
if (!withp) {
iterateChildren(nodep);
return;
}

View File

@ -1664,7 +1664,9 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
reorganize = true;
}
} else { // By pin number
if (ppinnum >= tpinnum) {
if (nodep->taskp()->prettyName() == "randomize") {
// Arguments to randomize() are special, will be handled in V3Randomize
} else if (ppinnum >= tpinnum) {
if (sformatp) {
tconnects.emplace_back(sformatp, static_cast<AstArg*>(nullptr));
tconnects[ppinnum].second = argp;

View File

@ -3134,8 +3134,10 @@ class WidthVisitor final : public VNVisitor {
// Should check types the method requires, but at present we don't do much
userIterate(nodep->fromp(), WidthVP{SELF, BOTH}.p());
// Any AstWith is checked later when know types, in methodWithArgument
for (AstArg* argp = VN_CAST(nodep->pinsp(), Arg); argp; argp = VN_AS(argp->nextp(), Arg)) {
if (argp->exprp()) userIterate(argp->exprp(), WidthVP{SELF, BOTH}.p());
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
if (AstArg* const argp = VN_CAST(pinp, Arg)) {
if (argp->exprp()) userIterate(argp->exprp(), WidthVP{SELF, BOTH}.p());
}
}
// Find the fromp dtype - should be a class
UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression");
@ -3178,13 +3180,16 @@ class WidthVisitor final : public VNVisitor {
AstNodeDType* returnDtp, AstNodeDType* indexDtp,
AstNodeDType* valueDtp) {
UASSERT_OBJ(arbReturn || returnDtp, nodep, "Null return type");
if (AstWith* const withp = VN_CAST(nodep->pinsp(), With)) {
withp->indexArgRefp()->dtypep(indexDtp);
withp->valueArgRefp()->dtypep(valueDtp);
userIterate(withp, WidthVP{returnDtp, BOTH}.p());
withp->unlinkFrBack();
return withp;
} else if (required) {
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
if (AstWith* const withp = VN_CAST(pinp, With)) {
withp->indexArgRefp()->dtypep(indexDtp);
withp->valueArgRefp()->dtypep(valueDtp);
userIterate(withp, WidthVP{returnDtp, BOTH}.p());
withp->unlinkFrBack();
return withp;
}
}
if (required) {
nodep->v3error("'with' statement is required for ." << nodep->prettyName()
<< " method");
}
@ -3792,6 +3797,80 @@ class WidthVisitor final : public VNVisitor {
nodep->v3error("Member reference from interface to "
<< nodep->prettyNameQ() << " is not referencing a valid task or function ");
}
void handleRandomizeArgs(AstNodeFTaskRef* const nodep, AstClass* const classp) {
bool hasNonNullArgs = false;
AstConst* nullp = nullptr;
for (AstNode *pinp = nodep->pinsp(), *nextp = nullptr; pinp; pinp = nextp) {
nextp = pinp->nextp();
AstArg* const argp = VN_CAST(pinp, Arg);
if (!argp) continue;
AstVar* randVarp = nullptr;
AstNodeExpr* exprp = argp->exprp();
if (AstConst* const constp = VN_CAST(exprp, Const)) {
if (constp->num().isNull()) {
nullp = constp;
continue;
}
}
hasNonNullArgs = true;
AstVar* fromVarp = nullptr; // If it's a method call, the leftmost element
// of the dot hierarchy
if (AstMethodCall* methodCallp = VN_CAST(nodep, MethodCall)) {
AstNodeExpr* fromp = methodCallp->fromp();
while (AstMemberSel* const memberSelp = VN_CAST(fromp, MemberSel)) {
fromp = memberSelp->fromp();
}
AstVarRef* const varrefp = VN_AS(fromp, VarRef);
fromVarp = varrefp->varp();
}
if (!VN_IS(exprp, VarRef) && !VN_IS(exprp, MemberSel)) {
argp->v3error("'randomize()' argument must be a variable contained in "
<< (fromVarp ? fromVarp->prettyNameQ() : classp->prettyNameQ()));
VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp);
continue;
}
while (exprp) {
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
randVarp = memberSelp->varp();
exprp = memberSelp->fromp();
} else {
if (AstVarRef* const varrefp = VN_CAST(exprp, VarRef)) {
randVarp = varrefp->varp();
} else {
argp->v3warn(
E_UNSUPPORTED,
"Unsupported: Non-variable expression as 'randomize()' argument");
VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp);
}
exprp = nullptr;
}
// All variables in the dot hierarchy must be randomizable
if (randVarp && !randVarp->isRand()) randVarp->rand(VRandAttr::RAND_INLINE);
}
if (!argp) continue; // Errored out, bail
// randVarp is now the leftmost element from the dot hierarchy in argp->exprp()
if (randVarp == fromVarp) {
// The passed in variable is MemberSel'ected from the MethodCall target
} else if (classp->existsMember([&](const AstClass*, const AstVar* memberVarp) {
return memberVarp == randVarp;
})) {
// The passed in variable is contained in the method call target
} else {
// Passed in a constant or complex expression, or the above conditions are not
// met
argp->v3error("'randomize()' argument must be a variable contained in "
<< (fromVarp ? fromVarp->prettyNameQ() : classp->prettyNameQ()));
VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp);
}
}
if (nullp) {
if (hasNonNullArgs) {
nullp->v3error("Cannot pass more arguments to 'randomize(null)'");
} else {
nullp->v3warn(E_UNSUPPORTED, "Unsupported: 'randomize(null)'");
}
}
}
void methodCallClass(AstMethodCall* nodep, AstClassRefDType* adtypep) {
// No need to width-resolve the class, as it was done when we did the child
AstClass* const first_classp = adtypep->classp();
@ -3801,9 +3880,9 @@ class WidthVisitor final : public VNVisitor {
m_randomizeFromp = nodep->fromp();
withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(),
adtypep->findBitDType(), adtypep);
methodOkArguments(nodep, 0, 0);
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
V3Randomize::newRandomizeFunc(m_memberMap, first_classp);
handleRandomizeArgs(nodep, first_classp);
} else if (nodep->name() == "srandom") {
methodOkArguments(nodep, 1, 1);
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
@ -5942,11 +6021,7 @@ class WidthVisitor final : public VNVisitor {
v3Global.rootp()->typeTablep()->addTypesp(adtypep);
withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(),
adtypep->findBitDType(), adtypep);
if (nodep->pinsp()) {
nodep->pinsp()->v3warn(CONSTRAINTIGN,
"Inline random variable control (unsupported)");
nodep->pinsp()->unlinkFrBackWithNext()->deleteTree();
}
handleRandomizeArgs(nodep, classp);
} else if (nodep->name() == "srandom") {
nodep->taskp(V3Randomize::newSRandomFunc(m_memberMap, classp));
m_memberMap.clear();

View File

@ -0,0 +1,25 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
if (!$Self->have_solver) {
skip("No constraint solver installed");
} else {
compile(
);
execute(
check_finished => 1,
);
}
ok(1);
1;

View File

@ -0,0 +1,156 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
class Foo;
rand int zero;
int two;
endclass
class Bar extends Foo;
rand int one;
static int three;
function void test;
logic[1:0] ok = '0;
zero = 100;
one = 200;
two = 300;
three = 400;
for (int i = 0; i < 20; i++) begin
void'(randomize(one));
if (zero != 100) $stop;
if (one != 200) ok[0] = 1;
if (two != 300) $stop;
if (three != 400) $stop;
end
if (!ok[0]) $stop;
ok = '0;
if (zero.rand_mode() != 1) $stop;
if (one.rand_mode() != 1) $stop;
zero = 500;
one = 600;
two = 700;
three = 800;
one.rand_mode(0);
for (int i = 0; i < 20; i++) begin
void'(randomize(one, two));
if (zero != 500) $stop;
if (one != 600) ok[0] = 1;
if (two != 700) ok[1] = 1;
if (three != 800) $stop;
end
if (one.rand_mode() != 0) $stop;
one.rand_mode(1);
if (ok != 'b11) $stop;
endfunction
endclass
class Baz;
int four;
Bar bar;
function new;
bar = new;
endfunction
endclass
class Qux;
Baz baz;
function new;
baz = new;
endfunction
endclass
class Boo extends Bar;
rand int five;
endclass
module t;
initial begin
Boo boo = new;
Bar bar = boo;
Qux qux = new;
logic[2:0] ok = '0;
bar.test;
bar.zero = 1000;
bar.one = 2000;
bar.two = 3000;
bar.three = 4000;
boo.five = 999999;
for (int i = 0; i < 20; i++) begin
int res = bar.randomize(two);
if (boo.five != 999999) $stop;
end
bar.zero = 1000;
bar.one = 2000;
bar.two = 3000;
bar.three = 4000;
boo.five = 999999;
for (int i = 0; i < 20; i++) begin
int res = bar.randomize(two) with { two > 3000 && two < 4000; };
if (bar.zero != 1000) $stop;
if (bar.one != 2000) $stop;
if (!(bar.two > 3000 && bar.two < 4000)) $stop;
if (bar.three != 4000) $stop;
if (boo.five != 999999) $stop;
end
qux.baz.bar.zero = 5000;
qux.baz.bar.one = 6000;
qux.baz.bar.two = 7000;
qux.baz.bar.three = 8000;
qux.baz.four = 9000;
for (int i = 0; i < 20; i++) begin
void'(qux.randomize(baz));
if (qux.baz.bar.zero != 5000) $stop;
if (qux.baz.bar.one != 6000) $stop;
if (qux.baz.bar.two != 7000) $stop;
if (qux.baz.bar.three != 8000) $stop;
if (qux.baz.four != 9000) $stop;
end
for (int i = 0; i < 20; i++) begin
void'(qux.randomize(baz.bar));
if (qux.baz.bar.zero != 5000) ok[0] = 1;
if (qux.baz.bar.one != 6000) ok[1] = 1;
if (qux.baz.bar.two != 7000) $stop;
if (qux.baz.bar.three != 8000) $stop;
if (qux.baz.four != 9000) $stop;
end
if (!ok[0]) $stop;
if (!ok[1]) $stop;
ok = '0;
qux.baz.bar.zero = 10000;
qux.baz.bar.one = 20000;
for (int i = 0; i < 20; i++) begin
void'(qux.randomize(baz.four));
if (qux.baz.bar.zero != 10000) $stop;
if (qux.baz.bar.one != 20000) $stop;
if (qux.baz.bar.two != 7000) $stop;
if (qux.baz.bar.three != 8000) $stop;
if (qux.baz.four != 9000) ok[0] = 1;
end
if (!ok[0]) $stop;
ok = '0;
qux.baz.four = 30000;
for (int i = 0; i < 20; i++) begin
void'(qux.randomize(baz.bar, qux.baz.bar.one, baz.four));
if (qux.baz.bar.zero != 10000) ok[0] = 1;
if (qux.baz.bar.one != 20000) ok[1] = 1;
if (qux.baz.bar.two != 7000) $stop;
if (qux.baz.bar.three != 8000) $stop;
if (qux.baz.four != 30000) ok[2] = 1;
end
if (ok != 'b111) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,25 @@
%Error: t/t_randomize_inline_var_ctl_bad.v:12:23: 'randomize()' argument must be a variable contained in 'Foo'
: ... note: In instance 't'
12 | void'(randomize(y));
| ^
%Error: t/t_randomize_inline_var_ctl_bad.v:26:46: 'randomize()' argument must be a variable contained in 'foo'
: ... note: In instance 't'
26 | void'(foo.randomize(x, foo.x, null, qux.x, bar.y, 0 + 1, x ** 2));
| ^
%Error: t/t_randomize_inline_var_ctl_bad.v:26:53: 'randomize()' argument must be a variable contained in 'foo'
: ... note: In instance 't'
26 | void'(foo.randomize(x, foo.x, null, qux.x, bar.y, 0 + 1, x ** 2));
| ^
%Error: t/t_randomize_inline_var_ctl_bad.v:26:59: 'randomize()' argument must be a variable contained in 'foo'
: ... note: In instance 't'
26 | void'(foo.randomize(x, foo.x, null, qux.x, bar.y, 0 + 1, x ** 2));
| ^
%Error: t/t_randomize_inline_var_ctl_bad.v:26:66: 'randomize()' argument must be a variable contained in 'foo'
: ... note: In instance 't'
26 | void'(foo.randomize(x, foo.x, null, qux.x, bar.y, 0 + 1, x ** 2));
| ^~
%Error: t/t_randomize_inline_var_ctl_bad.v:26:37: Cannot pass more arguments to 'randomize(null)'
: ... note: In instance 't'
26 | void'(foo.randomize(x, foo.x, null, qux.x, bar.y, 0 + 1, x ** 2));
| ^~~~
%Error: Exiting due to

View File

@ -8,11 +8,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(linter => 1);
scenarios(simulator => 1);
lint(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
fails => 1,
);
ok(1);

View File

@ -0,0 +1,28 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
class Foo;
int x;
function void test;
int y;
void'(randomize(y));
endfunction
endclass
class Bar;
int y;
endclass
module t;
initial begin
Foo foo = new;
Foo qux = new;
Bar bar = new;
int x;
void'(foo.randomize(x, foo.x, null, qux.x, bar.y, 0 + 1, x ** 2));
end
endmodule

View File

@ -1,9 +0,0 @@
%Warning-CONSTRAINTIGN: t/t_randomize_inline_var_ctl_unsup.v:13:23: Inline random variable control (unsupported)
13 | void'(randomize(one));
| ^~~
... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest
... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message.
%Warning-CONSTRAINTIGN: t/t_randomize_inline_var_ctl_unsup.v:14:23: Inline random variable control (unsupported)
14 | void'(randomize(two, three));
| ^~~
%Error: Exiting due to

View File

@ -0,0 +1,14 @@
%Error-UNSUPPORTED: t/t_randomize_inline_var_ctl_unsup_1.v:20:37: Unsupported: Non-variable expression as 'randomize()' argument
: ... note: In instance 't'
20 | void'(foo.randomize(Foo::get().x));
| ^
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_randomize_inline_var_ctl_unsup_1.v:21:34: Unsupported: Non-variable expression as 'randomize()' argument
: ... note: In instance 't'
21 | void'(foo.randomize(foos[0].x));
| ^
%Error-UNSUPPORTED: t/t_randomize_inline_var_ctl_unsup_1.v:22:27: Unsupported: 'randomize(null)'
: ... note: In instance 't'
22 | void'(foo.randomize(null));
| ^~~~
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
lint(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,24 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
class Foo;
int x;
static function Foo get;
Foo foo = new;
return foo;
endfunction
endclass
module t;
initial begin
Foo foo = Foo::get();
Foo foos[] = new[1];
void'(foo.randomize(Foo::get().x));
void'(foo.randomize(foos[0].x));
void'(foo.randomize(null));
end
endmodule

View File

@ -0,0 +1,8 @@
%Error-UNSUPPORTED: t/t_randomize_inline_var_ctl_unsup_2.v:17:29: Unsupported: Inline random variable control with 'randomize()' called on complex expressions
17 | initial void'(Foo::get().randomize(x));
| ^~~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: t/t_randomize_inline_var_ctl_unsup_2.v:17:39: Can't find definition of variable: 'x'
17 | initial void'(Foo::get().randomize(x));
| ^
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
lint(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -4,13 +4,15 @@
// any use, without warranty, 2024 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
class Packet;
rand int one;
int two;
static int three;
class Foo;
int x;
function void test;
void'(randomize(one));
void'(randomize(two, three));
static function Foo get;
Foo foo = new;
return foo;
endfunction
endclass
module t;
initial void'(Foo::get().randomize(x));
endmodule