Skip to content

Commit b9b6eb6

Browse files
committed
Support randsequence (verilator#6131)
1 parent 35615c2 commit b9b6eb6

32 files changed

+1271
-280
lines changed

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ set(HEADERS
156156
V3PreShell.h
157157
V3Premit.h
158158
V3ProtectLib.h
159+
V3RandSequence.h
159160
V3Randomize.h
160161
V3Reloop.h
161162
V3Rtti.h
@@ -319,6 +320,7 @@ set(COMMON_SOURCES
319320
V3PreShell.cpp
320321
V3Premit.cpp
321322
V3ProtectLib.cpp
323+
V3RandSequence.cpp
322324
V3Randomize.cpp
323325
V3Reloop.cpp
324326
V3Sampled.cpp

src/Makefile_obj.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ RAW_OBJS_PCH_ASTNOMT = \
309309
V3Param.o \
310310
V3Premit.o \
311311
V3ProtectLib.o \
312+
V3RandSequence.o \
312313
V3Randomize.o \
313314
V3Reloop.o \
314315
V3Sampled.o \

src/V3AstAttr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,7 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
940940

941941
class VCaseType final {
942942
public:
943-
enum en : uint8_t { CT_CASE, CT_CASEX, CT_CASEZ, CT_CASEINSIDE };
943+
enum en : uint8_t { CT_CASE, CT_CASEX, CT_CASEZ, CT_CASEINSIDE, CT_RANDSEQUENCE };
944944
enum en m_e;
945945
VCaseType()
946946
: m_e{CT_CASE} {}

src/V3AstNodeExpr.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1917,6 +1917,12 @@ class AstRand final : public AstNodeExpr {
19171917
, m_urandom{urandom} {
19181918
this->seedp(seedp);
19191919
}
1920+
class SRandomU32 {};
1921+
AstRand(FileLine* fl, SRandomU32)
1922+
: ASTGEN_SUPER_Rand(fl)
1923+
, m_urandom{true} {
1924+
dtypeSetUInt32();
1925+
}
19201926
ASTGEN_MEMBERS_AstRand;
19211927
string emitVerilog() override {
19221928
return seedp() ? (m_urandom ? "%f$urandom(%l)" : "%f$random(%l)")

src/V3AstNodeStmt.h

Lines changed: 34 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ class AstNodeReadWriteMem VL_NOT_FINAL : public AstNodeStmt {
199199

200200
// === AstNode ===
201201
class AstCaseItem final : public AstNode {
202-
// Single item of AstCase/AstRandCase/AstRSCase
202+
// Single item of AstCase/AstRandCase
203203
// @astgen op1 := condsp : List[AstNodeExpr]
204204
// @astgen op2 := stmtsp : List[AstNode]
205205
public:
@@ -887,42 +887,17 @@ class AstPrintTimeScale final : public AstNodeStmt {
887887
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
888888
VTimescale timeunit() const { return m_timeunit; }
889889
};
890-
class AstRSCase final : public AstNodeStmt {
891-
// Randsequence case statement
892-
// @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression
893-
// @astgen op2 := itemsp : List[AstCaseItem]
894-
public:
895-
AstRSCase(FileLine* fl, AstNodeExpr* exprp, AstCaseItem* itemsp)
896-
: ASTGEN_SUPER_Case(fl) {
897-
this->exprp(exprp);
898-
addItemsp(itemsp);
899-
}
900-
ASTGEN_MEMBERS_AstRSCase;
901-
int instrCount() const override { return INSTR_COUNT_BRANCH; }
902-
bool sameNode(const AstNode* samep) const override { return true; }
903-
};
904-
class AstRSIf final : public AstNodeStmt {
905-
// Randsequence if
906-
// @astgen op1 := condp : AstNodeExpr
907-
// @astgen op2 := thensp : List[AstNode]
908-
// @astgen op3 := elsesp : List[AstNode]
909-
public:
910-
AstRSIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp)
911-
: ASTGEN_SUPER_RSIf(fl) {
912-
this->condp(condp);
913-
addThensp(thensp);
914-
addElsesp(elsesp);
915-
}
916-
890+
class AstRSBreak final : public AstNodeStmt {
891+
// randsequence break
917892
public:
918-
ASTGEN_MEMBERS_AstRSIf;
919-
bool isGateOptimizable() const override { return false; }
920-
bool isGateDedupable() const override { return false; }
921-
int instrCount() const override { return INSTR_COUNT_BRANCH; }
922-
bool sameNode(const AstNode* /*samep*/) const override { return true; }
893+
explicit AstRSBreak(FileLine* fl)
894+
: ASTGEN_SUPER_RSBreak(fl) {}
895+
ASTGEN_MEMBERS_AstRSBreak;
896+
string verilogKwd() const override { return "break"; }
897+
bool isBrancher() const override { V3ERROR_NA_RETURN(true); } // Node removed early
923898
};
924899
class AstRSProd final : public AstNodeStmt {
925-
// randomsquence production, under a AstRandSequence
900+
// randomsequence production, under a AstRandSequence
926901
// @astgen op1 := fvarp : Optional[AstVar]
927902
// @astgen op2 := portsp : List[AstNode]
928903
// @astgen op3 := rulesp : List[AstRSRule]
@@ -935,12 +910,14 @@ class AstRSProd final : public AstNodeStmt {
935910
addRulesp(rulesp);
936911
}
937912
ASTGEN_MEMBERS_AstRSProd;
913+
bool maybePointedTo() const override VL_MT_SAFE { return true; }
938914
string name() const override VL_MT_STABLE { return m_name; }
939915
int instrCount() const override { return INSTR_COUNT_BRANCH; }
940916
};
941917
class AstRSProdItem final : public AstNodeStmt {
942918
// randomsquence production item
943919
// @astgen op1 := argsp : List[AstNodeExpr]
920+
// @astgen ptr := m_prodp : Optional[AstRSProd] // Pointer to production
944921
string m_name; // Name of block, or "" to use first production
945922
public:
946923
AstRSProdItem(FileLine* fl, const string& name, AstNodeExpr* argsp)
@@ -951,6 +928,8 @@ class AstRSProdItem final : public AstNodeStmt {
951928
ASTGEN_MEMBERS_AstRSProdItem;
952929
string name() const override VL_MT_STABLE { return m_name; }
953930
int instrCount() const override { return INSTR_COUNT_BRANCH; }
931+
AstRSProd* prodp() const { return m_prodp; }
932+
void prodp(AstRSProd* nodep) { m_prodp = nodep; }
954933
};
955934
class AstRSProdList final : public AstNodeStmt {
956935
// randomsquence production list
@@ -970,32 +949,26 @@ class AstRSProdList final : public AstNodeStmt {
970949
bool randJoin() const { return m_randJoin; }
971950
void randJoin(bool flag) { m_randJoin = flag; }
972951
};
973-
class AstRSRepeat final : public AstNodeStmt {
974-
// randsequence repeat
975-
// @astgen op1 := countp : AstNodeExpr
976-
// @astgen op2 := stmtsp : List[AstNode]
952+
class AstRSReturn final : public AstNodeStmt {
953+
// randsequence return
977954
public:
978-
AstRSRepeat(FileLine* fl, AstNodeExpr* countp, AstNode* stmtsp)
979-
: ASTGEN_SUPER_RSRepeat(fl) {
980-
this->countp(countp);
981-
addStmtsp(stmtsp);
982-
}
983-
ASTGEN_MEMBERS_AstRSRepeat;
984-
bool isGateOptimizable() const override { return false; }
985-
int instrCount() const override { return INSTR_COUNT_BRANCH; }
986-
bool sameNode(const AstNode* /*samep*/) const override { return true; }
955+
explicit AstRSReturn(FileLine* fl)
956+
: ASTGEN_SUPER_RSReturn(fl) {}
957+
ASTGEN_MEMBERS_AstRSReturn;
958+
string verilogKwd() const override { return "return"; }
959+
bool isBrancher() const override { V3ERROR_NA_RETURN(true); } // Node removed early
987960
};
988961
class AstRSRule final : public AstNodeStmt {
989962
// randomsquence rule
990963
// @astgen op1 := weightp : Optional[AstNodeExpr]
991964
// @astgen op2 := prodlistsp : List[AstRSProdList]
992-
// @astgen op3 := stmtsp : List[AstNode]
965+
// @astgen op3 := weightStmtsp : List[AstNode]
993966
public:
994-
AstRSRule(FileLine* fl, AstNodeExpr* weightp, AstRSProdList* prodlistsp, AstNode* stmtsp)
967+
AstRSRule(FileLine* fl, AstNodeExpr* weightp, AstRSProdList* prodlistsp, AstNode* weightStmtsp)
995968
: ASTGEN_SUPER_RSRule(fl) {
996969
this->weightp(weightp);
997970
addProdlistsp(prodlistsp);
998-
addStmtsp(stmtsp);
971+
addWeightStmtsp(weightStmtsp);
999972
}
1000973
ASTGEN_MEMBERS_AstRSRule;
1001974
int instrCount() const override { return INSTR_COUNT_BRANCH; }
@@ -1013,16 +986,24 @@ class AstRandCase final : public AstNodeStmt {
1013986
};
1014987
class AstRandSequence final : public AstNodeStmt {
1015988
// @astgen op2 := prodsp : List[AstRSProd]
1016-
string m_name; // Name of block, or "" to use first production
989+
// @astgen ptr := m_prodp : Optional[AstRSProd] // Pointer to start production (if any)
990+
string m_name; // Created unique name
991+
string m_start; // Name of start production, or "" to use first production
1017992
public:
1018-
AstRandSequence(FileLine* fl, const string& name, AstRSProd* prodsp)
993+
AstRandSequence(FileLine* fl, const string& start, AstRSProd* prodsp)
1019994
: ASTGEN_SUPER_RandSequence(fl)
1020-
, m_name{name} {
995+
, m_start{start} {
1021996
addProdsp(prodsp);
1022997
}
1023998
ASTGEN_MEMBERS_AstRandSequence;
999+
void dump(std::ostream& str) const override;
1000+
void dumpJson(std::ostream& str) const override;
10241001
string name() const override VL_MT_STABLE { return m_name; } // * = Block name
1002+
void name(const string& name) override { m_name = name; }
1003+
string start() const VL_MT_STABLE { return m_start; }
10251004
int instrCount() const override { return INSTR_COUNT_BRANCH; }
1005+
AstRSProd* prodp() const { return m_prodp; }
1006+
void prodp(AstRSProd* nodep) { m_prodp = nodep; }
10261007
};
10271008
class AstRelease final : public AstNodeStmt {
10281009
// Procedural 'release' statement

src/V3AstNodes.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2271,6 +2271,14 @@ void AstTypedefFwd::dumpJson(std::ostream& str) const {
22712271
}
22722272
void AstNodeRange::dump(std::ostream& str) const { this->AstNode::dump(str); }
22732273
void AstNodeRange::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
2274+
void AstRandSequence::dump(std::ostream& str) const {
2275+
this->AstNode::dump(str);
2276+
str << " start=" << start();
2277+
}
2278+
void AstRandSequence::dumpJson(std::ostream& str) const {
2279+
if (!start().empty()) dumpJsonStr(str, "start", start());
2280+
dumpJsonGen(str);
2281+
}
22742282
void AstRange::dump(std::ostream& str) const {
22752283
this->AstNodeRange::dump(str);
22762284
if (fromBracket()) str << " [FB]";

src/V3Global.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ class V3Global final {
125125
bool m_hasForceableSignals = false; // Need to apply V3Force pass
126126
bool m_hasSystemCSections = false; // Has AstSystemCSection that need to be emitted
127127
bool m_useParallelBuild = false; // Use parallel build for model
128+
bool m_useRandSequence = false; // Has `randsequence`
128129
bool m_useRandomizeMethods = false; // Need to define randomize() class methods
129130
uint64_t m_currentHierBlockCost = 0; // Total cost of this hier block, used for scheduling
130131

@@ -203,6 +204,8 @@ class V3Global final {
203204
void hierGraphp(V3HierGraph* graphp) { m_hierGraphp = graphp; }
204205
bool useParallelBuild() const { return m_useParallelBuild; }
205206
void useParallelBuild(bool flag) { m_useParallelBuild = flag; }
207+
bool useRandSequence() const { return m_useRandSequence; }
208+
void useRandSequence(bool flag) { m_useRandSequence = flag; }
206209
bool useRandomizeMethods() const { return m_useRandomizeMethods; }
207210
void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; }
208211
void saveJsonPtrFieldName(const std::string& fieldName);

src/V3LinkDot.cpp

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ class LinkNodeMatcherModport final : public VNodeMatcher {
9494
public:
9595
bool nodeMatch(const AstNode* nodep) const override { return VN_IS(nodep, Modport); }
9696
};
97+
class LinkNodeMatcherProd final : public VNodeMatcher {
98+
public:
99+
bool nodeMatch(const AstNode* nodep) const override { return VN_IS(nodep, RSProd); }
100+
};
97101
class LinkNodeMatcherVar final : public VNodeMatcher {
98102
public:
99103
bool nodeMatch(const AstNode* nodep) const override {
@@ -246,20 +250,22 @@ class LinkDotState final {
246250
} else {
247251
return "local type parameter";
248252
}
253+
} else if (VN_IS(nodep, Begin)) {
254+
return "block";
249255
} else if (VN_IS(nodep, Cell)) {
250256
return "instance";
251257
} else if (VN_IS(nodep, Constraint)) {
252258
return "constraint";
253-
} else if (VN_IS(nodep, Task)) {
254-
return "task";
255259
} else if (VN_IS(nodep, Func)) {
256260
return "function";
257-
} else if (VN_IS(nodep, Begin)) {
258-
return "block";
259-
} else if (VN_IS(nodep, Iface)) {
260-
return "interface";
261261
} else if (VN_IS(nodep, GenBlock)) {
262262
return "generate block";
263+
} else if (VN_IS(nodep, Iface)) {
264+
return "interface";
265+
} else if (VN_IS(nodep, RSProd)) {
266+
return "randsequence production";
267+
} else if (VN_IS(nodep, Task)) {
268+
return "task";
263269
} else {
264270
return nodep->prettyTypeName();
265271
}
@@ -1856,6 +1862,23 @@ class LinkDotFindVisitor final : public VNVisitor {
18561862
}
18571863
}
18581864

1865+
void visit(AstRandSequence* nodep) override {
1866+
VL_RESTORER(m_curSymp);
1867+
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep);
1868+
iterateChildren(nodep);
1869+
}
1870+
void visit(AstRSProd* nodep) override {
1871+
VL_RESTORER(m_curSymp);
1872+
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep);
1873+
if (nodep->fvarp())
1874+
nodep->fvarp()->v3warn(E_UNSUPPORTED,
1875+
"Unsupported: randsequence production function variable");
1876+
if (nodep->portsp())
1877+
nodep->portsp()->v3warn(E_UNSUPPORTED,
1878+
"Unsupported: randsequence production function ports");
1879+
iterateChildren(nodep);
1880+
}
1881+
18591882
void visit(AstWithParse* nodep) override { // FindVisitor::
18601883
// Change WITHPARSE(FUNCREF, equation) to FUNCREF(WITH(equation))
18611884
AstNodeFTaskRef* funcrefp = VN_CAST(nodep->funcrefp(), NodeFTaskRef);
@@ -2725,6 +2748,21 @@ class LinkDotResolveVisitor final : public VNVisitor {
27252748
UASSERT_OBJ(ifaceTopVarp, nodep, "Can't find interface var ref: " << findName);
27262749
return ifaceTopVarp;
27272750
}
2751+
AstRSProd* findProd(AstNode* nodep, VSymEnt* parentEntp, const string& name) {
2752+
const VSymEnt* const foundp = parentEntp->findIdFallback(name);
2753+
AstRSProd* foundNodep = foundp ? VN_CAST(foundp->nodep(), RSProd) : nullptr;
2754+
if (!foundNodep) {
2755+
VSpellCheck speller;
2756+
LinkNodeMatcherProd matcher;
2757+
parentEntp->candidateIdFlat(&speller, &matcher);
2758+
const string suggest = speller.bestCandidateMsg(name);
2759+
UINFO(1, " ErrParseRef curSymp=se" << cvtToHex(parentEntp));
2760+
nodep->v3error("Production " << AstNode::prettyNameQ(name) << " not found\n"
2761+
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
2762+
return nullptr;
2763+
}
2764+
return foundNodep;
2765+
}
27282766
void markAndCheckPinDup(AstPin* nodep, AstNode* refp, const char* whatp) {
27292767
const auto pair = m_usedPins.emplace(refp, nodep);
27302768
if (!pair.second) {
@@ -4722,6 +4760,27 @@ class LinkDotResolveVisitor final : public VNVisitor {
47224760
checkNoDot(nodep);
47234761
symIterateChildren(nodep, m_statep->getNodeSym(nodep));
47244762
}
4763+
void visit(AstRandSequence* nodep) override {
4764+
LINKDOT_VISIT_START();
4765+
UINFO(5, indent() << "visit " << nodep);
4766+
checkNoDot(nodep);
4767+
if (!nodep->start().empty())
4768+
nodep->prodp(findProd(nodep, m_statep->getNodeSym(nodep), nodep->start()));
4769+
symIterateChildren(nodep, m_statep->getNodeSym(nodep));
4770+
}
4771+
void visit(AstRSProd* nodep) override {
4772+
LINKDOT_VISIT_START();
4773+
UINFO(5, indent() << "visit " << nodep);
4774+
checkNoDot(nodep);
4775+
symIterateChildren(nodep, m_statep->getNodeSym(nodep));
4776+
}
4777+
void visit(AstRSProdItem* nodep) override {
4778+
LINKDOT_VISIT_START();
4779+
UINFO(5, indent() << "visit " << nodep);
4780+
checkNoDot(nodep);
4781+
nodep->prodp(findProd(nodep, m_curSymp, nodep->name()));
4782+
iterateChildren(nodep);
4783+
}
47254784
void visit(AstWith* nodep) override {
47264785
LINKDOT_VISIT_START();
47274786
UINFO(5, indent() << "visit " << nodep);

src/V3LinkJump.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class LinkJumpVisitor final : public VNVisitor {
5757
AstNodeModule* m_modp = nullptr; // Current module
5858
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
5959
AstNode* m_loopp = nullptr; // Current loop
60+
AstRandSequence* m_randsequencep = nullptr; // Current randsequence
6061
bool m_loopInc = false; // In loop increment
6162
bool m_inFork = false; // Under fork
6263
int m_modRepeatNum = 0; // Repeat counter
@@ -289,6 +290,11 @@ class LinkJumpVisitor final : public VNVisitor {
289290
iterateChildren(nodep);
290291
}
291292
}
293+
void visit(AstRandSequence* nodep) override {
294+
VL_RESTORER(m_randsequencep);
295+
m_randsequencep = nodep;
296+
iterateChildren(nodep);
297+
}
292298
void visit(AstRepeat* nodep) override {
293299
// So later optimizations don't need to deal with them,
294300
// REPEAT(count,body) -> loop=count,WHILE(loop>0) { body, loop-- }
@@ -354,6 +360,10 @@ class LinkJumpVisitor final : public VNVisitor {
354360
nodep->v3error("Return isn't legal under fork (IEEE 1800-2023 9.2.3)");
355361
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
356362
return;
363+
} else if (!m_ftaskp && m_randsequencep) {
364+
nodep->replaceWith(new AstRSReturn{nodep->fileline()});
365+
VL_DO_DANGLING(pushDeletep(nodep), nodep);
366+
return;
357367
} else if (!m_ftaskp) {
358368
nodep->v3error("Return isn't underneath a task or function");
359369
} else if (funcp && !nodep->lhsp() && !funcp->isConstructor()) {
@@ -377,7 +387,11 @@ class LinkJumpVisitor final : public VNVisitor {
377387
}
378388
void visit(AstBreak* nodep) override {
379389
iterateChildren(nodep);
380-
if (!m_loopp) {
390+
if (!m_loopp && m_randsequencep) {
391+
nodep->replaceWith(new AstRSBreak{nodep->fileline()});
392+
VL_DO_DANGLING(pushDeletep(nodep), nodep);
393+
return;
394+
} else if (!m_loopp) {
381395
nodep->v3error("break isn't underneath a loop");
382396
} else {
383397
// Jump to the end of the loop

0 commit comments

Comments
 (0)