diff --git a/Changes b/Changes index 45501947e..315b9d191 100644 --- a/Changes +++ b/Changes @@ -14,6 +14,7 @@ Verilator 4.203 devel **Minor:** * Add --reloop-limit argument (#2943) (#2960). [Geza Lore] +* Add --expand-limit argument (#3005). [Julien Margetts] * Add TRACE_THREADS to CMake (#2934). [Jonathan Drolet] * Optimize large lookup tables to static data (#2925). [Geza Lore] * Optimize reloop to accept constant index offsets (#2939). [Geza Lore] diff --git a/bin/verilator b/bin/verilator index 706de8e28..61064a9b4 100755 --- a/bin/verilator +++ b/bin/verilator @@ -315,6 +315,7 @@ detailed descriptions of these arguments. -E Preprocess, but do not compile --error-limit Abort after this number of errors --exe Link to create executable + --expand-limit Set expand optimization limit -F Parse arguments from a file, relatively -f Parse arguments from a file -FI Force include of a file diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 379971315..d7eebe90d 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -403,6 +403,12 @@ Summary: files on the command line that implement the main loop for your simulation. +.. option:: --expand-limit + + Rarely needed. Fine-tune optimizations to set the maximum size of an + expression in 32-bit words to expand into separate word-based + statements. + .. option:: -F Read the specified file, and act as if all text inside it was specified diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp index 2ec552cfe..dd5a5ec42 100644 --- a/src/V3Expand.cpp +++ b/src/V3Expand.cpp @@ -30,6 +30,7 @@ #include "V3Global.h" #include "V3Expand.h" +#include "V3Stats.h" #include "V3Ast.h" #include @@ -45,10 +46,24 @@ private: // STATE AstNode* m_stmtp = nullptr; // Current statement + VDouble0 m_statWides; // Statistic tracking + VDouble0 m_statWideWords; // Statistic tracking + VDouble0 m_statWideLimited; // Statistic tracking // METHODS VL_DEBUG_FUNC; // Declare debug() + bool doExpand(AstNode* nodep) { + ++m_statWides; + if (nodep->widthWords() <= v3Global.opt.expandLimit()) { + m_statWideWords += nodep->widthWords(); + return true; + } else { + ++m_statWideLimited; + return false; + } + } + int longOrQuadWidth(AstNode* nodep) { return (nodep->width() + (VL_EDATASIZE - 1)) & ~(VL_EDATASIZE - 1); } @@ -204,6 +219,7 @@ private: bool expandWide(AstNodeAssign* nodep, AstConst* rhsp) { UINFO(8, " Wordize ASSIGN(CONST) " << nodep << endl); + if (!doExpand(nodep)) return false; // -> {for each_word{ ASSIGN(WORDSEL(wide,#),WORDSEL(CONST,#))}} if (rhsp->num().isFourState()) { rhsp->v3warn(E_UNSUPPORTED, // LCOV_EXCL_LINE // impossible? @@ -219,6 +235,7 @@ private: //-------- Uniops bool expandWide(AstNodeAssign* nodep, AstVarRef* rhsp) { UINFO(8, " Wordize ASSIGN(VARREF) " << nodep << endl); + if (!doExpand(nodep)) return false; for (int w = 0; w < nodep->widthWords(); w++) { addWordAssign(nodep, w, newAstWordSelClone(rhsp, w)); } @@ -228,6 +245,7 @@ private: UINFO(8, " Wordize ASSIGN(ARRAYSEL) " << nodep << endl); UASSERT_OBJ(!VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType), nodep, "ArraySel with unpacked arrays should have been removed in V3Slice"); + if (!doExpand(nodep)) return false; for (int w = 0; w < nodep->widthWords(); w++) { addWordAssign(nodep, w, newAstWordSelClone(rhsp, w)); } @@ -236,6 +254,7 @@ private: bool expandWide(AstNodeAssign* nodep, AstNot* rhsp) { UINFO(8, " Wordize ASSIGN(NOT) " << nodep << endl); // -> {for each_word{ ASSIGN(WORDSEL(wide,#),NOT(WORDSEL(lhs,#))) }} + if (!doExpand(nodep)) return false; for (int w = 0; w < nodep->widthWords(); w++) { addWordAssign(nodep, w, new AstNot(rhsp->fileline(), newAstWordSelClone(rhsp->lhsp(), w))); @@ -245,6 +264,7 @@ private: //-------- Biops bool expandWide(AstNodeAssign* nodep, AstAnd* rhsp) { UINFO(8, " Wordize ASSIGN(AND) " << nodep << endl); + if (!doExpand(nodep)) return false; for (int w = 0; w < nodep->widthWords(); w++) { addWordAssign(nodep, w, new AstAnd(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w), @@ -254,6 +274,7 @@ private: } bool expandWide(AstNodeAssign* nodep, AstOr* rhsp) { UINFO(8, " Wordize ASSIGN(OR) " << nodep << endl); + if (!doExpand(nodep)) return false; for (int w = 0; w < nodep->widthWords(); w++) { addWordAssign(nodep, w, new AstOr(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w), @@ -263,6 +284,7 @@ private: } bool expandWide(AstNodeAssign* nodep, AstXor* rhsp) { UINFO(8, " Wordize ASSIGN(XOR) " << nodep << endl); + if (!doExpand(nodep)) return false; for (int w = 0; w < nodep->widthWords(); w++) { addWordAssign(nodep, w, new AstXor(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w), @@ -273,6 +295,7 @@ private: //-------- Triops bool expandWide(AstNodeAssign* nodep, AstNodeCond* rhsp) { UINFO(8, " Wordize ASSIGN(COND) " << nodep << endl); + if (!doExpand(nodep)) return false; for (int w = 0; w < nodep->widthWords(); w++) { addWordAssign(nodep, w, new AstCond(nodep->fileline(), rhsp->condp()->cloneTree(true), @@ -417,6 +440,7 @@ private: bool expandWide(AstNodeAssign* nodep, AstSel* rhsp) { UASSERT_OBJ(nodep->widthMin() == rhsp->widthConst(), nodep, "Width mismatch"); + if (!doExpand(nodep)) return false; if (VN_IS(rhsp->lsbp(), Const) && VL_BITBIT_E(rhsp->lsbConst()) == 0) { int lsb = rhsp->lsbConst(); UINFO(8, " Wordize ASSIGN(SEL,align) " << nodep << endl); @@ -647,6 +671,7 @@ private: } bool expandWide(AstNodeAssign* nodep, AstConcat* rhsp) { UINFO(8, " Wordize ASSIGN(CONCAT) " << nodep << endl); + if (!doExpand(rhsp)) return false; // Lhs or Rhs may be word, long, or quad. // newAstWordSelClone nicely abstracts the difference. int rhsshift = rhsp->rhsp()->widthMin(); @@ -701,6 +726,7 @@ private: } bool expandWide(AstNodeAssign* nodep, AstReplicate* rhsp) { UINFO(8, " Wordize ASSIGN(REPLICATE) " << nodep << endl); + if (!doExpand(rhsp)) return false; AstNode* lhsp = rhsp->lhsp(); int lhswidth = lhsp->widthMin(); const AstConst* constp = VN_CAST(rhsp->rhsp(), Const); @@ -857,6 +883,7 @@ private: iterateChildren(nodep); bool did = false; if (nodep->isWide() && ((VN_IS(nodep->lhsp(), VarRef) || VN_IS(nodep->lhsp(), ArraySel))) + && ((VN_IS(nodep->lhsp(), VarRef) || VN_IS(nodep->lhsp(), ArraySel))) && !AstVar::scVarRecurse(nodep->lhsp()) // Need special function for SC && !AstVar::scVarRecurse(nodep->rhsp())) { if (AstConst* rhsp = VN_CAST(nodep->rhsp(), Const)) { @@ -897,7 +924,11 @@ private: public: // CONSTRUCTORS explicit ExpandVisitor(AstNetlist* nodep) { iterate(nodep); } - virtual ~ExpandVisitor() override = default; + virtual ~ExpandVisitor() override { + V3Stats::addStat("Optimizations, expand wides", m_statWides); + V3Stats::addStat("Optimizations, expand wide words", m_statWideWords); + V3Stats::addStat("Optimizations, expand limited", m_statWideLimited); + } }; //---------------------------------------------------------------------- diff --git a/src/V3Options.cpp b/src/V3Options.cpp index d292b4096..4cb886835 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1060,6 +1060,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char DECL_OPTION("-E", Set, &m_preprocOnly); DECL_OPTION("-error-limit", CbVal, static_cast(&V3Error::errorLimit)); DECL_OPTION("-exe", OnOff, &m_exe); + DECL_OPTION("-expand-limit", CbVal, + [this, fl](const char* valp) { m_expandLimit = std::atoi(valp); }); DECL_OPTION("-F", CbVal, [this, fl, &optdir](const char* valp) { parseOptsFile(fl, parseFileArg(optdir, valp), true); diff --git a/src/V3Options.h b/src/V3Options.h index 4d3bd8612..dff879acf 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -285,6 +285,7 @@ private: int m_convergeLimit = 100; // main switch: --converge-limit int m_coverageMaxWidth = 256; // main switch: --coverage-max-width int m_dumpTree = 0; // main switch: --dump-tree + int m_expandLimit = 64; // main switch: --expand-limit int m_gateStmts = 100; // main switch: --gate-stmts int m_ifDepth = 0; // main switch: --if-depth int m_inlineMult = 2000; // main switch: --inline-mult @@ -482,6 +483,7 @@ public: int coverageMaxWidth() const { return m_coverageMaxWidth; } int dumpTree() const { return m_dumpTree; } bool dumpTreeAddrids() const { return m_dumpTreeAddrids; } + int expandLimit() const { return m_expandLimit; } int gateStmts() const { return m_gateStmts; } int ifDepth() const { return m_ifDepth; } int inlineMult() const { return m_inlineMult; } diff --git a/test_regress/t/t_flag_expand_limit.pl b/test_regress/t/t_flag_expand_limit.pl new file mode 100755 index 000000000..6270f76ca --- /dev/null +++ b/test_regress/t/t_flag_expand_limit.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2008 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); + +compile( + verilator_flags2 => ['--expand-limit 1 --stats'], + ); + +file_grep($Self->{stats}, qr/Optimizations, expand limited\s+(\d+)/i, 4); + +ok(1); +1; diff --git a/test_regress/t/t_flag_expand_limit.v b/test_regress/t/t_flag_expand_limit.v new file mode 100644 index 000000000..0640ce15a --- /dev/null +++ b/test_regress/t/t_flag_expand_limit.v @@ -0,0 +1,27 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2021 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +// issue3005 + +module t #(parameter NV = 2000) + ( + input a, + input w1, + input [127:0] w2, + output [ 31:0] o, + + input [319:0] bi, + output [319:0] bo + ); + + // verilator lint_off WIDTH + wire [NV-1:0] d = a ? NV'(0) : {NV{w2}}; + // verilator lint_on WIDTH + assign o = d[31:0]; + + assign bo = ~bi; + +endmodule diff --git a/test_regress/t/t_reloop_cam.pl b/test_regress/t/t_reloop_cam.pl index e6d8da916..7046308bc 100755 --- a/test_regress/t/t_reloop_cam.pl +++ b/test_regress/t/t_reloop_cam.pl @@ -12,6 +12,7 @@ scenarios(simulator => 1); compile( verilator_flags2 => ["-unroll-count 1024", + "--expand-limit 1024", $Self->wno_unopthreads_for_few_cores(), "--stats"], );