diff --git a/Changes b/Changes index 4b8796a49..623ff057f 100644 --- a/Changes +++ b/Changes @@ -19,6 +19,7 @@ Verilator 4.217 devel **Minor:** +* Support lower dimension looping in foreach loops (#3172). [Ehab Ibrahim] * Support up to 64 bit enums for .next/.prev/.name (#3244). [Alexander Grobman] * Fix MSWIN compile error (#2681). [Unai Martinez-Corral] * Fix break under foreach loop (#3230). diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index db2fd7487..a9c204666 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -183,6 +183,15 @@ public: static AstConst* parseParamLiteral(FileLine* fl, const string& literal); }; +class AstEmpty final : public AstNode { + // Represents something missing, e.g. a missing argument in FOREACH +public: + AstEmpty(FileLine* fl) + : ASTGEN_SUPER_Empty(fl) {} + ASTNODE_NODE_FUNCS(Empty) + virtual bool same(const AstNode* samep) const override { return true; } +}; + class AstEmptyQueue final : public AstNodeMath { public: AstEmptyQueue(FileLine* fl) diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index d76491408..d54b5d487 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1305,6 +1305,7 @@ class LinkDotFindVisitor final : public AstNVisitor { argrefp = largrefp; // Insert argref's name into symbol table m_statep->insertSym(m_curSymp, argrefp->name(), argrefp, nullptr); + } else if (VN_IS(argp, Empty)) { } else { argp->v3error("'foreach' loop variable expects simple variable name"); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index e5a0ee2f1..5a8be1a84 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -3810,16 +3810,17 @@ private: // Major dimension first while (AstNode* argsp = loopsp->elementsp()) { // Loop advances due to below varp->unlinkFrBack() + const bool empty = VN_IS(argsp, Empty); AstVar* const varp = VN_CAST(argsp, Var); - UASSERT_OBJ(varp, argsp, "Missing foreach loop variable"); - varp->usedLoopIdx(true); - varp->unlinkFrBack(); - fromDtp = fromDtp->skipRefp(); + UASSERT_OBJ(varp || empty, argsp, "Missing foreach loop variable"); + if (varp) varp->usedLoopIdx(true); + argsp->unlinkFrBack(); if (!fromDtp) { argsp->v3error("foreach loop variables exceed number of indices of array"); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); return; } + fromDtp = fromDtp->skipRefp(); UINFO(9, "- foreachArg " << argsp << endl); UINFO(9, "- from on " << fromp << endl); UINFO(9, "- from dtp " << fromDtp << endl); @@ -3828,7 +3829,9 @@ private: AstNode* bodyPointp = new AstBegin{fl, "[EditWrapper]", nullptr}; AstNode* loopp = nullptr; if (const AstNodeArrayDType* const adtypep = VN_CAST(fromDtp, NodeArrayDType)) { - loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange()); + if (varp) { + loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange()); + } // Prep for next fromDtp = fromDtp->subDTypep(); } else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) { @@ -3838,21 +3841,25 @@ private: VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); return; } - loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange()); + if (varp) { + loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange()); + } // Prep for next fromDtp = nullptr; } else if (VN_IS(fromDtp, DynArrayDType) || VN_IS(fromDtp, QueueDType)) { - auto* const leftp = new AstConst{fl, AstConst::Signed32{}, 0}; - auto* const sizep - = new AstCMethodHard{fl, fromp->cloneTree(false), "size", nullptr}; - sizep->dtypeSetSigned32(); - sizep->didWidth(true); - sizep->protect(false); - AstNode* const condp - = new AstLt{fl, new AstVarRef{fl, varp, VAccess::READ}, sizep}; - AstNode* const incp = new AstAdd{fl, new AstConst{fl, AstConst::Signed32{}, 1}, - new AstVarRef{fl, varp, VAccess::READ}}; - loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, condp, incp); + if (varp) { + auto* const leftp = new AstConst{fl, AstConst::Signed32{}, 0}; + auto* const sizep + = new AstCMethodHard{fl, fromp->cloneTree(false), "size", nullptr}; + sizep->dtypeSetSigned32(); + sizep->didWidth(true); + sizep->protect(false); + AstNode* const condp + = new AstLt{fl, new AstVarRef{fl, varp, VAccess::READ}, sizep}; + AstNode* const incp = new AstAdd{fl, new AstConst{fl, AstConst::Signed32{}, 1}, + new AstVarRef{fl, varp, VAccess::READ}}; + loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, condp, incp); + } // Prep for next fromDtp = fromDtp->subDTypep(); } else if (const AstAssocArrayDType* const adtypep @@ -3897,13 +3904,16 @@ private: return; } // New loop goes UNDER previous loop - if (!newp) { - newp = loopp; - } else { - lastBodyPointp->replaceWith(loopp); + if (varp) { + if (!newp) { + newp = loopp; + } else { + lastBodyPointp->replaceWith(loopp); + } + lastBodyPointp = bodyPointp; } - lastBodyPointp = bodyPointp; } + // The parser validates we don't have "foreach (array[,,,])" UASSERT_OBJ(newp, nodep, "foreach has no non-empty loop variable"); if (bodyp) { lastBodyPointp->replaceWith(bodyp); diff --git a/src/verilog.y b/src/verilog.y index a42133ba2..d2d68463e 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3523,7 +3523,8 @@ for_step_assignment: // ==IEEE: for_step_assignment loop_variables: // IEEE: loop_variables parseRefBase { $$ = $1; } - | loop_variables ',' parseRefBase { $$ = $1; $1->addNext($3); } + | loop_variables ',' parseRefBase { $$ = $1; $$->addNext($3); } + | ',' parseRefBase { $$ = new AstEmpty{$1}; $$->addNext($2); } ; //************************************************ @@ -5007,6 +5008,8 @@ idArrayedForeach: // IEEE: id + select (under foreach expression) // // To avoid conflicts we allow expr as first element, must post-check | idArrayed '[' expr ',' loop_variables ']' { $3 = AstNode::addNextNull($3, $5); $$ = new AstSelLoopVars($2, $1, $3); } + | idArrayed '[' ',' loop_variables ']' + { $4 = AstNode::addNextNull(new AstEmpty{$3}, $4); $$ = new AstSelLoopVars($2, $1, $4); } ; // VarRef without any dots or vectorizaion diff --git a/test_regress/t/t_foreach.v b/test_regress/t/t_foreach.v index d21d934c9..826abc114 100644 --- a/test_regress/t/t_foreach.v +++ b/test_regress/t/t_foreach.v @@ -71,6 +71,14 @@ module t (/*AUTOARG*/); end `checkh(sum, 64'h0030128ab2a8e557); + // comma syntax + sum = 0; + foreach (array[,index_b]) begin + $display(index_b); + sum = crc(sum, 0, index_b, 0, 0); + end + `checkh(sum, 64'h0000000006000000); + // sum = 0; foreach (larray[index_a]) begin diff --git a/test_regress/t/t_foreach_type_bad.out b/test_regress/t/t_foreach_type_bad.out index 1f4682a5a..df9bcdd48 100644 --- a/test_regress/t/t_foreach_type_bad.out +++ b/test_regress/t/t_foreach_type_bad.out @@ -7,4 +7,7 @@ %Error: t/t_foreach_type_bad.v:23:21: Illegal to foreach loop on basic 'BASICDTYPE 'bit'' 23 | foreach (b[i, j, k]); | ^ +%Error: t/t_foreach_type_bad.v:25:18: Illegal to foreach loop on basic 'BASICDTYPE 'real'' + 25 | foreach (r[, i]); + | ^ %Error: Exiting due to diff --git a/test_regress/t/t_foreach_type_bad.v b/test_regress/t/t_foreach_type_bad.v index 72585dee2..fbcf3a425 100644 --- a/test_regress/t/t_foreach_type_bad.v +++ b/test_regress/t/t_foreach_type_bad.v @@ -22,6 +22,8 @@ module t (/*AUTOARG*/); foreach (b[i, j, k]); // extra loop var + foreach (r[, i]); // no loop var and extra + $stop; end