Support randsequence (#6131)
This commit is contained in:
parent
35615c268b
commit
b9b6eb61d9
|
|
@ -156,6 +156,7 @@ set(HEADERS
|
|||
V3PreShell.h
|
||||
V3Premit.h
|
||||
V3ProtectLib.h
|
||||
V3RandSequence.h
|
||||
V3Randomize.h
|
||||
V3Reloop.h
|
||||
V3Rtti.h
|
||||
|
|
@ -319,6 +320,7 @@ set(COMMON_SOURCES
|
|||
V3PreShell.cpp
|
||||
V3Premit.cpp
|
||||
V3ProtectLib.cpp
|
||||
V3RandSequence.cpp
|
||||
V3Randomize.cpp
|
||||
V3Reloop.cpp
|
||||
V3Sampled.cpp
|
||||
|
|
|
|||
|
|
@ -309,6 +309,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3Param.o \
|
||||
V3Premit.o \
|
||||
V3ProtectLib.o \
|
||||
V3RandSequence.o \
|
||||
V3Randomize.o \
|
||||
V3Reloop.o \
|
||||
V3Sampled.o \
|
||||
|
|
|
|||
|
|
@ -940,7 +940,7 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
|
|||
|
||||
class VCaseType final {
|
||||
public:
|
||||
enum en : uint8_t { CT_CASE, CT_CASEX, CT_CASEZ, CT_CASEINSIDE };
|
||||
enum en : uint8_t { CT_CASE, CT_CASEX, CT_CASEZ, CT_CASEINSIDE, CT_RANDSEQUENCE };
|
||||
enum en m_e;
|
||||
VCaseType()
|
||||
: m_e{CT_CASE} {}
|
||||
|
|
|
|||
|
|
@ -1917,6 +1917,12 @@ public:
|
|||
, m_urandom{urandom} {
|
||||
this->seedp(seedp);
|
||||
}
|
||||
class SRandomU32 {};
|
||||
AstRand(FileLine* fl, SRandomU32)
|
||||
: ASTGEN_SUPER_Rand(fl)
|
||||
, m_urandom{true} {
|
||||
dtypeSetUInt32();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstRand;
|
||||
string emitVerilog() override {
|
||||
return seedp() ? (m_urandom ? "%f$urandom(%l)" : "%f$random(%l)")
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ public:
|
|||
|
||||
// === AstNode ===
|
||||
class AstCaseItem final : public AstNode {
|
||||
// Single item of AstCase/AstRandCase/AstRSCase
|
||||
// Single item of AstCase/AstRandCase
|
||||
// @astgen op1 := condsp : List[AstNodeExpr]
|
||||
// @astgen op2 := stmtsp : List[AstNode]
|
||||
public:
|
||||
|
|
@ -887,42 +887,17 @@ public:
|
|||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
};
|
||||
class AstRSCase final : public AstNodeStmt {
|
||||
// Randsequence case statement
|
||||
// @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression
|
||||
// @astgen op2 := itemsp : List[AstCaseItem]
|
||||
class AstRSBreak final : public AstNodeStmt {
|
||||
// randsequence break
|
||||
public:
|
||||
AstRSCase(FileLine* fl, AstNodeExpr* exprp, AstCaseItem* itemsp)
|
||||
: ASTGEN_SUPER_Case(fl) {
|
||||
this->exprp(exprp);
|
||||
addItemsp(itemsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstRSCase;
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
bool sameNode(const AstNode* samep) const override { return true; }
|
||||
};
|
||||
class AstRSIf final : public AstNodeStmt {
|
||||
// Randsequence if
|
||||
// @astgen op1 := condp : AstNodeExpr
|
||||
// @astgen op2 := thensp : List[AstNode]
|
||||
// @astgen op3 := elsesp : List[AstNode]
|
||||
public:
|
||||
AstRSIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp)
|
||||
: ASTGEN_SUPER_RSIf(fl) {
|
||||
this->condp(condp);
|
||||
addThensp(thensp);
|
||||
addElsesp(elsesp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstRSIf;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isGateDedupable() const override { return false; }
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
explicit AstRSBreak(FileLine* fl)
|
||||
: ASTGEN_SUPER_RSBreak(fl) {}
|
||||
ASTGEN_MEMBERS_AstRSBreak;
|
||||
string verilogKwd() const override { return "break"; }
|
||||
bool isBrancher() const override { V3ERROR_NA_RETURN(true); } // Node removed early
|
||||
};
|
||||
class AstRSProd final : public AstNodeStmt {
|
||||
// randomsquence production, under a AstRandSequence
|
||||
// randomsequence production, under a AstRandSequence
|
||||
// @astgen op1 := fvarp : Optional[AstVar]
|
||||
// @astgen op2 := portsp : List[AstNode]
|
||||
// @astgen op3 := rulesp : List[AstRSRule]
|
||||
|
|
@ -935,12 +910,14 @@ public:
|
|||
addRulesp(rulesp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstRSProd;
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
};
|
||||
class AstRSProdItem final : public AstNodeStmt {
|
||||
// randomsquence production item
|
||||
// @astgen op1 := argsp : List[AstNodeExpr]
|
||||
// @astgen ptr := m_prodp : Optional[AstRSProd] // Pointer to production
|
||||
string m_name; // Name of block, or "" to use first production
|
||||
public:
|
||||
AstRSProdItem(FileLine* fl, const string& name, AstNodeExpr* argsp)
|
||||
|
|
@ -951,6 +928,8 @@ public:
|
|||
ASTGEN_MEMBERS_AstRSProdItem;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
AstRSProd* prodp() const { return m_prodp; }
|
||||
void prodp(AstRSProd* nodep) { m_prodp = nodep; }
|
||||
};
|
||||
class AstRSProdList final : public AstNodeStmt {
|
||||
// randomsquence production list
|
||||
|
|
@ -970,32 +949,26 @@ public:
|
|||
bool randJoin() const { return m_randJoin; }
|
||||
void randJoin(bool flag) { m_randJoin = flag; }
|
||||
};
|
||||
class AstRSRepeat final : public AstNodeStmt {
|
||||
// randsequence repeat
|
||||
// @astgen op1 := countp : AstNodeExpr
|
||||
// @astgen op2 := stmtsp : List[AstNode]
|
||||
class AstRSReturn final : public AstNodeStmt {
|
||||
// randsequence return
|
||||
public:
|
||||
AstRSRepeat(FileLine* fl, AstNodeExpr* countp, AstNode* stmtsp)
|
||||
: ASTGEN_SUPER_RSRepeat(fl) {
|
||||
this->countp(countp);
|
||||
addStmtsp(stmtsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstRSRepeat;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
explicit AstRSReturn(FileLine* fl)
|
||||
: ASTGEN_SUPER_RSReturn(fl) {}
|
||||
ASTGEN_MEMBERS_AstRSReturn;
|
||||
string verilogKwd() const override { return "return"; }
|
||||
bool isBrancher() const override { V3ERROR_NA_RETURN(true); } // Node removed early
|
||||
};
|
||||
class AstRSRule final : public AstNodeStmt {
|
||||
// randomsquence rule
|
||||
// @astgen op1 := weightp : Optional[AstNodeExpr]
|
||||
// @astgen op2 := prodlistsp : List[AstRSProdList]
|
||||
// @astgen op3 := stmtsp : List[AstNode]
|
||||
// @astgen op3 := weightStmtsp : List[AstNode]
|
||||
public:
|
||||
AstRSRule(FileLine* fl, AstNodeExpr* weightp, AstRSProdList* prodlistsp, AstNode* stmtsp)
|
||||
AstRSRule(FileLine* fl, AstNodeExpr* weightp, AstRSProdList* prodlistsp, AstNode* weightStmtsp)
|
||||
: ASTGEN_SUPER_RSRule(fl) {
|
||||
this->weightp(weightp);
|
||||
addProdlistsp(prodlistsp);
|
||||
addStmtsp(stmtsp);
|
||||
addWeightStmtsp(weightStmtsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstRSRule;
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
|
|
@ -1013,16 +986,24 @@ public:
|
|||
};
|
||||
class AstRandSequence final : public AstNodeStmt {
|
||||
// @astgen op2 := prodsp : List[AstRSProd]
|
||||
string m_name; // Name of block, or "" to use first production
|
||||
// @astgen ptr := m_prodp : Optional[AstRSProd] // Pointer to start production (if any)
|
||||
string m_name; // Created unique name
|
||||
string m_start; // Name of start production, or "" to use first production
|
||||
public:
|
||||
AstRandSequence(FileLine* fl, const string& name, AstRSProd* prodsp)
|
||||
AstRandSequence(FileLine* fl, const string& start, AstRSProd* prodsp)
|
||||
: ASTGEN_SUPER_RandSequence(fl)
|
||||
, m_name{name} {
|
||||
, m_start{start} {
|
||||
addProdsp(prodsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstRandSequence;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Block name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
string start() const VL_MT_STABLE { return m_start; }
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
AstRSProd* prodp() const { return m_prodp; }
|
||||
void prodp(AstRSProd* nodep) { m_prodp = nodep; }
|
||||
};
|
||||
class AstRelease final : public AstNodeStmt {
|
||||
// Procedural 'release' statement
|
||||
|
|
|
|||
|
|
@ -2271,6 +2271,14 @@ void AstTypedefFwd::dumpJson(std::ostream& str) const {
|
|||
}
|
||||
void AstNodeRange::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
void AstNodeRange::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
void AstRandSequence::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " start=" << start();
|
||||
}
|
||||
void AstRandSequence::dumpJson(std::ostream& str) const {
|
||||
if (!start().empty()) dumpJsonStr(str, "start", start());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstRange::dump(std::ostream& str) const {
|
||||
this->AstNodeRange::dump(str);
|
||||
if (fromBracket()) str << " [FB]";
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ class V3Global final {
|
|||
bool m_hasForceableSignals = false; // Need to apply V3Force pass
|
||||
bool m_hasSystemCSections = false; // Has AstSystemCSection that need to be emitted
|
||||
bool m_useParallelBuild = false; // Use parallel build for model
|
||||
bool m_useRandSequence = false; // Has `randsequence`
|
||||
bool m_useRandomizeMethods = false; // Need to define randomize() class methods
|
||||
uint64_t m_currentHierBlockCost = 0; // Total cost of this hier block, used for scheduling
|
||||
|
||||
|
|
@ -203,6 +204,8 @@ public:
|
|||
void hierGraphp(V3HierGraph* graphp) { m_hierGraphp = graphp; }
|
||||
bool useParallelBuild() const { return m_useParallelBuild; }
|
||||
void useParallelBuild(bool flag) { m_useParallelBuild = flag; }
|
||||
bool useRandSequence() const { return m_useRandSequence; }
|
||||
void useRandSequence(bool flag) { m_useRandSequence = flag; }
|
||||
bool useRandomizeMethods() const { return m_useRandomizeMethods; }
|
||||
void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; }
|
||||
void saveJsonPtrFieldName(const std::string& fieldName);
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ class LinkNodeMatcherModport final : public VNodeMatcher {
|
|||
public:
|
||||
bool nodeMatch(const AstNode* nodep) const override { return VN_IS(nodep, Modport); }
|
||||
};
|
||||
class LinkNodeMatcherProd final : public VNodeMatcher {
|
||||
public:
|
||||
bool nodeMatch(const AstNode* nodep) const override { return VN_IS(nodep, RSProd); }
|
||||
};
|
||||
class LinkNodeMatcherVar final : public VNodeMatcher {
|
||||
public:
|
||||
bool nodeMatch(const AstNode* nodep) const override {
|
||||
|
|
@ -246,20 +250,22 @@ public:
|
|||
} else {
|
||||
return "local type parameter";
|
||||
}
|
||||
} else if (VN_IS(nodep, Begin)) {
|
||||
return "block";
|
||||
} else if (VN_IS(nodep, Cell)) {
|
||||
return "instance";
|
||||
} else if (VN_IS(nodep, Constraint)) {
|
||||
return "constraint";
|
||||
} else if (VN_IS(nodep, Task)) {
|
||||
return "task";
|
||||
} else if (VN_IS(nodep, Func)) {
|
||||
return "function";
|
||||
} else if (VN_IS(nodep, Begin)) {
|
||||
return "block";
|
||||
} else if (VN_IS(nodep, Iface)) {
|
||||
return "interface";
|
||||
} else if (VN_IS(nodep, GenBlock)) {
|
||||
return "generate block";
|
||||
} else if (VN_IS(nodep, Iface)) {
|
||||
return "interface";
|
||||
} else if (VN_IS(nodep, RSProd)) {
|
||||
return "randsequence production";
|
||||
} else if (VN_IS(nodep, Task)) {
|
||||
return "task";
|
||||
} else {
|
||||
return nodep->prettyTypeName();
|
||||
}
|
||||
|
|
@ -1856,6 +1862,23 @@ class LinkDotFindVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
void visit(AstRandSequence* nodep) override {
|
||||
VL_RESTORER(m_curSymp);
|
||||
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstRSProd* nodep) override {
|
||||
VL_RESTORER(m_curSymp);
|
||||
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep);
|
||||
if (nodep->fvarp())
|
||||
nodep->fvarp()->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: randsequence production function variable");
|
||||
if (nodep->portsp())
|
||||
nodep->portsp()->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: randsequence production function ports");
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstWithParse* nodep) override { // FindVisitor::
|
||||
// Change WITHPARSE(FUNCREF, equation) to FUNCREF(WITH(equation))
|
||||
AstNodeFTaskRef* funcrefp = VN_CAST(nodep->funcrefp(), NodeFTaskRef);
|
||||
|
|
@ -2725,6 +2748,21 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
UASSERT_OBJ(ifaceTopVarp, nodep, "Can't find interface var ref: " << findName);
|
||||
return ifaceTopVarp;
|
||||
}
|
||||
AstRSProd* findProd(AstNode* nodep, VSymEnt* parentEntp, const string& name) {
|
||||
const VSymEnt* const foundp = parentEntp->findIdFallback(name);
|
||||
AstRSProd* foundNodep = foundp ? VN_CAST(foundp->nodep(), RSProd) : nullptr;
|
||||
if (!foundNodep) {
|
||||
VSpellCheck speller;
|
||||
LinkNodeMatcherProd matcher;
|
||||
parentEntp->candidateIdFlat(&speller, &matcher);
|
||||
const string suggest = speller.bestCandidateMsg(name);
|
||||
UINFO(1, " ErrParseRef curSymp=se" << cvtToHex(parentEntp));
|
||||
nodep->v3error("Production " << AstNode::prettyNameQ(name) << " not found\n"
|
||||
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
|
||||
return nullptr;
|
||||
}
|
||||
return foundNodep;
|
||||
}
|
||||
void markAndCheckPinDup(AstPin* nodep, AstNode* refp, const char* whatp) {
|
||||
const auto pair = m_usedPins.emplace(refp, nodep);
|
||||
if (!pair.second) {
|
||||
|
|
@ -4722,6 +4760,27 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
checkNoDot(nodep);
|
||||
symIterateChildren(nodep, m_statep->getNodeSym(nodep));
|
||||
}
|
||||
void visit(AstRandSequence* nodep) override {
|
||||
LINKDOT_VISIT_START();
|
||||
UINFO(5, indent() << "visit " << nodep);
|
||||
checkNoDot(nodep);
|
||||
if (!nodep->start().empty())
|
||||
nodep->prodp(findProd(nodep, m_statep->getNodeSym(nodep), nodep->start()));
|
||||
symIterateChildren(nodep, m_statep->getNodeSym(nodep));
|
||||
}
|
||||
void visit(AstRSProd* nodep) override {
|
||||
LINKDOT_VISIT_START();
|
||||
UINFO(5, indent() << "visit " << nodep);
|
||||
checkNoDot(nodep);
|
||||
symIterateChildren(nodep, m_statep->getNodeSym(nodep));
|
||||
}
|
||||
void visit(AstRSProdItem* nodep) override {
|
||||
LINKDOT_VISIT_START();
|
||||
UINFO(5, indent() << "visit " << nodep);
|
||||
checkNoDot(nodep);
|
||||
nodep->prodp(findProd(nodep, m_curSymp, nodep->name()));
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstWith* nodep) override {
|
||||
LINKDOT_VISIT_START();
|
||||
UINFO(5, indent() << "visit " << nodep);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
AstNode* m_loopp = nullptr; // Current loop
|
||||
AstRandSequence* m_randsequencep = nullptr; // Current randsequence
|
||||
bool m_loopInc = false; // In loop increment
|
||||
bool m_inFork = false; // Under fork
|
||||
int m_modRepeatNum = 0; // Repeat counter
|
||||
|
|
@ -289,6 +290,11 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstRandSequence* nodep) override {
|
||||
VL_RESTORER(m_randsequencep);
|
||||
m_randsequencep = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstRepeat* nodep) override {
|
||||
// So later optimizations don't need to deal with them,
|
||||
// REPEAT(count,body) -> loop=count,WHILE(loop>0) { body, loop-- }
|
||||
|
|
@ -354,6 +360,10 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
nodep->v3error("Return isn't legal under fork (IEEE 1800-2023 9.2.3)");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
} else if (!m_ftaskp && m_randsequencep) {
|
||||
nodep->replaceWith(new AstRSReturn{nodep->fileline()});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
} else if (!m_ftaskp) {
|
||||
nodep->v3error("Return isn't underneath a task or function");
|
||||
} else if (funcp && !nodep->lhsp() && !funcp->isConstructor()) {
|
||||
|
|
@ -377,7 +387,11 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
}
|
||||
void visit(AstBreak* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (!m_loopp) {
|
||||
if (!m_loopp && m_randsequencep) {
|
||||
nodep->replaceWith(new AstRSBreak{nodep->fileline()});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
} else if (!m_loopp) {
|
||||
nodep->v3error("break isn't underneath a loop");
|
||||
} else {
|
||||
// Jump to the end of the loop
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
int m_genblkAbove = 0; // Begin block number of if/case/for above
|
||||
int m_genblkNum = 0; // Begin block number, 0=none seen
|
||||
int m_beginDepth = 0; // How many begin blocks above current node within current AstNodeModule
|
||||
int m_randSequenceNum = 0; // RandSequence uniqify number
|
||||
VLifetime m_lifetime = VLifetime::STATIC_IMPLICIT; // Propagating lifetime
|
||||
bool m_insideLoop = false; // True if the node is inside a loop
|
||||
bool m_lifetimeAllowed = false; // True to allow lifetime settings
|
||||
|
|
@ -647,30 +648,6 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstRandSequence* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: randsequence");
|
||||
iterateChildren(nodep);
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
void visit(AstRSCase* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: randsequence case");
|
||||
iterateChildren(nodep);
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
void visit(AstRSIf* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: randsequence if");
|
||||
iterateChildren(nodep);
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
void visit(AstRSRepeat* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: randsequence repeat");
|
||||
iterateChildren(nodep);
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
void visit(AstWait* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -701,6 +678,7 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
VL_RESTORER(m_lifetime);
|
||||
VL_RESTORER(m_lifetimeAllowed);
|
||||
VL_RESTORER(m_moduleWithGenericIface);
|
||||
VL_RESTORER(m_randSequenceNum);
|
||||
VL_RESTORER(m_valueModp);
|
||||
|
||||
// Module: Create sim table for entire module and iterate
|
||||
|
|
@ -718,6 +696,8 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
m_lifetime = nodep->lifetime().makeImplicit();
|
||||
m_lifetimeAllowed = VN_IS(nodep, Class);
|
||||
m_moduleWithGenericIface = false;
|
||||
m_randSequenceNum = 0;
|
||||
|
||||
if (m_lifetime.isNone()) {
|
||||
m_lifetime
|
||||
= VN_IS(nodep, Class) ? VLifetime::AUTOMATIC_IMPLICIT : VLifetime::STATIC_IMPLICIT;
|
||||
|
|
@ -892,6 +872,11 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
nodep->name(m_modp->name());
|
||||
nodep->timeunit(m_modp->timeunit());
|
||||
}
|
||||
void visit(AstRandSequence* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
nodep->name("__Vrs" + std::to_string(m_randSequenceNum++));
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstSFormatF* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
iterateChildren(nodep);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,633 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Transform randsequence statements
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-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
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3Randomize's Transformations:
|
||||
//
|
||||
// Convert each RandSequence production into a production task, and hookup.
|
||||
// PROD({x}) -> TASK(__Vrs_{x}, ARG(__VrsBreak), arguments)
|
||||
// JUMPBLOCK
|
||||
//
|
||||
// Every variable referenced in the RandSequence becomes an ARG passed
|
||||
// around. (Alternative would be make a VARXREF's but the needed variable may
|
||||
// be on the stack, subject to recursion, or otherwise.)
|
||||
//
|
||||
// A production becomes a conditional execution of the PRODLIST below
|
||||
// for a single production list
|
||||
// RSPROD(RSRULE(weight, stmt))
|
||||
// -> IF(weight != 0, stmt)
|
||||
// or RSPROD(RSRULE(weight1, stmt!), RSRULE(weight2, stmt2))
|
||||
// -> RANDCASE(CASEITEM(weight1, stmt1), CASEITEM(weight2, stmt2))
|
||||
//
|
||||
// Return becomes a jump to end of production task
|
||||
// RSRETURN -> JUMPBLOCK(..., JUMPGO)
|
||||
//
|
||||
// Break becomes a jump to end of production task, and sets a variable
|
||||
// that the production call will check
|
||||
// RSBREAK -> ASSIGH(__VrsBreak, 1)
|
||||
// JUMPBLOCK(..., JUMPGO)
|
||||
//
|
||||
// Production calls become a function call
|
||||
// call({x}) -> TASKCALL(__Vrs_{x}, ARG(__VrsBreak), arguments)
|
||||
// IF(__VrsBreak) JUMPGO-break
|
||||
//
|
||||
// Rand join maps to a large complicated function, described in comments below
|
||||
//
|
||||
// If, Case, and Repeat map to normal non-randsequence nodes
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3RandSequence.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
// ######################################################################
|
||||
// Matcher classes (for suggestion matching)
|
||||
|
||||
class RSProdMatcher final : public VNodeMatcher {
|
||||
public:
|
||||
bool nodeMatch(const AstNode* nodep) const override { return VN_IS(nodep, RSProd); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Visitor that marks classes needing a randomize() method
|
||||
|
||||
class RandSequenceVisitor final : public VNVisitor {
|
||||
// STATE - global
|
||||
std::map<AstRSProd*, AstNodeFTask*> m_prodFuncps; // Generated production functions
|
||||
|
||||
// STATE - for current visit position (use VL_RESTORER)
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstRandSequence* m_rsp = nullptr; // Current randsequence
|
||||
AstRSProd* m_startProdp = nullptr; // Starting production
|
||||
AstNodeFTask* m_prodFuncp = nullptr; // Production function being built
|
||||
AstJumpBlock* m_jumpBlockp = nullptr; // Building jumpblock for return/break
|
||||
AstVar* m_breakVarp = nullptr; // Break variable
|
||||
std::unordered_set<AstVar*> m_localizes; // Variables to become function inouts
|
||||
std::map<std::string, AstVar*> m_localizeNames; // Ordered names of function inouts
|
||||
std::unordered_map<const AstVar*, AstVar*> m_localizeRemaps; // New ports for old vars
|
||||
|
||||
// METHODS
|
||||
void findLocalizes(AstRandSequence* nodep) {
|
||||
std::set<AstVar*> localVars;
|
||||
nodep->foreach([&](AstNode* const nodep) {
|
||||
if (AstVarRef* const anodep = VN_CAST(nodep, VarRef)) {
|
||||
m_localizes.emplace(anodep->varp());
|
||||
} else if (AstVar* const anodep = VN_CAST(nodep, Var)) {
|
||||
localVars.emplace(anodep);
|
||||
}
|
||||
});
|
||||
// Clear every variable allocated in the RandSequence
|
||||
for (AstVar* varp : localVars) m_localizes.erase(varp);
|
||||
// Sort by name, so function arguments are stable
|
||||
for (AstVar* varp : m_localizes) {
|
||||
UINFO(9, "localize " << varp);
|
||||
m_localizeNames.emplace(varp->name(), varp);
|
||||
}
|
||||
}
|
||||
|
||||
AstVar* newBreakVar(FileLine* fl, bool asOutput) {
|
||||
AstVar* const varp = new AstVar{fl, VVarType::PORT, "__VrsBreak", VFlagBitPacked{}, 1};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
if (asOutput) varp->direction(VDirection::OUTPUT);
|
||||
AstNode* outp = varp;
|
||||
outp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitFalse{}}});
|
||||
|
||||
// Also add arguments as next's
|
||||
for (auto& itr : m_localizeNames) {
|
||||
const AstVar* const lvarp = itr.second;
|
||||
AstVar* const iovarp
|
||||
= new AstVar{fl, VVarType::PORT, "__Vrsarg_" + lvarp->name(), lvarp};
|
||||
iovarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
iovarp->funcLocal(true);
|
||||
iovarp->direction(VDirection::REF);
|
||||
varp->addNext(iovarp);
|
||||
m_localizeRemaps.emplace(lvarp, iovarp);
|
||||
}
|
||||
return varp;
|
||||
}
|
||||
|
||||
AstTask* newStartFunc(AstRandSequence* nodep) {
|
||||
// Create wrapper that is called by the sequence
|
||||
AstTask* const taskp = new AstTask{m_startProdp->fileline(), m_rsp->name(), nullptr};
|
||||
taskp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
m_modp->addStmtsp(taskp);
|
||||
|
||||
// Create local (not output) break variable
|
||||
VL_RESTORER(m_localizeRemaps);
|
||||
AstVar* breakVarp = newBreakVar(nodep->fileline(), false);
|
||||
taskp->addStmtsp(breakVarp);
|
||||
|
||||
// Call the start production's task
|
||||
taskp->addStmtsp(newProdFuncRef(nodep, m_startProdp, breakVarp));
|
||||
|
||||
UINFOTREE(9, taskp, "newStart", "");
|
||||
return taskp;
|
||||
}
|
||||
|
||||
AstNode* newProdFuncRef(AstNode* nodep, AstRSProd* prodp, AstVar* breakVarp) {
|
||||
auto it = m_prodFuncps.find(prodp);
|
||||
UASSERT_OBJ(it != m_prodFuncps.end(), nodep, "No production function made");
|
||||
AstNodeFTask* const prodFuncp = it->second;
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstArg* const argsp
|
||||
= new AstArg{fl, breakVarp->name(), new AstVarRef{fl, breakVarp, VAccess::WRITE}};
|
||||
for (auto& itr : m_localizeNames) {
|
||||
const AstVar* const lvarp = itr.second;
|
||||
AstVar* const iovarp = m_localizeRemaps[lvarp];
|
||||
UASSERT_OBJ(iovarp, nodep, "No new port variable for local variable" << lvarp);
|
||||
argsp->addNext(new AstArg{nodep->fileline(), "__Vrsarg_" + lvarp->name(),
|
||||
new AstVarRef{fl, iovarp, VAccess::READWRITE}});
|
||||
}
|
||||
AstNode* const newp
|
||||
= new AstStmtExpr{fl, new AstTaskRef{fl, VN_AS(prodFuncp, Task), argsp}};
|
||||
return newp;
|
||||
}
|
||||
|
||||
void newProdFunc(AstRSProd* nodep) {
|
||||
// Task, as time may pass
|
||||
AstTask* const newp
|
||||
= new AstTask{nodep->fileline(), m_rsp->name() + "_" + nodep->name(), nullptr};
|
||||
m_modp->addStmtsp(newp);
|
||||
m_prodFuncps.emplace(nodep, newp);
|
||||
UINFOTREE(9, newp, "newProd", "");
|
||||
}
|
||||
|
||||
AstNode* newProdRandJoin(AstRSProd* prodp, AstRSProdList* prodlistp) {
|
||||
// For weight == 1.0 longer sequence (favor stay in a)
|
||||
// For weight == 0.0 shorter squence (favor change a/b)
|
||||
UASSERT_OBJ(prodlistp->weightp(), prodlistp, "Weight should have default CONST(0.5)");
|
||||
AstNodeExpr* weightp = prodlistp->weightp()->unlinkFrBack();
|
||||
|
||||
// Build map of production lists and productions we need to join
|
||||
// Must mainain the relative order of each sequence.
|
||||
std::deque<AstRSProdItem*> lists;
|
||||
std::unordered_map<AstRSProdItem*, std::deque<AstNodeStmt*>> listStmts;
|
||||
for (AstRSProdItem* proditemp = VN_AS(prodlistp->prodsp(), RSProdItem); proditemp;
|
||||
proditemp = VN_AS(proditemp->nextp(), RSProdItem)) {
|
||||
lists.push_back(proditemp);
|
||||
AstRSProd* const subProdp = proditemp->prodp();
|
||||
if (!subProdp) continue;
|
||||
if (!subProdp->rulesp()) continue;
|
||||
if (!subProdp->rulesp()->prodlistsp()) continue;
|
||||
for (AstNodeStmt* subStmtp
|
||||
= VN_AS(subProdp->rulesp()->prodlistsp()->prodsp(), NodeStmt);
|
||||
subStmtp; subStmtp = VN_AS(subStmtp->nextp(), NodeStmt)) {
|
||||
listStmts[proditemp].push_back(subStmtp);
|
||||
}
|
||||
}
|
||||
|
||||
UINFO(9, "RandJoin productions called:");
|
||||
for (AstRSProdItem* proditemp : lists) {
|
||||
UINFO(9, " list " << proditemp);
|
||||
for (AstNodeStmt* prodp : listStmts[proditemp]) UINFO(9, " calls " << prodp);
|
||||
}
|
||||
|
||||
// Need to clone all nodes used
|
||||
|
||||
FileLine* fl = prodlistp->fileline();
|
||||
AstNode* newp = nullptr;
|
||||
|
||||
// "bool _Vjoin_repick = true;" // Pick a new longer sequence
|
||||
AstVar* repickVarp = new AstVar{fl, VVarType::VAR, "_Vjoin_repick", VFlagBitPacked{}, 1};
|
||||
repickVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
repickVarp->funcLocal(true);
|
||||
m_breakVarp->addNextHere(repickVarp); // Add to function top, not newp
|
||||
newp = AstNode::addNext(newp,
|
||||
new AstAssign{fl, new AstVarRef{fl, repickVarp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitTrue{}}});
|
||||
|
||||
// "int _Vjoin_nleft_0 = N(items);" // N(a) Number left to generate in list 0, starts at
|
||||
// N(list0)
|
||||
std::deque<AstVar*> nleftVarps;
|
||||
int i = 0;
|
||||
for (AstRSProdItem* proditemp : lists) {
|
||||
// 'rand join arule arule arule' is legal, so need numbered, not non-unique named
|
||||
// nleft's
|
||||
AstVar* const varp = new AstVar{
|
||||
fl, VVarType::VAR, "_Vjoin_nleft_" + std::to_string(i++), VFlagBitPacked{}, 32};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
m_breakVarp->addNextHere(varp); // Add to function top, not newp
|
||||
nleftVarps.push_back(varp);
|
||||
|
||||
newp = AstNode::addNext(
|
||||
newp,
|
||||
new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::WidthedValue{}, 32,
|
||||
static_cast<uint32_t>(listStmts[proditemp].size())}});
|
||||
}
|
||||
|
||||
// "int _Vjoin_picked_elist = 0;" // 0 = pick first eligible (non-finished) list, 1 = pick
|
||||
// second, etc
|
||||
AstVar* const pickedVarp
|
||||
= new AstVar{fl, VVarType::VAR, "_Vjoin_picked_elist", VFlagBitPacked{}, 1};
|
||||
pickedVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
pickedVarp->funcLocal(true);
|
||||
m_breakVarp->addNextHere(pickedVarp); // Add to function top, not newp
|
||||
|
||||
// "while(1) {" // Generate until make complete sequence
|
||||
AstJumpBlock* const whilep = new AstJumpBlock{fl, nullptr};
|
||||
newp = AstNode::addNext(newp, new AstLoop{fl, whilep});
|
||||
{
|
||||
// "if ((std::rand() & 0xffffUL) > (0xffff * weight)) _Vjoin_repick = true;"
|
||||
whilep->addStmtsp(new AstIf{
|
||||
fl,
|
||||
new AstGt{
|
||||
fl, new AstRand{fl, AstRand::SRandomU32{}},
|
||||
new AstRToIS{fl, new AstMulD{fl,
|
||||
new AstConst{fl, AstConst::RealDouble{},
|
||||
static_cast<double>(0xffffffffUL)},
|
||||
weightp}}},
|
||||
new AstAssign{fl, new AstVarRef{fl, repickVarp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitTrue{}}}});
|
||||
|
||||
// "if (_Vjoin_repick) {"
|
||||
AstIf* ifp = new AstIf{fl, new AstVarRef{fl, repickVarp, VAccess::READ}};
|
||||
whilep->addStmtsp(ifp);
|
||||
{
|
||||
// "// _Vjoin_nlists_eligible is how many lists eligable for picking"
|
||||
// "int _Vjoin_nlists_eligible = 0;"
|
||||
AstVar* const eligibleVarp = new AstVar{
|
||||
fl, VVarType::VAR, "_Vjoin_nlists_eligible", VFlagBitPacked{}, 32};
|
||||
eligibleVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
eligibleVarp->funcLocal(true);
|
||||
m_breakVarp->addNextHere(eligibleVarp); // Add to function top, not newp
|
||||
ifp->addThensp(new AstAssign{fl, new AstVarRef{fl, eligibleVarp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::WidthedValue{}, 32, 0}});
|
||||
|
||||
i = 0;
|
||||
for (AstRSProdItem* proditemp : lists) {
|
||||
(void)proditemp;
|
||||
// "_Vjoin_nlists_eligible += _Vjoin_nleft_0 ? 1 : 0;"
|
||||
ifp->addThensp(new AstAssign{
|
||||
fl, new AstVarRef{fl, eligibleVarp, VAccess::WRITE},
|
||||
new AstAdd{
|
||||
fl, new AstVarRef{fl, eligibleVarp, VAccess::READ},
|
||||
new AstCond{
|
||||
fl,
|
||||
new AstNeq{fl, new AstConst{fl, AstConst::WidthedValue{}, 32, 0},
|
||||
new AstVarRef{fl, nleftVarps[i], VAccess::READ}},
|
||||
new AstConst{fl, AstConst::WidthedValue{}, 32, 1},
|
||||
new AstConst{fl, AstConst::WidthedValue{}, 32, 0}}}});
|
||||
++i;
|
||||
}
|
||||
|
||||
// "if (!_Vjoin_nlists_eligible) break;" // Out of elements
|
||||
ifp->addThensp(
|
||||
new AstIf{fl,
|
||||
new AstEq{fl, new AstConst{fl, AstConst::WidthedValue{}, 32, 0},
|
||||
new AstVarRef{fl, eligibleVarp, VAccess::READ}},
|
||||
new AstJumpGo{fl, m_jumpBlockp}});
|
||||
|
||||
// "// +1 to simplify usage, so 0 = done"
|
||||
// "_Vjoin_picked_elist = 1 + (std::rand() % _Vjoin_nlists_eligible);"
|
||||
ifp->addThensp(new AstAssign{
|
||||
fl, new AstVarRef{fl, pickedVarp, VAccess::WRITE},
|
||||
new AstAdd{fl, new AstConst{fl, AstConst::WidthedValue{}, 32, 1},
|
||||
new AstModDiv{fl, new AstRand{fl, AstRand::SRandomU32{}},
|
||||
new AstVarRef{fl, eligibleVarp, VAccess::READ}}}});
|
||||
|
||||
// "_Vjoin_repick = false;
|
||||
ifp->addThensp(new AstAssign{fl, new AstVarRef{fl, repickVarp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitFalse{}}});
|
||||
}
|
||||
|
||||
// "// Number of eligible lists left before hit _Vjoin_pciked one"
|
||||
// "int _Vjoin_sel_elist = _Vjoin_picked_elist;"
|
||||
AstVar* const selVarp
|
||||
= new AstVar{fl, VVarType::VAR, "_Vjoin_sel_elist", VFlagBitPacked{}, 32};
|
||||
selVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
selVarp->funcLocal(true);
|
||||
m_breakVarp->addNextHere(selVarp); // Add to function top, not newp
|
||||
whilep->addStmtsp(new AstAssign{fl, new AstVarRef{fl, selVarp, VAccess::WRITE},
|
||||
new AstVarRef{fl, pickedVarp, VAccess::READ}});
|
||||
|
||||
i = -1;
|
||||
for (AstRSProdItem* proditemp : lists) {
|
||||
++i;
|
||||
// "if (_Vjoin_nleft_0) {"
|
||||
ifp = new AstIf{fl,
|
||||
new AstNeq{fl, new AstConst{fl, AstConst::WidthedValue{}, 32, 0},
|
||||
new AstVarRef{fl, nleftVarps[i], VAccess::READ}},
|
||||
nullptr, nullptr};
|
||||
whilep->addStmtsp(ifp);
|
||||
{
|
||||
// "--_Vjoin_sel_elist;"
|
||||
ifp->addThensp(new AstAssign{
|
||||
fl, new AstVarRef{fl, selVarp, VAccess::WRITE},
|
||||
new AstSub{fl, new AstVarRef{fl, selVarp, VAccess::READ},
|
||||
new AstConst{fl, AstConst::WidthedValue{}, 32, 1}}});
|
||||
|
||||
// "if (_Vjoin_sel_elist == 0) {"
|
||||
AstIf* const jIfp = new AstIf{
|
||||
fl,
|
||||
new AstEq{fl, new AstConst{fl, AstConst::WidthedValue{}, 32, 0},
|
||||
new AstVarRef{fl, selVarp, VAccess::READ}},
|
||||
nullptr, nullptr};
|
||||
ifp->addThensp(jIfp);
|
||||
{
|
||||
// "switch (_Vjoin_nleft_0) {"
|
||||
// "case 2 / * N(a) * /: {statement}; break;"
|
||||
// "case 1 / * N(a) - 1 * /: {statement}; break;"
|
||||
uint32_t j = static_cast<uint32_t>(listStmts[proditemp].size());
|
||||
for (AstNodeStmt* prodp : listStmts[proditemp]) {
|
||||
jIfp->addThensp(new AstIf{
|
||||
fl,
|
||||
new AstEq{fl, new AstConst{fl, AstConst::WidthedValue{}, 32, j},
|
||||
new AstVarRef{fl, nleftVarps[i], VAccess::READ}},
|
||||
prodp->cloneTree(false)});
|
||||
--j;
|
||||
}
|
||||
|
||||
// "--_Vjoin_nleft_0;"
|
||||
jIfp->addThensp(new AstAssign{
|
||||
fl, new AstVarRef{fl, nleftVarps[i], VAccess::WRITE},
|
||||
new AstSub{fl, new AstVarRef{fl, nleftVarps[i], VAccess::READ},
|
||||
new AstConst{fl, AstConst::WidthedValue{}, 32, 1}}});
|
||||
|
||||
// "if (0 == _Vjoin_nleft_0) _Vjoin_repick = true;"
|
||||
jIfp->addThensp(new AstIf{
|
||||
fl,
|
||||
new AstEq{fl, new AstConst{fl, AstConst::WidthedValue{}, 32, 0},
|
||||
new AstVarRef{fl, nleftVarps[i], VAccess::READ}},
|
||||
new AstAssign{fl, new AstVarRef{fl, repickVarp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitTrue{}}}});
|
||||
|
||||
// "continue;"
|
||||
jIfp->addThensp(new AstJumpGo{fl, whilep});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UINFOTREE(9, newp, "ForkJoin new", "-");
|
||||
return newp;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstRandSequence* nodep) override {
|
||||
UINFO(9, "visit " << nodep);
|
||||
UASSERT_OBJ(m_modp, nodep, "randsequence not under module");
|
||||
if (m_rsp) {
|
||||
// No examples found in the wild
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: randsequence under randsequence");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
UINFOTREE(9, nodep, "rsIn", "");
|
||||
VL_RESTORER(m_rsp);
|
||||
m_rsp = nodep;
|
||||
VL_RESTORER(m_startProdp);
|
||||
|
||||
VL_RESTORER(m_localizes);
|
||||
VL_RESTORER(m_localizeNames);
|
||||
VL_RESTORER(m_localizeRemaps);
|
||||
findLocalizes(nodep);
|
||||
|
||||
// Find first production
|
||||
m_startProdp = nodep->prodsp();
|
||||
if (nodep->prodp()) m_startProdp = nodep->prodp(); // Has a start production selected
|
||||
|
||||
// Build production functions
|
||||
for (AstRSProd* prodp = nodep->prodsp(); prodp; prodp = VN_AS(prodp->nextp(), RSProd)) {
|
||||
newProdFunc(prodp);
|
||||
// Don't iterate yet, need to have tasks we can reference
|
||||
}
|
||||
|
||||
// Iterate all of the new productions, moving guts into functions just made
|
||||
// Must do those with 'rand join' first, as they need to see the raw AstRS* nodes
|
||||
// before they are removed. (Alternative would be to keep them until the end).
|
||||
UINFOTREE(9, nodep, "RS Tree pre-it", "-");
|
||||
std::unordered_set<AstRSProd*> prodHasRandJoin;
|
||||
for (AstRSProd* prodp = nodep->prodsp(); prodp; prodp = VN_AS(prodp->nextp(), RSProd)) {
|
||||
prodp->foreach([&](AstRSProdList* const prodlistp) {
|
||||
if (prodlistp->randJoin()) prodHasRandJoin.emplace(prodp);
|
||||
});
|
||||
}
|
||||
for (AstRSProd *nextp, *prodp = nodep->prodsp(); prodp; prodp = nextp) {
|
||||
nextp = VN_AS(prodp->nextp(), RSProd);
|
||||
if (prodHasRandJoin.count(prodp)) VL_DO_DANGLING(iterate(prodp), prodp);
|
||||
}
|
||||
for (AstRSProd *nextp, *prodp = nodep->prodsp(); prodp; prodp = nextp) {
|
||||
nextp = VN_AS(prodp->nextp(), RSProd);
|
||||
if (!prodHasRandJoin.count(prodp)) VL_DO_DANGLING(iterate(prodp), prodp);
|
||||
}
|
||||
UINFOTREE(9, nodep, "RS Tree post-it", "-");
|
||||
|
||||
// Replace randsequence with call to randsequence start function
|
||||
AstTask* const startFuncp = newStartFunc(nodep);
|
||||
AstArg* argsp = nullptr;
|
||||
for (auto& itr : m_localizeNames) {
|
||||
AstVar* const lvarp = itr.second;
|
||||
argsp = AstNode::addNext(
|
||||
argsp, new AstArg{nodep->fileline(), "__Vrsarg_" + lvarp->name(),
|
||||
new AstVarRef{nodep->fileline(), lvarp, VAccess::READWRITE}});
|
||||
}
|
||||
AstTaskRef* const callStartp
|
||||
= new AstTaskRef{nodep->fileline(), startFuncp->name(), argsp};
|
||||
callStartp->taskp(startFuncp);
|
||||
nodep->replaceWith(new AstStmtExpr{nodep->fileline(), callStartp});
|
||||
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstRSProd* nodep) override {
|
||||
// Production, immediately under a randsequence
|
||||
UINFO(9, "visit " << nodep);
|
||||
UINFOTREE(9, nodep, "visit " << nodep, "-");
|
||||
UASSERT_OBJ(!m_prodFuncp, nodep, "recursive production definition?");
|
||||
VL_RESTORER(m_prodFuncp);
|
||||
auto it = m_prodFuncps.find(nodep);
|
||||
UASSERT_OBJ(it != m_prodFuncps.end(), nodep, "No production function made");
|
||||
m_prodFuncp = it->second;
|
||||
|
||||
// Create a break variable
|
||||
// TODO we could do this only if break exists in the downstream production,
|
||||
// but as-is we'll optimize it away in most cases anyways
|
||||
VL_RESTORER(m_breakVarp);
|
||||
VL_RESTORER(m_localizeRemaps);
|
||||
m_breakVarp = newBreakVar(nodep->fileline(), true);
|
||||
m_prodFuncp->addStmtsp(m_breakVarp);
|
||||
|
||||
// Put JumpBlock immediately under the new function to support
|
||||
// a future break/return. V3Const will rip it out if unneeded.
|
||||
VL_RESTORER(m_jumpBlockp);
|
||||
m_jumpBlockp = new AstJumpBlock{nodep->fileline(), nullptr};
|
||||
m_prodFuncp->addStmtsp(m_jumpBlockp);
|
||||
|
||||
if (nodep->fvarp())
|
||||
nodep->fvarp()->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: randsequence production function variable");
|
||||
if (nodep->portsp())
|
||||
nodep->portsp()->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: randsequence production function ports");
|
||||
|
||||
// Move children into m_prodFuncp, and iterate there
|
||||
if (!nodep->rulesp()) { // Nothing to do
|
||||
} else if (nodep->rulesp()->prodlistsp() && nodep->rulesp()->prodlistsp()->randJoin()) {
|
||||
AstRSProdList* const prodlistp = nodep->rulesp()->prodlistsp();
|
||||
AstNode* const itemsp = newProdRandJoin(nodep, prodlistp);
|
||||
m_jumpBlockp->addStmtsp(itemsp);
|
||||
iterateAndNextNull(itemsp);
|
||||
} else if (!nodep->rulesp()->nextp()) { // Single rule/list, can just do it
|
||||
// RSPROD(RSRULE(weight, stmt)) -> IF(weight != 0, stmt)
|
||||
AstRSRule* const rulep = nodep->rulesp();
|
||||
AstNode* itemsp = nullptr;
|
||||
if (rulep->weightStmtsp()) itemsp = rulep->weightStmtsp()->unlinkFrBackWithNext();
|
||||
if (rulep->prodlistsp())
|
||||
itemsp = AstNode::addNext(itemsp, rulep->prodlistsp()->unlinkFrBackWithNext());
|
||||
// Can ignore rulep->weightp() unless is zero, in which case noop, add if
|
||||
if (rulep->weightp())
|
||||
itemsp = new AstIf{rulep->weightp()->fileline(),
|
||||
new AstNeq{rulep->weightp()->fileline(),
|
||||
new AstConst{rulep->weightp()->fileline(), 0},
|
||||
rulep->weightp()->unlinkFrBack()},
|
||||
itemsp};
|
||||
// Move prodlist to parent (m_prodFunc) and process
|
||||
if (itemsp) {
|
||||
UINFOTREE(9, itemsp, "additems", "-");
|
||||
m_jumpBlockp->addStmtsp(itemsp);
|
||||
iterateAndNextNull(itemsp);
|
||||
UINFOTREE(9, m_jumpBlockp, "jumpBlockNow", "-");
|
||||
}
|
||||
} else {
|
||||
// List of rules with "OR", need to random-weight them
|
||||
// RSPROD(RSRULE(weight1, stmt!), RSRULE(weight2, stmt2))
|
||||
// -> RANDCASE(CASEITEM(weight1, stmt1), CASEITEM(weight2, stmt2))
|
||||
AstRandCase* const randcasep = new AstRandCase{nodep->fileline(), nullptr};
|
||||
for (AstRSRule* rulep = nodep->rulesp(); rulep;
|
||||
rulep = VN_AS(rulep->nextp(), RSRule)) {
|
||||
AstNode* itemsp = nullptr;
|
||||
if (rulep->weightStmtsp()) itemsp = rulep->weightStmtsp()->unlinkFrBackWithNext();
|
||||
if (rulep->prodlistsp())
|
||||
itemsp = AstNode::addNext(itemsp, rulep->prodlistsp()->unlinkFrBackWithNext());
|
||||
if (itemsp) {
|
||||
AstNodeExpr* const weightp = rulep->weightp()
|
||||
? rulep->weightp()->unlinkFrBack()
|
||||
: new AstConst{rulep->fileline(), 1};
|
||||
randcasep->addItemsp(new AstCaseItem{rulep->fileline(), weightp, itemsp});
|
||||
}
|
||||
}
|
||||
m_jumpBlockp->addStmtsp(randcasep);
|
||||
iterateAndNextNull(randcasep);
|
||||
}
|
||||
|
||||
// V3Task needs to know if we ended up making a recursive function
|
||||
m_prodFuncp->foreach([&](AstNodeFTaskRef* const nodep) {
|
||||
if (nodep->taskp() == m_prodFuncp) m_prodFuncp->recursive(true);
|
||||
});
|
||||
UINFOTREE(9, m_prodFuncp, "rsprod-task-done " << nodep, "-");
|
||||
|
||||
// Done with production, should have moved everything to new task
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstRSRule* nodep) override {
|
||||
// Rule of what to produce (as a list) under a production
|
||||
nodep->v3fatalSrc("randsequence rule should be iterated and deleted by AstRSProd");
|
||||
}
|
||||
void visit(AstRSProdList* nodep) override {
|
||||
// List of productions to call, potentially with a weight
|
||||
UINFO(9, "visit " << nodep);
|
||||
UASSERT_OBJ(m_prodFuncp, nodep, "RSProdList not under production");
|
||||
|
||||
// Move prodlist to parent (m_prodFunc) and process
|
||||
if (AstNode* const itemsp = nodep->prodsp()) {
|
||||
UASSERT_OBJ(!nodep->randJoin(), nodep,
|
||||
"rand join should have been handled in visit(RSRule)");
|
||||
nodep->replaceWith(itemsp->unlinkFrBackWithNext());
|
||||
// Will soon iterate itemsp as part of normal visit process
|
||||
// These will become sequential in the generated task
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstRSProdItem* nodep) override {
|
||||
// "call" to generate using another production
|
||||
UINFO(9, "visit " << nodep);
|
||||
UASSERT_OBJ(m_prodFuncp, nodep, "RSProdItem not under production");
|
||||
AstRSProd* const foundp = nodep->prodp();
|
||||
UASSERT_OBJ(foundp, nodep, "Unlinked production reference");
|
||||
// Convert to task call
|
||||
AstNode* const newp = newProdFuncRef(nodep, foundp, m_breakVarp);
|
||||
// The production might have done a "break;", skip other steps if so
|
||||
newp->addNext(new AstIf{nodep->fileline(),
|
||||
new AstVarRef{nodep->fileline(), m_breakVarp, VAccess::READ},
|
||||
new AstJumpGo{nodep->fileline(), m_jumpBlockp}});
|
||||
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
void visit(AstRSBreak* nodep) override {
|
||||
UASSERT_OBJ(m_jumpBlockp, nodep, "RSBreak not under production jump block");
|
||||
UASSERT_OBJ(m_breakVarp, nodep, "no break variable");
|
||||
AstNodeStmt* const newp = new AstAssign{
|
||||
nodep->fileline(), new AstVarRef{nodep->fileline(), m_breakVarp, VAccess::WRITE},
|
||||
new AstConst{nodep->fileline(), AstConst::BitTrue{}}};
|
||||
newp->addNext(new AstJumpGo{nodep->fileline(), m_jumpBlockp});
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstRSReturn* nodep) override {
|
||||
UASSERT_OBJ(m_jumpBlockp, nodep, "RSReturn not under production jump block");
|
||||
nodep->replaceWith(new AstJumpGo{nodep->fileline(), m_jumpBlockp});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
// Change references to variables inside randsequence to references to
|
||||
// ref variables on the new task
|
||||
const auto it = m_localizeRemaps.find(nodep->varp());
|
||||
if (it != m_localizeRemaps.end()) {
|
||||
AstVar* const iovarp = it->second;
|
||||
UINFO(9, "VARREF remap " << nodep << " -> " << iovarp);
|
||||
nodep->varp(iovarp);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit RandSequenceVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
~RandSequenceVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// RandSequence method class functions
|
||||
|
||||
void V3RandSequence::randSequenceNetlist(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ RandSequenceVisitor randSequenceVisitor{nodep}; }
|
||||
V3Global::dumpCheckGlobalTree("randsequence", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Generate randomization procedures
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-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
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3RANDSEQUENCE_H_
|
||||
#define VERILATOR_V3RANDSEQUENCE_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
class V3RandSequence final {
|
||||
public:
|
||||
static void randSequenceNetlist(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -5721,6 +5721,12 @@ class WidthVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
void visit(AstRSRule* nodep) override {
|
||||
if (nodep->weightp()) iterateCheckUInt32(nodep, "weight", nodep->weightp(), BOTH);
|
||||
userIterateAndNext(nodep->prodlistsp(), nullptr);
|
||||
userIterateAndNext(nodep->weightStmtsp(), nullptr);
|
||||
}
|
||||
|
||||
void visit(AstRelease* nodep) override {
|
||||
userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
|
||||
UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "How can LValue be untyped?");
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@
|
|||
#include "V3PreShell.h"
|
||||
#include "V3Premit.h"
|
||||
#include "V3ProtectLib.h"
|
||||
#include "V3RandSequence.h"
|
||||
#include "V3Randomize.h"
|
||||
#include "V3Reloop.h"
|
||||
#include "V3Sampled.h"
|
||||
|
|
@ -228,6 +229,9 @@ static void process() {
|
|||
// Before we do dead code elimination and inlining, or we'll lose it.
|
||||
if (v3Global.opt.coverage()) V3Coverage::coverage(v3Global.rootp());
|
||||
|
||||
// Resolve randsequence if they are used by the design
|
||||
if (v3Global.useRandSequence()) V3RandSequence::randSequenceNetlist(v3Global.rootp());
|
||||
|
||||
// Add randomize() class methods if they are used by the design
|
||||
if (v3Global.useRandomizeMethods()) V3Randomize::randomizeNetlist(v3Global.rootp());
|
||||
|
||||
|
|
|
|||
|
|
@ -7087,9 +7087,11 @@ hierarchical_btf_identifier<nodep>: // ==IEEE: hierarchical_btf_identifier
|
|||
|
||||
randsequence_statement<nodeStmtp>: // ==IEEE: randsequence_statement
|
||||
yRANDSEQUENCE '(' ')' rs_productionList yENDSEQUENCE
|
||||
{ $$ = new AstRandSequence{$1, "", $4}; }
|
||||
{ $$ = new AstRandSequence{$1, "", $4};
|
||||
v3Global.useRandSequence(true); }
|
||||
| yRANDSEQUENCE '(' idAny/*rs_production_identifier*/ ')' rs_productionList yENDSEQUENCE
|
||||
{ $$ = new AstRandSequence{$1, *$3, $5}; }
|
||||
{ $$ = new AstRandSequence{$1, *$3, $5};
|
||||
v3Global.useRandSequence(true); }
|
||||
;
|
||||
|
||||
rs_productionList<rSProdp>: // IEEE: rs_production+
|
||||
|
|
@ -7190,15 +7192,15 @@ rs_prod<nodep>: // ==IEEE: rs_prod
|
|||
| rs_code_block { $$ = $1; }
|
||||
// // IEEE: rs_if_else
|
||||
| yIF '(' expr ')' rs_production_item %prec prLOWER_THAN_ELSE
|
||||
{ $$ = new AstRSIf{$<fl>1, $3, $5, nullptr}; }
|
||||
{ $$ = new AstIf{$<fl>1, $3, $5, nullptr}; }
|
||||
| yIF '(' expr ')' rs_production_item yELSE rs_production_item
|
||||
{ $$ = new AstRSIf{$<fl>1, $3, $5, $7}; }
|
||||
{ $$ = new AstIf{$<fl>1, $3, $5, $7}; }
|
||||
// // IEEE: rs_repeat
|
||||
| yREPEAT '(' expr ')' rs_production_item
|
||||
{ $$ = new AstRSRepeat{$<fl>1, $3, $5}; }
|
||||
{ $$ = new AstRepeat{$<fl>1, $3, $5}; }
|
||||
// // IEEE: rs_case
|
||||
| yCASE '(' expr ')' rs_case_itemList yENDCASE
|
||||
{ $$ = new AstRSCase{$<fl>1, $3, $5}; }
|
||||
{ $$ = new AstCase{$<fl>1, VCaseType::CT_RANDSEQUENCE, $3, $5}; }
|
||||
;
|
||||
|
||||
rs_production_itemList<nodep>: // IEEE: rs_production_item+
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ EXEMPT_FILES_LIST = """
|
|||
test_regress/t/t_fuzz_eof_bad.v
|
||||
test_regress/t/t_incr_void.v
|
||||
test_regress/t/t_property_unsup.v
|
||||
test_regress/t/t_randsequence_svtests.v
|
||||
test_regress/t/t_sequence_first_match_unsup.v
|
||||
test_regress/t/tsub/t_flag_f_tsub.v
|
||||
test_regress/t/tsub/t_flag_f_tsub_inc.v
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
%Error-UNSUPPORTED: t/t_randsequence.v:23:5: Unsupported: randsequence
|
||||
23 | randsequence(main)
|
||||
| ^~~~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:40:5: Unsupported: randsequence
|
||||
40 | randsequence(main)
|
||||
| ^~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:51:5: Unsupported: randsequence
|
||||
51 | randsequence()
|
||||
| ^~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:58:5: Unsupported: randsequence
|
||||
58 | randsequence()
|
||||
| ^~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:65:7: Unsupported: randsequence
|
||||
65 | randsequence(main)
|
||||
| ^~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:79:7: Unsupported: randsequence
|
||||
79 | randsequence(main)
|
||||
| ^~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:81:17: Unsupported: randsequence if
|
||||
81 | one_if: if (i % 10 == 0) count_1 else most;
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:87:15: Unsupported: randsequence case
|
||||
87 | most: case (i % 10)
|
||||
| ^~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:103:7: Unsupported: randsequence
|
||||
103 | randsequence(main)
|
||||
| ^~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:105:17: Unsupported: randsequence if
|
||||
105 | one_if: if (i % 10 == 0) count_1 else most;
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:111:15: Unsupported: randsequence case
|
||||
111 | most: case (i % 10)
|
||||
| ^~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:126:5: Unsupported: randsequence
|
||||
126 | randsequence(main)
|
||||
| ^~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:127:13: Unsupported: randsequence repeat
|
||||
127 | main: repeat(10) count_1;
|
||||
| ^~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:135:7: Unsupported: randsequence
|
||||
135 | randsequence(main)
|
||||
| ^~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:147:7: Unsupported: randsequence
|
||||
147 | randsequence(main)
|
||||
| ^~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:152:7: Unsupported: randsequence
|
||||
152 | randsequence(main)
|
||||
| ^~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:167:7: Unsupported: randsequence
|
||||
167 | randsequence(main)
|
||||
| ^~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:185:7: Unsupported: randsequence
|
||||
185 | randsequence(main)
|
||||
| ^~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence.v:202:7: Unsupported: randsequence
|
||||
202 | randsequence(main)
|
||||
| ^~~~~~~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -11,11 +11,8 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=['--error-limit 999'],
|
||||
fails=test.vlt_all,
|
||||
expect_filename=test.golden_filename)
|
||||
test.compile()
|
||||
|
||||
if not test.vlt_all:
|
||||
test.execute()
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -6,9 +6,8 @@
|
|||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
`define check_range(gotv,minv,maxv) do if ((gotv) < (minv) || (gotv) > (maxv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d-%0d\n", `__FILE__,`__LINE__, (gotv), (minv), (maxv)); `stop; end while(0);
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||
`define check_range(gotv,minv,maxv) do if ((gotv) < (minv) || (gotv) > (maxv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d-%0d\n", `__FILE__,`__LINE__, (gotv), (minv), (maxv)); $stop; end while(0);
|
||||
`define check_within_30_percent(gotv,val) `check_range((gotv), (val) * 70 / 100, (val) * 130 / 100)
|
||||
|
||||
module t;
|
||||
|
|
@ -19,21 +18,107 @@ module t;
|
|||
int counts[8];
|
||||
|
||||
function automatic int sfunc();
|
||||
int o = 2;
|
||||
int fv;
|
||||
fv = 2;
|
||||
randsequence(main)
|
||||
main : one;
|
||||
one : { o = 1; };
|
||||
one : { fv = 1; };
|
||||
endsequence
|
||||
return o;
|
||||
return fv;
|
||||
endfunction
|
||||
|
||||
task prep();
|
||||
function void prep();
|
||||
for (int i = 0; i < COUNT; ++i) counts[i] = 0;
|
||||
endtask
|
||||
endfunction
|
||||
|
||||
initial begin
|
||||
int switch;
|
||||
int x;
|
||||
int wgt;
|
||||
|
||||
x = 0;
|
||||
randsequence()
|
||||
main : { x = 10; };
|
||||
ignore : { x = 20; };
|
||||
endsequence
|
||||
`checkd(x, 10);
|
||||
|
||||
x = 0;
|
||||
randsequence(first)
|
||||
ignore : { x = 20; };
|
||||
first : { x = 10; };
|
||||
endsequence
|
||||
`checkd(x, 10);
|
||||
|
||||
if (sfunc() != 1) $stop;
|
||||
|
||||
x = 0;
|
||||
randsequence(main)
|
||||
main : sub;
|
||||
sub : { x += 10; };
|
||||
endsequence
|
||||
`checkd(x, 10);
|
||||
|
||||
x = 0;
|
||||
switch = 1;
|
||||
randsequence(main)
|
||||
main : case (switch)
|
||||
default : third; // Not listed first; need to move internally to last position
|
||||
0 : zero;
|
||||
1 : first;
|
||||
endcase;
|
||||
zero : { x = 0; };
|
||||
first : { x += 10; };
|
||||
third : { x += 3; };
|
||||
endsequence
|
||||
`checkd(x, 10);
|
||||
|
||||
x = 0;
|
||||
randsequence(main)
|
||||
main : first; // Check single rules
|
||||
first : { x += 20; };
|
||||
endsequence
|
||||
`checkd(x, 20);
|
||||
|
||||
x = 0;
|
||||
randsequence(main)
|
||||
main : zero := 0; // Check single zero-weight
|
||||
zero : { x += 20; };
|
||||
endsequence
|
||||
`checkd(x, 0);
|
||||
|
||||
x = 0;
|
||||
wgt = 1;
|
||||
for (int i=0; i<2; ++i) begin
|
||||
randsequence()
|
||||
main : first := wgt { wgt = 0; };
|
||||
first : { x += 1000; };
|
||||
endsequence
|
||||
end
|
||||
`checkd(wgt, 0);
|
||||
`checkd(x, 1000);
|
||||
|
||||
x = 0;
|
||||
wgt = 1;
|
||||
for (int i=0; i<2; ++i) begin
|
||||
randsequence()
|
||||
main : first := wgt { wgt = 0; }
|
||||
| second := (1 - wgt) { };
|
||||
first : { x += 1000; };
|
||||
second : { x += 10; };
|
||||
endsequence
|
||||
end
|
||||
`checkd(wgt, 0);
|
||||
`checkd(x, 1010);
|
||||
|
||||
x = 0;
|
||||
randsequence(main)
|
||||
main : first second;
|
||||
first : { x += 20; };
|
||||
second : { x += 2; };
|
||||
endsequence
|
||||
`checkd(x, 22);
|
||||
|
||||
// simple
|
||||
prep();
|
||||
seq = 0;
|
||||
|
|
@ -53,12 +138,6 @@ module t;
|
|||
endsequence
|
||||
`checkd(seq, 2);
|
||||
|
||||
// empty block
|
||||
prep();
|
||||
randsequence()
|
||||
unnamed: { };
|
||||
endsequence
|
||||
|
||||
// weight
|
||||
prep();
|
||||
for (int i = 0; i < COUNT; ++i) begin
|
||||
|
|
@ -129,55 +208,6 @@ module t;
|
|||
endsequence
|
||||
`checkd(counts[1], 10);
|
||||
|
||||
// rand join
|
||||
prep();
|
||||
for (int i = 0; i < COUNT; ++i) begin
|
||||
randsequence(main)
|
||||
main: rand join count_1 count_2;
|
||||
count_1: { ++counts[1]; };
|
||||
count_2: { ++counts[2]; };
|
||||
endsequence
|
||||
end
|
||||
`check_within_30_percent(counts[1], COUNT * 1 / 1);
|
||||
`check_within_30_percent(counts[2], COUNT * 1 / 1);
|
||||
|
||||
// rand join weight (TODO weight not tested yet)
|
||||
prep();
|
||||
for (int i = 0; i < COUNT; ++i) begin
|
||||
randsequence(main)
|
||||
main: rand join (1.0) count_1 count_2;
|
||||
count_1: { ++counts[1]; };
|
||||
count_2: { ++counts[2]; };
|
||||
endsequence
|
||||
randsequence(main)
|
||||
main: rand join (0.0) count_3 count_4;
|
||||
count_3: { ++counts[3]; };
|
||||
count_4: { ++counts[4]; };
|
||||
endsequence
|
||||
end
|
||||
`check_within_30_percent(counts[1], COUNT * 1 / 1);
|
||||
`check_within_30_percent(counts[2], COUNT * 1 / 1);
|
||||
`check_within_30_percent(counts[3], COUNT * 1 / 1);
|
||||
`check_within_30_percent(counts[4], COUNT * 1 / 1);
|
||||
|
||||
// break
|
||||
prep();
|
||||
for (int i = 0; i < COUNT; ++i) begin
|
||||
automatic bit fiftyfifty = i[0];
|
||||
randsequence(main)
|
||||
main: count_1 check count_2;
|
||||
check: count_3 { if (fiftyfifty) break; } count_4;
|
||||
count_1: { ++counts[1]; };
|
||||
count_2: { ++counts[2]; };
|
||||
count_3: { ++counts[3]; };
|
||||
count_4: { ++counts[4]; };
|
||||
endsequence
|
||||
end
|
||||
`checkd(counts[1], COUNT * 1 / 1);
|
||||
`checkd(counts[2], COUNT * 1 / 2); // break
|
||||
`checkd(counts[3], COUNT * 1 / 1);
|
||||
`checkd(counts[4], COUNT * 1 / 2); // break or return
|
||||
|
||||
// return
|
||||
prep();
|
||||
for (int i = 0; i < COUNT; ++i) begin
|
||||
|
|
@ -196,21 +226,6 @@ module t;
|
|||
`checkd(counts[3], COUNT * 1 / 1);
|
||||
`checkd(counts[4], COUNT * 1 / 2); // break or return
|
||||
|
||||
// functions
|
||||
prep();
|
||||
for (int i = 0; i < COUNT; ++i) begin
|
||||
randsequence(main)
|
||||
main: f_1 f_2 f_3;
|
||||
f_1 : func(10);
|
||||
f_2 : func(20);
|
||||
f_3 : fnoarg;
|
||||
void func(int n) : { counts[1] += n; };
|
||||
void fnoarg : { ++counts[2]; };
|
||||
endsequence
|
||||
end
|
||||
`checkd(counts[1], COUNT * (10 + 20));
|
||||
`checkd(counts[2], COUNT * 1 / 1); // return
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
%Error-UNSUPPORTED: t/t_randsequence_bad.v:12:5: Unsupported: randsequence
|
||||
%Error: t/t_randsequence_bad.v:23:7: Duplicate declaration of randsequence production: 'duplicated_bad'
|
||||
23 | duplicated_bad: { $display("dup2"); };
|
||||
| ^~~~~~~~~~~~~~
|
||||
t/t_randsequence_bad.v:22:7: ... Location of original declaration
|
||||
22 | duplicated_bad: { $display("dup1"); };
|
||||
| ^~~~~~~~~~~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_randsequence_bad.v:12:5: Production 'no_such_production' not found
|
||||
: ... Suggested alternative: 'such_production'
|
||||
12 | randsequence(no_such_production)
|
||||
| ^~~~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_randsequence_bad.v:16:5: Unsupported: randsequence
|
||||
16 | randsequence(main)
|
||||
| ^~~~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randsequence_bad.v:21:5: Unsupported: randsequence
|
||||
21 | randsequence()
|
||||
| ^~~~~~~~~~~~
|
||||
%Error: t/t_randsequence_bad.v:17:13: Production 'production_bad' not found
|
||||
17 | main: production_bad;
|
||||
| ^~~~~~~~~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
%Error-UNSUPPORTED: t/t_randsequence_func.v:31:23: Unsupported: randsequence production function ports
|
||||
31 | void func(int n) : { counts[1] += n; };
|
||||
| ^
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: t/t_randsequence_func.v:31:23: Input/output/inout does not appear in port list: 'n'
|
||||
31 | void func(int n) : { counts[1] += n; };
|
||||
| ^
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
# 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
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(fails=test.vlt_all, expect_filename=test.golden_filename)
|
||||
|
||||
if not test.vlt_all:
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Copyright 2025 by Wilson Snyder. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||
|
||||
module t;
|
||||
|
||||
localparam int COUNT = 1000;
|
||||
|
||||
int seq;
|
||||
int counts[8];
|
||||
|
||||
task prep();
|
||||
for (int i = 0; i < COUNT; ++i) counts[i] = 0;
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
// functions
|
||||
prep();
|
||||
for (int i = 0; i < COUNT; ++i) begin
|
||||
randsequence(main)
|
||||
main: f_1 f_2 f_3;
|
||||
f_1 : func(10);
|
||||
f_2 : func(20);
|
||||
f_3 : fnoarg;
|
||||
void func(int n) : { counts[1] += n; };
|
||||
void fnoarg : { ++counts[2]; };
|
||||
endsequence
|
||||
end
|
||||
`checkd(counts[1], COUNT * (10 + 20));
|
||||
`checkd(counts[2], COUNT * 1 / 1); // return
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -9,8 +9,10 @@
|
|||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('linter')
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Copyright 2023 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
|
||||
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||
`define check_range(gotv,minv,maxv) do if ((gotv) < (minv) || (gotv) > (maxv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d-%0d\n", `__FILE__,`__LINE__, (gotv), (minv), (maxv)); $stop; end while(0);
|
||||
`define check_within_30_percent(gotv,val) `check_range((gotv), (val) * 70 / 100, (val) * 130 / 100)
|
||||
|
||||
module t;
|
||||
|
||||
localparam int COUNT = 1000;
|
||||
|
||||
int x;
|
||||
int seq;
|
||||
int counts[8];
|
||||
|
||||
task prep();
|
||||
for (int i = 0; i < COUNT; ++i) counts[i] = 0;
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
prep();
|
||||
|
||||
// rand join
|
||||
x = 0;
|
||||
randsequence(main)
|
||||
main : rand join first second;
|
||||
first : { x = x + 20; };
|
||||
second : { x = x - 9; } { x = x - 1; };
|
||||
endsequence
|
||||
`checkd(x, 10);
|
||||
|
||||
prep();
|
||||
for (int i = 0; i < COUNT; ++i) begin
|
||||
randsequence(main)
|
||||
main: rand join count_1 count_2;
|
||||
count_1: { ++counts[1]; };
|
||||
count_2: { ++counts[2]; };
|
||||
endsequence
|
||||
end
|
||||
`check_within_30_percent(counts[1], COUNT * 1 / 1);
|
||||
`check_within_30_percent(counts[2], COUNT * 1 / 1);
|
||||
|
||||
// rand join weight (TODO weight not tested yet)
|
||||
prep();
|
||||
for (int i = 0; i < COUNT; ++i) begin
|
||||
randsequence(main)
|
||||
main: rand join (1.0) count_1 count_2;
|
||||
count_1: { ++counts[1]; };
|
||||
count_2: { ++counts[2]; };
|
||||
endsequence
|
||||
randsequence(main)
|
||||
main: rand join (0.0) count_3 count_4;
|
||||
count_3: { ++counts[3]; };
|
||||
count_4: { ++counts[4]; };
|
||||
endsequence
|
||||
end
|
||||
`check_within_30_percent(counts[1], COUNT * 1 / 1);
|
||||
`check_within_30_percent(counts[2], COUNT * 1 / 1);
|
||||
`check_within_30_percent(counts[3], COUNT * 1 / 1);
|
||||
`check_within_30_percent(counts[4], COUNT * 1 / 1);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -11,9 +11,9 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(fails=test.vlt_all, expect_filename=test.golden_filename)
|
||||
# -fno-inline-funcs due to Issue #4698 ref arguments
|
||||
test.compile(verilator_flags2=['-fno-inline-funcs'])
|
||||
|
||||
if not test.vlt_all:
|
||||
test.execute()
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ module t(/*AUTOARG*/);
|
|||
i = 0;
|
||||
|
||||
randsequence(main)
|
||||
main : recurse recurse;
|
||||
recurse: { i++; if ((i % 4) == 0) break; } add recurse;
|
||||
main : add /*1*/ add /*1*/ recurse /* 2 */ recurse /* 0 */;
|
||||
recurse: { i++; if (i >= 3) break; } add recurse;
|
||||
add: { o++; } ;
|
||||
endsequence
|
||||
|
||||
`checkd(o, 3);
|
||||
`checkd(o, 4);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
%Error-UNSUPPORTED: t/t_randsequence_rs_bad.v:12:5: Unsupported: randsequence
|
||||
12 | randsequence()
|
||||
| ^~~~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_randsequence_rs_bad.v:14:10: Unsupported: randsequence
|
||||
%Error-UNSUPPORTED: t/t_randsequence_rs_bad.v:14:10: Unsupported: randsequence under randsequence
|
||||
: ... note: In instance 't'
|
||||
14 | randsequence()
|
||||
| ^~~~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
%Error-UNSUPPORTED: t/t_randsequence_rule_code_bad.v:12:5: Unsupported: randsequence
|
||||
12 | randsequence()
|
||||
| ^~~~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: Exiting due to
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Copyright 2025 by Wilson Snyder. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
module t;
|
||||
|
||||
initial begin
|
||||
randsequence()
|
||||
main : first := 1 { $stop; } | second := 0;
|
||||
first : { $display("first"); };
|
||||
second : { $display("second"); };
|
||||
endsequence
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# 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
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Based on code Copyright (C) 2019-2021 The SymbiFlow Authors.
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||
|
||||
module t;
|
||||
|
||||
initial begin
|
||||
int x;
|
||||
bit flag = 1;
|
||||
int switch = 1;
|
||||
int break_on = 1;
|
||||
static int return_on = 1;
|
||||
|
||||
x = 0;
|
||||
randsequence(main)
|
||||
main : first second done;
|
||||
first : { x = x + 1; };
|
||||
second : { x = x + 2; };
|
||||
done : { x = x + 3; };
|
||||
endsequence
|
||||
`checkd(x, 6);
|
||||
|
||||
x = 0;
|
||||
randsequence(main)
|
||||
main : or_first | or_second;
|
||||
or_first : { x += -2; };
|
||||
or_second : { x += 2; };
|
||||
endsequence
|
||||
if (x != 2 && x != -2) $stop;
|
||||
|
||||
x = 0;
|
||||
randsequence(main)
|
||||
main : or_first := 1 | or_second := 0;
|
||||
or_first : { x += 2; };
|
||||
or_second : { x += -2; };
|
||||
endsequence
|
||||
`checkd(x, 2);
|
||||
|
||||
x = 0;
|
||||
flag = 1;
|
||||
randsequence(main)
|
||||
main : first;
|
||||
first : { if (flag) x = 10; else x = 5; };
|
||||
endsequence
|
||||
`checkd(x, 10);
|
||||
|
||||
x = 0;
|
||||
flag = 0;
|
||||
randsequence(main)
|
||||
main : first;
|
||||
first : { if (flag) x = 10; else x = 5; };
|
||||
endsequence
|
||||
`checkd(x, 5);
|
||||
|
||||
x = 0;
|
||||
flag = 1;
|
||||
randsequence(main)
|
||||
main : first;
|
||||
first : if (flag) second else third;
|
||||
second : { x = 10; };
|
||||
third : { x = 5; };
|
||||
endsequence
|
||||
`checkd(x, 10);
|
||||
|
||||
x = 0;
|
||||
switch = 1;
|
||||
randsequence(main)
|
||||
main : case (switch)
|
||||
0 : zero;
|
||||
1 : first;
|
||||
2 : second;
|
||||
default : third;
|
||||
endcase;
|
||||
zero : { x = 0; };
|
||||
first : { x = 10; };
|
||||
second : { x = 2; };
|
||||
third : { x = 3; };
|
||||
endsequence
|
||||
`checkd(x, 10);
|
||||
|
||||
x = 0;
|
||||
randsequence(main)
|
||||
main : first;
|
||||
first : repeat(10) second;
|
||||
second : { x = x + 1; };
|
||||
endsequence
|
||||
`checkd(x, 10);
|
||||
|
||||
x = 0;
|
||||
randsequence(main)
|
||||
main : rand join first second;
|
||||
first : { x = x + 20; };
|
||||
second : { x = x - 10; };
|
||||
endsequence
|
||||
`checkd(x, 10);
|
||||
|
||||
x = 0;
|
||||
randsequence(main)
|
||||
main : rand join (0.5) first second;
|
||||
first : { x = x + 20; };
|
||||
second : { x = x - 10; };
|
||||
endsequence
|
||||
`checkd(x, 10);
|
||||
|
||||
x = 0;
|
||||
break_on = 1;
|
||||
randsequence(main)
|
||||
main : first second third;
|
||||
first : { x = x + 10; };
|
||||
second : { if (break_on == 1) break; } fourth;
|
||||
third : { x = x + 10; };
|
||||
fourth : { x = x + 15; };
|
||||
endsequence
|
||||
`checkd(x, 10);
|
||||
|
||||
x = 0;
|
||||
break_on = 0;
|
||||
randsequence(main)
|
||||
main : first second third;
|
||||
first : { x = x + 10; };
|
||||
second : { if (break_on == 1) break; } fourth;
|
||||
third : { x = x + 10; };
|
||||
fourth : { x = x + 15; };
|
||||
endsequence
|
||||
`checkd(x, 35);
|
||||
|
||||
x = 0;
|
||||
return_on = 1;
|
||||
randsequence(main)
|
||||
main : first second third;
|
||||
first : { x = x + 20; };
|
||||
second : { if (return_on == 1) return; x = x + 10; };
|
||||
third : { x = x + 5;};
|
||||
endsequence
|
||||
`checkd(x, 25);
|
||||
|
||||
x = 0;
|
||||
return_on = 0;
|
||||
randsequence(main)
|
||||
main : first second third;
|
||||
first : { x = x + 20; };
|
||||
second : { if (return_on == 1) return; x = x + 10; };
|
||||
third : { x = x + 5;};
|
||||
endsequence
|
||||
`checkd(x, 35);
|
||||
|
||||
`ifndef VERILATOR // Unsupported randsequence functions
|
||||
x = 0;
|
||||
randsequence(main)
|
||||
main : first second third;
|
||||
first : add(10);
|
||||
second : add(5);
|
||||
third : add(2);
|
||||
void add(int y) : { x = x + y; };
|
||||
void add(int y) : sub_a sub_b; // This is presumably legal, try it
|
||||
endsequence
|
||||
`checkd(x, 17);
|
||||
`endif
|
||||
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue