mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
Support constraint_mode
(#5338)
This commit is contained in:
parent
13e0fc7c27
commit
930f35acc9
@ -1102,22 +1102,23 @@ public:
|
||||
};
|
||||
class AstConstraintRef final : public AstNodeExpr {
|
||||
// A reference to a constraint identifier
|
||||
// Not saving pointer to constraint yet, as constraint_mode is an unsupported construct
|
||||
// @astgen op4 := scopeNamep : Optional[AstScopeName]
|
||||
// @astgen op1 := fromp : Optional[AstNodeExpr]
|
||||
//
|
||||
// @astgen ptr := m_constrp : AstConstraint // The constraint pointed to
|
||||
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package of constraint
|
||||
string m_name; // Name of constraint
|
||||
|
||||
public:
|
||||
AstConstraintRef(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_ConstraintRef(fl)
|
||||
, m_name{name} {
|
||||
AstConstraintRef(FileLine* fl, AstNodeExpr* fromp, AstConstraint* constrp)
|
||||
: ASTGEN_SUPER_ConstraintRef(fl) {
|
||||
this->fromp(fromp);
|
||||
this->constrp(constrp);
|
||||
dtypep(findConstraintRefDType());
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConstraintRef;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
string name() const override VL_MT_STABLE;
|
||||
AstConstraint* constrp() const VL_MT_STABLE { return m_constrp; }
|
||||
void constrp(AstConstraint* nodep) { m_constrp = nodep; }
|
||||
AstNodeModule* classOrPackagep() const VL_MT_STABLE { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
|
||||
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
|
||||
|
@ -1035,6 +1035,7 @@ public:
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
void isStatic(bool flag) { m_isStatic = flag; }
|
||||
bool isStatic() const { return m_isStatic; }
|
||||
|
@ -374,6 +374,8 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
string AstConstraintRef::name() const { return constrp()->name(); }
|
||||
|
||||
AstNetlist::AstNetlist()
|
||||
: ASTGEN_SUPER_Netlist(new FileLine{FileLine::builtInFilename()})
|
||||
, m_typeTablep{new AstTypeTable{fileline()}}
|
||||
|
@ -2932,7 +2932,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
} else if (AstConstraint* const consp = VN_CAST(foundp->nodep(), Constraint)) {
|
||||
AstNode* const newp = new AstConstraintRef{nodep->fileline(), consp->name()};
|
||||
AstNode* const newp = new AstConstraintRef{nodep->fileline(), nullptr, consp};
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
ok = true;
|
||||
@ -3440,7 +3440,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
// Resolved in V3Width
|
||||
} else if (nodep->name() == "randomize" || nodep->name() == "srandom"
|
||||
|| nodep->name() == "get_randstate" || nodep->name() == "set_randstate"
|
||||
|| nodep->name() == "rand_mode") {
|
||||
|| nodep->name() == "rand_mode" || nodep->name() == "constraint_mode") {
|
||||
if (AstClass* const classp = VN_CAST(m_modp, Class)) {
|
||||
nodep->classOrPackagep(classp);
|
||||
} else {
|
||||
|
@ -100,13 +100,13 @@ struct RandModeTarget final {
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
// Stores info about a variable's rand_mode state
|
||||
// Stores info about a variable's rand_mode state/a constraint's constraint_mode state
|
||||
|
||||
union VarRandMode final {
|
||||
union RandomizeMode final {
|
||||
// MEMBERS
|
||||
struct {
|
||||
bool usesRandMode : 1; // True if variable uses rand_mode
|
||||
uint32_t index : 31; // Index of var in rand_mode vector
|
||||
bool usesMode : 1; // Variable/constraint uses rand_mode/constraint_mode
|
||||
uint32_t index : 31; // Index of var/constraint in rand_mode/constraint_mode vector
|
||||
};
|
||||
int asInt; // Representation as int to be stored in nodep->user*
|
||||
};
|
||||
@ -155,8 +155,8 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
||||
}
|
||||
// If the class is randomized inline, all members use rand mode
|
||||
if (nodep->user1() == IS_RANDOMIZED_INLINE) {
|
||||
VarRandMode randMode = {};
|
||||
randMode.usesRandMode = true;
|
||||
RandomizeMode randMode = {};
|
||||
randMode.usesMode = true;
|
||||
varp->user1(randMode.asInt);
|
||||
}
|
||||
}
|
||||
@ -257,8 +257,8 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
||||
valid = false;
|
||||
} else if (randModeTarget.receiverp && randModeTarget.receiverp->isRand()) {
|
||||
// Called on a rand member variable
|
||||
VarRandMode randMode = {};
|
||||
randMode.usesRandMode = true;
|
||||
RandomizeMode randMode = {};
|
||||
randMode.usesMode = true;
|
||||
randModeTarget.receiverp->user1(randMode.asInt);
|
||||
} else {
|
||||
// Called on 'this' or a non-rand class instance
|
||||
@ -269,8 +269,8 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
||||
"Unsupported: 'rand_mode()' on static variable: "
|
||||
<< varp->prettyNameQ());
|
||||
}
|
||||
VarRandMode randMode = {};
|
||||
randMode.usesRandMode = true;
|
||||
RandomizeMode randMode = {};
|
||||
randMode.usesMode = true;
|
||||
varp->user1(randMode.asInt);
|
||||
});
|
||||
}
|
||||
@ -285,6 +285,69 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodep->name() == "constraint_mode") {
|
||||
bool valid = true;
|
||||
if (nodep->pinsp() && !VN_IS(nodep->backp(), StmtExpr)) {
|
||||
nodep->v3error(
|
||||
"'constraint_mode()' with arguments cannot be called as a function");
|
||||
valid = false;
|
||||
} else if (!nodep->pinsp() && VN_IS(nodep->backp(), StmtExpr)) {
|
||||
nodep->v3warn(
|
||||
IGNOREDRETURN,
|
||||
"Ignoring return value of non-void function (IEEE 1800-2023 13.4.1)");
|
||||
valid = false;
|
||||
}
|
||||
AstConstraint* constrp = nullptr;
|
||||
AstClass* classp = m_classp;
|
||||
if (const AstMethodCall* const methodCallp = VN_CAST(nodep, MethodCall)) {
|
||||
if (const AstConstraintRef* const constrRefp
|
||||
= VN_CAST(methodCallp->fromp(), ConstraintRef)) {
|
||||
constrp = constrRefp->constrp();
|
||||
if (constrRefp->fromp()) classp = VN_AS(constrRefp->classOrPackagep(), Class);
|
||||
if (constrp->isStatic()) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: 'constraint_mode()' on static constraint");
|
||||
valid = false;
|
||||
}
|
||||
} else if (AstClassRefDType* classRefDtp
|
||||
= VN_CAST(methodCallp->fromp()->dtypep()->skipRefp(), ClassRefDType)) {
|
||||
classp = classRefDtp->classp();
|
||||
} else {
|
||||
nodep->v3error("Cannot call 'constraint_mode()' on a non-class variable");
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
if (!nodep->pinsp() && !constrp) {
|
||||
nodep->v3error("Cannot call 'constraint_mode()' as a function on a variable");
|
||||
valid = false;
|
||||
}
|
||||
if (valid) {
|
||||
RandomizeMode constraintMode = {};
|
||||
constraintMode.usesMode = true;
|
||||
if (constrp) {
|
||||
constrp->user1(constraintMode.asInt);
|
||||
} else {
|
||||
classp->foreachMember([=](AstClass*, AstConstraint* constrp) {
|
||||
if (constrp->isStatic()) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: 'constraint_mode()' on static constraint: "
|
||||
<< constrp->prettyNameQ());
|
||||
}
|
||||
constrp->user1(constraintMode.asInt);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (!nodep->pinsp() && !VN_IS(nodep->backp(), StmtExpr)) {
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
} else {
|
||||
m_stmtp->unlinkFrBack();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodep->name() != "randomize") return;
|
||||
AstClass* classp = m_classp;
|
||||
if (const AstMethodCall* const methodCallp = VN_CAST(nodep, MethodCall)) {
|
||||
@ -324,8 +387,8 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
||||
if (randVarp == fromVarp) break;
|
||||
AstNode* backp = randVarp;
|
||||
while (backp && !VN_IS(backp, Class)) backp = backp->backp();
|
||||
VarRandMode randMode = {};
|
||||
randMode.usesRandMode = true;
|
||||
RandomizeMode randMode = {};
|
||||
randMode.usesMode = true;
|
||||
randVarp->user1(randMode.asInt);
|
||||
VN_AS(backp, Class)->user1(IS_RANDOMIZED_INLINE);
|
||||
}
|
||||
@ -493,15 +556,15 @@ class ConstraintExprVisitor final : public VNVisitor {
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
AstVar* const varp = nodep->varp();
|
||||
AstNodeModule* const classOrPackagep = nodep->classOrPackagep();
|
||||
const VarRandMode randMode = {.asInt = varp->user1()};
|
||||
if (!randMode.usesRandMode && editFormat(nodep)) return;
|
||||
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
|
||||
VNRelinker relinker;
|
||||
nodep->unlinkFrBack(&relinker);
|
||||
AstNodeExpr* exprp = new AstSFormatF{nodep->fileline(), smtName, false, nullptr};
|
||||
if (randMode.usesRandMode) {
|
||||
if (randMode.usesMode) {
|
||||
AstNodeExpr* constFormatp = getConstFormat(nodep);
|
||||
AstCMethodHard* const atp = new AstCMethodHard{
|
||||
nodep->fileline(),
|
||||
@ -533,7 +596,7 @@ class ConstraintExprVisitor final : public VNVisitor {
|
||||
= new AstCExpr{varp->fileline(), "\"" + smtName + "\"", varp->width()};
|
||||
varnamep->dtypep(varp->dtypep());
|
||||
methodp->addPinsp(varnamep);
|
||||
if (randMode.usesRandMode) {
|
||||
if (randMode.usesMode) {
|
||||
methodp->addPinsp(
|
||||
new AstConst{varp->fileline(), AstConst::Unsized64{}, randMode.index});
|
||||
}
|
||||
@ -1012,10 +1075,10 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
// AstVar::user2p() -> AstClass*. Pointer to containing class
|
||||
// AstEnumDType::user2() -> AstVar*. Pointer to table with enum values
|
||||
// AstConstraint::user2p() -> AstTask*. Pointer to constraint setup procedure
|
||||
// AstClass::user2p() -> AstTask*. Pointer to full constraint setup procedure
|
||||
// AstClass::user2p() -> AstVar*. Rand mode state variable
|
||||
// AstVar::user3() -> bool. Handled in constraints
|
||||
// AstClass::user3p() -> AstVar*. Constrained randomizer variable
|
||||
// AstClass::user4p() -> AstVar*. Rand mode state variable
|
||||
// AstClass::user4p() -> AstVar*. Constraint mode state variable
|
||||
// VNUser1InUse m_inuser1; (Allocated for use in RandomizeMarkVisitor)
|
||||
// VNUser2InUse m_inuser2; (Allocated for use in RandomizeMarkVisitor)
|
||||
const VNUser3InUse m_inuser3;
|
||||
@ -1023,8 +1086,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
|
||||
V3UniqueNames m_modeUniqueNames{"__Vmode"}; // For generating unique rand/constraint
|
||||
// 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
|
||||
@ -1053,20 +1116,49 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
return nullptr;
|
||||
}
|
||||
AstTask* getCreateConstraintSetupFunc(AstClass* classp) {
|
||||
if (classp->user2p()) return VN_AS(classp->user2p(), Task);
|
||||
AstTask* const setupAllTaskp
|
||||
= new AstTask{classp->fileline(), "__Vsetup_constraints", nullptr};
|
||||
static const char* const name = "__Vsetup_constraints";
|
||||
AstTask* setupAllTaskp = VN_AS(m_memberMap.findMember(classp, name), Task);
|
||||
if (setupAllTaskp) return setupAllTaskp;
|
||||
setupAllTaskp = new AstTask{classp->fileline(), "__Vsetup_constraints", nullptr};
|
||||
setupAllTaskp->classMethod(true);
|
||||
setupAllTaskp->isVirtual(true);
|
||||
classp->addMembersp(setupAllTaskp);
|
||||
classp->user2p(setupAllTaskp);
|
||||
m_memberMap.insert(classp, setupAllTaskp);
|
||||
return setupAllTaskp;
|
||||
}
|
||||
AstVar* getCreateRandModeVar(AstClass* const classp) {
|
||||
if (classp->user4p()) return VN_AS(classp->user4p(), Var);
|
||||
if (classp->user2p()) return VN_AS(classp->user2p(), Var);
|
||||
if (AstClassExtends* const extendsp = classp->extendsp()) {
|
||||
return getCreateRandModeVar(extendsp->classp());
|
||||
}
|
||||
AstVar* const randModeVarp = createModeVar(classp, "__Vrandmode");
|
||||
classp->user2p(randModeVarp);
|
||||
return randModeVarp;
|
||||
}
|
||||
static AstVar* getRandModeVar(AstClass* const classp) {
|
||||
if (classp->user2p()) return VN_AS(classp->user2p(), Var);
|
||||
if (AstClassExtends* const extendsp = classp->extendsp()) {
|
||||
return getRandModeVar(extendsp->classp());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
AstVar* getCreateConstraintModeVar(AstClass* const classp) {
|
||||
if (classp->user4p()) return VN_AS(classp->user4p(), Var);
|
||||
if (AstClassExtends* const extendsp = classp->extendsp()) {
|
||||
return getCreateConstraintModeVar(extendsp->classp());
|
||||
}
|
||||
AstVar* const constraintModeVarp = createModeVar(classp, "__Vconstraintmode");
|
||||
classp->user4p(constraintModeVarp);
|
||||
return constraintModeVarp;
|
||||
}
|
||||
static AstVar* getConstraintModeVar(AstClass* const classp) {
|
||||
if (classp->user4p()) return VN_AS(classp->user4p(), Var);
|
||||
if (AstClassExtends* const extendsp = classp->extendsp()) {
|
||||
return getConstraintModeVar(extendsp->classp());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
AstVar* createModeVar(AstClass* const classp, const char* const name) {
|
||||
FileLine* const fl = classp->fileline();
|
||||
if (!m_dynarrayDtp) {
|
||||
m_dynarrayDtp = new AstDynArrayDType{
|
||||
@ -1074,23 +1166,13 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
m_dynarrayDtp->dtypep(m_dynarrayDtp);
|
||||
v3Global.rootp()->typeTablep()->addTypesp(m_dynarrayDtp);
|
||||
}
|
||||
AstVar* const randModeVarp
|
||||
= new AstVar{fl, VVarType::MODULETEMP, "__Vrandmode", m_dynarrayDtp};
|
||||
|
||||
randModeVarp->user2p(classp);
|
||||
classp->addStmtsp(randModeVarp);
|
||||
classp->user4p(randModeVarp);
|
||||
return randModeVarp;
|
||||
AstVar* const modeVarp = new AstVar{fl, VVarType::MODULETEMP, name, m_dynarrayDtp};
|
||||
modeVarp->user2p(classp);
|
||||
classp->addStmtsp(modeVarp);
|
||||
return modeVarp;
|
||||
}
|
||||
AstVar* getRandModeVar(AstClass* const classp) {
|
||||
if (classp->user4p()) return VN_AS(classp->user4p(), Var);
|
||||
if (AstClassExtends* const extendsp = classp->extendsp()) {
|
||||
return getRandModeVar(extendsp->classp());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void addSetRandMode(AstNodeFTask* const ftaskp, AstVar* const genp,
|
||||
AstVar* const randModeVarp) {
|
||||
static void addSetRandMode(AstNodeFTask* const ftaskp, AstVar* const genp,
|
||||
AstVar* const randModeVarp) {
|
||||
FileLine* const fl = ftaskp->fileline();
|
||||
AstCMethodHard* const setRandModep = new AstCMethodHard{
|
||||
fl, new AstVarRef{fl, VN_AS(genp->user2p(), NodeModule), genp, VAccess::WRITE},
|
||||
@ -1101,46 +1183,62 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
ftaskp->addStmtsp(setRandModep->makeStmt());
|
||||
}
|
||||
void createRandomizeClassVars(AstNetlist* const netlistp) {
|
||||
netlistp->foreach([&](AstClass* const classp) {
|
||||
if (classp->existsMember(
|
||||
[&](const AstClass*, const AstConstraint*) { return true; })) {
|
||||
createRandomGenerator(classp);
|
||||
}
|
||||
netlistp->foreach([this](AstClass* const classp) {
|
||||
bool hasConstraints = false;
|
||||
uint32_t randModeCount = 0;
|
||||
classp->foreachMember([&](AstClass*, AstVar* memberVarp) {
|
||||
VarRandMode randMode = {.asInt = memberVarp->user1()};
|
||||
if (!randMode.usesRandMode) return;
|
||||
uint32_t constraintModeCount = 0;
|
||||
classp->foreachMember([&](AstClass*, AstNode* memberp) {
|
||||
// SystemVerilog only allows single inheritance, so we don't need to worry about
|
||||
// index overlap. If the index > 0, it's already been set.
|
||||
if (randMode.index == 0) {
|
||||
randMode.index = randModeCount++;
|
||||
memberVarp->user1(randMode.asInt);
|
||||
} else {
|
||||
randModeCount = randMode.index + 1;
|
||||
if (VN_IS(memberp, Constraint)) {
|
||||
hasConstraints = true;
|
||||
RandomizeMode constraintMode = {.asInt = memberp->user1()};
|
||||
if (!constraintMode.usesMode) return;
|
||||
if (constraintMode.index == 0) {
|
||||
constraintMode.index = constraintModeCount++;
|
||||
memberp->user1(constraintMode.asInt);
|
||||
} else {
|
||||
constraintModeCount = constraintMode.index + 1;
|
||||
}
|
||||
} else if (VN_IS(memberp, Var)) {
|
||||
RandomizeMode randMode = {.asInt = memberp->user1()};
|
||||
if (!randMode.usesMode) return;
|
||||
if (randMode.index == 0) {
|
||||
randMode.index = randModeCount++;
|
||||
memberp->user1(randMode.asInt);
|
||||
} else {
|
||||
randModeCount = randMode.index + 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (hasConstraints) createRandomGenerator(classp);
|
||||
if (randModeCount > 0) {
|
||||
AstVar* const randModeVarp = getCreateRandModeVar(classp);
|
||||
AstNodeModule* const randModeModp = VN_AS(randModeVarp->user2p(), NodeModule);
|
||||
FileLine* fl = randModeVarp->fileline();
|
||||
AstCMethodHard* const dynarrayNewp = new AstCMethodHard{
|
||||
fl, new AstVarRef{fl, randModeModp, randModeVarp, VAccess::WRITE},
|
||||
"renew_copy", new AstConst{fl, randModeCount}};
|
||||
dynarrayNewp->addPinsp(
|
||||
new AstVarRef{fl, randModeModp, randModeVarp, VAccess::READ});
|
||||
dynarrayNewp->dtypeSetVoid();
|
||||
AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask);
|
||||
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},
|
||||
new AstConst{fl, 1}, true));
|
||||
makeModeInit(randModeVarp, classp, randModeCount);
|
||||
}
|
||||
if (constraintModeCount > 0) {
|
||||
AstVar* const constraintModeVarp = getCreateConstraintModeVar(classp);
|
||||
makeModeInit(constraintModeVarp, classp, constraintModeCount);
|
||||
}
|
||||
});
|
||||
}
|
||||
static AstNode* makeRandModeInitLoop(FileLine* const fl, AstNodeExpr* const lhsp,
|
||||
AstNodeExpr* const rhsp, bool inTask) {
|
||||
void makeModeInit(AstVar* modeVarp, AstClass* classp, uint32_t modeCount) {
|
||||
AstNodeModule* const modeVarModp = VN_AS(modeVarp->user2p(), NodeModule);
|
||||
FileLine* fl = modeVarp->fileline();
|
||||
AstCMethodHard* const dynarrayNewp
|
||||
= new AstCMethodHard{fl, new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE},
|
||||
"renew_copy", new AstConst{fl, modeCount}};
|
||||
dynarrayNewp->addPinsp(new AstVarRef{fl, modeVarModp, modeVarp, VAccess::READ});
|
||||
dynarrayNewp->dtypeSetVoid();
|
||||
AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask);
|
||||
UASSERT_OBJ(newp, classp, "No new() in class");
|
||||
newp->addStmtsp(dynarrayNewp->makeStmt());
|
||||
newp->addStmtsp(makeModeSetLoop(fl,
|
||||
new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE},
|
||||
new AstConst{fl, 1}, true));
|
||||
}
|
||||
static AstNode* makeModeSetLoop(FileLine* const fl, AstNodeExpr* const lhsp,
|
||||
AstNodeExpr* const rhsp, bool inTask) {
|
||||
AstVar* const iterVarp = new AstVar{fl, VVarType::BLOCKTEMP, "i", lhsp->findUInt32DType()};
|
||||
iterVarp->funcLocal(inTask);
|
||||
iterVarp->lifetime(VLifetime::AUTOMATIC);
|
||||
@ -1160,16 +1258,20 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
new AstVarRef{fl, iterVarp, VAccess::READ}}}});
|
||||
return new AstBegin{fl, "", stmtsp, false, true};
|
||||
}
|
||||
AstNodeStmt* wrapIfRandMode(AstVar* const varp, AstNodeStmt* stmtp) {
|
||||
static AstNodeStmt* wrapIfRandMode(AstClass* classp, AstVar* const varp, AstNodeStmt* stmtp) {
|
||||
return VN_AS(wrapIfMode({.asInt = varp->user1()}, getRandModeVar(classp), stmtp),
|
||||
NodeStmt);
|
||||
}
|
||||
static AstNode* wrapIfConstraintMode(AstClass* classp, AstConstraint* const constrp,
|
||||
AstNode* stmtp) {
|
||||
return wrapIfMode({.asInt = constrp->user1()}, getConstraintModeVar(classp), stmtp);
|
||||
}
|
||||
static AstNode* wrapIfMode(const RandomizeMode mode, AstVar* modeVarp, AstNode* stmtp) {
|
||||
FileLine* const fl = stmtp->fileline();
|
||||
const VarRandMode randMode = {.asInt = varp->user1()};
|
||||
if (randMode.usesRandMode) {
|
||||
AstVar* randModeVarp = getRandModeVar(VN_AS(m_modp, Class));
|
||||
AstCMethodHard* const atp
|
||||
= new AstCMethodHard{fl,
|
||||
new AstVarRef{fl, VN_AS(randModeVarp->user2p(), Class),
|
||||
randModeVarp, VAccess::READ},
|
||||
"at", new AstConst{fl, randMode.index}};
|
||||
if (mode.usesMode) {
|
||||
AstCMethodHard* const atp = new AstCMethodHard{
|
||||
fl, new AstVarRef{fl, VN_AS(modeVarp->user2p(), Class), modeVarp, VAccess::READ},
|
||||
"at", new AstConst{fl, mode.index}};
|
||||
atp->dtypeSetUInt32();
|
||||
return new AstIf{fl, atp, stmtp};
|
||||
}
|
||||
@ -1299,7 +1401,7 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
if (varrefp->access().isWriteOrRW()) varp = varrefp->varp();
|
||||
return varp != nullptr;
|
||||
});
|
||||
return wrapIfRandMode(varp, assignp);
|
||||
return wrapIfRandMode(VN_AS(m_modp, Class), varp, assignp);
|
||||
}
|
||||
}
|
||||
AstNodeExpr* newRandValue(FileLine* const fl, AstVar* const randcVarp,
|
||||
@ -1365,9 +1467,8 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
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()};
|
||||
AstVar* const randModeTmpVarp = new AstVar{
|
||||
fl, VVarType::BLOCKTEMP, m_modeUniqueNames.get(randModeVarp), randModeVarp->dtypep()};
|
||||
randModeTmpVarp->funcLocal(m_ftaskp);
|
||||
randModeTmpVarp->lifetime(VLifetime::AUTOMATIC);
|
||||
storeStmtspr = AstNode::addNext(
|
||||
@ -1376,8 +1477,8 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
makeSiblingRefp(siblingExprp, randModeVarp, VAccess::READ)});
|
||||
storeStmtspr = AstNode::addNext(
|
||||
storeStmtspr,
|
||||
makeRandModeInitLoop(fl, makeSiblingRefp(siblingExprp, randModeVarp, VAccess::WRITE),
|
||||
new AstConst{fl, 0}, m_ftaskp));
|
||||
makeModeSetLoop(fl, makeSiblingRefp(siblingExprp, randModeVarp, VAccess::WRITE),
|
||||
new AstConst{fl, 0}, m_ftaskp));
|
||||
restoreStmtspr = AstNode::addNext(
|
||||
restoreStmtspr,
|
||||
new AstAssign{fl, makeSiblingRefp(siblingExprp, randModeVarp, VAccess::WRITE),
|
||||
@ -1438,8 +1539,9 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
UASSERT_OBJ(newp, nodep, "No new() in class");
|
||||
nodep->foreachMember([&](AstClass* classp, AstVar* memberVarp) {
|
||||
if (!memberVarp->rand().isRandomizable()) return;
|
||||
const VarRandMode randMode = {.asInt = memberVarp->user1()};
|
||||
if (randMode.usesRandMode && !memberVarp->isRand()) { // Not randomizable by default
|
||||
const RandomizeMode randMode = {.asInt = memberVarp->user1()};
|
||||
if (randMode.usesMode
|
||||
&& !memberVarp->rand().isRand()) { // Not randomizable by default
|
||||
AstCMethodHard* atp = new AstCMethodHard{
|
||||
nodep->fileline(),
|
||||
new AstVarRef{fl, VN_AS(randModeVarp->user2p(), NodeModule), randModeVarp,
|
||||
@ -1477,7 +1579,7 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
new AstConst{fl, AstConst::Null{}}},
|
||||
new AstAssign{fl, basicFvarRefp->cloneTree(false),
|
||||
new AstAnd{fl, basicFvarRefReadp, callp}}};
|
||||
basicRandomizep->addStmtsp(wrapIfRandMode(memberVarp, assignIfNotNullp));
|
||||
basicRandomizep->addStmtsp(wrapIfRandMode(nodep, memberVarp, assignIfNotNullp));
|
||||
} else {
|
||||
memberVarp->v3warn(E_UNSUPPORTED, "Unsupported: random member variable with type "
|
||||
<< memberVarp->dtypep()->prettyDTypeNameQ());
|
||||
@ -1485,6 +1587,54 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
});
|
||||
}
|
||||
|
||||
// Creates a lvalue reference to the randomize mode var. Called by visit(AstNodeFTaskRef*)
|
||||
AstNodeExpr* makeModeAssignLhs(FileLine* const fl, AstClass* const classp,
|
||||
AstNodeExpr* const fromp, AstVar* const modeVarp) {
|
||||
if (classp == m_modp) {
|
||||
// Called on 'this' or a member of 'this'
|
||||
return new AstVarRef{fl, VN_AS(modeVarp->user2p(), NodeModule), modeVarp,
|
||||
VAccess::WRITE};
|
||||
} else {
|
||||
AstMemberSel* const memberselp = new AstMemberSel{fl, fromp->unlinkFrBack(), modeVarp};
|
||||
memberselp->foreach([](AstVarRef* varrefp) { varrefp->access(VAccess::WRITE); });
|
||||
return memberselp;
|
||||
}
|
||||
}
|
||||
// Replace the node with an assignment to the mode variable. Called by visit(AstNodeFTaskRef*)
|
||||
void replaceWithModeAssign(AstNodeFTaskRef* const ftaskRefp, AstNode* const receiverp,
|
||||
AstNodeExpr* const lhsp) {
|
||||
FileLine* const fl = ftaskRefp->fileline();
|
||||
if (ftaskRefp->pinsp()) {
|
||||
UASSERT_OBJ(VN_IS(ftaskRefp->backp(), StmtExpr), ftaskRefp, "Should be a statement");
|
||||
AstNodeExpr* const rhsp = VN_AS(ftaskRefp->pinsp(), Arg)->exprp()->unlinkFrBack();
|
||||
if (receiverp) {
|
||||
// Called on a rand member variable/constraint. Set the variable/constraint's
|
||||
// mode
|
||||
const RandomizeMode mode = {.asInt = receiverp->user1()};
|
||||
UASSERT_OBJ(mode.usesMode, ftaskRefp, "Failed to set usesMode");
|
||||
AstCMethodHard* const atp
|
||||
= new AstCMethodHard{fl, lhsp, "at", new AstConst{fl, mode.index}};
|
||||
atp->dtypeSetUInt32();
|
||||
m_stmtp->replaceWith(new AstAssign{fl, atp, rhsp});
|
||||
} else {
|
||||
// For rand_mode: Called on 'this' or a non-rand class instance.
|
||||
// For constraint_mode: Called on a class instance.
|
||||
// Set the rand mode of all members
|
||||
m_stmtp->replaceWith(makeModeSetLoop(fl, lhsp, rhsp, m_ftaskp));
|
||||
}
|
||||
pushDeletep(m_stmtp);
|
||||
} else {
|
||||
UASSERT_OBJ(receiverp, ftaskRefp, "Should have receiver");
|
||||
const RandomizeMode mode = {.asInt = receiverp->user1()};
|
||||
UASSERT_OBJ(mode.usesMode, ftaskRefp, "Failed to set usesMode");
|
||||
AstCMethodHard* const atp
|
||||
= new AstCMethodHard{fl, lhsp, "at", new AstConst{fl, mode.index}};
|
||||
atp->dtypeSetUInt32();
|
||||
ftaskRefp->replaceWith(atp);
|
||||
VL_DO_DANGLING(pushDeletep(ftaskRefp), ftaskRefp);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle inline random variable control. After this, the randomize() call has no args
|
||||
void handleRandomizeArgs(AstNodeFTaskRef* const nodep) {
|
||||
if (!nodep->pinsp()) return;
|
||||
@ -1545,7 +1695,7 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
savedRandModeVarps.insert(randModeVarp);
|
||||
tmpVarps = AstNode::addNext(tmpVarps, randModeTmpVarp);
|
||||
}
|
||||
const VarRandMode randMode = {.asInt = randVarp->user1()};
|
||||
const RandomizeMode randMode = {.asInt = randVarp->user1()};
|
||||
AstCMethodHard* atp
|
||||
= new AstCMethodHard{fl, makeSiblingRefp(exprp, randModeVarp, VAccess::WRITE),
|
||||
"at", new AstConst{fl, randMode.index}};
|
||||
@ -1616,7 +1766,10 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
setupAllTaskp->addStmtsp(setupTaskRefp->makeStmt());
|
||||
|
||||
ConstraintExprVisitor{m_memberMap, constrp->itemsp(), nullptr, genp, randModeVarp};
|
||||
if (constrp->itemsp()) taskp->addStmtsp(constrp->itemsp()->unlinkFrBackWithNext());
|
||||
if (constrp->itemsp()) {
|
||||
taskp->addStmtsp(wrapIfConstraintMode(
|
||||
nodep, constrp, constrp->itemsp()->unlinkFrBackWithNext()));
|
||||
}
|
||||
});
|
||||
randomizep->addStmtsp(implementConstraintsClear(fl, genp));
|
||||
AstTask* setupAllTaskp = getCreateConstraintSetupFunc(nodep);
|
||||
@ -1727,49 +1880,36 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
const RandModeTarget randModeTarget = RandModeTarget::get(fromp, m_modp);
|
||||
UASSERT_OBJ(randModeTarget.classp, nodep,
|
||||
"Should have checked in RandomizeMarkVisitor");
|
||||
AstVar* const receiverp = randModeTarget.receiverp;
|
||||
AstVar* const randModeVarp = getRandModeVar(randModeTarget.classp);
|
||||
AstNodeExpr* lhsp = nullptr;
|
||||
if (randModeTarget.classp == m_modp) {
|
||||
// Called on 'this' or a member of 'this'
|
||||
lhsp = new AstVarRef{nodep->fileline(), VN_AS(randModeVarp->user2p(), NodeModule),
|
||||
randModeVarp, VAccess::WRITE};
|
||||
} else {
|
||||
AstMemberSel* const memberselp = new AstMemberSel{
|
||||
nodep->fileline(), randModeTarget.fromp->unlinkFrBack(), randModeVarp};
|
||||
memberselp->foreach([](AstVarRef* varrefp) { varrefp->access(VAccess::WRITE); });
|
||||
lhsp = memberselp;
|
||||
}
|
||||
if (nodep->pinsp()) { // Set rand mode
|
||||
UASSERT_OBJ(VN_IS(nodep->backp(), StmtExpr), nodep, "Should be a statement");
|
||||
AstNodeExpr* const rhsp = VN_AS(nodep->pinsp(), Arg)->exprp()->unlinkFrBack();
|
||||
if (randModeTarget.receiverp && randModeTarget.receiverp->isRand()) {
|
||||
// Called on a rand member variable. Set the variable's rand mode
|
||||
const VarRandMode randMode = {.asInt = randModeTarget.receiverp->user1()};
|
||||
UASSERT_OBJ(randMode.usesRandMode, nodep, "Failed to set usesRandMode");
|
||||
AstCMethodHard* const atp
|
||||
= new AstCMethodHard{nodep->fileline(), lhsp, "at",
|
||||
new AstConst{nodep->fileline(), randMode.index}};
|
||||
atp->dtypeSetUInt32();
|
||||
m_stmtp->replaceWith(new AstAssign{nodep->fileline(), atp, rhsp});
|
||||
} else {
|
||||
// Called on 'this' or a non-rand class instance. Set the rand mode of all
|
||||
// members
|
||||
m_stmtp->replaceWith(
|
||||
makeRandModeInitLoop(nodep->fileline(), lhsp, rhsp, m_ftaskp));
|
||||
AstNodeExpr* const lhsp = makeModeAssignLhs(nodep->fileline(), randModeTarget.classp,
|
||||
randModeTarget.fromp, randModeVarp);
|
||||
replaceWithModeAssign(nodep,
|
||||
// If the receiver is not rand, set the rand_mode for all members
|
||||
receiverp && receiverp->rand().isRand() ? receiverp : nullptr,
|
||||
lhsp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodep->name() == "constraint_mode") {
|
||||
AstMethodCall* const methodCallp = VN_CAST(nodep, MethodCall);
|
||||
AstNodeExpr* fromp = methodCallp ? methodCallp->fromp() : nullptr;
|
||||
AstConstraint* constrp = nullptr;
|
||||
AstClass* classp = VN_CAST(m_modp, Class);
|
||||
if (AstConstraintRef* const constrRefp = VN_CAST(fromp, ConstraintRef)) {
|
||||
constrp = constrRefp->constrp();
|
||||
if (constrRefp->fromp()) {
|
||||
fromp = constrRefp->fromp();
|
||||
classp = VN_AS(fromp->dtypep()->skipRefp(), ClassRefDType)->classp();
|
||||
}
|
||||
pushDeletep(m_stmtp);
|
||||
} else { // Retrieve rand mode
|
||||
UASSERT_OBJ(randModeTarget.receiverp, nodep, "Should have receiver");
|
||||
UASSERT_OBJ(randModeTarget.receiverp->isRand(), nodep, "Should be rand");
|
||||
const VarRandMode randMode = {.asInt = randModeTarget.receiverp->user1()};
|
||||
UASSERT_OBJ(randMode.usesRandMode, nodep, "Failed to set usesRandMode");
|
||||
AstCMethodHard* const atp
|
||||
= new AstCMethodHard{nodep->fileline(), lhsp, "at",
|
||||
new AstConst{nodep->fileline(), randMode.index}};
|
||||
atp->dtypeSetUInt32();
|
||||
nodep->replaceWith(atp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else if (fromp) {
|
||||
classp = VN_AS(fromp->dtypep()->skipRefp(), ClassRefDType)->classp();
|
||||
}
|
||||
UASSERT_OBJ(classp, nodep, "Failed to find class");
|
||||
AstVar* const constraintModeVarp = getConstraintModeVar(classp);
|
||||
AstNodeExpr* const lhsp
|
||||
= makeModeAssignLhs(nodep->fileline(), classp, fromp, constraintModeVarp);
|
||||
replaceWithModeAssign(nodep, constrp, lhsp);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2956,6 +2956,7 @@ class WidthVisitor final : public VNVisitor {
|
||||
|| VN_IS(fromDtp, UnpackArrayDType) //
|
||||
|| VN_IS(fromDtp, DynArrayDType) //
|
||||
|| VN_IS(fromDtp, QueueDType) //
|
||||
|| VN_IS(fromDtp, ConstraintRefDType) //
|
||||
|| VN_IS(fromDtp, BasicDType)) {
|
||||
// Method call on enum without following parenthesis, e.g. "ENUM.next"
|
||||
// Convert this into a method call, and let that visitor figure out what to do next
|
||||
@ -3006,6 +3007,12 @@ class WidthVisitor final : public VNVisitor {
|
||||
V3LinkLValue::linkLValueSet(nodep);
|
||||
return true;
|
||||
}
|
||||
if (AstConstraint* constrp = VN_CAST(foundp, Constraint)) {
|
||||
nodep->replaceWith(new AstConstraintRef{
|
||||
nodep->fileline(), nodep->fromp()->unlinkFrBack(), constrp});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return true;
|
||||
}
|
||||
if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
|
||||
nodep->replaceWith(adfoundp->cloneTree(false));
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
@ -3152,6 +3159,8 @@ class WidthVisitor final : public VNVisitor {
|
||||
userIterate(fromDtp, WidthVP{SELF, BOTH}.p());
|
||||
if (nodep->name() == "rand_mode") {
|
||||
methodCallRandMode(nodep);
|
||||
} else if (nodep->name() == "constraint_mode") {
|
||||
methodCallConstraint(nodep, nullptr);
|
||||
} else if (AstEnumDType* const adtypep = VN_CAST(fromDtp, EnumDType)) {
|
||||
methodCallEnum(nodep, adtypep);
|
||||
} else if (AstAssocArrayDType* const adtypep = VN_CAST(fromDtp, AssocArrayDType)) {
|
||||
@ -3947,10 +3956,8 @@ class WidthVisitor final : public VNVisitor {
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
} else if (nodep->name() == "constraint_mode") {
|
||||
nodep->v3warn(CONSTRAINTIGN, "Unsupported: 'constraint_mode' called on object");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitTrue{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
v3Global.useRandomizeMethods(true);
|
||||
nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT));
|
||||
}
|
||||
classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
|
||||
}
|
||||
@ -3971,13 +3978,15 @@ class WidthVisitor final : public VNVisitor {
|
||||
nodep->dtypeSetSigned32(); // Guess on error
|
||||
}
|
||||
void methodCallConstraint(AstMethodCall* nodep, AstConstraintRefDType*) {
|
||||
// Method call on constraint
|
||||
if (nodep->name() == "constraint_mode") {
|
||||
// IEEE 1800-2023 18.9
|
||||
methodOkArguments(nodep, 0, 1);
|
||||
nodep->v3warn(CONSTRAINTIGN, "constraint_mode ignored (unsupported)");
|
||||
// Constraints ignored, so we just return "OFF"
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
if (nodep->pinsp()) {
|
||||
nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT));
|
||||
} else {
|
||||
nodep->dtypeSetVoid();
|
||||
}
|
||||
v3Global.useRandomizeMethods(true);
|
||||
} else {
|
||||
nodep->v3error("No such constraint method " << nodep->prettyNameQ());
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
@ -5998,9 +6007,10 @@ class WidthVisitor final : public VNVisitor {
|
||||
// Function hasn't been widthed, so make it so.
|
||||
UINFO(5, " FTASKREF " << nodep << endl);
|
||||
AstWith* withp = nullptr;
|
||||
if (nodep->name() == "rand_mode") {
|
||||
if (nodep->name() == "rand_mode" || nodep->name() == "constraint_mode") {
|
||||
v3Global.useRandomizeMethods(true);
|
||||
nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT));
|
||||
return; // Handled in V3Randomize
|
||||
} else if (nodep->name() == "randomize" || nodep->name() == "srandom"
|
||||
|| (!nodep->taskp()
|
||||
&& (nodep->name() == "get_randstate"
|
||||
@ -6060,7 +6070,6 @@ class WidthVisitor final : public VNVisitor {
|
||||
UASSERT_OBJ(false, nodep, "Bad case");
|
||||
}
|
||||
}
|
||||
if (nodep->name() == "rand_mode") return; // Handled in V3Randomize
|
||||
UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked");
|
||||
if (nodep->didWidth()) return;
|
||||
if ((nodep->taskp()->classMethod() && !nodep->taskp()->isStatic())
|
||||
|
@ -80,8 +80,9 @@ private:
|
||||
bool local = false;
|
||||
bool prot = false;
|
||||
if (!defp) {
|
||||
// rand_mode() handled in V3Randomize
|
||||
UASSERT_OBJ(nodep->name() == "rand_mode", nodep, "Only rand_mode() can have no def");
|
||||
// rand_mode() / constraint_mode() handled in V3Randomize
|
||||
UASSERT_OBJ(nodep->name() == "rand_mode" || nodep->name() == "constraint_mode", nodep,
|
||||
"Only rand_mode() and constraint_mode() can have no def");
|
||||
return;
|
||||
}
|
||||
if (const auto varp = VN_CAST(defp, Var)) {
|
||||
|
@ -14,7 +14,6 @@ if (!$Self->have_solver) {
|
||||
skip("No constraint solver installed");
|
||||
} else {
|
||||
compile(
|
||||
verilator_flags2 => ['-Wno-CONSTRAINTIGN'],
|
||||
);
|
||||
|
||||
execute(
|
||||
|
@ -4,61 +4,96 @@
|
||||
// any use, without warranty, 2023 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class Packet;
|
||||
rand int m_one;
|
||||
|
||||
constraint cons { m_one > 0 && m_one < 2; }
|
||||
|
||||
Packet other;
|
||||
|
||||
task test1;
|
||||
// TODO Verilator ignores this setting currently, always returning 0 (constraint off)
|
||||
cons.constraint_mode(1);
|
||||
cons.constraint_mode(0);
|
||||
if (cons.constraint_mode() != 0) $stop;
|
||||
endtask
|
||||
|
||||
class Foo;
|
||||
rand int x;
|
||||
endclass
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
class Bar extends Foo;
|
||||
rand int y;
|
||||
|
||||
Packet p;
|
||||
constraint cons_x {x > 0; x < 10;};
|
||||
constraint cons_y {y == 10;};
|
||||
endclass
|
||||
|
||||
int v;
|
||||
class Qux extends Bar;
|
||||
rand int z;
|
||||
rand Bar bar;
|
||||
constraint cons_z {z == x || z == y;};
|
||||
|
||||
initial begin
|
||||
p = new;
|
||||
v = p.randomize();
|
||||
if (v != 1) $stop;
|
||||
if (p.m_one != 1) $stop;
|
||||
function new;
|
||||
bar = new;
|
||||
endfunction
|
||||
|
||||
// IEEE: function void object[.constraint_identifier].constraint_mode(bit on_off);
|
||||
// IEEE: function int object.constraint_identifier.constraint_mode();
|
||||
function void test;
|
||||
logic[1:0] ok = 0;
|
||||
cons_x.constraint_mode(1);
|
||||
if (cons_x.constraint_mode == 0) $stop;
|
||||
cons_y.constraint_mode(0);
|
||||
if (cons_y.constraint_mode == 1) $stop;
|
||||
bar.cons_x.constraint_mode(0);
|
||||
if (bar.cons_x.constraint_mode == 1) $stop;
|
||||
for (int i = 0; i < 20; ++i) begin
|
||||
x = 11;
|
||||
y = 12;
|
||||
z = 13;
|
||||
bar.x = 8;
|
||||
bar.y = 9;
|
||||
void'(randomize());
|
||||
if (x <= 0 || x >= 10) $stop;
|
||||
if (y != 10 && y != 12) ok[0] = 1;
|
||||
if (z != x && z != y) $stop;
|
||||
if (bar.x <= 0 || bar.x >= 10) ok[1] = 1;
|
||||
if (bar.y != 10) $stop;
|
||||
end
|
||||
if (ok != 2'b11) $stop;
|
||||
constraint_mode(1);
|
||||
if (cons_x.constraint_mode != 1) $stop;
|
||||
if (cons_y.constraint_mode != 1) $stop;
|
||||
if (cons_z.constraint_mode != 1) $stop;
|
||||
x = 14;
|
||||
y = 15;
|
||||
z = 16;
|
||||
void'(randomize());
|
||||
if (x <= 0 || x >= 10) $stop;
|
||||
if (y != 10) $stop;
|
||||
if (z != x && z != y) $stop;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// TODO Verilator ignores this setting currently, always returning 1 (rand on)
|
||||
// TODO test that these control constraints as specified
|
||||
p.constraint_mode(0);
|
||||
p.constraint_mode(1);
|
||||
// Not legal to get current constraint_mode() value on a class-only call
|
||||
module t;
|
||||
initial begin
|
||||
logic[1:0] ok = 0;
|
||||
int res;
|
||||
Qux qux = new;
|
||||
Bar bar = qux;
|
||||
|
||||
// TODO Verilator ignores this setting currently, always returning 0 (constraint off)
|
||||
// TODO test that these control constraints as specified
|
||||
p.cons.constraint_mode(1);
|
||||
p.cons.constraint_mode(0);
|
||||
if (p.cons.constraint_mode() != 0) $stop;
|
||||
qux.test;
|
||||
|
||||
// TODO Verilator ignores this setting currently, always returning 0 (constraint off)
|
||||
// TODO test that these control constraints as specified
|
||||
p.other = new;
|
||||
p.other.cons.constraint_mode(1);
|
||||
p.other.cons.constraint_mode(0);
|
||||
if (p.other.cons.constraint_mode() != 0) $stop;
|
||||
qux.bar.constraint_mode(1);
|
||||
bar.cons_y.constraint_mode(1);
|
||||
if (bar.cons_y.constraint_mode == 0) $stop;
|
||||
qux.cons_z.constraint_mode(0);
|
||||
if (qux.cons_z.constraint_mode == 1) $stop;
|
||||
qux.bar.cons_y.constraint_mode(0);
|
||||
if (qux.bar.cons_y.constraint_mode == 1) $stop;
|
||||
for (int i = 0; i < 20; ++i) begin
|
||||
qux.x = 17;
|
||||
qux.y = 18;
|
||||
qux.z = 19;
|
||||
qux.bar.x = 20;
|
||||
qux.bar.y = 10;
|
||||
void'(bar.randomize());
|
||||
if (qux.x <= 0 || qux.x >= 10) $stop;
|
||||
if (qux.y != 10 && qux.y != 12) $stop;
|
||||
if (qux.z != qux.x && qux.z != qux.y) ok[0] = 1;
|
||||
if (qux.bar.x <= 0 || qux.bar.x >= 10) $stop;
|
||||
if (qux.bar.y != 10) ok[1] = 1;
|
||||
res = qux.randomize() with {z == 100;};
|
||||
if (qux.z != 100) $stop;
|
||||
end
|
||||
if (ok != 2'b11) $stop;
|
||||
|
||||
p.test1();
|
||||
|
||||
// TODO for example way to test this, see t_randomize_rand_mode.v
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
23
test_regress/t/t_constraint_mode_bad.out
Normal file
23
test_regress/t/t_constraint_mode_bad.out
Normal file
@ -0,0 +1,23 @@
|
||||
%Error: t/t_constraint_mode_bad.v:21:15: Cannot call 'constraint_mode()' on a non-class variable
|
||||
: ... note: In instance 't'
|
||||
21 | p.m_one.constraint_mode(0);
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Error: t/t_constraint_mode_bad.v:22:45: Cannot call 'constraint_mode()' as a function on a variable
|
||||
: ... note: In instance 't'
|
||||
22 | $display("p.constraint_mode()=%0d", p.constraint_mode());
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Error: t/t_constraint_mode_bad.v:23:18: 'constraint_mode()' with arguments cannot be called as a function
|
||||
: ... note: In instance 't'
|
||||
23 | $display(p.constraint_mode(0));
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Warning-IGNOREDRETURN: t/t_constraint_mode_bad.v:24:14: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1)
|
||||
: ... note: In instance 't'
|
||||
24 | p.cons.constraint_mode();
|
||||
| ^~~~~~~~~~~~~~~
|
||||
... For warning description see https://verilator.org/warn/IGNOREDRETURN?v=latest
|
||||
... Use "/* verilator lint_off IGNOREDRETURN */" and lint_on around source to disable this message.
|
||||
%Error: t/t_constraint_mode_bad.v:12:14: Cannot call 'constraint_mode()' as a function on a variable
|
||||
: ... note: In instance 't'
|
||||
12 | return constraint_mode();
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Error: Exiting due to
|
@ -2,7 +2,7 @@
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2019 by Wilson Snyder. This program is free software; you
|
||||
# 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.
|
||||
@ -10,8 +10,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
||||
|
||||
scenarios(vlt => 1);
|
||||
|
||||
top_filename("t/t_constraint_mode.v");
|
||||
|
||||
lint(
|
||||
fails => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
26
test_regress/t/t_constraint_mode_bad.v
Normal file
26
test_regress/t/t_constraint_mode_bad.v
Normal file
@ -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, 2024 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class Packet;
|
||||
int m_one;
|
||||
constraint cons { m_one > 0 && m_one < 2; }
|
||||
|
||||
function int get_constraint_mode;
|
||||
return constraint_mode();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
Packet p;
|
||||
|
||||
initial begin
|
||||
p = new;
|
||||
p.m_one.constraint_mode(0);
|
||||
$display("p.constraint_mode()=%0d", p.constraint_mode());
|
||||
$display(p.constraint_mode(0));
|
||||
p.cons.constraint_mode();
|
||||
end
|
||||
endmodule
|
14
test_regress/t/t_constraint_mode_unsup.out
Normal file
14
test_regress/t/t_constraint_mode_unsup.out
Normal file
@ -0,0 +1,14 @@
|
||||
%Error-UNSUPPORTED: t/t_constraint_mode_unsup.v:17:55: Unsupported: 'constraint_mode()' on static constraint
|
||||
: ... note: In instance 't'
|
||||
17 | $display("p.cons.constraint_mode()=%0d", p.cons.constraint_mode());
|
||||
| ^~~~~~~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_constraint_mode_unsup.v:18:14: Unsupported: 'constraint_mode()' on static constraint
|
||||
: ... note: In instance 't'
|
||||
18 | p.cons.constraint_mode(0);
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_constraint_mode_unsup.v:19:9: Unsupported: 'constraint_mode()' on static constraint: 'cons'
|
||||
: ... note: In instance 't'
|
||||
19 | p.constraint_mode(0);
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Error: Exiting due to
|
19
test_regress/t/t_constraint_mode_unsup.pl
Executable file
19
test_regress/t/t_constraint_mode_unsup.pl
Executable 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(vlt => 1);
|
||||
|
||||
lint(
|
||||
fails => $Self->{vlt_all},
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
21
test_regress/t/t_constraint_mode_unsup.v
Normal file
21
test_regress/t/t_constraint_mode_unsup.v
Normal file
@ -0,0 +1,21 @@
|
||||
// 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 Packet;
|
||||
int m_one;
|
||||
static constraint cons { m_one > 0 && m_one < 2; }
|
||||
endclass
|
||||
|
||||
module t;
|
||||
Packet p;
|
||||
|
||||
initial begin
|
||||
p = new;
|
||||
$display("p.cons.constraint_mode()=%0d", p.cons.constraint_mode());
|
||||
p.cons.constraint_mode(0);
|
||||
p.constraint_mode(0);
|
||||
end
|
||||
endmodule
|
@ -1,47 +0,0 @@
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:16:12: constraint_mode ignored (unsupported)
|
||||
: ... note: In instance 't'
|
||||
16 | cons.constraint_mode(1);
|
||||
| ^~~~~~~~~~~~~~~
|
||||
... 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_constraint_mode.v:17:12: constraint_mode ignored (unsupported)
|
||||
: ... note: In instance 't'
|
||||
17 | cons.constraint_mode(0);
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:18:16: constraint_mode ignored (unsupported)
|
||||
: ... note: In instance 't'
|
||||
18 | if (cons.constraint_mode() != 0) $stop;
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:40:9: Unsupported: 'constraint_mode' called on object
|
||||
: ... note: In instance 't'
|
||||
40 | p.constraint_mode(0);
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:41:9: Unsupported: 'constraint_mode' called on object
|
||||
: ... note: In instance 't'
|
||||
41 | p.constraint_mode(1);
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:46:14: constraint_mode ignored (unsupported)
|
||||
: ... note: In instance 't'
|
||||
46 | p.cons.constraint_mode(1);
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:47:14: constraint_mode ignored (unsupported)
|
||||
: ... note: In instance 't'
|
||||
47 | p.cons.constraint_mode(0);
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:48:18: constraint_mode ignored (unsupported)
|
||||
: ... note: In instance 't'
|
||||
48 | if (p.cons.constraint_mode() != 0) $stop;
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:53:20: constraint_mode ignored (unsupported)
|
||||
: ... note: In instance 't'
|
||||
53 | p.other.cons.constraint_mode(1);
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:54:20: constraint_mode ignored (unsupported)
|
||||
: ... note: In instance 't'
|
||||
54 | p.other.cons.constraint_mode(0);
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:55:24: constraint_mode ignored (unsupported)
|
||||
: ... note: In instance 't'
|
||||
55 | if (p.other.cons.constraint_mode() != 0) $stop;
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Error: Exiting due to
|
Loading…
Reference in New Issue
Block a user