Support inline random variable control (#5317)
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
This commit is contained in:
parent
124e3463fc
commit
6cb0a41857
30
src/V3Ast.h
30
src/V3Ast.h
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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]";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
105
src/V3Width.cpp
105
src/V3Width.cpp
|
@ -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();
|
||||
|
|
|
@ -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;
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
|
@ -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
|
Loading…
Reference in New Issue