//************************************************************************* // DESCRIPTION: Verilator: main() // // Code available from: http://www.veripool.org/verilator // // AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli // //************************************************************************* // // Copyright 2003-2010 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. // // Verilator is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //************************************************************************* #include "V3Global.h" #include "V3Ast.h" #include #include #include "V3Active.h" #include "V3ActiveTop.h" #include "V3Assert.h" #include "V3AssertPre.h" #include "V3Begin.h" #include "V3Branch.h" #include "V3Case.h" #include "V3Cast.h" #include "V3Changed.h" #include "V3Clean.h" #include "V3ClkGater.h" #include "V3Clock.h" #include "V3Combine.h" #include "V3Const.h" #include "V3Coverage.h" #include "V3CoverageJoin.h" #include "V3Dead.h" #include "V3Delayed.h" #include "V3Depth.h" #include "V3DepthBlock.h" #include "V3Descope.h" #include "V3EmitC.h" #include "V3EmitMk.h" #include "V3EmitV.h" #include "V3Expand.h" #include "V3File.h" #include "V3Cdc.h" #include "V3Gate.h" #include "V3Graph.h" #include "V3GenClk.h" #include "V3Inline.h" #include "V3Inst.h" #include "V3Life.h" #include "V3LifePost.h" #include "V3Link.h" #include "V3LinkCells.h" #include "V3LinkDot.h" #include "V3LinkLevel.h" #include "V3LinkParse.h" #include "V3LinkLValue.h" #include "V3LinkResolve.h" #include "V3Localize.h" #include "V3Name.h" #include "V3Order.h" #include "V3Param.h" #include "V3Parse.h" #include "V3PreShell.h" #include "V3Premit.h" #include "V3Scope.h" #include "V3Signed.h" #include "V3Slice.h" #include "V3Split.h" #include "V3SplitAs.h" #include "V3Stats.h" #include "V3Subst.h" #include "V3Table.h" #include "V3Task.h" #include "V3Trace.h" #include "V3TraceDecl.h" #include "V3Tristate.h" #include "V3Unknown.h" #include "V3Unroll.h" #include "V3Width.h" V3Global v3Global; //###################################################################### // V3 Class -- top level void V3Global::readFiles() { // NODE STATE // AstNode::user4p() // V3SymTable* Package and typedef symbol names AstUser4InUse inuser4; V3Parse parser (v3Global.rootp()); // Read top module for (V3StringList::const_iterator it = v3Global.opt.vFiles().begin(); it != v3Global.opt.vFiles().end(); ++it) { string filename = *it; parser.parseFile(new FileLine("CommandLine",0), filename, false); } // Read libraries // To be compatible with other simulators, // this needs to be done after the top file is read for (V3StringSet::const_iterator it = v3Global.opt.libraryFiles().begin(); it != v3Global.opt.libraryFiles().end(); ++it) { string filename = *it; parser.parseFile(new FileLine("CommandLine",0), filename, true); } //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("parse.tree")); V3Error::abortIfErrors(); if (!v3Global.opt.preprocOnly()) { // Resolve all modules cells refer to V3LinkCells::link(v3Global.rootp()); } } //###################################################################### void process () { // Sort modules by level so later algorithms don't need to care V3LinkLevel::modSortByLevel(); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("cells.tree")); V3Error::abortIfErrors(); // Convert parseref's to varrefs, and other directly post parsing fixups V3LinkParse::linkParse(v3Global.rootp()); // Cross-link signal names V3Link::link(v3Global.rootp()); // Cross-link dotted hierarchical references V3LinkDot::linkDotPrearrayed(v3Global.rootp()); // Correct state we couldn't know at parse time, repair SEL's V3LinkResolve::linkResolve(v3Global.rootp()); // Set Lvalue's in variable refs V3LinkLValue::linkLValue(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("link.tree")); V3Error::abortIfErrors(); if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "Link"); // Remove parameters by cloning modules to de-parameterized versions // This requires some width calculations and constant propagation V3Param::param(v3Global.rootp()); V3LinkDot::linkDotPrearrayed(v3Global.rootp()); // Cleanup as made new modules v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param.tree")); V3Error::abortIfErrors(); // Remove any modules that were parameterized and are no longer referenced. V3Dead::deadifyAll(v3Global.rootp(), false); //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("dead.tree")); v3Global.checkTree(); // Calculate and check widths, edit tree to TRUNC/EXTRACT any width mismatches V3Width::width(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("width.tree")); // Compute signed/unsigned V3Signed::signedAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("signed.tree")); V3Error::abortIfErrors(); // Commit to the widths we've chosen; Make widthMin==width V3Width::widthCommit(v3Global.rootp()); v3Global.assertWidthsSame(true); //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("widthcommit.tree")); // Coverage insertion // Before we do dead code elimination and inlining, or we'll loose it. if (v3Global.opt.coverage()) { V3Coverage::coverage(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("coverage.tree")); } // Assertion insertion // After we've added block coverage, but before other nasty transforms V3AssertPre::assertPreAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("assertpre.tree")); // V3Assert::assertAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("assert.tree")); // Add top level wrapper with instance pointing to old top // Move packages to under new top // Must do this after we know the width of any parameters // We also do it after coverage/assertion insertion so we don't 'cover' the top level. V3LinkLevel::wrapTop(v3Global.rootp()); // Propagate constants into expressions V3Const::constifyAllLint(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); // Expand Inouts V3Tristate::inoutAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("inouts.tree")); // Remove cell arrays (must be between V3Width and scoping) V3Inst::dearrayAll(v3Global.rootp()); //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("dearray.tree")); // Expand inouts, stage 2 // Also simplify pin connections to always be AssignWs in prep for V3Unknown V3Tristate::tristateAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("tristate.tree")); // Task inlining & pushing BEGINs names to variables/cells // Begin processing must be after Param, before module inlining V3Begin::debeginAll(v3Global.rootp()); // Flatten cell names, before inliner v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("begin.tree")); // Move assignments from X into MODULE temps. // (Before flattening, so each new X variable is shared between all scopes of that module.) V3Unknown::unknownAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("unknown.tree")); // Module inlining // Cannot remove dead variables after this, as alias information for final // V3Scope's V3LinkDot is in the AstVar. if (v3Global.opt.oInline()) { V3Inline::inlineAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("inline.tree")); V3LinkDot::linkDotArrayed(v3Global.rootp()); // Cleanup as made new modules //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkdot.tree")); } //--PRE-FLAT OPTIMIZATIONS------------------ // Initial const/dead to reduce work for ordering code V3Const::constifyAll(v3Global.rootp()); v3Global.checkTree(); V3Dead::deadifyAll(v3Global.rootp(), false); v3Global.checkTree(); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); V3Error::abortIfErrors(); //--FLATTENING--------------- // We're going to flatten the hierarchy, so as many optimizations that // can be done as possible should be before this.... // Convert instantiations to wassigns and always blocks V3Inst::instAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("inst.tree")); // Inst may have made lots of concats; fix them V3Const::constifyAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); // Flatten hierarchy, creating a SCOPE for each module's usage as a cell V3Scope::scopeAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("scope.tree")); V3LinkDot::linkDotScope(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkdot.tree")); //--SCOPE BASED OPTIMIZATIONS-------------- // Cleanup V3Const::constifyAll(v3Global.rootp()); V3Dead::deadifyAll(v3Global.rootp(), false); v3Global.checkTree(); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); // Inline all tasks V3Task::taskAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("task.tree")); // Add __PVT's // After V3Task so task internal variables will get renamed V3Name::nameAll(v3Global.rootp()); //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("name.tree")); // Loop unrolling & convert FORs to WHILEs V3Unroll::unrollAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("unroll.tree")); // Expand slices of arrays V3Slice::sliceAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("slices.tree")); // Convert case statements to if() blocks. Must be after V3Unknown V3Case::caseAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("case.tree")); // Push constants across variables and remove redundant assignments V3Const::constifyAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); if (v3Global.opt.oLife()) { V3Life::lifeAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("life.tree")); } // Make large low-fanin logic blocks into lookup tables // This should probably be done much later, once we have common logic elimination. if (!v3Global.opt.lintOnly() && v3Global.opt.oTable()) { V3Table::tableAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("table.tree")); } // Cleanup V3Const::constifyAll(v3Global.rootp()); V3Dead::deadifyAll(v3Global.rootp(), false); v3Global.checkTree(); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); // Detect clock enables and mode into sensitives, and split always based on clocks // (so this is a good prelude to splitAlways.) if (v3Global.opt.oFlopGater()) { V3ClkGater::clkGaterAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("clkgater.tree")); } // Move assignments/sensitives into a SBLOCK for each unique sensitivity list // (May convert some ALWAYS to combo blocks, so should be before V3Gate step.) V3Active::activeAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("active.tree")); // Split single ALWAYS blocks into multiple blocks for better ordering chances if (v3Global.opt.oSplit()) { V3Split::splitAlwaysAll(v3Global.rootp()); //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("split.tree")); } V3SplitAs::splitAsAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("splitas.tree")); // Create tracing sample points, before we start eliminating signals if (v3Global.opt.trace()) { V3TraceDecl::traceDeclAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("tracedecl.tree")); } // Gate-based logic elimination; eliminate signals and push constant across cell boundaries // Instant propagation makes lots-o-constant reduction possibilities. if (v3Global.opt.oGate()) { V3Gate::gateAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("gate.tree")); // V3Gate calls constant propagation itself. } else { v3info("Command Line disabled gate optimization with -Og/-O0. This may cause ordering problems."); } // Combine COVERINCs with duplicate terms if (v3Global.opt.coverage()) { V3CoverageJoin::coverageJoin(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("coveragejoin.tree")); } // Remove unused vars V3Const::constifyAll(v3Global.rootp()); V3Dead::deadifyAll(v3Global.rootp(), true); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); // Clock domain crossing analysis if (v3Global.opt.cdc()) { V3Cdc::cdcAll(v3Global.rootp()); V3Error::abortIfErrors(); return; } // Reorder assignments in pipelined blocks if (v3Global.opt.oReorder()) { V3Split::splitReorderAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("reorder.tree")); } // Create delayed assignments // This creates lots of duplicate ACTIVES so ActiveTop needs to be after this step V3Delayed::delayedAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("delayed.tree")); // Make Active's on the top level // Differs from V3Active, because identical clocks may be pushed down to a module and now be identical V3ActiveTop::activeTopAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("activetop.tree")); if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "PreOrder"); // Order the code; form SBLOCKs and BLOCKCALLs V3Order::orderAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("order.tree")); #ifndef NEW_ORDERING // Change generated clocks to look at delayed signals V3GenClk::genClkAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("genclk.tree")); #endif // Convert sense lists into IF statements. V3Clock::clockAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("clock.tree")); // Cleanup any dly vars or other temps that are simple assignments // Life must be done before Subst, as it assumes each CFunc under _eval is called only once. if (v3Global.opt.oLife()) { V3Const::constifyAll(v3Global.rootp()); V3Life::lifeAll(v3Global.rootp()); } if (v3Global.opt.oLifePost()) { V3LifePost::lifepostAll(v3Global.rootp()); } if (v3Global.opt.oLife() || v3Global.opt.oLifePost()) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("life.tree")); } // Remove unused vars V3Const::constifyAll(v3Global.rootp()); V3Dead::deadifyAll(v3Global.rootp(), true); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); #ifndef NEW_ORDERING // Detect change loop V3Changed::changedAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("changed.tree")); #endif // Create tracing logic, since we ripped out some signals the user might want to trace // Note past this point, we presume traced variables won't move between CFuncs // (It's OK if untraced temporaries move around, or vars "effectively" activate the same way.) if (v3Global.opt.trace()) { V3Trace::traceAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("trace.tree")); } if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "Scoped"); // Remove scopes; make varrefs/funccalls relative to current module V3Descope::descopeAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("descope.tree")); //--MODULE OPTIMIZATIONS-------------- // Split deep blocks to appease MSVC++. Must be before Localize. if (!v3Global.opt.lintOnly() && v3Global.opt.compLimitBlocks()) { V3DepthBlock::depthBlockAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("deepblock.tree")); } // Move BLOCKTEMPS from class to local variables if (v3Global.opt.oLocalize()) { V3Localize::localizeAll(v3Global.rootp()); } // Icache packing; combine common code in each module's functions into subroutines if (v3Global.opt.oCombine()) { V3Combine::combineAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("combine.tree")); } V3Error::abortIfErrors(); //--GENERATION------------------ // Remove unused vars V3Const::constifyAll(v3Global.rootp()); V3Dead::deadifyAll(v3Global.rootp(), true); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); // Here down, widthMin() is the Verilog width, and width() is the C++ width // Bits between widthMin() and width() are irrelevant, but may be non zero. v3Global.assertWidthsSame(false); // Make all operations a multiple of 32 bits V3Clean::cleanAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("clean.tree")); // Move wide constants to BLOCK temps. V3Premit::premitAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("premit.tree")); // Expand macros and wide operators into C++ primitives if (v3Global.opt.oExpand()) { V3Expand::expandAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("expand.tree")); } // Propagate constants across WORDSEL arrayed temporaries if (v3Global.opt.oSubst()) { // Constant folding of expanded stuff V3Const::constifyCpp(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); V3Subst::substituteAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("subst.tree")); } if (v3Global.opt.oSubstConst()) { // Constant folding of substitutions V3Const::constifyCpp(v3Global.rootp()); V3Dead::deadifyAll(v3Global.rootp(), true); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("dead.tree")); } if (!v3Global.opt.lintOnly()) { // Fix very deep expressions // Mark evaluation functions as member functions, if needed. V3Depth::depthAll(v3Global.rootp()); //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("depth.tree")); // Branch prediction V3Branch::branchAll(v3Global.rootp()); // Add C casts when longs need to become long-long and vice-versa // Note depth may insert something needing a cast, so this must be last. V3Cast::castAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("cast.tree")); } V3Error::abortIfErrors(); // Output the text if (!v3Global.opt.lintOnly()) { // emitcInlines is first, as it may set needHInlines which other emitters read V3EmitC::emitcInlines(); V3EmitC::emitcSyms(); V3EmitC::emitcTrace(); } // Unfortunately we have some lint checks in emitc. V3EmitC::emitc(); // Statistics if (v3Global.opt.stats()) { V3Stats::statsFinalAll(v3Global.rootp()); V3Stats::statsReport(); } if (!v3Global.opt.lintOnly()) { // Makefile must be after all other emitters V3EmitMk::emitmk(v3Global.rootp()); } // Note early return above when opt.cdc() } //###################################################################### int main(int argc, char** argv, char** env) { // General initialization ios::sync_with_stdio(); time_t randseed; time(&randseed); srand( (int) randseed); // Preprocessor // Before command parsing so we can handle -Ds on command line. V3PreShell::boot(env); // Command option parsing v3Global.opt.bin(argv[0]); string argString = V3Options::argString(argc-1, argv+1); v3Global.opt.parseOpts(new FileLine("COMMAND_LINE",0), argc-1, argv+1); if (v3Global.opt.coverage() && !v3Global.opt.systemPerl() && !v3Global.opt.lintOnly()) { v3fatal("Unsupported: Coverage analysis requires --sp output."); } if (!v3Global.opt.outFormatOk() && !v3Global.opt.preprocOnly() && !v3Global.opt.lintOnly() && !v3Global.opt.cdc()) { v3fatal("verilator: Need --cc, --sc, --sp, --cdc, --lint-only or --E option"); } // Check environment V3Options::getenvSYSTEMC(); V3Options::getenvSYSTEMC_ARCH(); V3Options::getenvSYSTEMPERL(); V3Options::getenvSYSTEMPERL_INCLUDE(); V3Error::abortIfErrors(); // Can we skip doing everything if times are ok? V3File::addSrcDepend(v3Global.opt.bin()); if (v3Global.opt.skipIdentical() && !v3Global.opt.preprocOnly() && !v3Global.opt.lintOnly() && !v3Global.opt.cdc() && V3File::checkTimes(v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__verFiles.dat", argString)) { UINFO(1,"--skip-identical: No change to any source files, exiting\n"); exit(0); } // Internal tests (after option parsing as need debug() setting) V3Graph::test(); //--FRONTEND------------------ // Cleanup V3Options::unlinkRegexp(v3Global.opt.makeDir(), v3Global.opt.prefix()+"_*.tree"); V3Options::unlinkRegexp(v3Global.opt.makeDir(), v3Global.opt.prefix()+"_*.dot"); V3Options::unlinkRegexp(v3Global.opt.makeDir(), v3Global.opt.prefix()+"_*.txt"); // Read first filename v3Global.readFiles(); // Link, etc, if needed if (!v3Global.opt.preprocOnly()) { process(); } // Final steps v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree",99)); V3Error::abortIfWarnings(); if (!v3Global.opt.lintOnly() && !v3Global.opt.cdc() && v3Global.opt.makeDepend()) { V3File::writeDepend(v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__ver.d"); } if (!v3Global.opt.lintOnly() && !v3Global.opt.cdc() && (v3Global.opt.skipIdentical() || v3Global.opt.makeDepend())) { V3File::writeTimes(v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__verFiles.dat", argString); } #ifdef VL_LEAK_CHECKS // Cleanup memory for valgrind leak analysis v3Global.clear(); #endif FileLine::deleteAllRemaining(); UINFO(1,"Done, Exiting...\n"); }