diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index f2e032c67..2d4fcfae9 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -182,6 +182,7 @@ RAW_OBJS = \ V3EmitCSyms.o \ V3EmitMk.o \ V3EmitV.o \ + V3EmitXml.o \ V3Error.o \ V3Expand.o \ V3File.o \ diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp new file mode 100644 index 000000000..035923d51 --- /dev/null +++ b/src/V3EmitXml.cpp @@ -0,0 +1,196 @@ +//************************************************************************* +// DESCRIPTION: Verilator: Emit Verilog from tree +// +// Code available from: http://www.veripool.org/verilator +// +// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli +// +//************************************************************************* +// +// Copyright 2004-2012 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 "config_build.h" +#include "verilatedos.h" +#include +#include +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3EmitXml.h" +#include "V3EmitCBase.h" + +//###################################################################### +// Emit statements and math operators + +class EmitXmlFileVisitor : public EmitCBaseVisitor { + // MEMBERS + V3OutFile* m_ofp; + + // METHODS + static int debug() { + static int level = -1; + if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); + return level; + } + + // Outfile methods + V3OutFile* ofp() const { return m_ofp; } + virtual void puts(const string& str) { ofp()->puts(str); } + virtual void putbs(const string& str) { ofp()->putbs(str); } + virtual void putfs(AstNode*, const string& str) { putbs(str); } + virtual void putqs(AstNode*, const string& str) { putbs(str); } + virtual void putsNoTracking(const string& str) { ofp()->putsNoTracking(str); } + virtual void putsQuoted(const string& str) { + // Quote \ and " for use inside C programs + // Don't use to quote a filename for #include - #include doesn't \ escape. + // Duplicate in V3File - here so we can print to string + putsNoTracking("\""); + putsNoTracking(AstNode::quoteName(str)); + putsNoTracking("\""); + } + + // XML methods + void outputTag(AstNode* nodep, string tag) { + if (tag=="") tag = V3Options::downcase(nodep->typeName()); + puts("<"+tag+" "+nodep->fileline()->xml()); + if (nodep->name()!="") { puts(" name="); putsQuoted(nodep->prettyName()); } + } + void outputChildrenEnd(AstNode* nodep, string tag) { + if (tag=="") tag = V3Options::downcase(nodep->typeName()); + if (nodep->op1p() || nodep->op2p() || nodep->op3p() || nodep->op4p()) { + puts(">\n"); + nodep->iterateChildren(*this); + puts("\n"); + } else { + puts("/>\n"); + } + } + + // VISITORS + virtual void visit(AstNetlist* nodep, AstNUser*) { + puts("\n"); + nodep->iterateChildren(*this); + puts("\n"); + } + virtual void visit(AstNodeModule* nodep, AstNUser*) { + outputTag(nodep, ""); + if (nodep->level()==1 || nodep->level()==2) // ==2 because we don't add wrapper when in XML mode + puts(" topModule=\"1\""); // IEEE vpiTopModule + outputChildrenEnd(nodep, ""); + } + virtual void visit(AstCell* nodep, AstNUser*) { + outputTag(nodep, "instance"); // IEEE: vpiInstance + puts(" defName="); putsQuoted(nodep->modName()); // IEEE vpiDefName + outputChildrenEnd(nodep, "instance"); + } + virtual void visit(AstPin* nodep, AstNUser*) { + // What we call a pin in verilator is a port in the IEEE spec. + outputTag(nodep, "port"); // IEEE: vpiPort + if (nodep->modVarp()->isInOnly()) + puts(" direction=\"in\""); + else if (nodep->modVarp()->isOutOnly()) + puts(" direction=\"out\""); + else puts(" direction=\"inout\""); + puts(" portIndex=\""+cvtToStr(nodep->pinNum())+"\""); // IEEE: vpiPortIndex + // Children includes vpiHighConn and vpiLowConn; we don't support port bits (yet?) + outputChildrenEnd(nodep, "port"); + } + virtual void visit(AstAssignW* nodep, AstNUser*) { + outputTag(nodep, "contAssign"); // IEEE: vpiContAssign + outputChildrenEnd(nodep, "contAssign"); + } + + // Data types + virtual void visit(AstBasicDType* nodep, AstNUser*) { + outputTag(nodep, "basicDType "); + if (nodep->isRanged()) { + puts(" left=\""+cvtToStr(nodep->left())+"\""); + puts(" right=\""+cvtToStr(nodep->right())+"\""); + } + puts("/>\n"); + } + + // Default + virtual void visit(AstNode* nodep, AstNUser*) { + outputTag(nodep, ""); + outputChildrenEnd(nodep, ""); + } +public: + EmitXmlFileVisitor(AstNode* nodep, V3OutFile* ofp) { + m_ofp = ofp; + nodep->accept(*this); + } + virtual ~EmitXmlFileVisitor() {} +}; + +//###################################################################### +// Emit to a stream (perhaps stringstream) + +class EmitXmlPrefixedFormatter : public V3OutFormatter { + ostream& m_os; + string m_prefix; // What to print at beginning of each line + int m_flWidth; // Padding of fileline + int m_column; // Rough location; need just zero or non-zero + FileLine* m_prefixFl; + // METHODS + virtual void putcOutput(char chr) { + if (chr == '\n') { + m_column = 0; + m_os<ascii()+":"; + m_os<ascii().length()+1)); + m_os<<" "; + m_os<fileline(); // NETLIST's fileline instead of NULL to avoid NULL checks + } + virtual ~EmitXmlPrefixedFormatter() {} +}; + +//###################################################################### +// EmitXml class functions + +void V3EmitXml::emitxml() { + UINFO(2,__FUNCTION__<<": "<\n"); + of.puts("\n"); + { + stringstream sstr; + FileLine::fileNameNumMapDumpXml(sstr); + of.puts(sstr.str()); + } + EmitXmlFileVisitor visitor (v3Global.rootp(), &of); + of.puts("\n"); +} diff --git a/src/V3EmitXml.h b/src/V3EmitXml.h new file mode 100644 index 000000000..7548d72f8 --- /dev/null +++ b/src/V3EmitXml.h @@ -0,0 +1,37 @@ +//-*- C++ -*- +//************************************************************************* +// DESCRIPTION: Verilator: Emit XML code +// +// Code available from: http://www.veripool.org/verilator +// +// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli +// +//************************************************************************* +// +// Copyright 2003-2012 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. +// +//************************************************************************* + +#ifndef _V3EMITXML_H_ +#define _V3EMITXML_H_ 1 +#include "config_build.h" +#include "verilatedos.h" +#include "V3Error.h" +#include "V3Ast.h" + +//============================================================================ + +class V3EmitXml { +public: + static void emitxml(); +}; + +#endif // Guard diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 2b25d48d1..4d0ad735b 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -92,6 +92,14 @@ int FileLineSingleton::nameToNumber(const string& filename) { return num; } +void FileLineSingleton::fileNameNumMapDumpXml(ostream& os) { + os<<"\n"; + for (FileNameNumMap::const_iterator it = m_namemap.begin(); it != m_namemap.end(); ++it) { + os<<"second)<<"\" filename=\""<first<<"\"/>\n"; + } + os<<"\n"; +} + //###################################################################### // FileLine class functions diff --git a/src/V3Error.h b/src/V3Error.h index 3ca2ebc6d..7b5aab634 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -271,6 +271,7 @@ protected: int nameToNumber(const string& filename); const string numberToName(int filenameno) const { return m_names[filenameno]; } void clear() { m_namemap.clear(); m_names.clear(); } + void fileNameNumMapDumpXml(ostream& os); static const string filenameLetters(int fileno); }; @@ -327,6 +328,7 @@ public: const string filebasename () const; const string filebasenameNoExt () const; const string profileFuncname() const; + const string xml() const { return "fl=\""+filenameLetters()+cvtToStr(lineno())+"\""; } string lineDirectiveStrg(int enter_exit_level) const; void warnOn(V3ErrorCode code, bool flag) { m_warnOn.set(code,flag); } // Turn on/off warning messages on this line. void warnOff(V3ErrorCode code, bool flag) { warnOn(code,!flag); } @@ -352,6 +354,8 @@ public: defaultFileLine().warnOff(code, flag); } static bool globalWarnOff(const string& code, bool flag) { return defaultFileLine().warnOff(code, flag); } + static void fileNameNumMapDumpXml(ostream& os) { + singleton().fileNameNumMapDumpXml(os); } // METHODS - Called from netlist // Merge warning disables from another fileline diff --git a/src/V3Options.cpp b/src/V3Options.cpp index d9f3a82a1..d56bb6cdd 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -726,6 +726,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char else if ( !strcmp (sw, "-debug-sigsegv") ) { throwSigsegv(); } // Undocumented, see also --debug-abort else if ( !strcmp (sw, "-debug-fatalsrc") ) { v3fatalSrc("--debug-fatal-src"); } // Undocumented, see also --debug-abort else if ( onoff (sw, "-dump-tree", flag/*ref*/) ) { m_dumpTree = flag; } + else if ( onoff (sw, "-dump-tree-more", flag/*ref*/) ) { m_dumpTreeMore = m_dumpTree = flag; } // Undocumented else if ( onoff (sw, "-exe", flag/*ref*/) ) { m_exe = flag; } else if ( onoff (sw, "-ignc", flag/*ref*/) ) { m_ignc = flag; } else if ( onoff (sw, "-inhibit-sim", flag/*ref*/)){ m_inhibitSim = flag; } @@ -747,6 +748,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char else if ( onoff (sw, "-trace-dups", flag/*ref*/) ) { m_traceDups = flag; } else if ( onoff (sw, "-trace-underscore", flag/*ref*/) ) { m_traceUnderscore = flag; } else if ( onoff (sw, "-underline-zero", flag/*ref*/) ) { m_underlineZero = flag; } // Undocumented, old Verilator-2 + else if ( onoff (sw, "-xml-only", flag/*ref*/) ) { m_xmlOnly = flag; } // Undocumented, still experimental // Optimization else if ( !strncmp (sw, "-O", 2) ) { for (const char* cp=sw+strlen("-O"); *cp; ++cp) { @@ -1161,6 +1163,7 @@ V3Options::V3Options() { m_coverageUser = false; m_debugCheck = false; m_dumpTree = false; + m_dumpTreeMore = false; m_exe = false; m_ignc = false; m_l2Name = true; @@ -1182,6 +1185,7 @@ V3Options::V3Options() { m_traceDups = false; m_traceUnderscore = false; m_underlineZero = false; + m_xmlOnly = false; m_errorLimit = 50; m_ifDepth = 0; diff --git a/src/V3Options.h b/src/V3Options.h index 5510de0f0..f7f249947 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -107,6 +107,7 @@ class V3Options { bool m_coverageUser; // main switch: --coverage-func bool m_debugCheck; // main switch: --debug-check bool m_dumpTree; // main switch: --dump-tree + bool m_dumpTreeMore; // main switch: --dump-tree-more bool m_exe; // main switch: --exe bool m_ignc; // main switch: --ignc bool m_inhibitSim; // main switch: --inhibit-sim @@ -126,6 +127,7 @@ class V3Options { bool m_traceDups; // main switch: --trace-dups bool m_traceUnderscore;// main switch: --trace-underscore bool m_underlineZero;// main switch: --underline-zero; undocumented old Verilator 2 + bool m_xmlOnly; // main switch: --xml-netlist int m_errorLimit; // main switch: --error-limit int m_ifDepth; // main switch: --if-depth @@ -237,6 +239,7 @@ class V3Options { bool coverageUser() const { return m_coverageUser; } bool debugCheck() const { return m_debugCheck; } bool dumpTree() const { return m_dumpTree; } + bool dumpTreeMore() const { return m_dumpTreeMore; } bool exe() const { return m_exe; } bool trace() const { return m_trace; } bool traceDups() const { return m_traceDups; } @@ -252,6 +255,7 @@ class V3Options { bool lintOnly() const { return m_lintOnly; } bool ignc() const { return m_ignc; } bool inhibitSim() const { return m_inhibitSim; } + bool xmlOnly() const { return m_xmlOnly; } int errorLimit() const { return m_errorLimit; } int ifDepth() const { return m_ifDepth; } diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 8b86e0f20..f7ad6d393 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -48,6 +48,7 @@ #include "V3EmitC.h" #include "V3EmitMk.h" #include "V3EmitV.h" +#include "V3EmitXml.h" #include "V3Expand.h" #include "V3File.h" #include "V3Cdc.h" @@ -142,6 +143,8 @@ void V3Global::readFiles() { //###################################################################### void process () { + bool dumpMore = v3Global.opt.dumpTreeMore(); + // Sort modules by level so later algorithms don't need to care V3LinkLevel::modSortByLevel(); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("cells.tree")); @@ -149,14 +152,19 @@ void process () { // Convert parseref's to varrefs, and other directly post parsing fixups V3LinkParse::linkParse(v3Global.rootp()); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkparse.tree")); // Cross-link signal names V3Link::link(v3Global.rootp()); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkmain.tree")); // Cross-link dotted hierarchical references V3LinkDot::linkDotPrearrayed(v3Global.rootp()); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkdot.tree")); // Correct state we couldn't know at parse time, repair SEL's V3LinkResolve::linkResolve(v3Global.rootp()); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkresolve.tree")); // Set Lvalue's in variable refs V3LinkLValue::linkLValue(v3Global.rootp()); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linklvalue.tree")); // Convert return/continue/disable to jumps V3LinkJump::linkJump(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("link.tree")); @@ -173,7 +181,7 @@ void process () { // Remove any modules that were parameterized and are no longer referenced. V3Dead::deadifyAll(v3Global.rootp(), false); - //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("dead.tree")); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("dead.tree")); v3Global.checkTree(); // Calculate and check widths, edit tree to TRUNC/EXTRACT any width mismatches @@ -186,7 +194,7 @@ void process () { V3Width::widthCommit(v3Global.rootp()); v3Global.assertDTypesResolved(true); v3Global.assertWidthsMatch(true); - //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("widthcommit.tree")); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("widthcommit.tree")); // Coverage insertion // Before we do dead code elimination and inlining, or we'll lose it. @@ -212,321 +220,350 @@ void process () { 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()); + if (!v3Global.opt.xmlOnly()) { + // 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")); + if (!v3Global.opt.xmlOnly()) { + // 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")); + if (!v3Global.opt.xmlOnly()) { + // Remove cell arrays (must be between V3Width and scoping) + V3Inst::dearrayAll(v3Global.rootp()); + if (dumpMore) 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")); + if (!v3Global.opt.xmlOnly()) { + // 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")); + // 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")); + // 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")); + // 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 + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkdot.tree")); + } } //--PRE-FLAT OPTIMIZATIONS------------------ // Initial const/dead to reduce work for ordering code V3Const::constifyAll(v3Global.rootp()); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const_predad.tree")); v3Global.checkTree(); + V3Dead::deadifyAll(v3Global.rootp(), false); - v3Global.checkTree(); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); + v3Global.checkTree(); V3Error::abortIfErrors(); //--FLATTENING--------------- - // We're going to flatten the hierarchy, so as many optimizations that - // can be done as possible should be before this.... + if (!v3Global.opt.xmlOnly()) { + // 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")); + // 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")); + // 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")); + // 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()) { + if (!v3Global.opt.xmlOnly()) { + // Cleanup 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")); - } + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const_predad.tree")); + V3Dead::deadifyAll(v3Global.rootp(), false); + v3Global.checkTree(); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); - // Remove unused vars - V3Const::constifyAll(v3Global.rootp()); - V3Dead::deadifyAll(v3Global.rootp(), true); - 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()); + if (dumpMore) 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()); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const_predad.tree")); + 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()); + if (dumpMore) 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()); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const_predad.tree")); + 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 - // Detect change loop - V3Changed::changedAll(v3Global.rootp()); - v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("changed.tree")); + // Change generated clocks to look at delayed signals + V3GenClk::genClkAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("genclk.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")); + // 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()); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const_predad.tree")); + 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")); } - 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")); - } + if (!v3Global.opt.xmlOnly()) { + // 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()); - } + // Move BLOCKTEMPS from class to local variables + if (v3Global.opt.oLocalize()) { + V3Localize::localizeAll(v3Global.rootp()); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("localize.tree")); + } - // 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")); + // 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")); + if (!v3Global.opt.xmlOnly()) { + // Remove unused vars + V3Const::constifyAll(v3Global.rootp()); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const_predad.tree")); + 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.assertWidthsMatch(false); + // 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.assertWidthsMatch(false); - // Make all math operations either 8, 16, 32 or 64 bits - V3Clean::cleanAll(v3Global.rootp()); - v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("clean.tree")); + // Make all math operations either 8, 16, 32 or 64 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")); + // 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()) { + if (!v3Global.opt.xmlOnly() + && v3Global.opt.oExpand()) { V3Expand::expandAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("expand.tree")); } // Propagate constants across WORDSEL arrayed temporaries - if (v3Global.opt.oSubst()) { + if (!v3Global.opt.xmlOnly() + && 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()) { + if (!v3Global.opt.xmlOnly() + && v3Global.opt.oSubstConst()) { // Constant folding of substitutions V3Const::constifyCpp(v3Global.rootp()); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("constc.tree")); V3Dead::deadifyAll(v3Global.rootp(), true); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("dead.tree")); } - if (!v3Global.opt.lintOnly()) { + if (!v3Global.opt.lintOnly() + && !v3Global.opt.xmlOnly()) { // Fix very deep expressions // Mark evaluation functions as member functions, if needed. V3Depth::depthAll(v3Global.rootp()); - //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("depth.tree")); + if (dumpMore) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("depth.tree")); // Branch prediction V3Branch::branchAll(v3Global.rootp()); @@ -540,14 +577,21 @@ void process () { V3Error::abortIfErrors(); // Output the text - if (!v3Global.opt.lintOnly()) { + if (!v3Global.opt.lintOnly() + && !v3Global.opt.xmlOnly()) { // 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(); + if (!v3Global.opt.xmlOnly()) { // Unfortunately we have some lint checks in emitc. + V3EmitC::emitc(); + } + if (v3Global.opt.xmlOnly() + // Check XML when debugging to make sure no missing node types + || (v3Global.opt.debugCheck() && !v3Global.opt.lintOnly())) { + V3EmitXml::emitxml(); + } // Statistics if (v3Global.opt.stats()) { @@ -555,7 +599,8 @@ void process () { V3Stats::statsReport(); } - if (!v3Global.opt.lintOnly()) { + if (!v3Global.opt.lintOnly() + && !v3Global.opt.xmlOnly()) { // Makefile must be after all other emitters V3EmitMk::emitmk(v3Global.rootp()); } @@ -584,8 +629,9 @@ int main(int argc, char** argv, char** env) { if (!v3Global.opt.outFormatOk() && !v3Global.opt.preprocOnly() && !v3Global.opt.lintOnly() + && !v3Global.opt.xmlOnly() && !v3Global.opt.cdc()) { - v3fatal("verilator: Need --cc, --sc, --sp, --cdc, --lint-only or --E option"); + v3fatal("verilator: Need --cc, --sc, --sp, --cdc, --lint-only, --xml_only or --E option"); } // Check environment V3Options::getenvSYSTEMC(); diff --git a/test_regress/t/t_xml_first.out b/test_regress/t/t_xml_first.out new file mode 100755 index 000000000..06897ec83 --- /dev/null +++ b/test_regress/t/t_xml_first.out @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_regress/t/t_xml_first.pl b/test_regress/t/t_xml_first.pl new file mode 100755 index 000000000..7a95f3b37 --- /dev/null +++ b/test_regress/t/t_xml_first.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2012 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. + +$Self->{vlt} or $Self->skip("Verilator only test"); + +$Self->{golden_out} ||= "t/$Self->{name}.out"; +my $out_filename = "$Self->{obj_dir}/V$Self->{name}.xml"; + +compile ( + verilator_flags2 => ['--xml-only'], + verilator_make_gcc => 0, + ); + +ok(files_identical($out_filename, $Self->{golden_out})); + +1; diff --git a/test_regress/t/t_xml_first.v b/test_regress/t/t_xml_first.v new file mode 100644 index 000000000..cf58091f8 --- /dev/null +++ b/test_regress/t/t_xml_first.v @@ -0,0 +1,53 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2012 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Outputs + q, + // Inputs + clk, d + ); + input clk; + input [3:0] d; + output wire [3:0] q; + + logic [3:0] between; + + mod1 cell1 (.q(between), + /*AUTOINST*/ + // Inputs + .clk (clk), + .d (d[3:0])); + + mod2 cell2 (.d(between), + /*AUTOINST*/ + // Outputs + .q (q[3:0]), + // Inputs + .clk (clk)); + +endmodule + +module mod1 + ( + input clk, + input [3:0] d, + output logic [3:0] q + ); + always @(posedge clk) + q <= d; + +endmodule + +module mod2 + ( + input clk, + input [3:0] d, + output wire [3:0] q + ); + + assign q = d; + +endmodule