diff --git a/Changes b/Changes index 529ed7825..32636f486 100644 --- a/Changes +++ b/Changes @@ -19,6 +19,8 @@ The contributors that suggested a given feature are shown in []. Thanks! **** Fix class constructor error on assignments to const. +**** Fix splitting eval functions with --output-split-cfuncs (#2368). [Geza Lore] + * Verilator 4.040 2020-08-15 diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index 7563ead35..f652de107 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -160,6 +160,43 @@ private: m_lastSenp = nullptr; m_lastIfp = nullptr; } + void splitCheck(AstCFunc* ofuncp) { + if (!v3Global.opt.outputSplitCFuncs() || !ofuncp->stmtsp()) return; + if (EmitCBaseCounterVisitor(ofuncp->stmtsp()).count() < v3Global.opt.outputSplitCFuncs()) + return; + + int funcnum = 0; + int func_stmts = 0; + AstCFunc* funcp = nullptr; + + // Unlink all statements, then add item by item to new sub-functions + AstBegin* tempp = new AstBegin{ofuncp->fileline(), "[EditWrapper]", + ofuncp->stmtsp()->unlinkFrBackWithNext()}; + if (ofuncp->finalsp()) tempp->addStmtsp(ofuncp->finalsp()->unlinkFrBackWithNext()); + while (tempp->stmtsp()) { + AstNode* itemp = tempp->stmtsp()->unlinkFrBack(); + int stmts = EmitCBaseCounterVisitor(itemp).count(); + if (!funcp || (func_stmts + stmts) > v3Global.opt.outputSplitCFuncs()) { + // Make a new function + funcp = new AstCFunc{ofuncp->fileline(), ofuncp->name() + cvtToStr(++funcnum), + m_topScopep->scopep()}; + funcp->argTypes(EmitCBaseVisitor::symClassVar()); + funcp->dontCombine(true); + funcp->symProlog(true); + funcp->isStatic(true); + funcp->slow(ofuncp->slow()); + m_topScopep->scopep()->addActivep(funcp); + // + AstCCall* callp = new AstCCall{funcp->fileline(), funcp}; + callp->argTypes("vlSymsp"); + ofuncp->addStmtsp(callp); + func_stmts = 0; + } + funcp->addStmtsp(itemp); + func_stmts += stmts; + } + VL_DO_DANGLING(tempp->deleteTree(), tempp); + } // VISITORS virtual void visit(AstTopScope* nodep) override { @@ -172,28 +209,29 @@ private: AstNode::user1ClearTree(); // Make top functions { - AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval", m_scopep); + AstCFunc* funcp = new AstCFunc{nodep->fileline(), "_eval", m_topScopep->scopep()}; funcp->argTypes(EmitCBaseVisitor::symClassVar()); funcp->dontCombine(true); funcp->symProlog(true); funcp->isStatic(true); funcp->entryPoint(true); - m_scopep->addActivep(funcp); + m_topScopep->scopep()->addActivep(funcp); m_evalFuncp = funcp; } { - AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_initial", m_scopep); + AstCFunc* funcp + = new AstCFunc{nodep->fileline(), "_eval_initial", m_topScopep->scopep()}; funcp->argTypes(EmitCBaseVisitor::symClassVar()); funcp->dontCombine(true); funcp->slow(true); funcp->symProlog(true); funcp->isStatic(true); funcp->entryPoint(true); - m_scopep->addActivep(funcp); + m_topScopep->scopep()->addActivep(funcp); m_initFuncp = funcp; } { - AstCFunc* funcp = new AstCFunc(nodep->fileline(), "final", m_scopep); + AstCFunc* funcp = new AstCFunc{nodep->fileline(), "final", m_topScopep->scopep()}; funcp->skipDecl(true); funcp->dontCombine(true); funcp->slow(true); @@ -204,22 +242,29 @@ private: + " = this->__VlSymsp;\n")); funcp->addInitsp( new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign() + "\n")); - m_scopep->addActivep(funcp); + m_topScopep->scopep()->addActivep(funcp); m_finalFuncp = funcp; } { - AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_settle", m_scopep); + AstCFunc* funcp + = new AstCFunc{nodep->fileline(), "_eval_settle", m_topScopep->scopep()}; funcp->argTypes(EmitCBaseVisitor::symClassVar()); funcp->dontCombine(true); funcp->slow(true); funcp->isStatic(true); funcp->symProlog(true); funcp->entryPoint(true); - m_scopep->addActivep(funcp); + m_topScopep->scopep()->addActivep(funcp); m_settleFuncp = funcp; } // Process the activates iterateChildren(nodep); + UINFO(4, " TOPSCOPE iter done " << nodep << endl); + // Split large functions + splitCheck(m_evalFuncp); + splitCheck(m_initFuncp); + splitCheck(m_finalFuncp); + splitCheck(m_settleFuncp); // Done, clear so we can detect errors UINFO(4, " TOPSCOPEDONE " << nodep << endl); clearLastSen(); diff --git a/test_regress/t/t_flag_csplit_eval.pl b/test_regress/t/t_flag_csplit_eval.pl new file mode 100755 index 000000000..c2012b9f4 --- /dev/null +++ b/test_regress/t/t_flag_csplit_eval.pl @@ -0,0 +1,40 @@ +#!/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 + +sub check_evals { + my $got = 0; + foreach my $file (glob("$Self->{obj_dir}/*.cpp")) { + my $fh = IO::File->new("<$file"); + local $/; undef $/; + my $wholefile = <$fh>; + + if ($wholefile =~ /::_eval[0-9]+/) { + ++$got; + } + } + $got >= 3 or error("Too few _eval functions found: $got"); +} + + +scenarios(vlt_all => 1); + +compile( + v_flags2 => ["--output-split 1 --output-split-cfuncs 1 --exe ../$Self->{main_filename}"], +# verilator_make_gmake => 0, + ); + +# Very slow to compile, so generally skip it +execute( + check_finished => 1, + ); + +check_evals(); +ok(1); +1; diff --git a/test_regress/t/t_flag_csplit_eval.v b/test_regress/t/t_flag_csplit_eval.v new file mode 100644 index 000000000..685fc7612 --- /dev/null +++ b/test_regress/t/t_flag_csplit_eval.v @@ -0,0 +1,31 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2005 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Outputs + cnt0, cnt1, + // Inputs + clk, clk1 + ); + input clk; + input clk1; + + output int cnt0; + output int cnt1; + + always @ (posedge clk) cnt0 <= cnt0 + 1; + always @ (posedge clk1) cnt1 <= cnt1 + 1; + + final if (cnt0 == 0) $stop; + final if (cnt1 != 0) $stop; + + always_comb begin + if (cnt0==99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule