Internals: Refactor generate construct Ast handling (#6280) (#6470)

Internals: Refactor generate construct Ast handling (#6280)

We introduce AstNodeGen, the common base class of AstGenBlock,
AstGenCase, AstGenFor, and AstGenIf, which together represent all SV
generate constructs. Subsequently remove AstNodeFor, AstNodeCase
(AstCase is now directly derived from AstNodeStmt) and adjust internals
to work on the new representation.

Output is identical modulo hashes do to changed AstNode type ids, no
functional change intended.

Step towards #6280.
This commit is contained in:
Geza Lore 2025-09-23 20:49:01 +02:00 committed by GitHub
parent df187c4406
commit 800af37975
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 687 additions and 517 deletions

View File

@ -129,7 +129,7 @@ class AssertVisitor final : public VNVisitor {
// STATE
AstNodeModule* m_modp = nullptr; // Last module
const AstBegin* m_beginp = nullptr; // Last begin
const AstNode* m_beginp = nullptr; // Last AstBegin/AstGenBlock
unsigned m_monitorNum = 0; // Global $monitor numbering (not per module)
AstVar* m_monitorNumVarp = nullptr; // $monitor number variable
AstVar* m_monitorOffVarp = nullptr; // $monitoroff variable
@ -809,6 +809,13 @@ class AssertVisitor final : public VNVisitor {
m_procedurep = nodep;
iterateChildren(nodep);
}
void visit(AstGenBlock* nodep) override {
// This code is needed rather than a visitor in V3Begin,
// because V3Assert is called before V3Begin
VL_RESTORER(m_beginp);
m_beginp = nodep;
iterateChildren(nodep);
}
void visit(AstBegin* nodep) override {
// This code is needed rather than a visitor in V3Begin,
// because V3Assert is called before V3Begin

View File

@ -349,7 +349,7 @@ private:
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
cntVarp->funcLocal(true);
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, false, true};
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true};
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
beginp->addStmtsp(new AstWhile{
nodep->fileline(),

View File

@ -1078,8 +1078,7 @@ AstNode* AstNode::iterateSubtreeReturnEdits(VNVisitor& v) {
} else if (!nodep->backp()) {
// Calling on standalone tree; insert a shim node so we can keep
// track, then delete it on completion
AstBegin* const tempp
= new AstBegin{nodep->fileline(), "[EditWrapper]", nodep, false, false};
AstBegin* const tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", nodep, false};
// nodep to null as may be replaced
VL_DO_DANGLING(tempp->stmtsp()->accept(v), nodep);
nodep = tempp->stmtsp()->unlinkFrBackWithNext();

View File

@ -267,6 +267,13 @@ public:
string name() const override VL_MT_STABLE { return m_name; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstNodeGen VL_NOT_FINAL : public AstNode {
// Generate construct
public:
AstNodeGen(VNType t, FileLine* fl)
: AstNode{t, fl} {}
ASTGEN_MEMBERS_AstNodeGen;
};
class AstNodeModule VL_NOT_FINAL : public AstNode {
// A module, package, program or interface declaration;
// something that can live directly under the TOP,
@ -668,21 +675,6 @@ public:
string name() const override VL_MT_STABLE { return m_name; }
VUseType useType() const { return m_useType; }
};
class AstCaseItem final : public AstNode {
// Single item of a case statement
// @astgen op1 := condsp : List[AstNodeExpr]
// @astgen op2 := stmtsp : List[AstNode]
public:
AstCaseItem(FileLine* fl, AstNodeExpr* condsp, AstNode* stmtsp)
: ASTGEN_SUPER_CaseItem(fl) {
addCondsp(condsp);
addStmtsp(stmtsp);
}
ASTGEN_MEMBERS_AstCaseItem;
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
bool isDefault() const { return condsp() == nullptr; }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
};
class AstCell final : public AstNode {
// A instantiation cell or interface call (don't know which until link)
// @astgen op1 := pinsp : List[AstPin] // List of port assignments
@ -1137,6 +1129,19 @@ public:
V3Graph* depGraphp() { return m_depGraphp; }
const V3Graph* depGraphp() const { return m_depGraphp; }
};
class AstGenCaseItem final : public AstNode {
// Single item of an AstGenCase
// @astgen op1 := condsp : List[AstNodeExpr]
// @astgen op2 := itemsp : List[AstNode]
public:
AstGenCaseItem(FileLine* fl, AstNodeExpr* condsp, AstNode* itemsp)
: ASTGEN_SUPER_GenCaseItem(fl) {
addCondsp(condsp);
addItemsp(itemsp);
}
ASTGEN_MEMBERS_AstGenCaseItem;
bool isDefault() const { return condsp() == nullptr; }
};
class AstImplicit final : public AstNode {
// Create implicit wires and do nothing else, for gates that are ignored
// Parents: MODULE
@ -2287,22 +2292,18 @@ public:
class AstBegin final : public AstNodeBlock {
// A Begin/end named block, only exists shortly after parsing until linking
// Parents: statement
// @astgen op1 := genforp : Optional[AstNode]
const bool m_generate : 1; // Underneath a generate
bool m_needProcess : 1; // Uses VlProcess
const bool m_implied : 1; // Not inserted by user
public:
// Node that puts name into the output stream
AstBegin(FileLine* fl, const string& name, AstNode* stmtsp, bool generate, bool implied)
AstBegin(FileLine* fl, const string& name, AstNode* stmtsp, bool implied)
: ASTGEN_SUPER_Begin(fl, name, stmtsp)
, m_generate{generate}
, m_needProcess{false}
, m_implied{implied} {}
ASTGEN_MEMBERS_AstBegin;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool generate() const { return m_generate; }
void setNeedProcess() { m_needProcess = true; }
bool needProcess() const { return m_needProcess; }
bool implied() const { return m_implied; }
@ -2463,6 +2464,74 @@ public:
void dumpJson(std::ostream& str = std::cout) const override;
};
// === AstNodeGen ===
class AstGenBlock final : public AstNodeGen {
// Generate 'begin'
// @astgen op1 := genforp : Optional[AstNode]
// @astgen op2 := itemsp : List[AstNode]
std::string m_name; // Name of block
const bool m_unnamed; // Originally unnamed (name change does not affect this)
const bool m_implied; // Not inserted by user
public:
AstGenBlock(FileLine* fl, const string& name, AstNode* itemsp, bool implied)
: ASTGEN_SUPER_GenBlock(fl)
, m_name{name}
, m_unnamed{name.empty()}
, m_implied{implied} {
this->addItemsp(itemsp);
}
ASTGEN_MEMBERS_AstGenBlock;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
std::string name() const override VL_MT_STABLE { return m_name; }
void name(const std::string& name) override { m_name = name; }
bool unnamed() const { return m_unnamed; }
bool implied() const { return m_implied; }
};
class AstGenCase final : public AstNodeGen {
// Generate 'case'
// @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression
// @astgen op2 := itemsp : List[AstGenCaseItem]
public:
AstGenCase(FileLine* fl, AstNodeExpr* exprp, AstGenCaseItem* itemsp)
: ASTGEN_SUPER_GenCase(fl) {
this->exprp(exprp);
this->addItemsp(itemsp);
}
ASTGEN_MEMBERS_AstGenCase;
};
class AstGenFor final : public AstNodeGen {
// Generate 'for'
// @astgen op1 := initsp : List[AstNode]
// @astgen op2 := condp : AstNodeExpr
// @astgen op3 := incsp : List[AstNode]
// @astgen op4 := itemsp : List[AstNode]
public:
AstGenFor(FileLine* fl, AstNode* initsp, AstNodeExpr* condp, AstNode* incsp, AstNode* itemsp)
: ASTGEN_SUPER_GenFor(fl) {
this->addInitsp(initsp);
this->condp(condp);
this->addIncsp(incsp);
this->addItemsp(itemsp);
}
ASTGEN_MEMBERS_AstGenFor;
};
class AstGenIf final : public AstNodeGen {
// Generate 'if'
// @astgen op1 := condp : AstNodeExpr
// @astgen op2 := thensp : List[AstNode]
// @astgen op3 := elsesp : List[AstNode]
public:
AstGenIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp)
: ASTGEN_SUPER_GenIf(fl) {
this->condp(condp);
this->addThensp(thensp);
this->addElsesp(elsesp);
}
ASTGEN_MEMBERS_AstGenIf;
};
// === AstNodeModule ===
class AstClass final : public AstNodeModule {
// @astgen op4 := extendsp : List[AstClassExtends]

View File

@ -70,21 +70,6 @@ public:
bool isTimingControl() const override { return timingControlp(); }
virtual bool brokeLhsMustBeLvalue() const = 0;
};
class AstNodeCase VL_NOT_FINAL : public AstNodeStmt {
// @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression
// @astgen op2 := itemsp : List[AstCaseItem]
// @astgen op3 := notParallelp : List[AstNode] // assertion code for non-full case's
protected:
AstNodeCase(VNType t, FileLine* fl, AstNodeExpr* exprp, AstCaseItem* itemsp)
: AstNodeStmt{t, fl} {
this->exprp(exprp);
addItemsp(itemsp);
}
public:
ASTGEN_MEMBERS_AstNodeCase;
int instrCount() const override { return INSTR_COUNT_BRANCH; }
};
class AstNodeCoverOrAssert VL_NOT_FINAL : public AstNodeStmt {
// Cover or Assert
// Parents: {statement list}
@ -121,27 +106,6 @@ public:
|| this->type() == VAssertType::INTERNAL;
}
};
class AstNodeFor VL_NOT_FINAL : public AstNodeStmt {
// @astgen op1 := initsp : List[AstNode]
// @astgen op2 := condp : AstNodeExpr
// @astgen op3 := incsp : List[AstNode]
// @astgen op4 := stmtsp : List[AstNode]
protected:
AstNodeFor(VNType t, FileLine* fl, AstNode* initsp, AstNodeExpr* condp, AstNode* incsp,
AstNode* stmtsp)
: AstNodeStmt{t, fl} {
addInitsp(initsp);
this->condp(condp);
addIncsp(incsp);
addStmtsp(stmtsp);
}
public:
ASTGEN_MEMBERS_AstNodeFor;
bool isGateOptimizable() const override { return false; }
int instrCount() const override { return INSTR_COUNT_BRANCH; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstNodeForeach VL_NOT_FINAL : public AstNodeStmt {
// @astgen op1 := arrayp : AstNode
// @astgen op2 := stmtsp : List[AstNode]
@ -218,6 +182,23 @@ public:
// === Concrete node types =====================================================
// === AstNode ===
class AstCaseItem final : public AstNode {
// Single item of AstCase/AstRandCase
// @astgen op1 := condsp : List[AstNodeExpr]
// @astgen op2 := stmtsp : List[AstNode]
public:
AstCaseItem(FileLine* fl, AstNodeExpr* condsp, AstNode* stmtsp)
: ASTGEN_SUPER_CaseItem(fl) {
addCondsp(condsp);
addStmtsp(stmtsp);
}
ASTGEN_MEMBERS_AstCaseItem;
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
bool isDefault() const { return condsp() == nullptr; }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
};
// === AstNodeStmt ===
class AstAssertCtl final : public AstNodeStmt {
// @astgen op1 := controlTypep : AstNodeExpr
@ -306,6 +287,47 @@ public:
bool isPredictOptimizable() const override { return false; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstCase final : public AstNodeStmt {
// Case statement
// @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression
// @astgen op2 := itemsp : List[AstCaseItem]
// @astgen op3 := notParallelp : List[AstNode] // assertion code for non-full case's
VCaseType m_casex; // 0=case, 1=casex, 2=casez
bool m_fullPragma = false; // Synthesis full_case
bool m_parallelPragma = false; // Synthesis parallel_case
bool m_uniquePragma = false; // unique case
bool m_unique0Pragma = false; // unique0 case
bool m_priorityPragma = false; // priority case
public:
AstCase(FileLine* fl, VCaseType casex, AstNodeExpr* exprp, AstCaseItem* itemsp)
: ASTGEN_SUPER_Case(fl)
, m_casex{casex} {
this->exprp(exprp);
addItemsp(itemsp);
}
ASTGEN_MEMBERS_AstCase;
int instrCount() const override { return INSTR_COUNT_BRANCH; }
string verilogKwd() const override { return casez() ? "casez" : casex() ? "casex" : "case"; }
bool sameNode(const AstNode* samep) const override {
return m_casex == VN_DBG_AS(samep, Case)->m_casex;
}
bool casex() const { return m_casex == VCaseType::CT_CASEX; }
bool casez() const { return m_casex == VCaseType::CT_CASEZ; }
bool caseInside() const { return m_casex == VCaseType::CT_CASEINSIDE; }
bool caseSimple() const { return m_casex == VCaseType::CT_CASE; }
void caseInsideSet() { m_casex = VCaseType::CT_CASEINSIDE; }
bool fullPragma() const { return m_fullPragma; }
void fullPragma(bool flag) { m_fullPragma = flag; }
bool parallelPragma() const { return m_parallelPragma; }
void parallelPragma(bool flag) { m_parallelPragma = flag; }
bool uniquePragma() const { return m_uniquePragma; }
void uniquePragma(bool flag) { m_uniquePragma = flag; }
bool unique0Pragma() const { return m_unique0Pragma; }
void unique0Pragma(bool flag) { m_unique0Pragma = flag; }
bool priorityPragma() const { return m_priorityPragma; }
void priorityPragma(bool flag) { m_priorityPragma = flag; }
string pragmaString() const;
};
class AstComment final : public AstNodeStmt {
// Some comment to put into the output stream
const string m_name; // Text of comment
@ -1130,49 +1152,6 @@ public:
AstAlways* convertToAlways();
};
// === AstNodeCase ===
class AstCase final : public AstNodeCase {
// Case statement
VCaseType m_casex; // 0=case, 1=casex, 2=casez
bool m_fullPragma = false; // Synthesis full_case
bool m_parallelPragma = false; // Synthesis parallel_case
bool m_uniquePragma = false; // unique case
bool m_unique0Pragma = false; // unique0 case
bool m_priorityPragma = false; // priority case
public:
AstCase(FileLine* fl, VCaseType casex, AstNodeExpr* exprp, AstCaseItem* itemsp)
: ASTGEN_SUPER_Case(fl, exprp, itemsp)
, m_casex{casex} {}
ASTGEN_MEMBERS_AstCase;
string verilogKwd() const override { return casez() ? "casez" : casex() ? "casex" : "case"; }
bool sameNode(const AstNode* samep) const override {
return m_casex == VN_DBG_AS(samep, Case)->m_casex;
}
bool casex() const { return m_casex == VCaseType::CT_CASEX; }
bool casez() const { return m_casex == VCaseType::CT_CASEZ; }
bool caseInside() const { return m_casex == VCaseType::CT_CASEINSIDE; }
bool caseSimple() const { return m_casex == VCaseType::CT_CASE; }
void caseInsideSet() { m_casex = VCaseType::CT_CASEINSIDE; }
bool fullPragma() const { return m_fullPragma; }
void fullPragma(bool flag) { m_fullPragma = flag; }
bool parallelPragma() const { return m_parallelPragma; }
void parallelPragma(bool flag) { m_parallelPragma = flag; }
bool uniquePragma() const { return m_uniquePragma; }
void uniquePragma(bool flag) { m_uniquePragma = flag; }
bool unique0Pragma() const { return m_unique0Pragma; }
void unique0Pragma(bool flag) { m_unique0Pragma = flag; }
bool priorityPragma() const { return m_priorityPragma; }
void priorityPragma(bool flag) { m_priorityPragma = flag; }
string pragmaString() const;
};
class AstGenCase final : public AstNodeCase {
// Generate Case statement
public:
AstGenCase(FileLine* fl, AstNodeExpr* exprp, AstCaseItem* itemsp)
: ASTGEN_SUPER_GenCase(fl, exprp, itemsp) {}
ASTGEN_MEMBERS_AstGenCase;
};
// === AstNodeCoverOrAssert ===
class AstAssert final : public AstNodeCoverOrAssert {
// @astgen op3 := failsp: List[AstNode] // Statements when propp is failing/falsey
@ -1215,14 +1194,6 @@ public:
VAssertDirectiveType::RESTRICT) {}
};
// === AstNodeFor ===
class AstGenFor final : public AstNodeFor {
public:
AstGenFor(FileLine* fl, AstNode* initsp, AstNodeExpr* condp, AstNode* incsp, AstNode* stmtsp)
: ASTGEN_SUPER_GenFor(fl, initsp, condp, incsp, stmtsp) {}
ASTGEN_MEMBERS_AstGenFor;
};
// === AstNodeForeach ===
class AstConstraintForeach final : public AstNodeForeach {
// Constraint foreach statement
@ -1245,12 +1216,7 @@ public:
: ASTGEN_SUPER_ConstraintIf(fl, condp, thensp, elsesp) {}
ASTGEN_MEMBERS_AstConstraintIf;
};
class AstGenIf final : public AstNodeIf {
public:
AstGenIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp)
: ASTGEN_SUPER_GenIf(fl, condp, thensp, elsesp) {}
ASTGEN_MEMBERS_AstGenIf;
};
class AstIf final : public AstNodeIf {
bool m_uniquePragma = false; // unique case
bool m_unique0Pragma = false; // unique0 case

View File

@ -1928,6 +1928,18 @@ const char* AstEnumDType::broken() const {
}
void AstEnumItemRef::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstGenBlock::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (implied()) str << " [IMPLIED]";
if (unnamed()) str << " [UNNAMED]";
}
void AstGenBlock::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, implied);
dumpJsonBoolFunc(str, unnamed);
dumpJsonGen(str);
}
void AstIfaceRefDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
if (isPortDecl()) str << " [PORTDECL]";
@ -2941,14 +2953,10 @@ void AstNodeBlock::dumpJson(std::ostream& str) const {
}
void AstBegin::dump(std::ostream& str) const {
this->AstNodeBlock::dump(str);
if (generate()) str << " [GEN]";
if (genforp()) str << " [GENFOR]";
if (implied()) str << " [IMPLIED]";
if (needProcess()) str << " [NPRC]";
}
void AstBegin::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, generate);
dumpJsonBool(str, "genfor", bool(genforp()));
dumpJsonBoolFunc(str, implied);
dumpJsonBoolFunc(str, needProcess);
dumpJsonGen(str);
@ -3258,7 +3266,7 @@ AstAlways* AstAssignW::convertToAlways() {
if (hasTimingControl) {
// If there's a timing control, put the assignment in a fork..join_none. This process won't
// get marked as suspendable and thus will be scheduled normally
AstBegin* const beginp = new AstBegin{flp, "", bodysp, false, false};
AstBegin* const beginp = new AstBegin{flp, "", bodysp, false};
AstFork* const forkp = new AstFork{flp, "", beginp};
forkp->joinType(VJoinType::JOIN_NONE);
bodysp = forkp;

View File

@ -73,31 +73,32 @@ class BeginVisitor final : public VNVisitor {
string dot(const string& a, const string& b) { return VString::dot(a, "__DOT__", b); }
void dotNames(const AstNodeBlock* const nodep, const char* const blockName) {
void dotNames(const std::string& name, FileLine* const flp, AstNode* stmtsp,
const char* const blockName) {
UINFO(8, "nname " << m_namedScope);
if (nodep->name() != "") { // Else unneeded unnamed block
if (name != "") { // Else unneeded unnamed block
// Create data for dotted variable resolution
string dottedname = nodep->name() + "__DOT__"; // So always found
string dottedname = name + "__DOT__"; // So always found
string::size_type pos;
while ((pos = dottedname.find("__DOT__")) != string::npos) {
const string ident = dottedname.substr(0, pos);
dottedname = dottedname.substr(pos + std::strlen("__DOT__"));
if (nodep->name() != "") {
if (name != "") {
m_displayScope = dot(m_displayScope, ident);
m_namedScope = dot(m_namedScope, ident);
}
m_unnamedScope = dot(m_unnamedScope, ident);
// Create CellInline for dotted var resolution
if (!m_ftaskp) {
AstCellInline* const inlinep = new AstCellInline{
nodep->fileline(), m_unnamedScope, blockName, m_modp->timeunit()};
AstCellInline* const inlinep
= new AstCellInline{flp, m_unnamedScope, blockName, m_modp->timeunit()};
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
}
}
}
// Remap var names and replace lower Begins
iterateAndNextNull(nodep->stmtsp());
iterateAndNextNull(stmtsp);
}
void liftNode(AstNode* nodep) {
@ -125,14 +126,13 @@ class BeginVisitor final : public VNVisitor {
// replaced with multiple statements)
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (!VN_IS(stmtp, Begin)) {
AstBegin* const beginp
= new AstBegin{stmtp->fileline(), "", nullptr, false, false};
AstBegin* const beginp = new AstBegin{stmtp->fileline(), "", nullptr, false};
stmtp->replaceWith(beginp);
beginp->addStmtsp(stmtp);
stmtp = beginp;
}
}
dotNames(nodep, "__FORK__");
dotNames(nodep->name(), nodep->fileline(), nodep->stmtsp(), "__FORK__");
nodep->name("");
}
void visit(AstForeach* nodep) override {
@ -199,6 +199,20 @@ class BeginVisitor final : public VNVisitor {
m_liftedp = nullptr;
}
}
void visit(AstGenBlock* nodep) override {
// GenBlocks were only useful in variable creation, change names and delete
UINFO(8, " " << nodep);
VL_RESTORER(m_displayScope);
VL_RESTORER(m_namedScope);
VL_RESTORER(m_unnamedScope);
UASSERT_OBJ(!m_keepBegins, nodep, "Should be able to eliminate all AstGenBlock");
dotNames(nodep->name(), nodep->fileline(), nodep->itemsp(), "__BEGIN__");
// Repalce node with body then delete
if (AstNode* const itemsp = nodep->itemsp()) {
nodep->addNextHere(itemsp->unlinkFrBackWithNext());
}
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
void visit(AstBegin* nodep) override {
// Begin blocks were only useful in variable creation, change names and delete
UINFO(8, " " << nodep);
@ -208,9 +222,8 @@ class BeginVisitor final : public VNVisitor {
{
VL_RESTORER(m_keepBegins);
m_keepBegins = false;
dotNames(nodep, "__BEGIN__");
dotNames(nodep->name(), nodep->fileline(), nodep->stmtsp(), "__BEGIN__");
}
UASSERT_OBJ(!nodep->genforp(), nodep, "GENFORs should have been expanded earlier");
// Cleanup
if (m_keepBegins) {
@ -431,7 +444,7 @@ AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
AstNodeDType* fromDtp = fromp->dtypep()->skipRefp();
// Split into for loop
// We record where the body needs to eventually go with bodyPointp
AstNode* bodyPointp = new AstBegin{nodep->fileline(), "[EditWrapper]", nullptr, false, false};
AstNode* bodyPointp = new AstBegin{nodep->fileline(), "[EditWrapper]", nullptr, false};
AstNode* newp = nullptr;
AstNode* lastp = nodep;
AstVar* nestedIndexp = nullptr;

View File

@ -382,7 +382,7 @@ void V3Broken::allowMidvisitorCheck(bool flag) { s_brokenAllowMidvisitorCheck =
void V3Broken::selfTest() {
// Exercise addNewed and deleted for coverage, as otherwise only used with VL_LEAK_CHECKS
FileLine* const fl = new FileLine{FileLine::commandLineFilename()};
AstNode* const newp = new AstBegin{fl, "[EditWrapper]", nullptr, false, false};
AstNode* const newp = new AstBegin{fl, "[EditWrapper]", nullptr, false};
// Don't actually do it with VL_LEAK_CHECKS, when new/delete calls these.
// Otherwise you call addNewed twice on the same address, which is an error.
#ifndef VL_LEAK_CHECKS

View File

@ -49,64 +49,80 @@ VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
class CaseLintVisitor final : public VNVisitorConst {
const AstNodeCase* m_caseExprp
= nullptr; // Under a CASE value node, if so the relevant case statement
// Under a CASE value node, if so the relevant case statement
const AstNode* m_casep = nullptr;
// METHODS
template <typename CaseItem>
static void detectMultipleDefaults(CaseItem* itemsp) {
bool hitDefault = false;
for (CaseItem* itemp = itemsp; itemp; itemp = AstNode::as<CaseItem>(itemp->nextp())) {
if (!itemp->isDefault()) continue;
if (hitDefault) itemp->v3error("Multiple default statements in case statement.");
hitDefault = true;
}
}
void visit(AstNodeCase* nodep) override {
if (VN_IS(nodep, Case) && VN_AS(nodep, Case)->casex()) {
template <typename CaseItem>
void checkXZinNonCaseX(AstNode* casep, AstNodeExpr* exprp, CaseItem* itemsp) {
VL_RESTORER(m_casep);
m_casep = casep;
iterateConst(exprp);
for (CaseItem* itemp = itemsp; itemp; itemp = AstNode::as<CaseItem>(itemp->nextp())) {
iterateAndNextConstNull(itemp->condsp());
}
}
// VISITORS
void visit(AstGenCase* nodep) override {
// Detect multiple defaults
detectMultipleDefaults(nodep->itemsp());
// Check for X/Z in non-casex statements
checkXZinNonCaseX(nodep, nodep->exprp(), nodep->itemsp());
}
void visit(AstCase* nodep) override {
if (nodep->casex()) {
nodep->v3warn(CASEX, "Suggest casez (with ?'s) in place of casex (with X's)");
}
// Detect multiple defaults
bool hitDefault = false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), CaseItem)) {
if (itemp->isDefault()) {
if (hitDefault) {
itemp->v3error("Multiple default statements in case statement.");
}
hitDefault = true;
}
}
detectMultipleDefaults(nodep->itemsp());
// Check for X/Z in non-casex statements
{
VL_RESTORER(m_caseExprp);
m_caseExprp = nodep;
iterateConst(nodep->exprp());
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), CaseItem)) {
iterateAndNextConstNull(itemp->condsp());
}
}
checkXZinNonCaseX(nodep, nodep->exprp(), nodep->itemsp());
}
void visit(AstConst* nodep) override {
// See also neverItem
if (m_caseExprp && nodep->num().isFourState()) {
if (VN_IS(m_caseExprp, GenCase)) {
nodep->v3error("Use of x/? constant in generate case statement, "
"(no such thing as 'generate casez')");
} else if (VN_IS(m_caseExprp, Case) && VN_AS(m_caseExprp, Case)->casex()) {
// Don't sweat it, we already complained about casex in general
} else if (VN_IS(m_caseExprp, Case)
&& (VN_AS(m_caseExprp, Case)->casez()
|| VN_AS(m_caseExprp, Case)->caseInside())) {
if (nodep->num().isAnyX()) {
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, "
"(perhaps intended ?/z in constant)");
}
} else {
nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, "
"(perhaps intended casex/casez)");
}
if (!nodep->num().isFourState()) return;
// Error if generate case
if (VN_IS(m_casep, GenCase)) {
nodep->v3error("Use of x/? constant in generate case statement, "
"(no such thing as 'generate casez')");
return;
}
// Otherwise must be a case statement
const AstCase* const casep = VN_AS(m_casep, Case);
// Don't sweat it, we already complained about casex in general
if (casep->casex()) return;
if (casep->casez() || casep->caseInside()) {
if (nodep->num().isAnyX()) {
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, "
"(perhaps intended ?/z in constant)");
}
return;
}
nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, "
"(perhaps intended casex/casez)");
}
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
public:
// CONSTRUCTORS
explicit CaseLintVisitor(AstNodeCase* nodep) { iterateConst(nodep); }
explicit CaseLintVisitor(AstCase* nodep) { iterateConst(nodep); }
explicit CaseLintVisitor(AstGenCase* nodep) { iterateConst(nodep); }
~CaseLintVisitor() override = default;
};
@ -562,7 +578,7 @@ class CaseVisitor final : public VNVisitor {
// VISITORS
void visit(AstCase* nodep) override {
V3Case::caseLint(nodep);
{ CaseLintVisitor{nodep}; }
iterateChildren(nodep);
UINFOTREE(9, nodep, "", "case_old");
if (isCaseTreeFast(nodep) && v3Global.opt.fCase()) {
@ -605,7 +621,7 @@ void V3Case::caseAll(AstNetlist* nodep) {
{ CaseVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("case", 0, dumpTreeEitherLevel() >= 3);
}
void V3Case::caseLint(AstNodeCase* nodep) {
void V3Case::caseLint(AstGenCase* nodep) {
UINFO(4, __FUNCTION__ << ": ");
{ CaseLintVisitor{nodep}; }
}

View File

@ -21,14 +21,14 @@
#include "verilatedos.h"
class AstNetlist;
class AstNodeCase;
class AstGenCase;
//============================================================================
class V3Case final {
public:
static void caseAll(AstNetlist* nodep) VL_MT_DISABLED;
static void caseLint(AstNodeCase* nodep) VL_MT_DISABLED;
static void caseLint(AstGenCase* nodep) VL_MT_DISABLED;
};
#endif // Guard

View File

@ -259,6 +259,17 @@ public:
}
}
void applyBlock(AstGenBlock* nodep) {
const VPragmaType pragma = VPragmaType::COVERAGE_BLOCK_OFF;
if (!nodep->unnamed()) {
for (const string& i : m_coverageOffBlocks) {
if (VString::wildmatch(nodep->prettyOrigOrName(), i)) {
nodep->addItemsp(new AstPragma{nodep->fileline(), pragma});
}
}
}
}
void applyBlock(AstNodeBlock* nodep) {
const VPragmaType pragma = VPragmaType::COVERAGE_BLOCK_OFF;
if (!nodep->unnamed()) {
@ -375,6 +386,13 @@ public:
m_waivers.emplace_back(WaiverSetting{code, contents, newMatch});
}
void applyBlock(AstGenBlock* nodep) {
// Apply to block at this line
const VPragmaType pragma = VPragmaType::COVERAGE_BLOCK_OFF;
if (lineMatch(nodep->fileline()->lineno(), pragma)) {
nodep->addItemsp(new AstPragma{nodep->fileline(), pragma});
}
}
void applyBlock(AstNodeBlock* nodep) {
// Apply to block at this line
const VPragmaType pragma = VPragmaType::COVERAGE_BLOCK_OFF;
@ -741,6 +759,15 @@ void V3Control::applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep) {
if (modp) modp->applyBlock(nodep);
}
void V3Control::applyCoverageBlock(AstNodeModule* modulep, AstGenBlock* nodep) {
const string& filename = nodep->fileline()->filename();
V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename);
if (filep) filep->applyBlock(nodep);
const string& modname = modulep->prettyOrigOrName();
V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modname);
if (modp) modp->applyBlock(nodep);
}
void V3Control::applyIgnores(FileLine* filelinep) {
const string& filename = filelinep->filename();
V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename);

View File

@ -48,6 +48,7 @@ public:
static void applyCase(AstCase* nodep);
static void applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep);
static void applyCoverageBlock(AstNodeModule* modulep, AstGenBlock* nodep);
static void applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp);
static void applyIgnores(FileLine* filelinep);
static void applyModule(AstNodeModule* modulep);

View File

@ -710,6 +710,16 @@ class CoverageVisitor final : public VNVisitor {
lineTrack(nodep);
}
}
void visit(AstGenBlock* nodep) override {
// Similar to AstBegin
VL_RESTORER(m_beginHier);
if (nodep->name() != "") {
m_beginHier = m_beginHier + (m_beginHier != "" ? "__DOT__" : "") + nodep->name();
}
iterateChildren(nodep);
lineTrack(nodep);
}
void visit(AstBegin* nodep) override {
// Record the hierarchy of any named begins, so we can apply to user
// coverage points. This is because there may be cov points inside
@ -718,7 +728,7 @@ class CoverageVisitor final : public VNVisitor {
// covers the code in that line.)
VL_RESTORER(m_beginHier);
VL_RESTORER(m_inToggleOff);
if (!nodep->generate()) m_inToggleOff = true;
m_inToggleOff = true;
if (nodep->name() != "") {
m_beginHier = m_beginHier + (m_beginHier != "" ? "__DOT__" : "") + nodep->name();
}

View File

@ -1297,9 +1297,6 @@ class DelayedVisitor final : public VNVisitor {
// Record write reference
recordWriteRef(nodep, false);
}
void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("For statements should have been converted to while statements");
}
void visit(AstWhile* nodep) override {
VL_RESTORER(m_inLoop);
m_inLoop = true;

View File

@ -680,7 +680,7 @@ public:
iterateAndNextConstNull(nodep->exprp());
puts("}\n");
}
void visit(AstNodeCase* nodep) override { // LCOV_EXCL_LINE
void visit(AstCase* nodep) override { // LCOV_EXCL_LINE
// In V3Case...
nodep->v3fatalSrc("Case statements should have been reduced out");
}

View File

@ -89,6 +89,54 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
putfs(nodep, func ? "endfunction\n" : "endtask\n");
}
void visit(AstGenBlock* nodep) override {
const std::string name = nodep->name().empty() ? "" : " : " + nodep->name();
putbs("/* generate */ begin" + name + '\n');
iterateChildrenConst(nodep);
puts("end" + name + '\n');
}
void visit(AstGenCase* nodep) override {
putfs(nodep, "/* generate */ case (");
iterateAndNextConstNull(nodep->exprp());
puts(")\n");
iterateAndNextConstNull(nodep->itemsp());
putqs(nodep, "endcase\n");
}
void visit(AstGenCaseItem* nodep) override {
if (nodep->condsp()) {
iterateAndNextConstNull(nodep->condsp());
} else {
putbs("default");
}
iterateAndNextConstNull(nodep->itemsp());
}
void visit(AstGenFor* nodep) override {
putfs(nodep, "/* generate */ for (");
{
VL_RESTORER(m_suppressSemi);
m_suppressSemi = true;
iterateAndNextConstNull(nodep->initsp());
puts(";");
iterateAndNextConstNull(nodep->condp());
puts(";");
iterateAndNextConstNull(nodep->incsp());
}
puts(") begin\n");
iterateAndNextConstNull(nodep->itemsp());
putqs(nodep, "end\n");
}
void visit(AstGenIf* nodep) override {
putfs(nodep, "");
puts("/* generate */ if (");
iterateAndNextConstNull(nodep->condp());
puts(") begin\n");
iterateAndNextConstNull(nodep->thensp());
if (nodep->elsesp()) {
putqs(nodep, "end else begin\n");
iterateAndNextConstNull(nodep->elsesp());
}
putqs(nodep, "end\n");
}
void visit(AstBegin* nodep) override {
if (nodep->name() == "") {
putbs("begin\n");
@ -193,23 +241,19 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
if (nodep->sensp()) puts(" ");
iterateChildrenConst(nodep);
}
void visit(AstNodeCase* nodep) override {
void visit(AstCase* nodep) override {
putfs(nodep, "");
if (const AstCase* const casep = VN_CAST(nodep, Case)) {
if (casep->priorityPragma()) puts("priority ");
if (casep->uniquePragma()) puts("unique ");
if (casep->unique0Pragma()) puts("unique0 ");
}
if (nodep->priorityPragma()) puts("priority ");
if (nodep->uniquePragma()) puts("unique ");
if (nodep->unique0Pragma()) puts("unique0 ");
puts(nodep->verilogKwd());
puts(" (");
iterateAndNextConstNull(nodep->exprp());
puts(")\n");
if (const AstCase* const casep = VN_CAST(nodep, Case)) {
if (casep->fullPragma() || casep->parallelPragma()) {
puts(" // synopsys");
if (casep->fullPragma()) puts(" full_case");
if (casep->parallelPragma()) puts(" parallel_case");
}
if (nodep->fullPragma() || nodep->parallelPragma()) {
puts(" // synopsys");
if (nodep->fullPragma()) puts(" full_case");
if (nodep->parallelPragma()) puts(" parallel_case");
}
iterateAndNextConstNull(nodep->itemsp());
putqs(nodep, "endcase\n");
@ -348,21 +392,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
iterateAndNextConstNull(nodep->exprsp());
puts(");\n");
}
void visit(AstNodeFor* nodep) override {
putfs(nodep, "for (");
{
VL_RESTORER(m_suppressSemi);
m_suppressSemi = true;
iterateAndNextConstNull(nodep->initsp());
puts(";");
iterateAndNextConstNull(nodep->condp());
puts(";");
iterateAndNextConstNull(nodep->incsp());
}
puts(") begin\n");
iterateAndNextConstNull(nodep->stmtsp());
putqs(nodep, "end\n");
}
void visit(AstRepeat* nodep) override {
putfs(nodep, "repeat (");
iterateAndNextConstNull(nodep->countp());

View File

@ -410,6 +410,11 @@ class HierCellsXmlVisitor final : public VNVisitorConst {
m_hier = hier;
m_hasChildren = true;
}
void visit(AstGenBlock* nodep) override {
VL_RESTORER(m_hier);
if (nodep->name() != "") m_hier += nodep->name() + ".";
iterateChildrenConst(nodep);
}
void visit(AstBegin* nodep) override {
VL_RESTORER(m_hier);
if (nodep->name() != "") m_hier += nodep->name() + ".";

View File

@ -231,7 +231,7 @@ private:
AstBegin* const beginp = new AstBegin{
forkp->fileline(),
"_Vwrapped_" + (forkp->name().empty() ? "" : forkp->name() + "_") + cvtToStr(m_id),
m_instance.m_handlep, false, true};
m_instance.m_handlep, true};
forkHandle.relink(beginp);
AstNode* const instAsgnp = instantiateDynScope(memberMap);
@ -470,7 +470,7 @@ class DynScopeVisitor final : public VNVisitor {
})) {
nodep->user2(true);
// Put it in a fork to prevent lifetime issues with the local
AstBegin* const beginp = new AstBegin{nodep->fileline(), "", nullptr, false, false};
AstBegin* const beginp = new AstBegin{nodep->fileline(), "", nullptr, false};
AstFork* const forkp = new AstFork{nodep->fileline(), "", beginp};
forkp->joinType(VJoinType::JOIN_NONE);
nodep->replaceWith(forkp);

View File

@ -260,6 +260,8 @@ public:
return "block";
} else if (VN_IS(nodep, Iface)) {
return "interface";
} else if (VN_IS(nodep, GenBlock)) {
return "generate block";
} else {
return nodep->prettyTypeName();
}
@ -295,8 +297,8 @@ public:
} else if (foundp->imported()) { // From package
// We don't throw VARHIDDEN as if the import is later the symbol
// table's import wouldn't warn
} else if (forPrimary() && VN_IS(nodep, Begin) && VN_IS(fnodep, Begin)
&& VN_AS(nodep, Begin)->generate()) {
} else if (forPrimary() && VN_IS(nodep, GenBlock)
&& (VN_IS(fnodep, Begin) || VN_IS(fnodep, GenBlock))) {
// Begin: ... blocks often replicate under genif/genfor, so
// suppress duplicate checks. See t_gen_forif.v for an example.
} else {
@ -882,7 +884,6 @@ class LinkDotFindVisitor final : public VNVisitor {
string
m_hierParamsName; // Name of module with hierarchical type parameters, empty when not used
string m_scope; // Scope text
const AstNodeBlock* m_blockp = nullptr; // Current Begin/end block
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
bool m_inRecursion = false; // Inside a recursive module
int m_paramNum = 0; // Parameter number, for position based connection
@ -1158,7 +1159,6 @@ class LinkDotFindVisitor final : public VNVisitor {
iterateChildren(nodep);
// Recurse in, preserving state
VL_RESTORER(m_scope);
VL_RESTORER(m_blockp);
VL_RESTORER(m_modSymp);
VL_RESTORER(m_curSymp);
VL_RESTORER(m_paramNum);
@ -1181,7 +1181,6 @@ class LinkDotFindVisitor final : public VNVisitor {
{
m_scope = m_scope + "." + nodep->name();
m_curSymp = m_modSymp = m_statep->insertCell(aboveSymp, m_modSymp, nodep, m_scope);
m_blockp = nullptr;
m_inRecursion = nodep->recursive();
// We don't report NotFoundModule, as may be a unused module in a generate
if (nodep->modp()) iterate(nodep->modp());
@ -1216,6 +1215,43 @@ class LinkDotFindVisitor final : public VNVisitor {
nodep->user1p(m_curSymp);
iterateChildren(nodep);
}
void visit(AstGenBlock* nodep) override { // FindVisitor::
UINFO(5, " " << nodep);
if (nodep->name() == "" && nodep->unnamed()) {
// Unnamed blocks are only important when they contain var
// decls, so search for them. (Otherwise adding all the
// unnamed#'s would just confuse tracing variables in
// places such as tasks, where "task ...; begin ... end"
// are common.
for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
if (VN_IS(itemp, Var) || VN_IS(itemp, Foreach)) {
std::string name;
const std::string stepStr = m_statep->forPrimary()
? ""
: std::to_string(m_statep->stepNumber()) + "_";
do {
++m_modBlockNum;
name = "unnamedblk" + stepStr + cvtToStr(m_modBlockNum);
// Increment again if earlier pass of V3LinkDot claimed this name
} while (m_curSymp->findIdFlat(name));
nodep->name(name);
break;
}
}
}
if (nodep->name() == "") {
iterateChildren(nodep);
} else {
VL_RESTORER(m_curSymp);
{
m_curSymp
= m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep);
m_curSymp->fallbackp(VL_RESTORER_PREV(m_curSymp));
// Iterate
iterateChildren(nodep);
}
}
}
void visit(AstNodeBlock* nodep) override { // FindVisitor::
UINFO(5, " " << nodep);
if (nodep->name() == "" && nodep->unnamed()) {
@ -1243,10 +1279,8 @@ class LinkDotFindVisitor final : public VNVisitor {
if (nodep->name() == "") {
iterateChildren(nodep);
} else {
VL_RESTORER(m_blockp);
VL_RESTORER(m_curSymp);
{
m_blockp = nodep;
m_curSymp
= m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep);
m_curSymp->fallbackp(VL_RESTORER_PREV(m_curSymp));
@ -2331,6 +2365,9 @@ class LinkDotScopeVisitor final : public VNVisitor {
// We have stored the link, we don't need these any more
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
void visit(AstNodeGen* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Generate constructs should have been reduced out");
}
// For speed, don't recurse things that can't have scope
// Note we allow AstNodeStmt's as generates may be under them
void visit(AstCell*) override {} // ScopeVisitor::
@ -3509,6 +3546,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
if (!foundp) {
} else if (VN_IS(foundp->nodep(), Cell) || VN_IS(foundp->nodep(), NodeBlock)
|| VN_IS(foundp->nodep(), GenBlock)
|| VN_IS(foundp->nodep(), Netlist) // for $root
|| VN_IS(foundp->nodep(), Module)) { // if top
if (allowScope) {
@ -3521,14 +3559,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
// last component, `targetp()` field will be overwritten by next components
m_ds.m_disablep->targetp(foundp->nodep());
}
if (const AstBegin* const beginp = VN_CAST(foundp->nodep(), Begin)) {
if (beginp->generate()) {
m_ds.m_genBlk = true;
if (m_ds.m_disablep) {
m_ds.m_disablep->v3warn(
E_UNSUPPORTED,
"Unsupported: Generate block referenced by disable");
}
if (VN_IS(foundp->nodep(), GenBlock)) {
m_ds.m_genBlk = true;
if (m_ds.m_disablep) {
m_ds.m_disablep->v3warn(
E_UNSUPPORTED,
"Unsupported: Generate block referenced by disable");
}
}
// Upper AstDot visitor will handle it from here
@ -4472,6 +4508,21 @@ class LinkDotResolveVisitor final : public VNVisitor {
LINKDOT_VISIT_START();
iterateChildren(nodep);
}
void visit(AstGenBlock* nodep) override {
LINKDOT_VISIT_START();
UINFO(5, indent() << "visit " << nodep);
checkNoDot(nodep);
{
VL_RESTORER(m_curSymp);
VL_RESTORER(m_ds);
if (nodep->name() != "") {
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
UINFO(5, indent() << "cur=se" << cvtToHex(m_curSymp));
}
iterateChildren(nodep);
}
UINFO(5, indent() << "cur=se" << cvtToHex(m_curSymp));
}
void visit(AstNodeBlock* nodep) override {
LINKDOT_VISIT_START();
UINFO(5, indent() << "visit " << nodep);

View File

@ -162,10 +162,6 @@ class LinkIncVisitor final : public VNVisitor {
m_insStmtp = nullptr; // Next thing should be new statement
iterateAndNextNull(nodep->stmtsp());
}
void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc(
"For statements should have been converted to while statements in V3Begin.cpp");
}
void visit(AstDelay* nodep) override {
m_insStmtp = nodep;
iterateAndNextNull(nodep->lhsp());

View File

@ -289,7 +289,7 @@ class LinkJumpVisitor final : public VNVisitor {
// Note var can be signed or unsigned based on original number.
AstNodeExpr* const countp = nodep->countp()->unlinkFrBackWithNext();
const string name = "__Vrepeat"s + cvtToStr(m_modRepeatNum++);
AstBegin* const beginp = new AstBegin{nodep->fileline(), "", nullptr, false, true};
AstBegin* const beginp = new AstBegin{nodep->fileline(), "", nullptr, true};
// Spec says value is integral, if negative is ignored
AstVar* const varp
= new AstVar{nodep->fileline(), VVarType::BLOCKTEMP, name, nodep->findSigned32DType()};
@ -425,7 +425,7 @@ class LinkJumpVisitor final : public VNVisitor {
// Further handling of disable stmt requires all forks to be begin blocks
AstBegin* beginp = VN_CAST(forkItemp, Begin);
if (!beginp) {
beginp = new AstBegin{fl, "", nullptr, false, false};
beginp = new AstBegin{fl, "", nullptr, false};
forkItemp->replaceWith(beginp);
beginp->addStmtsp(forkItemp);
// In order to continue the iteration

View File

@ -111,19 +111,19 @@ class LinkParseVisitor final : public VNVisitor {
iterateChildren(nodep);
}
bool nestedIfBegin(AstBegin* nodep) { // Point at begin inside the GenIf
bool nestedIfBegin(AstGenBlock* nodep) { // Point at begin inside the GenIf
// IEEE says directly nested item is not a new block
// The genblk name will get attached to the if true/false LOWER begin block(s)
// 1: GENIF
// -> 1:3: BEGIN [GEN] [IMPLIED] // nodep passed to this function
// -> 1:3: GENBLOCK [IMPLIED] // nodep passed to this function
// 1:3:1: GENIF
// 1:3:1:2: BEGIN genblk1 [GEN] [IMPLIED]
// 1:3:1:2: GENBLOCK genblk1 [IMPLIED]
const AstNode* const backp = nodep->backp();
return (nodep->implied() // User didn't provide begin/end
&& VN_IS(backp, GenIf) && VN_CAST(backp, GenIf)->elsesp() == nodep
&& !nodep->nextp() // No other statements under upper genif else
&& (VN_IS(nodep->stmtsp(), GenIf)) // Begin has if underneath
&& !nodep->stmtsp()->nextp()); // Has only one item
&& (VN_IS(nodep->itemsp(), GenIf)) // Begin has if underneath
&& !nodep->itemsp()->nextp()); // Has only one item
}
void checkIndent(AstNode* nodep, AstNode* childp) {
@ -657,7 +657,7 @@ class LinkParseVisitor final : public VNVisitor {
void visit(AstCover* nodep) override { visitIterateNoValueMod(nodep); }
void visit(AstRestrict* nodep) override { visitIterateNoValueMod(nodep); }
void visit(AstBegin* nodep) override {
void visit(AstGenBlock* nodep) override {
V3Control::applyCoverageBlock(m_modp, nodep);
cleanFileline(nodep);
VL_RESTORER(m_beginDepth);
@ -671,13 +671,13 @@ class LinkParseVisitor final : public VNVisitor {
if (nodep->genforp()) {
++m_genblkNum;
if (nodep->name() == "") assignGenBlkNum = m_genblkNum;
} else if (nodep->generate() && nodep->name() == ""
&& (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) {
} else if (nodep->name() == "" && (VN_IS(backp, GenCaseItem) || VN_IS(backp, GenIf))
&& !nestedIf) {
assignGenBlkNum = m_genblkAbove;
}
if (assignGenBlkNum != -1) {
nodep->name("genblk" + cvtToStr(assignGenBlkNum));
if (nodep->stmtsp()) {
if (nodep->itemsp()) {
nodep->v3warn(GENUNNAMED,
"Unnamed generate block "
<< nodep->prettyNameQ() << " (IEEE 1800-2023 27.6)\n"
@ -696,6 +696,31 @@ class LinkParseVisitor final : public VNVisitor {
iterateChildren(nodep);
}
}
void visit(AstGenCase* nodep) override {
++m_genblkNum;
cleanFileline(nodep);
VL_RESTORER(m_genblkAbove);
VL_RESTORER(m_genblkNum);
m_genblkAbove = m_genblkNum;
m_genblkNum = 0;
iterateChildren(nodep);
}
void visit(AstGenIf* nodep) override {
cleanFileline(nodep);
checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp());
const bool nestedIf = (VN_IS(nodep->backp(), GenBlock)
&& nestedIfBegin(VN_CAST(nodep->backp(), GenBlock)));
if (nestedIf) {
iterateChildren(nodep);
} else {
++m_genblkNum;
VL_RESTORER(m_genblkAbove);
VL_RESTORER(m_genblkNum);
m_genblkAbove = m_genblkNum;
m_genblkNum = 0;
iterateChildren(nodep);
}
}
void visit(AstCell* nodep) override {
if (nodep->origName().empty()) {
if (!VN_IS(nodep->modp(), Primitive)) { // Module/Program/Iface
@ -710,31 +735,11 @@ class LinkParseVisitor final : public VNVisitor {
}
iterateChildren(nodep);
}
void visit(AstGenCase* nodep) override {
++m_genblkNum;
void visit(AstBegin* nodep) override {
V3Control::applyCoverageBlock(m_modp, nodep);
cleanFileline(nodep);
VL_RESTORER(m_genblkAbove);
VL_RESTORER(m_genblkNum);
m_genblkAbove = m_genblkNum;
m_genblkNum = 0;
iterateChildren(nodep);
}
void visit(AstGenIf* nodep) override {
cleanFileline(nodep);
checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp());
const bool nestedIf
= (VN_IS(nodep->backp(), Begin) && nestedIfBegin(VN_CAST(nodep->backp(), Begin)));
if (nestedIf) {
iterateChildren(nodep);
} else {
++m_genblkNum;
VL_RESTORER(m_genblkAbove);
VL_RESTORER(m_genblkNum);
m_genblkAbove = m_genblkNum;
m_genblkNum = 0;
iterateChildren(nodep);
}
}
void visit(AstCase* nodep) override {
V3Control::applyCase(nodep);
cleanFileline(nodep);

View File

@ -1610,7 +1610,7 @@ class ParamVisitor final : public VNVisitor {
}
}
void visit(AstBegin* nodep) override {
void visit(AstGenBlock* nodep) override {
// Parameter substitution for generated for loops.
// TODO Unlike generated IF, we don't have to worry about short-circuiting the
// conditional expression, since this is currently restricted to simple
@ -1663,8 +1663,8 @@ class ParamVisitor final : public VNVisitor {
V3Const::constifyParamsEdit(nodep->exprp()); // exprp may change
const AstConst* const exprp = VN_AS(nodep->exprp(), Const);
// Constify
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), CaseItem)) {
for (AstGenCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), GenCaseItem)) {
for (AstNode* ep = itemp->condsp(); ep;) {
AstNode* const nextp = ep->nextp(); // May edit list
iterateAndNextNull(ep);
@ -1673,8 +1673,8 @@ class ParamVisitor final : public VNVisitor {
}
}
// Item match
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), CaseItem)) {
for (AstGenCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), GenCaseItem)) {
if (!itemp->isDefault()) {
for (AstNode* ep = itemp->condsp(); ep; ep = ep->nextp()) {
if (const AstConst* const ccondp = VN_CAST(ep, Const)) {
@ -1682,7 +1682,7 @@ class ParamVisitor final : public VNVisitor {
match.opEq(ccondp->num(), exprp->num());
if (!hit && match.isNeqZero()) {
hit = true;
keepp = itemp->stmtsp();
keepp = itemp->itemsp();
}
} else {
itemp->v3error("Generate Case item does not evaluate to constant");
@ -1691,12 +1691,12 @@ class ParamVisitor final : public VNVisitor {
}
}
// Else default match
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), CaseItem)) {
for (AstGenCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), GenCaseItem)) {
if (itemp->isDefault()) {
if (!hit) {
hit = true;
keepp = itemp->stmtsp();
keepp = itemp->itemsp();
}
}
}

View File

@ -252,7 +252,7 @@ public:
// would misfire
AstNode* newBlock(FileLine* fl, AstNode* nodep) {
if (nodep) return nodep;
return new AstBegin{fl, "", nullptr, false, true};
return new AstBegin{fl, "", nullptr, true};
}
// Bison sometimes needs error context without a token, so remember last token's line

View File

@ -597,7 +597,7 @@ class ConstraintExprVisitor final : public VNVisitor {
{
AstBegin* const tempp
= new AstBegin{fl, "[EditWrapper]", itemsp->unlinkFrBackWithNext(), false, false};
= new AstBegin{fl, "[EditWrapper]", itemsp->unlinkFrBackWithNext(), false};
VL_DO_DANGLING(iterateAndNextNull(tempp->stmtsp()), itemsp);
itemsp = tempp->stmtsp();
if (itemsp) itemsp->unlinkFrBackWithNext();
@ -937,6 +937,10 @@ class ConstraintExprVisitor final : public VNVisitor {
}
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void visit(AstGenBlock* nodep) override {
// Dubious but this is what we used to do. Does that mean no randomzie
// methods work under a generage block?
}
void visit(AstBegin* nodep) override {}
void visit(AstConstraintForeach* nodep) override {
// Convert to plain foreach
@ -951,7 +955,7 @@ class ConstraintExprVisitor final : public VNVisitor {
exprsp->addNext(new AstBegin{
fl, "",
new AstForeach{fl, nodep->arrayp()->unlinkFrBack(), new AstCStmt{fl, cstmtp}},
false, true});
true});
exprsp->addNext(
new AstText{fl, "return ret.empty() ? \"#b1\" : \"(bvand\" + ret + \")\";})()"});
AstNodeExpr* const newp = new AstCExpr{fl, exprsp};
@ -963,7 +967,7 @@ class ConstraintExprVisitor final : public VNVisitor {
new AstBegin{fl, "",
new AstForeach{fl, nodep->arrayp()->unlinkFrBack(),
nodep->stmtsp()->unlinkFrBackWithNext()},
false, true});
true});
}
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
@ -1027,8 +1031,8 @@ class ConstraintExprVisitor final : public VNVisitor {
cstmtp->addNext(iterateSubtreeReturnEdits(itemp));
cstmtp->addNext(new AstText{fl, ";"});
AstNode* const exprsp = new AstText{fl, "([&]{ std::string ret;"};
exprsp->addNext(new AstBegin{
fl, "", new AstForeach{fl, arrayp, new AstCStmt{fl, cstmtp}}, false, true});
exprsp->addNext(
new AstBegin{fl, "", new AstForeach{fl, arrayp, new AstCStmt{fl, cstmtp}}, true});
exprsp->addNext(
new AstText{fl, "return ret.empty() ? \"#b0\" : \"(bvor\" + ret + \")\";})()"});
AstNodeExpr* const newp = new AstCExpr{fl, exprsp};
@ -1530,7 +1534,7 @@ class RandomizeVisitor final : public VNVisitor {
new AstAssign{fl, new AstVarRef{fl, iterVarp, VAccess::WRITE},
new AstAdd{fl, new AstConst{fl, 1},
new AstVarRef{fl, iterVarp, VAccess::READ}}}});
return new AstBegin{fl, "", stmtsp, false, true};
return new AstBegin{fl, "", stmtsp, true};
}
static AstNodeStmt* wrapIfRandMode(AstClass* classp, AstVar* const varp, AstNodeStmt* stmtp) {
const RandomizeMode rmode = {.asInt = varp->user1()};
@ -1939,7 +1943,7 @@ class RandomizeVisitor final : public VNVisitor {
AstVarRef* const refp = new AstVarRef{fl, classp, memberVarp, VAccess::WRITE};
AstNodeStmt* const stmtp = newRandStmtsp(fl, refp, randcVarp, basicFvarp);
if (!refp->backp()) VL_DO_DANGLING(refp->deleteTree(), refp);
basicRandomizep->addStmtsp(new AstBegin{fl, "", stmtp, false, false});
basicRandomizep->addStmtsp(new AstBegin{fl, "", stmtp, false});
}
});
}
@ -2072,7 +2076,7 @@ class RandomizeVisitor final : public VNVisitor {
stmtsp->addNext(setStmtsp);
stmtsp->addNext(m_stmtp);
stmtsp->addNext(restoreStmtsp);
relinker.relink(new AstBegin{nodep->fileline(), "", stmtsp, false, true});
relinker.relink(new AstBegin{nodep->fileline(), "", stmtsp, true});
}
}

View File

@ -345,7 +345,7 @@ void splitCheck(AstCFunc* ofuncp) {
// Unlink all statements, then add item by item to new sub-functions
AstBegin* const tempp = new AstBegin{ofuncp->fileline(), "[EditWrapper]",
ofuncp->stmtsp()->unlinkFrBackWithNext(), false, false};
ofuncp->stmtsp()->unlinkFrBackWithNext(), false};
// Currently we do not use finalsp in V3Sched, if we do, it needs to be handled here
UASSERT_OBJ(!ofuncp->finalsp(), ofuncp, "Should not have any finalps");
while (tempp->stmtsp()) {

View File

@ -991,7 +991,7 @@ private:
checkNodeInfo(nodep);
iterateChildrenConst(nodep);
}
void visit(AstNodeCase* nodep) override {
void visit(AstCase* nodep) override {
if (jumpingOver()) return;
UINFO(5, " CASE " << nodep);
checkNodeInfo(nodep);
@ -1083,39 +1083,6 @@ private:
checkNodeInfo(nodep);
}
void visit(AstNodeFor* nodep) override {
// Doing lots of Whiles is slow, so only for parameters
UINFO(5, " FOR " << nodep);
if (!m_params) {
badNodeType(nodep);
return;
}
checkNodeInfo(nodep);
if (m_checkOnly) {
iterateChildrenConst(nodep);
} else if (optimizable()) {
int loops = 0;
iterateAndNextConstNull(nodep->initsp());
while (true) {
UINFO(5, " FOR-ITER " << nodep);
iterateAndNextConstNull(nodep->condp());
if (!optimizable()) break;
if (!fetchConst(nodep->condp())->num().isNeqZero()) { //
break;
}
iterateAndNextConstNull(nodep->stmtsp());
iterateAndNextConstNull(nodep->incsp());
if (loops++ > v3Global.opt.unrollCountAdjusted(VOptionBool{}, m_params, true)) {
clearOptimizable(nodep, "Loop unrolling took too long; probably this is an"
"infinite loop, or use /*verilator unroll_full*/, or "
"set --unroll-count above "
+ cvtToStr(loops));
break;
}
}
}
}
void visit(AstWhile* nodep) override {
// Doing lots of Whiles is slow, so only for parameters
if (jumpingOver()) return;

View File

@ -338,7 +338,7 @@ protected:
void visit(AstAlways* nodep) override = 0;
void visit(AstNodeIf* nodep) override = 0;
// We don't do AstNodeFor/AstWhile loops, due to the standard question
// We don't do AstWhile loops, due to the standard question
// of what is before vs. after
void visit(AstAssignDly* nodep) override {

View File

@ -194,7 +194,7 @@ struct SplitVarImpl VL_NOT_FINAL {
stmtp->unlinkFrBack();
// Insert begin-end because temp value may be inserted to this block later.
const std::string name = "__VsplitVarBlk" + cvtToStr(modp->user1Inc(1));
ap->addStmtsp(new AstBegin{ap->fileline(), name, stmtp, false, false});
ap->addStmtsp(new AstBegin{ap->fileline(), name, stmtp, false});
}
}
@ -204,7 +204,7 @@ struct SplitVarImpl VL_NOT_FINAL {
// Insert begin-end because temp value may be inserted to this block later.
FileLine* const fl = initp->fileline();
const std::string name = "__VsplitVarBlk" + cvtToStr(modp->user1Inc(1));
initp->replaceWith(new AstInitial{fl, new AstBegin{fl, name, stmtp, false, false}});
initp->replaceWith(new AstInitial{fl, new AstBegin{fl, name, stmtp, false}});
VL_DO_DANGLING(initp->deleteTree(), initp);
}
}

View File

@ -1362,7 +1362,7 @@ class TaskVisitor final : public VNVisitor {
if (bodysp) {
unlinkAndClone(nodep, bodysp, true);
AstBegin* const tempp
= new AstBegin{nodep->fileline(), "[EditWrapper]", bodysp, false, false};
= new AstBegin{nodep->fileline(), "[EditWrapper]", bodysp, false};
VL_DANGLING(bodysp);
// If we cloned due to recursion, now need to rip out the ports
// (that remained in place) then got cloned
@ -1624,10 +1624,6 @@ class TaskVisitor final : public VNVisitor {
nodep->v3fatalSrc(
"Foreach statements should have been converted to while statements in V3Begin.cpp");
}
void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc(
"For statements should have been converted to while statements in V3Begin.cpp");
}
void visit(AstNodeStmt* nodep) override {
VL_RESTORER(m_insStmtp);
m_insStmtp = nodep;

View File

@ -1029,7 +1029,7 @@ class TimingControlVisitor final : public VNVisitor {
}
controlp->replaceWith(forkp);
AstBegin* beginp = VN_CAST(controlp, Begin);
if (!beginp) beginp = new AstBegin{nodep->fileline(), "", controlp, false, false};
if (!beginp) beginp = new AstBegin{nodep->fileline(), "", controlp, false};
forkp->addStmtsp(beginp);
controlp = forkp;
}

View File

@ -209,8 +209,7 @@ class UnrollVisitor final : public VNVisitor {
if (loopValue) {
AstConst* varValuep = new AstConst{nodep->fileline(), *loopValue};
// Iteration requires a back, so put under temporary node
AstBegin* tempp
= new AstBegin{nodep->fileline(), "[EditWrapper]", clonep, false, false};
AstBegin* tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", clonep, false};
replaceVarRef(tempp->stmtsp(), varValuep);
clonep = tempp->stmtsp()->unlinkFrBackWithNext();
VL_DO_CLEAR(tempp->deleteTree(), tempp = nullptr);
@ -303,8 +302,7 @@ class UnrollVisitor final : public VNVisitor {
AstNode* clonep = initp->cloneTree(true);
AstConst* varValuep = new AstConst{nodep->fileline(), loopValue};
// Iteration requires a back, so put under temporary node
AstBegin* tempp
= new AstBegin{nodep->fileline(), "[EditWrapper]", clonep, false, false};
AstBegin* tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", clonep, false};
replaceVarRef(clonep, varValuep);
clonep = tempp->stmtsp()->unlinkFrBackWithNext();
VL_DO_CLEAR(tempp->deleteTree(), tempp = nullptr);
@ -329,8 +327,8 @@ class UnrollVisitor final : public VNVisitor {
AstConst* varValuep = new AstConst{nodep->fileline(), loopValue};
if (oneloopp) {
// Iteration requires a back, so put under temporary node
AstBegin* const tempp = new AstBegin{oneloopp->fileline(), "[EditWrapper]",
oneloopp, false, false};
AstBegin* const tempp
= new AstBegin{oneloopp->fileline(), "[EditWrapper]", oneloopp, false};
replaceVarRef(tempp->stmtsp(), varValuep);
oneloopp = tempp->stmtsp()->unlinkFrBackWithNext();
VL_DO_DANGLING(tempp->deleteTree(), tempp);
@ -338,8 +336,7 @@ class UnrollVisitor final : public VNVisitor {
if (m_generate) {
const string index = AstNode::encodeNumber(varValuep->toSInt());
const string nname = m_beginName + "__BRA__" + index + "__KET__";
oneloopp
= new AstBegin{oneloopp->fileline(), nname, oneloopp, true, false};
oneloopp = new AstGenBlock{oneloopp->fileline(), nname, oneloopp, false};
}
VL_DO_DANGLING(pushDeletep(varValuep), varValuep);
if (newbodysp) {
@ -421,9 +418,7 @@ class UnrollVisitor final : public VNVisitor {
}
}
void visit(AstGenFor* nodep) override {
if (!m_generate) {
iterateChildren(nodep);
} // else V3Param will recursively call each for loop to be unrolled for us
UASSERT_OBJ(m_generate, nodep, "There should be no GenFor left when unrolling all");
if (!m_varModeCheck) {
// Constify before unroll call, as it may change what is underneath.
if (nodep->initsp()) V3Const::constifyEdit(nodep->initsp()); // initsp may change
@ -439,7 +434,7 @@ class UnrollVisitor final : public VNVisitor {
// deleted by V3Const.
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
} else if (forUnrollCheck(nodep, VOptionBool{}, nodep->initsp(), nodep->condp(),
nodep->incsp(), nodep->stmtsp())) {
nodep->incsp(), nodep->itemsp())) {
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Did replacement
} else {
nodep->v3error("For loop doesn't have genvar index, or is malformed");
@ -448,13 +443,6 @@ class UnrollVisitor final : public VNVisitor {
}
}
}
void visit(AstNodeFor* nodep) override {
if (m_generate) { // Ignore for's when expanding genfor's
iterateChildren(nodep);
} else {
nodep->v3fatalSrc("V3Begin should have removed standard FORs");
}
}
void visit(AstVarRef* nodep) override {
if (m_varModeCheck && nodep->varp() == m_forVarp && nodep->varScopep() == m_forVscp
@ -514,18 +502,16 @@ UnrollStateful::UnrollStateful()
: m_unrollerp{new UnrollVisitor} {}
UnrollStateful::~UnrollStateful() { delete m_unrollerp; }
void UnrollStateful::unrollGen(AstNodeFor* nodep, const string& beginName) {
void UnrollStateful::unrollGen(AstGenFor* nodep, const string& beginName) {
UINFO(5, __FUNCTION__ << ": ");
m_unrollerp->process(nodep, true, beginName);
}
void UnrollStateful::unrollAll(AstNetlist* nodep) { m_unrollerp->process(nodep, false, ""); }
void V3Unroll::unrollAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":");
{
UnrollStateful unroller;
unroller.unrollAll(nodep);
UnrollVisitor visitor;
visitor.process(nodep, false, "");
} // Destruct before checking
V3Global::dumpCheckGlobalTree("unroll", 0, dumpTreeEitherLevel() >= 3);
}

View File

@ -38,8 +38,7 @@ public:
UnrollStateful() VL_MT_DISABLED;
~UnrollStateful() VL_MT_DISABLED;
// METHODS
void unrollGen(AstNodeFor* nodep, const string& beginName) VL_MT_DISABLED;
void unrollAll(AstNetlist* nodep) VL_MT_DISABLED;
void unrollGen(AstGenFor* nodep, const string& beginName) VL_MT_DISABLED;
};
//============================================================================

View File

@ -744,8 +744,7 @@ class WidthVisitor final : public VNVisitor {
|| (!nodep->stmtsp()->nextp() && !nodep->joinType().joinNone())))) {
AstNode* stmtsp = nullptr;
if (nodep->stmtsp()) stmtsp = nodep->stmtsp()->unlinkFrBack();
AstBegin* const newp
= new AstBegin{nodep->fileline(), nodep->name(), stmtsp, false, false};
AstBegin* const newp = new AstBegin{nodep->fileline(), nodep->name(), stmtsp, false};
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
} else if (v3Global.opt.timing().isSetTrue()) {
@ -5261,91 +5260,123 @@ class WidthVisitor final : public VNVisitor {
//--------------------
// Top levels
void visit(AstNodeCase* nodep) override {
template <typename CaseItem>
void handleCaseType(AstNode* casep, AstNodeExpr* exprp, CaseItem* itemsp) {
AstAttrOf* const exprap = VN_CAST(exprp, AttrOf);
if (!exprap) return;
if (exprap->attrType() != VAttrType::TYPEID) return;
const AstNodeDType* const exprDtp = exprap->dtypep();
UINFO(9, "case type exprDtp " << exprDtp);
// V3Param may have a pointer to this case statement, and we need
// dotted references to remain properly named, so rather than
// removing we convert it to a "normal" expression "case (1) ..."
FileLine* const newfl = casep->fileline();
newfl->warnOff(V3ErrorCode::CASEINCOMPLETE, true); // Side effect of transform
newfl->warnOff(V3ErrorCode::CASEOVERLAP, true); // Side effect of transform
casep->fileline(newfl);
for (CaseItem* itemp = itemsp; itemp; itemp = AstNode::as<CaseItem>(itemp->nextp())) {
if (itemp->isDefault()) continue;
bool hit = false;
for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) {
const AstAttrOf* const condAttrp = VN_CAST(condp, AttrOf);
if (!condAttrp) {
condp->v3error("Case(type) statement requires items that have type() items");
} else {
AstNodeDType* const condDtp = condAttrp->dtypep();
if (AstNode::computeCastable(exprDtp, condDtp, casep) == VCastable::SAMEISH) {
hit = true;
break;
}
}
}
pushDeletep(itemp->condsp()->unlinkFrBackWithNext());
// Item condition becomes constant 1 if hits else 0
itemp->addCondsp(new AstConst{newfl, AstConst::BitTrue{}, hit});
}
exprap->replaceWith(new AstConst{newfl, AstConst::BitTrue{}});
VL_DO_DANGLING(pushDeletep(exprap), exprap);
}
template <typename CaseItem>
void handleCase(AstNode* casep, AstNodeExpr* exprp, CaseItem* itemsp) {
// IEEE-2012 12.5:
// Width: MAX(expr, all items)
// Signed: Only if expr, and all items signed
// Take width as maximum across all items, if any is real whole thing is real
AstNodeDType* subDTypep = exprp->dtypep();
for (CaseItem* itemp = itemsp; itemp; itemp = AstNode::as<CaseItem>(itemp->nextp())) {
for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) {
if (condp->dtypep() == subDTypep) continue;
if (condp->dtypep()->isDouble() || subDTypep->isDouble()) {
subDTypep = casep->findDoubleDType();
} else if (condp->dtypep()->isString() || subDTypep->isString()) {
subDTypep = casep->findStringDType();
} else {
const int width = std::max(subDTypep->width(), condp->width());
const int mwidth = std::max(subDTypep->widthMin(), condp->widthMin());
const bool issigned = subDTypep->isSigned() && condp->isSigned();
subDTypep = casep->findLogicDType(width, mwidth, VSigning::fromBool(issigned));
}
}
}
// Apply width
iterateCheck(casep, "Case expression", exprp, CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP);
for (CaseItem* itemp = itemsp; itemp; itemp = AstNode::as<CaseItem>(itemp->nextp())) {
for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) {
nextcp = condp->nextp(); // Final may cause the node to get replaced
iterateCheck(casep, "Case Item", condp, CONTEXT_DET, FINAL, subDTypep, EXTEND_LHS);
}
}
}
void visit(AstGenCase* nodep) override {
assertAtStatement(nodep);
// Type check expression and case item conditions, but not bodies
userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p());
for (AstGenCaseItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
// Prelim may cause the node to get replaced, pick up next up front
nextip = VN_AS(itemp->nextp(), GenCaseItem);
for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) {
// Prelim may cause the node to get replaced, pick up next up front
nextcp = condp->nextp();
VL_DO_DANGLING(userIterate(condp, WidthVP{CONTEXT_DET, PRELIM}.p()), condp);
}
}
// Deal with case(type(data_type))
handleCaseType(nodep, nodep->exprp(), nodep->itemsp());
// Type check
handleCase(nodep, nodep->exprp(), nodep->itemsp());
}
void visit(AstGenFor* nodep) override {
assertAtStatement(nodep);
userIterateAndNext(nodep->initsp(), nullptr);
iterateCheckBool(nodep, "For Test Condition", nodep->condp(), BOTH);
userIterateAndNext(nodep->incsp(), nullptr);
}
void visit(AstGenIf* nodep) override {
assertAtStatement(nodep);
iterateCheckBool(nodep, "If", nodep->condp(), BOTH);
}
void visit(AstCase* nodep) override {
assertAtStatement(nodep);
// Type check expression case item conditions and bodies
userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p());
for (AstCaseItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
nextip = VN_AS(itemp->nextp(), CaseItem); // Prelim may cause the node to get replaced
if (!VN_IS(nodep, GenCase)) userIterateAndNext(itemp->stmtsp(), nullptr);
userIterateAndNext(itemp->stmtsp(), nullptr);
for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) {
nextcp = condp->nextp(); // Prelim may cause the node to get replaced
VL_DO_DANGLING(userIterate(condp, WidthVP{CONTEXT_DET, PRELIM}.p()), condp);
}
}
// Deal with case(type(data_type))
if (AstAttrOf* const exprap = VN_CAST(nodep->exprp(), AttrOf)) {
if (exprap->attrType() == VAttrType::TYPEID) {
const AstNodeDType* const exprDtp = exprap->dtypep();
UINFO(9, "case type exprDtp " << exprDtp);
// V3Param may have a pointer to this case statement, and we need
// dotted references to remain properly named, so rather than
// removing we convert it to a "normal" expression "case (1) ..."
FileLine* const newfl = nodep->fileline();
newfl->warnOff(V3ErrorCode::CASEINCOMPLETE, true); // Side effect of transform
newfl->warnOff(V3ErrorCode::CASEOVERLAP, true); // Side effect of transform
nodep->fileline(newfl);
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), CaseItem)) {
if (!itemp->isDefault()) {
bool hit = false;
for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) {
const AstAttrOf* const condAttrp = VN_CAST(condp, AttrOf);
if (!condAttrp) {
condp->v3error(
"Case(type) statement requires items that have type() items");
} else {
AstNodeDType* const condDtp = condAttrp->dtypep();
if (AstNode::computeCastable(exprDtp, condDtp, nodep)
== VCastable::SAMEISH) {
hit = true;
break;
}
}
}
pushDeletep(itemp->condsp()->unlinkFrBackWithNext());
// Item condition becomes constant 1 if hits else 0
itemp->addCondsp(new AstConst{newfl, AstConst::BitTrue{}, hit});
}
}
VL_DO_DANGLING(pushDeletep(exprap->unlinkFrBack()), exprap);
nodep->exprp(new AstConst{newfl, AstConst::BitTrue{}});
}
}
// Take width as maximum across all items, if any is real whole thing is real
AstNodeDType* subDTypep = nodep->exprp()->dtypep();
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), CaseItem)) {
for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) {
if (condp->dtypep() != subDTypep) {
if (condp->dtypep()->isDouble() || subDTypep->isDouble()) {
subDTypep = nodep->findDoubleDType();
} else if (condp->dtypep()->isString() || subDTypep->isString()) {
subDTypep = nodep->findStringDType();
} else {
const int width = std::max(subDTypep->width(), condp->width());
const int mwidth = std::max(subDTypep->widthMin(), condp->widthMin());
const bool issigned = subDTypep->isSigned() && condp->isSigned();
subDTypep
= nodep->findLogicDType(width, mwidth, VSigning::fromBool(issigned));
}
}
}
}
// Apply width
iterateCheck(nodep, "Case expression", nodep->exprp(), CONTEXT_DET, FINAL, subDTypep,
EXTEND_EXP);
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), CaseItem)) {
for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) {
nextcp = condp->nextp(); // Final may cause the node to get replaced
iterateCheck(nodep, "Case Item", condp, CONTEXT_DET, FINAL, subDTypep, EXTEND_LHS);
}
}
handleCaseType(nodep, nodep->exprp(), nodep->itemsp());
// Type check
handleCase(nodep, nodep->exprp(), nodep->itemsp());
}
void visit(AstRandCase* nodep) override {
// IEEE says each item is a int (32-bits), and sizes are based on natural sizing,
@ -5365,14 +5396,6 @@ class WidthVisitor final : public VNVisitor {
}
}
void visit(AstNodeFor* nodep) override {
assertAtStatement(nodep);
userIterateAndNext(nodep->initsp(), nullptr);
iterateCheckBool(nodep, "For Test Condition", nodep->condp(),
BOTH); // it's like an if() condition.
if (!VN_IS(nodep, GenFor)) userIterateAndNext(nodep->stmtsp(), nullptr);
userIterateAndNext(nodep->incsp(), nullptr);
}
void visit(AstRepeat* nodep) override {
assertAtStatement(nodep);
userIterateAndNext(nodep->countp(), WidthVP{SELF, BOTH}.p());
@ -5388,10 +5411,8 @@ class WidthVisitor final : public VNVisitor {
void visit(AstNodeIf* nodep) override {
assertAtStatement(nodep);
// UINFOTREE(1, nodep, "", "IfPre");
if (!VN_IS(nodep, GenIf)) { // for m_paramsOnly
userIterateAndNext(nodep->thensp(), nullptr);
userIterateAndNext(nodep->elsesp(), nullptr);
}
userIterateAndNext(nodep->thensp(), nullptr);
userIterateAndNext(nodep->elsesp(), nullptr);
iterateCheckBool(nodep, "If", nodep->condp(), BOTH); // it's like an if() condition.
// UINFOTREE(1, nodep, "", "IfOut");
}

View File

@ -179,6 +179,7 @@ static void process() {
// Remove parameters by cloning modules to de-parameterized versions
// This requires some width calculations and constant propagation
// No more AstGenCase/AstGenFor/AstGenIf after this
V3Param::param(v3Global.rootp());
V3LinkDot::linkDotParamed(v3Global.rootp()); // Cleanup as made new modules
V3LinkLValue::linkLValue(v3Global.rootp()); // Resolve new VarRefs
@ -271,6 +272,7 @@ static void process() {
// Task inlining & pushing BEGINs names to variables/cells
// Begin processing must be after Param, before module inlining
// No more AstGenBlocks after this
V3Begin::debeginAll(v3Global.rootp()); // Flatten cell names, before inliner
// Expand inouts, stage 2

View File

@ -2713,7 +2713,7 @@ generate_block_or_null<nodep>: // IEEE: generate_block_or_null (called from gen
// // IEEE: generate_block
// // Must always return a BEGIN node, or nullptr - see GenFor construction
~c~generate_item
{ $$ = $1 ? (new AstBegin{$1->fileline(), "", $1, true, true}) : nullptr; }
{ $$ = $1 ? (new AstGenBlock{$1->fileline(), "", $1, true}) : nullptr; }
| ~c~genItemBegin { $$ = $1; }
;
@ -2722,19 +2722,19 @@ c_generate_block_or_null<nodep>: // IEEE: generate_block_or_null (for checkers)
;
genItemBegin<nodep>: // IEEE: part of generate_block
yBEGIN ~c~genItemList yEND { $$ = new AstBegin{$1, "", $2, true, false}; }
yBEGIN ~c~genItemList yEND { $$ = new AstGenBlock{$1, "", $2, false}; }
| yBEGIN yEND { $$ = nullptr; }
| id yP_COLON__BEGIN yBEGIN ~c~genItemList yEND endLabelE
{ $$ = new AstBegin{$<fl>1, *$1, $4, true, false};
{ $$ = new AstGenBlock{$<fl>1, *$1, $4, false};
GRAMMARP->endLabel($<fl>6, *$1, $6); }
| id yP_COLON__BEGIN yBEGIN yEND endLabelE
{ $$ = new AstBegin{$<fl>1, *$1, nullptr, true, false};
{ $$ = new AstGenBlock{$<fl>1, *$1, nullptr, false};
GRAMMARP->endLabel($<fl>5, *$1, $5); }
| yBEGIN ':' idAny ~c~genItemList yEND endLabelE
{ $$ = new AstBegin{$<fl>3, *$3, $4, true, false};
{ $$ = new AstGenBlock{$<fl>3, *$3, $4, false};
GRAMMARP->endLabel($<fl>6, *$3, $6); }
| yBEGIN ':' idAny yEND endLabelE
{ $$ = new AstBegin{$<fl>3, *$3, nullptr, true, false};
{ $$ = new AstGenBlock{$<fl>3, *$3, nullptr, false};
GRAMMARP->endLabel($<fl>5, *$3, $5); }
;
@ -2803,27 +2803,23 @@ c_conditional_generate_construct<nodep>: // IEEE: conditional_generate_construc
loop_generate_construct<nodep>: // ==IEEE: loop_generate_construct
yFOR '(' genvar_initialization ';' expr ';' genvar_iteration ')' ~c~generate_block_or_null
{ // Convert BEGIN(...) to BEGIN(GENFOR(...)), as we need the BEGIN to hide the local genvar
AstBegin* lowerBegp = VN_CAST($9, Begin);
UASSERT_OBJ(!($9 && !lowerBegp), $9, "Child of GENFOR should have been begin");
if (!lowerBegp) lowerBegp = new AstBegin{$1, "", nullptr, true, false}; // Empty body
AstNode* const lowerNoBegp = lowerBegp->stmtsp();
if (lowerNoBegp) lowerNoBegp->unlinkFrBackWithNext();
//
AstBegin* const blkp = new AstBegin{$1, lowerBegp->name(), nullptr, true, true};
AstGenBlock* lowerp = VN_CAST($9, GenBlock);
UASSERT_OBJ(!$9 || lowerp, $9, "Child of GENFOR should have been begin");
AstNode* const itemsp = lowerp && lowerp->itemsp() ? lowerp->itemsp()->unlinkFrBackWithNext() : nullptr;
AstGenBlock* const blkp = new AstGenBlock{$1, lowerp ? lowerp->name() : "", nullptr, true};
// V3LinkDot detects BEGIN(GENFOR(...)) as a special case
AstNode* initp = $3;
AstNode* const varp = $3;
if (VN_IS(varp, Var)) { // Genvar
initp = varp->nextp();
initp->unlinkFrBackWithNext(); // Detach 2nd from varp, make 1st init
blkp->addStmtsp(varp);
blkp->addItemsp(varp);
}
// Statements are under 'genforp' as instances under this
// for loop won't get an extra layer of hierarchy tacked on
blkp->genforp(new AstGenFor{$1, initp, $5, $7, lowerNoBegp});
blkp->genforp(new AstGenFor{$1, initp, $5, $7, itemsp});
$$ = blkp;
VL_DO_DANGLING(lowerBegp->deleteTree(), lowerBegp);
DEL(lowerp);
}
;
@ -2878,22 +2874,22 @@ genvar_iteration<nodep>: // ==IEEE: genvar_iteration
new AstConst{$2, AstConst::StringToParse{}, "'b1"}}}; }
;
case_generate_itemList<caseItemp>: // IEEE: { case_generate_itemList }
case_generate_itemList<genCaseItemp>: // IEEE: { case_generate_itemList }
~c~case_generate_item { $$ = $1; }
| ~c~case_generate_itemList ~c~case_generate_item { $$ = $1; $1->addNext($2); }
;
c_case_generate_itemList<caseItemp>: // IEEE: { case_generate_item } (for checkers)
c_case_generate_itemList<genCaseItemp>: // IEEE: { case_generate_item } (for checkers)
BISONPRE_COPY(case_generate_itemList,{s/~c~/c_/g}) // {copied}
;
case_generate_item<caseItemp>: // ==IEEE: case_generate_item
caseCondList colon ~c~generate_block_or_null { $$ = new AstCaseItem{$2, $1, $3}; }
| yDEFAULT colon ~c~generate_block_or_null { $$ = new AstCaseItem{$1, nullptr, $3}; }
| yDEFAULT ~c~generate_block_or_null { $$ = new AstCaseItem{$1, nullptr, $2}; }
case_generate_item<genCaseItemp>: // ==IEEE: case_generate_item
caseCondList colon ~c~generate_block_or_null { $$ = new AstGenCaseItem{$2, $1, $3}; }
| yDEFAULT colon ~c~generate_block_or_null { $$ = new AstGenCaseItem{$1, nullptr, $3}; }
| yDEFAULT ~c~generate_block_or_null { $$ = new AstGenCaseItem{$1, nullptr, $2}; }
;
c_case_generate_item<caseItemp>: // IEEE: case_generate_item (for checkers)
c_case_generate_item<genCaseItemp>: // IEEE: case_generate_item (for checkers)
BISONPRE_COPY(case_generate_item,{s/~c~/c_/g}) // {copied}
;
@ -3409,9 +3405,9 @@ par_blockPreId<nodep>: // ==IEEE: par_block but called with leading ID
seq_blockFront<beginp>: // IEEE: part of seq_block
yBEGIN
{ $$ = new AstBegin{$1, "", nullptr, false, false}; }
{ $$ = new AstBegin{$1, "", nullptr, false}; }
| yBEGIN ':' idAny/*new-block_identifier*/
{ $$ = new AstBegin{$<fl>3, *$3, nullptr, false, false}; }
{ $$ = new AstBegin{$<fl>3, *$3, nullptr, false}; }
;
par_blockFront<forkp>: // IEEE: part of par_block
@ -3423,7 +3419,7 @@ par_blockFront<forkp>: // IEEE: part of par_block
seq_blockFrontPreId<beginp>: // IEEE: part of seq_block/stmt with leading id
id/*block_identifier*/ yP_COLON__BEGIN yBEGIN
{ $$ = new AstBegin{$3, *$1, nullptr, false, false}; }
{ $$ = new AstBegin{$3, *$1, nullptr, false}; }
;
par_blockFrontPreId<forkp>: // IEEE: part of par_block/stmt with leading id
@ -3468,7 +3464,7 @@ stmtList<nodep>:
stmt<nodep>: // IEEE: statement_or_null == function_statement_or_null
statement_item { $$ = $1; }
// // S05 block creation rule
| id/*block_identifier*/ ':' statement_item { $$ = new AstBegin{$<fl>1, *$1, $3, false, false}; }
| id/*block_identifier*/ ':' statement_item { $$ = new AstBegin{$<fl>1, *$1, $3, false}; }
// // from _or_null
| ';' { $$ = nullptr; }
// // labeled par_block/seq_block with leading ':'
@ -3590,7 +3586,7 @@ statement_item<nodep>: // IEEE: statement_item
| yDO stmtBlock yWHILE '(' expr ')' ';' { $$ = new AstDoWhile{$1, $5, $2}; }
// // IEEE says array_identifier here, but dotted accepted in VMM and 1800-2009
| yFOREACH '(' idClassSelForeach ')' stmtBlock
{ $$ = new AstBegin{$1, "", new AstForeach{$1, $3, $5}, false, true}; }
{ $$ = new AstBegin{$1, "", new AstForeach{$1, $3, $5}, true}; }
//
// // IEEE: jump_statement
| yRETURN ';' { $$ = new AstReturn{$1}; }
@ -3652,10 +3648,10 @@ statement_item<nodep>: // IEEE: statement_item
statementFor<beginp>: // IEEE: part of statement
yFOR beginForParen for_initialization expr ';' for_stepE ')' stmtBlock
{ $$ = new AstBegin{$1, "", $3, false, true};
{ $$ = new AstBegin{$1, "", $3, true};
$$->addStmtsp(new AstWhile{$1, $4, $8, $6}); }
| yFOR beginForParen for_initialization ';' for_stepE ')' stmtBlock
{ $$ = new AstBegin{$1, "", $3, false, true};
{ $$ = new AstBegin{$1, "", $3, true};
$$->addStmtsp(new AstWhile{$1, new AstConst{$1, AstConst::BitTrue{}}, $7, $5}); }
;
beginForParen: // IEEE: Part of statement (for loop beginning paren)
@ -6160,7 +6156,7 @@ assertion_item<nodep>: // ==IEEE: assertion_item
deferred_immediate_assertion_item<nodep>: // ==IEEE: deferred_immediate_assertion_item
deferred_immediate_assertion_statement { $$ = $1; }
| id/*block_identifier*/ ':' deferred_immediate_assertion_statement
{ $$ = new AstBegin{$<fl>1, *$1, $3, false, true}; }
{ $$ = new AstBegin{$<fl>1, *$1, $3, true}; }
;
procedural_assertion_statement<nodep>: // ==IEEE: procedural_assertion_statement
@ -6216,7 +6212,7 @@ deferred_immediate_assertion_statement<nodep>: // ==IEEE: deferred_immediate_as
concurrent_assertion_item<nodep>: // IEEE: concurrent_assertion_item
concurrent_assertion_statement { $$ = $1; }
| id/*block_identifier*/ ':' concurrent_assertion_statement
{ $$ = new AstBegin{$<fl>1, *$1, $3, false, true}; }
{ $$ = new AstBegin{$<fl>1, *$1, $3, true}; }
// // IEEE: checker_instantiation
// // identical to module_instantiation; see etcInst
;

View File

@ -5,7 +5,7 @@
{"type":"VAR","name":"p","addr":"(G)","loc":"d,69:11,69:12","dtypep":"(H)","origName":"p","isSc":false,"isPrimaryIO":false,"isPrimaryClock":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"lifetime":"VSTATICI","varType":"VAR","dtypeName":"Packet","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []},
{"type":"INITIAL","name":"","addr":"(I)","loc":"d,71:4,71:11","isSuspendable":false,"needProcess":false,
"stmtsp": [
{"type":"BEGIN","name":"","addr":"(J)","loc":"d,71:12,71:17","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],
{"type":"BEGIN","name":"","addr":"(J)","loc":"d,71:12,71:17","implied":false,"needProcess":false,"unnamed":true,
"stmtsp": [
{"type":"DISPLAY","name":"","addr":"(K)","loc":"d,73:7,73:13",
"fmtp": [

View File

@ -160,7 +160,7 @@
]}
],
"stmtsp": [
{"type":"BEGIN","name":"","addr":"(XC)","loc":"e,36:27,36:32","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],
{"type":"BEGIN","name":"","addr":"(XC)","loc":"e,36:27,36:32","implied":false,"needProcess":false,"unnamed":true,
"stmtsp": [
{"type":"ASSIGNDLY","name":"","addr":"(YC)","loc":"e,40:11,40:13","dtypep":"UNLINKED",
"rhsp": [
@ -308,7 +308,7 @@
]}
],
"thensp": [
{"type":"BEGIN","name":"","addr":"(ZE)","loc":"e,43:21,43:26","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],
{"type":"BEGIN","name":"","addr":"(ZE)","loc":"e,43:21,43:26","implied":false,"needProcess":false,"unnamed":true,
"stmtsp": [
{"type":"ASSIGNDLY","name":"","addr":"(AF)","loc":"e,45:14,45:16","dtypep":"UNLINKED",
"rhsp": [
@ -338,7 +338,7 @@
]}
],
"thensp": [
{"type":"BEGIN","name":"","addr":"(MF)","loc":"e,48:26,48:31","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],
{"type":"BEGIN","name":"","addr":"(MF)","loc":"e,48:26,48:31","implied":false,"needProcess":false,"unnamed":true,
"stmtsp": [
{"type":"ASSIGNDLY","name":"","addr":"(NF)","loc":"e,49:14,49:16","dtypep":"UNLINKED",
"rhsp": [
@ -361,7 +361,7 @@
]}
],
"thensp": [
{"type":"BEGIN","name":"","addr":"(VF)","loc":"e,51:26,51:31","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],"stmtsp": []}
{"type":"BEGIN","name":"","addr":"(VF)","loc":"e,51:26,51:31","implied":false,"needProcess":false,"unnamed":true,"stmtsp": []}
],
"elsesp": [
{"type":"IF","name":"","addr":"(WF)","loc":"e,53:12,53:14",
@ -375,7 +375,7 @@
]}
],
"thensp": [
{"type":"BEGIN","name":"","addr":"(AG)","loc":"e,53:27,53:32","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],
{"type":"BEGIN","name":"","addr":"(AG)","loc":"e,53:27,53:32","implied":false,"needProcess":false,"unnamed":true,
"stmtsp": [
{"type":"DISPLAY","name":"","addr":"(BG)","loc":"e,54:10,54:16",
"fmtp": [
@ -479,7 +479,7 @@
]}
],
"stmtsp": [
{"type":"BEGIN","name":"","addr":"(UH)","loc":"e,82:26,82:31","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],
{"type":"BEGIN","name":"","addr":"(UH)","loc":"e,82:26,82:31","implied":false,"needProcess":false,"unnamed":true,
"stmtsp": [
{"type":"ASSIGNDLY","name":"","addr":"(VH)","loc":"e,83:11,83:13","dtypep":"UNLINKED",
"rhsp": [
@ -529,7 +529,7 @@
"assertTypesp": [
{"type":"CONST","name":"?32?sh8","addr":"(OI)","loc":"e,90:25,90:26","dtypep":"(LF)"}
],"directiveTypesp": []},
{"type":"BEGIN","name":"blk","addr":"(PI)","loc":"e,91:15,91:18","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [],
{"type":"BEGIN","name":"blk","addr":"(PI)","loc":"e,91:15,91:18","implied":false,"needProcess":false,"unnamed":false,
"stmtsp": [
{"type":"DISABLE","name":"","addr":"(QI)","loc":"e,92:10,92:17",
"targetRefp": [
@ -639,7 +639,7 @@
]}
],
"thensp": [
{"type":"BEGIN","name":"","addr":"(GK)","loc":"d,55:44,55:49","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],
{"type":"BEGIN","name":"","addr":"(GK)","loc":"d,55:44,55:49","implied":false,"needProcess":false,"unnamed":true,
"stmtsp": [
{"type":"STMTEXPR","name":"","addr":"(HK)","loc":"d,56:16,56:17",
"exprp": [
@ -695,7 +695,7 @@
]}
],
"thensp": [
{"type":"BEGIN","name":"","addr":"(DL)","loc":"d,72:22,72:27","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],
{"type":"BEGIN","name":"","addr":"(DL)","loc":"d,72:22,72:27","implied":false,"needProcess":false,"unnamed":true,
"stmtsp": [
{"type":"ASSIGN","name":"","addr":"(EL)","loc":"d,73:17,73:18","dtypep":"UNLINKED",
"rhsp": [
@ -748,7 +748,7 @@
]}
],
"thensp": [
{"type":"BEGIN","name":"","addr":"(YL)","loc":"d,89:22,89:27","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],
{"type":"BEGIN","name":"","addr":"(YL)","loc":"d,89:22,89:27","implied":false,"needProcess":false,"unnamed":true,
"stmtsp": [
{"type":"ASSIGN","name":"","addr":"(ZL)","loc":"d,90:17,90:18","dtypep":"UNLINKED",
"rhsp": [
@ -854,7 +854,7 @@
]}
],
"thensp": [
{"type":"BEGIN","name":"","addr":"(ON)","loc":"d,118:35,118:40","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],
{"type":"BEGIN","name":"","addr":"(ON)","loc":"d,118:35,118:40","implied":false,"needProcess":false,"unnamed":true,
"stmtsp": [
{"type":"ASSIGN","name":"","addr":"(PN)","loc":"d,119:20,119:22","dtypep":"UNLINKED",
"rhsp": [

View File

@ -1,4 +1,4 @@
%Error: t/t_duplicated_gen_blocks_bad.v:11:12: Duplicate declaration of block: 'block'
%Error: t/t_duplicated_gen_blocks_bad.v:11:12: Duplicate declaration of generate block: 'block'
: ... note: In instance 't'
11 | begin : block
| ^~~~~
@ -6,7 +6,7 @@
9 | begin : block
| ^~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_duplicated_gen_blocks_bad.v:15:23: Duplicate declaration of block: 'block1'
%Error: t/t_duplicated_gen_blocks_bad.v:15:23: Duplicate declaration of generate block: 'block1'
: ... note: In instance 't'
15 | if (X > 1) begin : block1
| ^~~~~~

View File

@ -3,21 +3,21 @@
{"type":"MODULE","name":"test","addr":"(E)","loc":"d,22:8,22:12","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"test","level":2,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [],
"stmtsp": [
{"type":"VAR","name":"N","addr":"(F)","loc":"d,24:12,24:13","dtypep":"(G)","origName":"N","isSc":false,"isPrimaryIO":false,"isPrimaryClock":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":true,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"lifetime":"VSTATICI","varType":"GENVAR","dtypeName":"integer","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []},
{"type":"BEGIN","name":"FOR_GENERATE","addr":"(H)","loc":"d,25:14,25:17","generate":true,"genfor":false,"implied":true,"needProcess":false,"unnamed":false,"genforp": [],"stmtsp": []},
{"type":"BEGIN","name":"FOR_GENERATE[0]","addr":"(I)","loc":"d,27:21,27:31","generate":true,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [],
"stmtsp": [
{"type":"GENBLOCK","name":"FOR_GENERATE","addr":"(H)","loc":"d,25:14,25:17","implied":true,"unnamed":false,"genforp": [],"itemsp": []},
{"type":"GENBLOCK","name":"FOR_GENERATE[0]","addr":"(I)","loc":"d,27:21,27:31","implied":false,"unnamed":false,"genforp": [],
"itemsp": [
{"type":"CELL","name":"submod_for","addr":"(J)","loc":"d,27:21,27:31","origName":"submod_for","recursive":false,"modp":"(K)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []},
{"type":"BEGIN","name":"genblk1","addr":"(L)","loc":"d,28:19,28:24","generate":true,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],
"stmtsp": [
{"type":"GENBLOCK","name":"genblk1","addr":"(L)","loc":"d,28:19,28:24","implied":false,"unnamed":true,"genforp": [],
"itemsp": [
{"type":"CELL","name":"submod_2","addr":"(M)","loc":"d,29:25,29:33","origName":"submod_2","recursive":false,"modp":"(K)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []}
]},
{"type":"CELL","name":"submod_3","addr":"(N)","loc":"d,31:21,31:29","origName":"submod_3","recursive":false,"modp":"(K)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []}
]},
{"type":"BEGIN","name":"FOR_GENERATE[1]","addr":"(O)","loc":"d,27:21,27:31","generate":true,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [],
"stmtsp": [
{"type":"GENBLOCK","name":"FOR_GENERATE[1]","addr":"(O)","loc":"d,27:21,27:31","implied":false,"unnamed":false,"genforp": [],
"itemsp": [
{"type":"CELL","name":"submod_for","addr":"(P)","loc":"d,27:21,27:31","origName":"submod_for","recursive":false,"modp":"(K)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []},
{"type":"BEGIN","name":"genblk1","addr":"(Q)","loc":"d,28:19,28:24","generate":true,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],
"stmtsp": [
{"type":"GENBLOCK","name":"genblk1","addr":"(Q)","loc":"d,28:19,28:24","implied":false,"unnamed":true,"genforp": [],
"itemsp": [
{"type":"CELL","name":"submod_2","addr":"(R)","loc":"d,29:25,29:33","origName":"submod_2","recursive":false,"modp":"(K)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []}
]},
{"type":"CELL","name":"submod_3","addr":"(S)","loc":"d,31:21,31:29","origName":"submod_3","recursive":false,"modp":"(K)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []}
@ -25,11 +25,11 @@
]},
{"type":"MODULE","name":"submod","addr":"(K)","loc":"d,10:8,10:14","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"submod","level":3,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"1ps","inlinesp": [],
"stmtsp": [
{"type":"BEGIN","name":"submod_gen","addr":"(T)","loc":"d,12:19,12:29","generate":true,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [],
"stmtsp": [
{"type":"GENBLOCK","name":"submod_gen","addr":"(T)","loc":"d,12:19,12:29","implied":false,"unnamed":false,"genforp": [],
"itemsp": [
{"type":"VAR","name":"l1_sig","addr":"(U)","loc":"d,13:14,13:20","dtypep":"(V)","origName":"l1_sig","isSc":false,"isPrimaryIO":false,"isPrimaryClock":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"lifetime":"VSTATICI","varType":"WIRE","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []},
{"type":"BEGIN","name":"nested_gen","addr":"(W)","loc":"d,14:23,14:33","generate":true,"genfor":false,"implied":false,"needProcess":false,"unnamed":false,"genforp": [],
"stmtsp": [
{"type":"GENBLOCK","name":"nested_gen","addr":"(W)","loc":"d,14:23,14:33","implied":false,"unnamed":false,"genforp": [],
"itemsp": [
{"type":"CELL","name":"submod_nested","addr":"(X)","loc":"d,15:21,15:34","origName":"submod_nested","recursive":false,"modp":"(Y)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []}
]},
{"type":"CELL","name":"submod_l1","addr":"(Z)","loc":"d,17:17,17:26","origName":"submod_l1","recursive":false,"modp":"(Y)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []}

View File

@ -33,7 +33,7 @@
],"scopeNamep": []},
{"type":"INITIAL","name":"","addr":"(FB)","loc":"d,39:4,39:11","isSuspendable":false,"needProcess":false,
"stmtsp": [
{"type":"BEGIN","name":"","addr":"(GB)","loc":"d,39:12,39:17","generate":false,"genfor":false,"implied":false,"needProcess":false,"unnamed":true,"genforp": [],
{"type":"BEGIN","name":"","addr":"(GB)","loc":"d,39:12,39:17","implied":false,"needProcess":false,"unnamed":true,
"stmtsp": [
{"type":"STMTEXPR","name":"","addr":"(HB)","loc":"d,41:7,41:8",
"exprp": [

View File

@ -47,30 +47,30 @@
<netlist>
<module loc="d,22,8,22,12" name="test" origName="test" topModule="1">
<var loc="d,24,12,24,13" name="N" dtype_id="1" vartype="integer" origName="N"/>
<begin loc="d,25,14,25,17" name="FOR_GENERATE"/>
<begin loc="d,27,21,27,31" name="FOR_GENERATE[0]">
<genblock loc="d,25,14,25,17" name="FOR_GENERATE"/>
<genblock loc="d,27,21,27,31" name="FOR_GENERATE[0]">
<instance loc="d,27,21,27,31" name="submod_for" defName="submod" origName="submod_for"/>
<begin loc="d,28,19,28,24" name="genblk1">
<genblock loc="d,28,19,28,24" name="genblk1">
<instance loc="d,29,25,29,33" name="submod_2" defName="submod" origName="submod_2"/>
</begin>
</genblock>
<instance loc="d,31,21,31,29" name="submod_3" defName="submod" origName="submod_3"/>
</begin>
<begin loc="d,27,21,27,31" name="FOR_GENERATE[1]">
</genblock>
<genblock loc="d,27,21,27,31" name="FOR_GENERATE[1]">
<instance loc="d,27,21,27,31" name="submod_for" defName="submod" origName="submod_for"/>
<begin loc="d,28,19,28,24" name="genblk1">
<genblock loc="d,28,19,28,24" name="genblk1">
<instance loc="d,29,25,29,33" name="submod_2" defName="submod" origName="submod_2"/>
</begin>
</genblock>
<instance loc="d,31,21,31,29" name="submod_3" defName="submod" origName="submod_3"/>
</begin>
</genblock>
</module>
<module loc="d,10,8,10,14" name="submod" origName="submod">
<begin loc="d,12,19,12,29" name="submod_gen">
<genblock loc="d,12,19,12,29" name="submod_gen">
<var loc="d,13,14,13,20" name="l1_sig" dtype_id="2" vartype="logic" origName="l1_sig"/>
<begin loc="d,14,23,14,33" name="nested_gen">
<genblock loc="d,14,23,14,33" name="nested_gen">
<instance loc="d,15,21,15,34" name="submod_nested" defName="submod2" origName="submod_nested"/>
</begin>
</genblock>
<instance loc="d,17,17,17,26" name="submod_l1" defName="submod2" origName="submod_l1"/>
</begin>
</genblock>
<instance loc="d,19,13,19,22" name="submod_l0" defName="submod2" origName="submod_l0"/>
</module>
<module loc="d,7,8,7,15" name="submod2" origName="submod2"/>