Support constraint_mode (#5338)

This commit is contained in:
Krzysztof Bieganski 2024-08-21 12:16:44 +02:00 committed by GitHub
parent 13e0fc7c27
commit 930f35acc9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 495 additions and 253 deletions

View File

@ -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(""); }

View File

@ -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; }

View File

@ -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()}}

View File

@ -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 {

View File

@ -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;
}

View File

@ -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())

View File

@ -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)) {

View File

@ -14,7 +14,6 @@ if (!$Self->have_solver) {
skip("No constraint solver installed");
} else {
compile(
verilator_flags2 => ['-Wno-CONSTRAINTIGN'],
);
execute(

View File

@ -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

View 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

View File

@ -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},

View 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

View 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

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(vlt => 1);
lint(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View 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

View File

@ -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