From d6f8ccd20be5e9df5b23ddeada8f53ba78eb7584 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 26 Jan 2024 07:49:07 -0500 Subject: [PATCH] Add `unroll_disable` and `unroll_full` loop control metacomments (#3260). --- Changes | 4 +- docs/guide/exe_verilator.rst | 12 ++++-- docs/guide/extensions.rst | 17 ++++++++ docs/spelling.txt | 1 + src/V3Ast.h | 2 + src/V3AstNodeOther.h | 4 ++ src/V3AstNodes.cpp | 7 ++++ src/V3LinkJump.cpp | 26 ++++++++++-- src/V3Options.cpp | 10 +++++ src/V3Options.h | 1 + src/V3Simulate.h | 21 +++++----- src/V3Unroll.cpp | 51 ++++++++++++----------- src/verilog.l | 2 + src/verilog.y | 6 +++ test_regress/t/t_func_const_bad.out | 2 +- test_regress/t/t_unroll_pragma.v | 45 ++++++++++++++++++++ test_regress/t/t_unroll_pragma_disable.pl | 25 +++++++++++ test_regress/t/t_unroll_pragma_full.pl | 25 +++++++++++ test_regress/t/t_unroll_pragma_none.pl | 25 +++++++++++ 19 files changed, 241 insertions(+), 45 deletions(-) create mode 100644 test_regress/t/t_unroll_pragma.v create mode 100755 test_regress/t/t_unroll_pragma_disable.pl create mode 100755 test_regress/t/t_unroll_pragma_full.pl create mode 100755 test_regress/t/t_unroll_pragma_none.pl diff --git a/Changes b/Changes index 5d3ed06c7..acf973c25 100644 --- a/Changes +++ b/Changes @@ -16,7 +16,9 @@ Verilator 5.021 devel * Add predicted stack overflow warning (#4799). * Add +verilator+coverage+file runtime option. * Add --runtime-debug for Verilated executable runtime debugging. -* Add '--decorations node' for inserting debug comments into emitted code. +* Add `--decorations node` for inserting debug comments into emitted code. +* Add `unroll_disable` and `unroll_full` loop control metacomments (#3260). [Jiaxun Yang] + * Remove deprecated 32-bit pointer mode (`gcc -m32`). * Change zero replication width error to ZEROREPL warning (#4753) (#4762). [Pengcheng Xu] * Support dumping coverage with --main. diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 049a297e3..d38560352 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -1219,7 +1219,7 @@ Summary: This option has the same effect as the following flags: - :vlopt:`--decorations node` + :vlopt:`--decorations node <--decorations>` Instructs Verilator to add comments to the Verilated C++ code to assist determining what Verilog code was responsible for each C++ statement. @@ -1486,13 +1486,17 @@ Summary: .. option:: --unroll-count - Rarely needed. Specifies the maximum number of loop iterations that may be - unrolled. See also :option:`BLKLOOPINIT` warning. + Rarely needed. Specifies the maximum number of loop iterations that may + be unrolled. See also :option:`BLKLOOPINIT` warning, and + :option:`/*verilator&32;unroll_disable*/` and + :option:`/*verilator&32;unroll_full*/` metacomments. .. option:: --unroll-stmts Rarely needed. Specifies the maximum number of statements in a loop for - that loop to be unrolled. See also :option:`BLKLOOPINIT` warning. + that loop to be unrolled. See also :option:`BLKLOOPINIT` warning, and + :option:`/*verilator&32;unroll_disable*/` and + :option:`/*verilator&32;unroll_full*/` metacomments. .. option:: --unused-regexp diff --git a/docs/guide/extensions.rst b/docs/guide/extensions.rst index 1583a5993..c8575bec1 100644 --- a/docs/guide/extensions.rst +++ b/docs/guide/extensions.rst @@ -594,6 +594,23 @@ or "`ifdef`"'s may break other tools. Re-enable waveform tracing for all future signals or instances that are declared. +.. option:: /*verilator&32;unroll_disable*/ + + Used in a statement position to indicate the immediately following loop + at the same statement level should not be unrolled by Verilator, + ignoring :vlopt:`--unroll-count`. This is similar to clang's ``#pragma + clang loop unroll(disable)``. + + This option does not currently disable the C++ compiler's unrolling (or + not) of any loops that make it through to the Verilated C++ code. + +.. option:: /*verilator&32;unroll_full*/ + + Rarely needed. Used in a statement position to indicate the immediately + following loop at the same statement level should always be fully + unrolled by Verilator, ignoring :vlopt:`--unroll-count`. This is + similar to clang's ``#pragma clang loop unroll(full)``. + .. option:: $stacktrace Called as a task, print a stack trace. Called as a function, return a diff --git a/docs/spelling.txt b/docs/spelling.txt index 017b60fe9..edb4c1bcc 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -688,6 +688,7 @@ genvars getenv getline ggdb +glibc gmake gmon gotFinish diff --git a/src/V3Ast.h b/src/V3Ast.h index 172632fd3..f40cd3774 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -292,6 +292,8 @@ public: NO_INLINE_TASK, PUBLIC_MODULE, PUBLIC_TASK, + UNROLL_DISABLE, + UNROLL_FULL, FULL_CASE, PARALLEL_CASE, ENUM_SIZE diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index cdb1e39b9..4c72499da 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -3349,6 +3349,7 @@ class AstWhile final : public AstNodeStmt { // @astgen op2 := condp : AstNodeExpr // @astgen op3 := stmtsp : List[AstNode] // @astgen op4 := incsp : List[AstNode] + VOptionBool m_unrollFull; // Full, disable, or default unrolling public: AstWhile(FileLine* fl, AstNodeExpr* condp, AstNode* stmtsp = nullptr, AstNode* incsp = nullptr) : ASTGEN_SUPER_While(fl) { @@ -3357,12 +3358,15 @@ public: this->addIncsp(incsp); } ASTGEN_MEMBERS_AstWhile; + void dump(std::ostream& str) const override; bool isGateOptimizable() const override { return false; } int instrCount() const override { return INSTR_COUNT_BRANCH; } bool same(const AstNode* /*samep*/) const override { return true; } // Stop statement searchback here void addNextStmt(AstNode* newp, AstNode* belowp) override; bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } + void unrollFull(const VOptionBool flag) { m_unrollFull = flag; } + VOptionBool unrollFull() const { return m_unrollFull; } }; // === AstNodeAssign === diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 7b00c3bb1..9e1b9afb1 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2010,6 +2010,13 @@ bool AstVar::same(const AstNode* samep) const { const AstVar* const asamep = VN_DBG_AS(samep, Var); return name() == asamep->name() && varType() == asamep->varType(); } +void AstWhile::dump(std::ostream& str) const { + this->AstNode::dump(str); + if (unrollFull().isSetTrue()) + str << " [unrollfull]"; + else if (unrollFull().isSetFalse()) + str << " [unrolldis]"; +} void AstScope::dump(std::ostream& str) const { this->AstNode::dump(str); str << " [abovep=" << nodeAddr(aboveScopep()) << "]"; diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 2adf3f1e2..5c976da8b 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -55,6 +55,7 @@ class LinkJumpVisitor final : public VNVisitor { bool m_loopInc = false; // In loop increment bool m_inFork = false; // Under fork int m_modRepeatNum = 0; // Repeat counter + VOptionBool m_unrollFull; // Pragma full, disable, or default unrolling std::vector m_blockStack; // All begin blocks above current node // METHODS @@ -175,6 +176,7 @@ class LinkJumpVisitor final : public VNVisitor { void visit(AstNodeBlock* nodep) override { UINFO(8, " " << nodep << endl); VL_RESTORER(m_inFork); + VL_RESTORER(m_unrollFull); m_blockStack.push_back(nodep); { m_inFork = m_inFork || VN_IS(nodep, Fork); @@ -182,6 +184,17 @@ class LinkJumpVisitor final : public VNVisitor { } m_blockStack.pop_back(); } + void visit(AstPragma* nodep) override { + if (nodep->pragType() == VPragmaType::UNROLL_DISABLE) { + m_unrollFull = VOptionBool::OPT_FALSE; + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + } else if (nodep->pragType() == VPragmaType::UNROLL_FULL) { + m_unrollFull = VOptionBool::OPT_TRUE; + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + } else { + iterateChildren(nodep); + } + } void visit(AstRepeat* nodep) override { // So later optimizations don't need to deal with them, // REPEAT(count,body) -> loop=count,WHILE(loop>0) { body, loop-- } @@ -205,14 +218,17 @@ class LinkJumpVisitor final : public VNVisitor { nodep->fileline(), new AstVarRef{nodep->fileline(), varp, VAccess::READ}, zerosp}; AstNode* const bodysp = nodep->stmtsp(); if (bodysp) bodysp->unlinkFrBackWithNext(); - AstNode* newp = new AstWhile{nodep->fileline(), condp, bodysp, decp}; - initsp = initsp->addNext(newp); - newp = initsp; - nodep->replaceWith(newp); + AstWhile* const whilep = new AstWhile{nodep->fileline(), condp, bodysp, decp}; + if (!m_unrollFull.isDefault()) whilep->unrollFull(m_unrollFull); + m_unrollFull = VOptionBool::OPT_DEFAULT_FALSE; + initsp = initsp->addNext(whilep); + nodep->replaceWith(initsp); VL_DO_DANGLING(nodep->deleteTree(), nodep); } void visit(AstWhile* nodep) override { // Don't need to track AstRepeat/AstFor as they have already been converted + if (!m_unrollFull.isDefault()) nodep->unrollFull(m_unrollFull); + m_unrollFull = VOptionBool::OPT_DEFAULT_FALSE; VL_RESTORER(m_loopp); VL_RESTORER(m_loopInc); { @@ -236,6 +252,8 @@ class LinkJumpVisitor final : public VNVisitor { AstNodeExpr* const condp = nodep->condp() ? nodep->condp()->unlinkFrBack() : nullptr; AstNode* const bodyp = nodep->stmtsp() ? nodep->stmtsp()->unlinkFrBack() : nullptr; AstWhile* const whilep = new AstWhile{nodep->fileline(), condp, bodyp}; + if (!m_unrollFull.isDefault()) whilep->unrollFull(m_unrollFull); + m_unrollFull = VOptionBool::OPT_DEFAULT_FALSE; nodep->replaceWith(whilep); VL_DO_DANGLING(nodep->deleteTree(), nodep); if (bodyp) { diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 5993a7b84..4f3b3d9f4 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -959,6 +959,16 @@ VTimescale V3Options::timeComputeUnit(const VTimescale& flag) const { } } +int V3Options::unrollCountAdjusted(const VOptionBool& full, bool generate, bool simulate) { + int count = unrollCount(); + // std::max to avoid rollover if unrollCount is e.g. std::numeric_limits::max() + // With /*verilator unroll_full*/ still have a limit to avoid infinite loops + if (full.isSetTrue()) count = std::max(count, count * 1024); + if (generate) count = std::max(count, count * 16); + if (simulate) count = std::max(count, count * 16); + return count; +} + //###################################################################### // V3 Options utilities diff --git a/src/V3Options.h b/src/V3Options.h index 8361899a0..b48c4450c 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -564,6 +564,7 @@ public: return useTraceParallel() ? threads() : useTraceOffload() ? 1 : 0; } int unrollCount() const { return m_unrollCount; } + int unrollCountAdjusted(const VOptionBool& full, bool generate, bool simulate); int unrollStmts() const { return m_unrollStmts; } int verilateJobs() const { return m_verilateJobs; } diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 42f1b4009..10de98e53 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -393,9 +393,6 @@ private: UASSERT_OBJ(vscp, nodep, "Not linked"); return vscp; } - int unrollCount() const { - return m_params ? v3Global.opt.unrollCount() * 16 : v3Global.opt.unrollCount(); - } bool jumpingOver(const AstNode* nodep) const { // True to jump over this node - all visitors must call this up front return (m_jumpp && m_jumpp->labelp() != nodep); @@ -960,10 +957,11 @@ private: } iterateAndNextConstNull(nodep->stmtsp()); iterateAndNextConstNull(nodep->incsp()); - if (loops++ > unrollCount() * 16) { + if (loops++ > v3Global.opt.unrollCountAdjusted(VOptionBool{}, m_params, true)) { clearOptimizable(nodep, "Loop unrolling took too long; probably this is an" - "infinite loop, or set --unroll-count above " - + cvtToStr(unrollCount())); + "infinite loop, or use /*verilator unroll_full*/, or " + "set --unroll-count above " + + cvtToStr(loops)); break; } } @@ -999,11 +997,12 @@ private: if (jumpingOver(nodep)) break; // Prep for next loop - if (loops++ > unrollCount() * 16) { - clearOptimizable(nodep, - "Loop unrolling took too long; probably this is an infinite" - " loop, or set --unroll-count above " - + cvtToStr(unrollCount())); + if (loops++ + > v3Global.opt.unrollCountAdjusted(nodep->unrollFull(), 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; } } diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index 076b0518a..b7bc3ab9c 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -47,7 +47,6 @@ class UnrollVisitor final : public VNVisitor { bool m_varModeReplace; // Replacing varrefs bool m_varAssignHit; // Assign var hit bool m_generate; // Expand single generate For loop - int m_unrollLimit; // Unrolling limit string m_beginName; // What name to give begin iterations VDouble0 m_statLoops; // Statistic tracking VDouble0 m_statIters; // Statistic tracking @@ -80,6 +79,7 @@ class UnrollVisitor final : public VNVisitor { bool forUnrollCheck( AstNode* const nodep, + const VOptionBool& unrollFull, // Pragma unroll_full, unroll_disable AstNode* const initp, // Maybe under nodep (no nextp), or standalone (ignore nextp) AstNode* const precondsp, AstNode* condp, AstNode* const incp, // Maybe under nodep or in bodysp @@ -91,6 +91,8 @@ class UnrollVisitor final : public VNVisitor { if (condp) UINFO(6, " Cond " << condp << endl); if (incp) UINFO(6, " Inc " << incp << endl); + if (unrollFull.isSetFalse()) return cantUnroll(nodep, "pragma unroll_disable"); + // Initial value check AstAssign* const initAssp = VN_CAST(initp, Assign); if (!initAssp) return cantUnroll(nodep, "no initial assignment"); @@ -156,22 +158,25 @@ class UnrollVisitor final : public VNVisitor { // Check whether to we actually want to try and unroll. int loops; - if (!countLoops(initAssp, condp, incp, m_unrollLimit, loops)) { + const int limit = v3Global.opt.unrollCountAdjusted(unrollFull, m_generate, false); + if (!countLoops(initAssp, condp, incp, limit, loops)) { return cantUnroll(nodep, "Unable to simulate loop"); } // Less than 10 statements in the body? - int bodySize = 0; - int bodyLimit = v3Global.opt.unrollStmts(); - if (loops > 0) bodyLimit = v3Global.opt.unrollStmts() / loops; - if (bodySizeOverRecurse(precondsp, bodySize /*ref*/, bodyLimit) - || bodySizeOverRecurse(bodysp, bodySize /*ref*/, bodyLimit) - || bodySizeOverRecurse(incp, bodySize /*ref*/, bodyLimit)) { - return cantUnroll(nodep, "too many statements"); + if (!unrollFull.isSetTrue()) { + int bodySize = 0; + int bodyLimit = v3Global.opt.unrollStmts(); + if (loops > 0) bodyLimit = v3Global.opt.unrollStmts() / loops; + if (bodySizeOverRecurse(precondsp, bodySize /*ref*/, bodyLimit) + || bodySizeOverRecurse(bodysp, bodySize /*ref*/, bodyLimit) + || bodySizeOverRecurse(incp, bodySize /*ref*/, bodyLimit)) { + return cantUnroll(nodep, "too many statements"); + } } } // Finally, we can do it - if (!forUnroller(nodep, initAssp, condp, precondsp, incp, bodysp)) { + if (!forUnroller(nodep, unrollFull, initAssp, condp, precondsp, incp, bodysp)) { return cantUnroll(nodep, "Unable to unroll loop"); } VL_DANGLING(nodep); @@ -259,8 +264,8 @@ class UnrollVisitor final : public VNVisitor { return true; } - bool forUnroller(AstNode* nodep, AstAssign* initp, AstNode* condp, AstNode* precondsp, - AstNode* incp, AstNode* bodysp) { + bool forUnroller(AstNode* nodep, const VOptionBool& unrollFull, AstAssign* initp, + AstNode* condp, AstNode* precondsp, AstNode* incp, AstNode* bodysp) { UINFO(9, "forUnroller " << nodep << endl); V3Number loopValue{nodep}; if (!simulateTree(initp->rhsp(), nullptr, initp, loopValue)) { // @@ -329,11 +334,14 @@ class UnrollVisitor final : public VNVisitor { } ++m_statIters; - if (++times / 3 > m_unrollLimit) { + const int limit + = v3Global.opt.unrollCountAdjusted(unrollFull, m_generate, false); + if (++times / 3 > limit) { nodep->v3error( "Loop unrolling took too long;" - " probably this is an infinite loop, or set --unroll-count above " - << m_unrollLimit); + " probably this is an infinite loop, " + " or use /*verilator unroll_full*/, or set --unroll-count above " + << times); break; } @@ -396,7 +404,8 @@ class UnrollVisitor final : public VNVisitor { if (incp == stmtsp) stmtsp = nullptr; } // And check it - if (forUnrollCheck(nodep, initp, nodep->precondsp(), nodep->condp(), incp, stmtsp)) { + if (forUnrollCheck(nodep, nodep->unrollFull(), initp, nodep->precondsp(), + nodep->condp(), incp, stmtsp)) { VL_DO_DANGLING(pushDeletep(nodep), nodep); // Did replacement } } @@ -420,8 +429,8 @@ class UnrollVisitor final : public VNVisitor { // condition, but they'll become while's which can be // deleted by V3Const. VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); - } else if (forUnrollCheck(nodep, nodep->initsp(), nullptr, nodep->condp(), - nodep->incsp(), nodep->stmtsp())) { + } else if (forUnrollCheck(nodep, VOptionBool{}, nodep->initsp(), nullptr, + nodep->condp(), nodep->incsp(), nodep->stmtsp())) { VL_DO_DANGLING(pushDeletep(nodep), nodep); // Did replacement } else { nodep->v3error("For loop doesn't have genvar index, or is malformed"); @@ -478,12 +487,6 @@ public: m_varModeReplace = false; m_varAssignHit = false; m_generate = generate; - m_unrollLimit = v3Global.opt.unrollCount(); - if (generate) { - m_unrollLimit = std::numeric_limits::max() / 16 > m_unrollLimit - ? m_unrollLimit * 16 - : std::numeric_limits::max(); - } m_beginName = beginName; } void process(AstNode* nodep, bool generate, const string& beginName) { diff --git a/src/verilog.l b/src/verilog.l index 8f6a09897..1f67441c8 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -776,6 +776,8 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "/*verilator trace_init_task*/" { FL; return yVL_TRACE_INIT_TASK; } "/*verilator tracing_off*/" { FL_FWD; PARSEP->lexFileline()->tracingOn(false); FL_BRK; } "/*verilator tracing_on*/" { FL_FWD; PARSEP->lexFileline()->tracingOn(true); FL_BRK; } + "/*verilator unroll_disable*/" { FL; return yVL_UNROLL_DISABLE; } + "/*verilator unroll_full*/" { FL; return yVL_UNROLL_FULL; } "/**/" { FL_FWD; FL_BRK; } "/*"[^*]+"*/" { FL; V3ParseImp::lexVerilatorCmtBad(yylval.fl, yytext); FL_BRK; } diff --git a/src/verilog.y b/src/verilog.y index 364288d6e..2fe59b719 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -988,6 +988,8 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yVL_SPLIT_VAR "/*verilator split_var*/" %token yVL_TAG "/*verilator tag*/" %token yVL_TRACE_INIT_TASK "/*verilator trace_init_task*/" +%token yVL_UNROLL_DISABLE "/*verilator unroll_disable*/" +%token yVL_UNROLL_FULL "/*verilator unroll_full*/" %token yP_TICK "'" %token yP_TICKBRA "'{" @@ -3679,6 +3681,10 @@ statementFor: // IEEE: part of statement statementVerilatorPragmas: yVL_COVERAGE_BLOCK_OFF { $$ = new AstPragma{$1, VPragmaType::COVERAGE_BLOCK_OFF}; } + | yVL_UNROLL_DISABLE + { $$ = new AstPragma{$1, VPragmaType::UNROLL_DISABLE}; } + | yVL_UNROLL_FULL + { $$ = new AstPragma{$1, VPragmaType::UNROLL_FULL}; } ; foperator_assignment: // IEEE: operator_assignment (for first part of expression) diff --git a/test_regress/t/t_func_const_bad.out b/test_regress/t/t_func_const_bad.out index f8802f4db..621df1c53 100644 --- a/test_regress/t/t_func_const_bad.out +++ b/test_regress/t/t_func_const_bad.out @@ -19,7 +19,7 @@ | ^~~~~~~~~~~~~~ %Error: t/t_func_const_bad.v:36:20: Expecting expression to be constant, but can't determine constant for FUNCREF 'f_bad_infinite' : ... note: In instance 't' - t/t_func_const_bad.v:38:7: ... Location of non-constant WHILE: Loop unrolling took too long; probably this is an infinite loop, or set --unroll-count above 1024 + t/t_func_const_bad.v:38:7: ... Location of non-constant WHILE: Loop unrolling took too long; probably this is aninfinite loop, or use /*verilator unroll_full*/, or set --unroll-count above 16386 t/t_func_const_bad.v:36:20: ... Called from 'f_bad_infinite()' with parameters: a = ?32?h3 36 | localparam B4 = f_bad_infinite(3); diff --git a/test_regress/t/t_unroll_pragma.v b/test_regress/t/t_unroll_pragma.v new file mode 100644 index 000000000..c57af7909 --- /dev/null +++ b/test_regress/t/t_unroll_pragma.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`ifdef TEST_DISABLE + `define PRAGMA /*verilator unroll_disable*/ +`elsif TEST_FULL + `define PRAGMA /*verilator unroll_full*/ +`elsif TEST_NONE + `define PRAGMA +`endif + +module t (/*AUTOARG*/); + + int i, j; + + // This must always unroll + for (genvar g = 0; g < 10; ++g) begin + initial $c("gened();"); + end + + initial begin + // Test a loop smaller than --unroll-count + `PRAGMA + for (i = 0; i < 2; ++i) begin + `PRAGMA + for (j = 0; j < 2; ++j) begin + $c("small();"); + end + end + // Test a loop larger than --unroll-count + `PRAGMA + for (i = 0; i < 5; ++i) begin + `PRAGMA + for (j = 0; j < 5; ++j) begin + $c("large();"); + end + end + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_unroll_pragma_disable.pl b/test_regress/t/t_unroll_pragma_disable.pl new file mode 100755 index 000000000..be362c068 --- /dev/null +++ b/test_regress/t/t_unroll_pragma_disable.pl @@ -0,0 +1,25 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +top_filename("t/t_unroll_pragma.v"); + +compile( + verilator_flags2 => ['--unroll-count 4 --unroll-stmts 9999 --stats -DTEST_DISABLE'], + verilator_make_gmake => 0, + make_top_shell => 0, + make_main => 0, + ); + +file_grep($Self->{stats}, qr/Optimizations, Unrolled Loops\s+(\d+)/i, 1); + +ok(1); +1; diff --git a/test_regress/t/t_unroll_pragma_full.pl b/test_regress/t/t_unroll_pragma_full.pl new file mode 100755 index 000000000..0b58b01a9 --- /dev/null +++ b/test_regress/t/t_unroll_pragma_full.pl @@ -0,0 +1,25 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +top_filename("t/t_unroll_pragma.v"); + +compile( + verilator_flags2 => ['--unroll-count 4 --unroll-stmts 9999 --stats -DTEST_FULL'], + verilator_make_gmake => 0, + make_top_shell => 0, + make_main => 0, + ); + +file_grep($Self->{stats}, qr/Optimizations, Unrolled Loops\s+(\d+)/i, 5); + +ok(1); +1; diff --git a/test_regress/t/t_unroll_pragma_none.pl b/test_regress/t/t_unroll_pragma_none.pl new file mode 100755 index 000000000..933e315a3 --- /dev/null +++ b/test_regress/t/t_unroll_pragma_none.pl @@ -0,0 +1,25 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +top_filename("t/t_unroll_pragma.v"); + +compile( + verilator_flags2 => ['--unroll-count 4 --unroll-stmts 9999 --stats -DTEST_NONE'], + verilator_make_gmake => 0, + make_top_shell => 0, + make_main => 0, + ); + +file_grep($Self->{stats}, qr/Optimizations, Unrolled Loops\s+(\d+)/i, 3); + +ok(1); +1;