This commit is contained in:
parent
b408e097f6
commit
10ac99ac05
|
@ -580,4 +580,22 @@ public:
|
|||
#endif
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlStdRandomizer provides a light wrapper for RNG used by std::randomize()
|
||||
// to support scope-level randomization.
|
||||
class VlStdRandomizer final {
|
||||
// MEMBERS
|
||||
VlRNG m_rng; // Random number generator
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlStdRandomizer() = default;
|
||||
~VlStdRandomizer() = default;
|
||||
|
||||
template <typename T>
|
||||
bool basicStdRandomization(T& value, size_t width) {
|
||||
value = VL_MASK_I(width) & VL_RANDOM_RNG_I(m_rng);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
#endif // Guard
|
||||
|
|
26
src/V3Ast.h
26
src/V3Ast.h
|
@ -591,6 +591,7 @@ public:
|
|||
FORK_SYNC,
|
||||
PROCESS_REFERENCE,
|
||||
RANDOM_GENERATOR,
|
||||
RANDOM_STDGENERATOR,
|
||||
// Unsigned and two state; fundamental types
|
||||
UINT32,
|
||||
UINT64,
|
||||
|
@ -625,6 +626,7 @@ public:
|
|||
"VlFork",
|
||||
"VlProcessRef",
|
||||
"VlRandomizer",
|
||||
"VlStdRandomizer",
|
||||
"IData",
|
||||
"QData",
|
||||
"LOGIC_IMPLICIT",
|
||||
|
@ -632,13 +634,16 @@ public:
|
|||
return names[m_e];
|
||||
}
|
||||
const char* dpiType() const {
|
||||
static const char* const names[]
|
||||
= {"%E-unk", "svBit", "char", "void*", "char",
|
||||
"int", "%E-integer", "svLogic", "long long", "double",
|
||||
"short", "%E-time", "const char*", "%E-untyped", "dpiScope",
|
||||
"const char*", "%E-mtaskstate", "%E-triggervec", "%E-dly-sched", "%E-trig-sched",
|
||||
"%E-dyn-sched", "%E-fork", "%E-proc-ref", "%E-rand-gen", "IData",
|
||||
"QData", "%E-logic-implct", " MAX"};
|
||||
static const char* const names[] = {"%E-unk", "svBit", "char",
|
||||
"void*", "char", "int",
|
||||
"%E-integer", "svLogic", "long long",
|
||||
"double", "short", "%E-time",
|
||||
"const char*", "%E-untyped", "dpiScope",
|
||||
"const char*", "%E-mtaskstate", "%E-triggervec",
|
||||
"%E-dly-sched", "%E-trig-sched", "%E-dyn-sched",
|
||||
"%E-fork", "%E-proc-ref", "%E-rand-gen",
|
||||
"%E-stdrand-gen", "IData", "QData",
|
||||
"%E-logic-implct", " MAX"};
|
||||
return names[m_e];
|
||||
}
|
||||
static void selfTest() {
|
||||
|
@ -679,6 +684,7 @@ public:
|
|||
case FORK_SYNC: return 0; // opaque
|
||||
case PROCESS_REFERENCE: return 0; // opaque
|
||||
case RANDOM_GENERATOR: return 0; // opaque
|
||||
case RANDOM_STDGENERATOR: return 0; // opaque
|
||||
case UINT32: return 32;
|
||||
case UINT64: return 64;
|
||||
default: return 0;
|
||||
|
@ -718,8 +724,8 @@ public:
|
|||
return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR
|
||||
|| m_e == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER
|
||||
|| m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC
|
||||
|| m_e == PROCESS_REFERENCE || m_e == RANDOM_GENERATOR || m_e == DOUBLE
|
||||
|| m_e == UNTYPED);
|
||||
|| m_e == PROCESS_REFERENCE || m_e == RANDOM_GENERATOR
|
||||
|| m_e == RANDOM_STDGENERATOR || m_e == DOUBLE || m_e == UNTYPED);
|
||||
}
|
||||
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
|
||||
bool isEvent() const { return m_e == EVENT; }
|
||||
|
@ -771,6 +777,8 @@ public:
|
|||
/* DYNAMIC_TRIGGER_SCHEDULER: */ "", // Should not be traced
|
||||
/* FORK_SYNC: */ "", // Should not be traced
|
||||
/* PROCESS_REFERENCE: */ "", // Should not be traced
|
||||
/* RANDOM_GENERATOR: */ "", // Should not be traced
|
||||
/* RANDOM_STD_GENERATOR: */ "", // Should not be traced
|
||||
/* UINT32: */ "BIT",
|
||||
/* UINT64: */ "BIT",
|
||||
/* LOGIC_IMPLICIT: */ "", // Should not be traced
|
||||
|
|
|
@ -473,6 +473,9 @@ public:
|
|||
bool isRandomGenerator() const VL_MT_SAFE {
|
||||
return keyword() == VBasicDTypeKwd::RANDOM_GENERATOR;
|
||||
}
|
||||
bool isStdRandomGenerator() const VL_MT_SAFE {
|
||||
return keyword() == VBasicDTypeKwd::RANDOM_STDGENERATOR;
|
||||
}
|
||||
bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); }
|
||||
bool isString() const VL_MT_STABLE { return keyword().isString(); }
|
||||
bool isZeroInit() const { return keyword().isZeroInit(); }
|
||||
|
|
|
@ -1007,6 +1007,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
|||
info.m_type = "VlProcessRef";
|
||||
} else if (bdtypep->isRandomGenerator()) {
|
||||
info.m_type = "VlRandomizer";
|
||||
} else if (bdtypep->isStdRandomGenerator()) {
|
||||
info.m_type = "VlStdRandomizer";
|
||||
} else if (bdtypep->isEvent()) {
|
||||
info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent";
|
||||
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
|
||||
|
@ -3098,7 +3100,8 @@ void AstCMethodHard::setPurity() {
|
|||
{"unique", true},
|
||||
{"unique_index", true},
|
||||
{"word", true},
|
||||
{"write_var", false}};
|
||||
{"write_var", false},
|
||||
{"basicStdRandomization", false}};
|
||||
|
||||
if (name() == "atWriteAppend" || name() == "atWriteAppendBack") {
|
||||
m_pure = false;
|
||||
|
|
|
@ -127,7 +127,8 @@ static void makeToStringMiddle(AstClass* nodep) {
|
|||
if (const auto* const varp = VN_CAST(itemp, Var)) {
|
||||
if (!varp->isParam() && !varp->isInternal()
|
||||
&& !(varp->dtypeSkipRefp()->basicp()
|
||||
&& varp->dtypeSkipRefp()->basicp()->isRandomGenerator())) {
|
||||
&& (varp->dtypeSkipRefp()->basicp()->isRandomGenerator()
|
||||
|| varp->dtypeSkipRefp()->basicp()->isStdRandomGenerator()))) {
|
||||
string stmt = "out += \"";
|
||||
stmt += comma;
|
||||
comma = ", ";
|
||||
|
|
|
@ -745,7 +745,7 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
|
|||
return "";
|
||||
} else if (basicp && basicp->isDynamicTriggerScheduler()) {
|
||||
return "";
|
||||
} else if (basicp && basicp->isRandomGenerator()) {
|
||||
} else if (basicp && (basicp->isRandomGenerator() || basicp->isStdRandomGenerator())) {
|
||||
return "";
|
||||
} else if (basicp) {
|
||||
const bool zeroit
|
||||
|
|
|
@ -55,6 +55,7 @@ enum ClassRandom : uint8_t {
|
|||
NONE, // randomize() is not called
|
||||
IS_RANDOMIZED, // randomize() is called
|
||||
IS_RANDOMIZED_INLINE, // randomize() with args is called
|
||||
IS_STD_RANDOMIZED, // std::randomize() is called
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
|
@ -118,11 +119,13 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
|||
// NODE STATE
|
||||
// Cleared on Netlist
|
||||
// AstClass::user1() -> bool. Set true to indicate needs randomize processing
|
||||
// AstNodeModule::user1() -> bool. Set true to indicate needs std::randomize processing
|
||||
// AstNodeExpr::user1() -> bool. Set true to indicate constraint expression depending on a
|
||||
// randomized variable
|
||||
// AstVar::user1() -> bool. Set true to indicate needs rand_mode
|
||||
// AstVar::user2p() -> AstNodeModule*. Pointer to containing module
|
||||
// AstNodeFTask::user2p() -> AstNodeModule*. Pointer to containing module
|
||||
// AstMemberSel::user2p() -> AstNodeModule*. Pointer to containing module
|
||||
const VNUser1InUse m_inuser1;
|
||||
const VNUser2InUse m_inuser2;
|
||||
|
||||
|
@ -367,6 +370,33 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
|||
if (!classp->user1()) classp->user1(IS_RANDOMIZED);
|
||||
markMembers(classp);
|
||||
}
|
||||
if (nodep->classOrPackagep()->name() == "std") {
|
||||
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
|
||||
AstArg* const argp = VN_CAST(pinp, Arg);
|
||||
if (!argp) continue;
|
||||
AstNodeExpr* exprp = argp->exprp();
|
||||
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;
|
||||
}
|
||||
UASSERT_OBJ(randVarp, nodep, "No rand variable found");
|
||||
AstNode* backp = randVarp;
|
||||
while (backp && (!VN_IS(backp, Class) && !VN_IS(backp, NodeModule))) {
|
||||
backp = backp->backp();
|
||||
}
|
||||
UASSERT_OBJ(VN_IS(backp, NodeModule), randVarp,
|
||||
"No class or module found for rand variable");
|
||||
backp->user1(IS_STD_RANDOMIZED);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
|
||||
AstArg* const argp = VN_CAST(pinp, Arg);
|
||||
if (!argp) continue;
|
||||
|
@ -431,6 +461,7 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
|||
// of type AstLambdaArgRef. They are randomized too.
|
||||
const bool randObject = nodep->fromp()->user1() || VN_IS(nodep->fromp(), LambdaArgRef);
|
||||
nodep->user1(randObject && nodep->varp()->rand().isRandomizable());
|
||||
nodep->user2p(m_modp);
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
|
@ -472,7 +503,8 @@ public:
|
|||
class ConstraintExprVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// AstVar::user3() -> bool. Handled in constraints
|
||||
// AstNodeExpr::user1() -> bool. Depending on a randomized variable
|
||||
// AstNodeExpr::user1() -> bool. Depending on a randomized variable
|
||||
// AstMemberSel::user2p() -> AstNodeModule*. Pointer to containing module
|
||||
// VNuser3InUse m_inuser3; (Allocated for use in RandomizeVisitor)
|
||||
|
||||
AstNodeFTask* const m_inlineInitTaskp; // Method to add write_var calls to
|
||||
|
@ -598,19 +630,25 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
|
||||
// VISITORS
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
AstVar* const varp = nodep->varp();
|
||||
AstVar* varp = nodep->varp();
|
||||
if (varp->user4p()) {
|
||||
varp->user4p()->v3warn(
|
||||
CONSTRAINTIGN,
|
||||
"Size constraint combined with element constraint may not work correctly");
|
||||
}
|
||||
|
||||
AstMemberSel* membersel = VN_IS(nodep->backp(), MemberSel)
|
||||
? VN_AS(nodep->backp(), MemberSel)->cloneTree(false)
|
||||
: nullptr;
|
||||
if (membersel) varp = membersel->varp();
|
||||
AstNodeModule* const classOrPackagep = nodep->classOrPackagep();
|
||||
const RandomizeMode randMode = {.asInt = varp->user1()};
|
||||
if (!randMode.usesMode && editFormat(nodep)) return;
|
||||
|
||||
// In SMT just variable name, but we also ensure write_var for the variable
|
||||
const std::string smtName = nodep->name(); // Can be anything unique
|
||||
const std::string smtName = membersel
|
||||
? membersel->fromp()->name() + "." + membersel->name()
|
||||
: nodep->name(); // Can be anything unique
|
||||
|
||||
VNRelinker relinker;
|
||||
nodep->unlinkFrBack(&relinker);
|
||||
AstNodeExpr* exprp = new AstSFormatF{nodep->fileline(), smtName, false, nullptr};
|
||||
|
@ -648,11 +686,12 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
dimension = 1;
|
||||
}
|
||||
methodp->dtypeSetVoid();
|
||||
AstClass* const classp = VN_AS(varp->user2p(), Class);
|
||||
AstClass* const classp
|
||||
= membersel ? VN_AS(membersel->user2p(), Class) : VN_AS(varp->user2p(), Class);
|
||||
AstVarRef* const varRefp
|
||||
= new AstVarRef{varp->fileline(), classp, varp, VAccess::WRITE};
|
||||
varRefp->classOrPackagep(classOrPackagep);
|
||||
methodp->addPinsp(varRefp);
|
||||
membersel ? methodp->addPinsp(membersel) : methodp->addPinsp(varRefp);
|
||||
AstNodeDType* tmpDtypep = varp->dtypep();
|
||||
while (VN_IS(tmpDtypep, UnpackArrayDType) || VN_IS(tmpDtypep, DynArrayDType)
|
||||
|| VN_IS(tmpDtypep, QueueDType) || VN_IS(tmpDtypep, AssocArrayDType))
|
||||
|
@ -838,6 +877,12 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
if (nodep->user1()) {
|
||||
nodep->v3warn(CONSTRAINTIGN, "Global constraints ignored (unsupported)");
|
||||
}
|
||||
if (VN_IS(nodep->fromp(), NodeVarRef) && nodep->varp()->isRand() && m_inlineInitTaskp) {
|
||||
iterateChildren(nodep);
|
||||
nodep->replaceWith(nodep->fromp()->unlinkFrBack());
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
editFormat(nodep);
|
||||
}
|
||||
void visit(AstSFormatF* nodep) override {}
|
||||
|
@ -1121,9 +1166,11 @@ class CaptureVisitor final : public VNVisitor {
|
|||
void captureRefByThis(AstNodeVarRef* nodep, CaptureMode capModeFlags) {
|
||||
AstVar* const thisp = importThisp(nodep->fileline());
|
||||
AstVarRef* const thisRefp = new AstVarRef{nodep->fileline(), thisp, nodep->access()};
|
||||
thisRefp->user1(true);
|
||||
m_ignore.emplace(thisRefp);
|
||||
AstMemberSel* const memberSelp
|
||||
= new AstMemberSel(nodep->fileline(), thisRefp, nodep->varp());
|
||||
memberSelp->user2p(m_targetp);
|
||||
nodep->replaceWith(memberSelp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
m_ignore.emplace(memberSelp);
|
||||
|
@ -1249,6 +1296,7 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
// AstConstraint::user3p() -> AstTask*. Pointer to resize procedure
|
||||
// AstClass::user4p() -> AstVar*. Constraint mode state variable
|
||||
// AstVar::user4p() -> AstVar*. Size variable for constrained queues
|
||||
// AstMemberSel::user2p() -> AstNodeModule*. Pointer to containing module
|
||||
// VNUser1InUse m_inuser1; (Allocated for use in RandomizeMarkVisitor)
|
||||
// VNUser2InUse m_inuser2; (Allocated for use in RandomizeMarkVisitor)
|
||||
const VNUser3InUse m_inuser3;
|
||||
|
@ -1258,8 +1306,10 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
V3UniqueNames m_inlineUniqueNames; // For generating unique function names
|
||||
V3UniqueNames m_modeUniqueNames{"__Vmode"}; // For generating unique rand/constraint
|
||||
// mode state var names
|
||||
V3UniqueNames m_inlineUniqueStdName{"__VStdrand"};
|
||||
VMemberMap m_memberMap; // Member names cached for fast lookup
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
std::unordered_map<AstNodeModule*, AstVar*> m_stdMap; // Map from module/class to AST Var
|
||||
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
AstNodeStmt* m_stmtp = nullptr; // Current statement
|
||||
AstDynArrayDType* m_dynarrayDtp = nullptr; // Dynamic array type (for rand mode)
|
||||
|
@ -1281,6 +1331,18 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
classp->addMembersp(genp);
|
||||
classp->user3p(genp);
|
||||
}
|
||||
AstVar* createStdRandomGenerator(AstNodeModule* const modp) {
|
||||
auto it = m_stdMap.find(modp);
|
||||
if (it == m_stdMap.end()) {
|
||||
AstVar* const stdgenp
|
||||
= new AstVar{modp->fileline(), VVarType::MEMBER, "stdrand",
|
||||
modp->findBasicDType(VBasicDTypeKwd::RANDOM_STDGENERATOR)};
|
||||
modp->addStmtsp(stdgenp);
|
||||
m_stdMap.emplace(modp, stdgenp);
|
||||
return stdgenp;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
AstVar* getRandomGenerator(AstClass* const classp) {
|
||||
if (classp->user3p()) return VN_AS(classp->user3p(), Var);
|
||||
if (classp->extendsp()) return getRandomGenerator(classp->extendsp()->classp());
|
||||
|
@ -2184,16 +2246,53 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
|
||||
if (nodep->name() != "randomize") return;
|
||||
|
||||
if (nodep->classOrPackagep() && nodep->classOrPackagep()->name() == "std") {
|
||||
AstVar* const stdrand = createStdRandomGenerator(m_modp);
|
||||
AstFunc* const randomizeFuncp = V3Randomize::newRandomizeStdFunc(
|
||||
m_memberMap, m_modp, m_inlineUniqueStdName.get(nodep));
|
||||
randomizeFuncp->addStmtsp(
|
||||
new AstAssign{nodep->fileline(),
|
||||
new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var),
|
||||
VAccess::WRITE},
|
||||
new AstConst{nodep->fileline(), AstConst::WidthedValue{}, 32, 1}});
|
||||
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
|
||||
AstArg* const argp = VN_CAST(pinp, Arg);
|
||||
if (!argp) continue;
|
||||
AstNodeExpr* exprp = argp->exprp();
|
||||
AstCMethodHard* const basicMethodp = new AstCMethodHard{
|
||||
nodep->fileline(),
|
||||
new AstVarRef{nodep->fileline(), stdrand, VAccess::READWRITE},
|
||||
"basicStdRandomization"};
|
||||
const size_t width = exprp->width();
|
||||
basicMethodp->addPinsp(exprp->unlinkFrBack());
|
||||
basicMethodp->addPinsp(
|
||||
new AstConst{nodep->fileline(), AstConst::Unsized64{}, width});
|
||||
basicMethodp->dtypeSetBit();
|
||||
randomizeFuncp->addStmtsp(new AstAssign{
|
||||
nodep->fileline(),
|
||||
new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var),
|
||||
VAccess::WRITE},
|
||||
new AstAnd{nodep->fileline(),
|
||||
new AstVarRef{nodep->fileline(),
|
||||
VN_AS(randomizeFuncp->fvarp(), Var), VAccess::READ},
|
||||
basicMethodp}});
|
||||
}
|
||||
// Replace the node with a call to that function
|
||||
nodep->name(randomizeFuncp->name());
|
||||
nodep->taskp(randomizeFuncp);
|
||||
nodep->dtypeFrom(randomizeFuncp->dtypep());
|
||||
if (VN_IS(m_modp, Class)) nodep->classOrPackagep(m_modp);
|
||||
if (nodep->pinsp()) pushDeletep(nodep->pinsp()->unlinkFrBackWithNext());
|
||||
return;
|
||||
}
|
||||
handleRandomizeArgs(nodep);
|
||||
|
||||
AstWith* const withp = VN_CAST(nodep->pinsp(), With);
|
||||
|
||||
if (!withp) {
|
||||
iterateChildren(nodep);
|
||||
return;
|
||||
}
|
||||
withp->unlinkFrBack();
|
||||
|
||||
iterateChildren(nodep);
|
||||
|
||||
AstClass* classp = nullptr;
|
||||
|
@ -2232,7 +2331,6 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
|
||||
// Detach the expression and prepare variable copies
|
||||
const CaptureVisitor captured{withp->exprp(), m_modp, classp};
|
||||
|
||||
// Add function arguments
|
||||
captured.addFunctionArguments(randomizeFuncp);
|
||||
|
||||
|
@ -2406,6 +2504,28 @@ AstFunc* V3Randomize::newRandomizeFunc(VMemberMap& memberMap, AstClass* nodep,
|
|||
return funcp;
|
||||
}
|
||||
|
||||
AstFunc* V3Randomize::newRandomizeStdFunc(VMemberMap& memberMap, AstNodeModule* nodep,
|
||||
const std::string& name) {
|
||||
AstFunc* funcp = nullptr;
|
||||
v3Global.useRandomizeMethods(true);
|
||||
AstNodeDType* const dtypep = nodep->findBitDType(32, 32, VSigning::SIGNED);
|
||||
AstVar* const fvarp = new AstVar{nodep->fileline(), VVarType::MEMBER, name, dtypep};
|
||||
fvarp->lifetime(VLifetime::AUTOMATIC);
|
||||
fvarp->funcLocal(true);
|
||||
fvarp->funcReturn(true);
|
||||
fvarp->direction(VDirection::OUTPUT);
|
||||
funcp = new AstFunc{nodep->fileline(), name, nullptr, fvarp};
|
||||
funcp->dtypep(dtypep);
|
||||
if (VN_IS(nodep, Class)) {
|
||||
funcp->classMethod(true);
|
||||
} else {
|
||||
funcp->classMethod(false);
|
||||
funcp->isStatic(true);
|
||||
}
|
||||
nodep->addStmtsp(funcp);
|
||||
return funcp;
|
||||
}
|
||||
|
||||
AstFunc* V3Randomize::newSRandomFunc(VMemberMap& memberMap, AstClass* nodep) {
|
||||
AstClass* const basep = nodep->baseMostClassp();
|
||||
AstFunc* funcp = VN_AS(memberMap.findMember(basep, "srandom"), Func);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
class AstClass;
|
||||
class AstFunc;
|
||||
class AstNetlist;
|
||||
class AstNodeModule;
|
||||
|
||||
class VMemberMap;
|
||||
|
||||
|
@ -34,6 +35,8 @@ public:
|
|||
const std::string& name = "randomize",
|
||||
bool allowVirtual = true,
|
||||
bool childDType = false) VL_MT_DISABLED;
|
||||
static AstFunc* newRandomizeStdFunc(VMemberMap& memberMap, AstNodeModule* nodep,
|
||||
const std::string& name) VL_MT_DISABLED;
|
||||
static AstFunc* newSRandomFunc(VMemberMap& memberMap, AstClass* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
|
|
|
@ -6282,9 +6282,39 @@ class WidthVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleStdRandomizeArgs(AstNodeFTaskRef* const nodep) {
|
||||
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;
|
||||
AstNodeExpr* const exprp = argp->exprp();
|
||||
if (AstConst* const constp = VN_CAST(exprp, Const)) {
|
||||
if (constp->num().isNull()) {
|
||||
nullp = constp;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (VN_IS(exprp, MemberSel)) {
|
||||
// Non-standard usage: std::randomize() with class-scoped member
|
||||
// IEEE 1800-2023 (18.12) limits args to current scope variables.
|
||||
// Verilator accepts this for compatibility with other simulators.
|
||||
continue;
|
||||
} else if (VN_IS(exprp, VarRef)) {
|
||||
// Valid usage
|
||||
continue;
|
||||
} else {
|
||||
argp->v3error("Non-variable arguments for 'std::randomize()'.");
|
||||
}
|
||||
if (!argp) continue;
|
||||
}
|
||||
if (nullp) { nullp->v3error("'std::randomize()' does not accept 'null' as arguments."); }
|
||||
}
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
// For arguments, is assignment-like context; see IEEE rules in AstNodeAssign
|
||||
// Function hasn't been widthed, so make it so.
|
||||
if (nodep->didWidth()) return;
|
||||
UINFO(5, " FTASKREF " << nodep);
|
||||
AstWith* withp = nullptr;
|
||||
if (nodep->name() == "rand_mode" || nodep->name() == "constraint_mode") {
|
||||
|
@ -6297,16 +6327,23 @@ class WidthVisitor final : public VNVisitor {
|
|||
|| nodep->name() == "set_randstate"))) {
|
||||
// TODO perhaps this should move to V3LinkDot
|
||||
AstClass* const classp = VN_CAST(nodep->classOrPackagep(), Class);
|
||||
if (!classp) {
|
||||
if (nodep->classOrPackagep()->name() == "std") {
|
||||
v3Global.useRandomizeMethods(true);
|
||||
AstNodeDType* const adtypep = nodep->findBitDType();
|
||||
withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(),
|
||||
adtypep->findBitDType(), adtypep);
|
||||
for (const AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp())
|
||||
userIterateAndNext(VN_AS(argp, Arg)->exprp(), WidthVP{SELF, BOTH}.p());
|
||||
handleStdRandomizeArgs(nodep); // Provided args should be in current scope
|
||||
if (withp) {
|
||||
nodep->v3warn(CONSTRAINTIGN, "Unsupported: std::randomize()'s 'with'");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
processFTaskRefArgs(nodep);
|
||||
nodep->addPinsp(withp);
|
||||
nodep->v3warn(CONSTRAINTIGN, "std::randomize ignored (unsupported)");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
nodep->didWidth(true);
|
||||
return;
|
||||
}
|
||||
UASSERT_OBJ(classp, nodep, "Should have failed in V3LinkDot");
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 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
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.lint(verilator_flags2=["--binary"])
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,81 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by PlanV GmbH.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class std_randomize_class;
|
||||
|
||||
rand bit [7:0] addr;
|
||||
rand bit [31:0] data;
|
||||
rand bit [63:0] data_x_4;
|
||||
|
||||
bit [7:0] old_addr;
|
||||
bit [31:0] old_data;
|
||||
bit [63:0] old_data_x_4;
|
||||
|
||||
function bit std_randomize();
|
||||
int success;
|
||||
bit valid;
|
||||
|
||||
old_addr = addr;
|
||||
old_data = data;
|
||||
old_data_x_4 = data_x_4;
|
||||
|
||||
success = std::randomize(addr, data);
|
||||
|
||||
valid = (success == 1) && !(addr == old_addr || data == old_data) && data_x_4 == old_data_x_4;
|
||||
|
||||
return valid;
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
module t_scope_std_randomize;
|
||||
bit [7:0] addr;
|
||||
bit [15:0] data;
|
||||
|
||||
function bit run();
|
||||
int ready;
|
||||
int success;
|
||||
|
||||
bit [7:0] old_addr;
|
||||
bit [15:0] old_data;
|
||||
int old_ready;
|
||||
|
||||
old_addr = addr;
|
||||
old_data = data;
|
||||
old_ready = ready;
|
||||
success = randomize(addr, ready); // std::randomize
|
||||
if (success == 0) return 0;
|
||||
if (addr == old_addr && data != old_data && ready == old_ready) begin
|
||||
return 0;
|
||||
end
|
||||
return 1;
|
||||
endfunction
|
||||
|
||||
std_randomize_class test;
|
||||
|
||||
initial begin
|
||||
bit ok = 0;
|
||||
int success;
|
||||
|
||||
test = new();
|
||||
test.old_addr = test.addr;
|
||||
test.old_data = test.data;
|
||||
test.old_data_x_4 = test.data_x_4;
|
||||
success = std::randomize(test.addr, test.data);
|
||||
ok = (success == 1) && !(test.addr == test.old_addr || test.data == test.old_data) && test.data_x_4 == test.old_data_x_4;
|
||||
if (!ok) $stop;
|
||||
|
||||
ok = 0;
|
||||
ok = run();
|
||||
if (!ok) $stop;
|
||||
ok = 0;
|
||||
ok = test.std_randomize();
|
||||
if (!ok) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,10 @@
|
|||
%Error: t/t_std_randomize_bad1.v:12:36: Non-variable arguments for 'std::randomize()'.
|
||||
: ... note: In instance 't_std_randomize_bad1'
|
||||
12 | success = std::randomize(a + 1);
|
||||
| ^
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_std_randomize_bad1.v:19:30: 'std::randomize()' does not accept 'null' as arguments.
|
||||
: ... note: In instance 't_std_randomize_bad1'
|
||||
19 | void'(std::randomize(null));
|
||||
| ^~~~
|
||||
%Error: Exiting due to
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 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
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('linter')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,23 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by PlanV GmbH.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t_std_randomize_bad1;
|
||||
bit [3:0] a;
|
||||
|
||||
function int run();
|
||||
int success;
|
||||
success = std::randomize(a + 1); // ERROR: argument is not a variable
|
||||
return success;
|
||||
endfunction
|
||||
|
||||
initial begin
|
||||
int ok, x;
|
||||
ok = run();
|
||||
void'(std::randomize(null));
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 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
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.lint(verilator_flags2=["--binary"])
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,26 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by PlanV GmbH.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t_no_args;
|
||||
bit [7:0] addr;
|
||||
bit [15:0] data;
|
||||
bit [7:0] old_addr;
|
||||
bit [15:0] old_data;
|
||||
int success;
|
||||
bit valid;
|
||||
|
||||
initial begin
|
||||
old_addr = addr;
|
||||
old_data = data;
|
||||
|
||||
success = std::randomize();
|
||||
valid = (success == 1) && (addr == old_addr) && (data == old_data);
|
||||
if (!valid) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
|
@ -1,11 +1,7 @@
|
|||
%Warning-CONSTRAINTIGN: t/t_std_randomize_unsup_bad.v:10:16: std::randomize ignored (unsupported)
|
||||
: ... note: In instance 't'
|
||||
10 | if (std::randomize(a, b) != 1) $stop;
|
||||
| ^~~~~~~~~
|
||||
... 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_std_randomize_unsup_bad.v:11:16: std::randomize ignored (unsupported)
|
||||
%Warning-CONSTRAINTIGN: t/t_std_randomize_unsup_bad.v:11:16: Unsupported: std::randomize()'s 'with'
|
||||
: ... note: In instance 't'
|
||||
11 | if (std::randomize(a, b) with { 2 < a; a < 7; b < a; } != 1) $stop;
|
||||
| ^~~~~~~~~
|
||||
... 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.
|
||||
%Error: Exiting due to
|
||||
|
|
Loading…
Reference in New Issue