diff --git a/Changes b/Changes index 8372f482e..8f67eaf38 100644 --- a/Changes +++ b/Changes @@ -26,6 +26,10 @@ indicates the contributor was also the author of the fix; Thanks! *** With --Wall, add INCABSPATH warning on `include with absolute paths. +*** With --Wall, add UNDRIVEN warning on undriven nets. + +*** With --Wall, add UNUSED warning on unused nets. + *** The VARHIDDEN warning is now disabled by default, use -Wall to enable. * Verilator 3.805 2010/11/02 diff --git a/bin/verilator b/bin/verilator index 9529360ae..dc992dfed 100755 --- a/bin/verilator +++ b/bin/verilator @@ -884,7 +884,7 @@ received from third parties. Disable all code style related warning messages (note by default they are already disabled). This is equivalent to "-Wno-DECLFILENAME -Wno-DEFPARAM --Wno-INCABSPATH -Wno-VARHIDDEN". +-Wno-INCABSPATH -Wno-UNDRIVEN -Wno-UNUSED -Wno-VARHIDDEN". =item -Wwarn-I @@ -901,7 +901,8 @@ enabled), but do not affect style messages. This is equivalent to =item -Wwarn-style Enable all code style related warning messages. This is equivalent to -"-Wwarn-DECLFILENAME -Wwarn-DEFPARAM -Wwarn-INCABSPATH -Wwarn-VARHIDDEN". +"-Wwarn-DECLFILENAME -Wwarn-DEFPARAM -Wwarn-INCABSPATH -Wwarn-UNDRIVEN +-Wwarn-UNUSED -Wwarn-VARHIDDEN". =item -x-assign 0 @@ -2582,7 +2583,9 @@ cases would result in simulator mismatches. =item UNDRIVEN -Warns that the specified signal is never sourced. +Warns that the specified signal is never sourced. Verilator is fairly +liberal in the usage calculations; making a signal public, or loading only +a single array element marks the entire signal as driven. Disabled by default as this is a code style warning, it will simulate correctly. @@ -2667,8 +2670,9 @@ correctly. =item UNUSED -Warns that the specified signal is never sinked. This is a future message, -currently Verilator will not produce this warning. +Warns that the specified signal is never sinked. Verilator is fairly +liberal in the usage calculations; making a signal public, or accessing +only a single array element marks the entire signal as used. Disabled by default as this is a code style warning, it will simulate correctly. diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 854a47807..59f1a1f72 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -221,6 +221,7 @@ RAW_OBJS = \ V3Trace.o \ V3TraceDecl.o \ V3Tristate.o \ + V3Undriven.o \ V3Unknown.o \ V3Unroll.o \ V3Width.o \ diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp new file mode 100644 index 000000000..283262599 --- /dev/null +++ b/src/V3Undriven.cpp @@ -0,0 +1,274 @@ +//************************************************************************* +// DESCRIPTION: Verilator: Check for unused/undriven signals +// +// Code available from: http://www.veripool.org/verilator +// +// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli +// +//************************************************************************* +// +// Copyright 2004-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. +// +//************************************************************************* +// V3Undriven's Transformations: +// +// Each module: +// Make vector for all variables +// SEL(VARREF(...))) mark only some bits as used/driven +// else VARREF(...) mark all bits as used/driven +// Report unused/undriven nets +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Undriven.h" +#include "V3Ast.h" + +//###################################################################### +// Class for every variable we may process + +class UndrivenVarEntry { + // MEMBERS + AstVar* m_varp; // Variable this tracks + bool m_usedWhole; // True if whole vector used + bool m_drivenWhole; // True if whole vector driven + vector m_flags; // Used/Driven on each subbit + + enum { FLAG_USED = 0, FLAG_DRIVEN = 1, FLAGS_PER_BIT = 2 }; + + static int debug() { + static int level = -1; + if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); + return level; + } + +public: + // CONSTRUCTORS + UndrivenVarEntry (AstVar* varp) { // Construction for when a var is used + UINFO(9, "create "<width()*FLAGS_PER_BIT); + for (int i=0; iwidth()*FLAGS_PER_BIT; i++) { + m_flags[i] = false; + } + } + ~UndrivenVarEntry() {} +private: + // METHODS + inline bool bitNumOk(int bit) const { return (bit*FLAGS_PER_BIT < (int)m_flags.size()); } + inline bool usedFlag(int bit) { return m_flags[bit*FLAGS_PER_BIT + FLAG_USED]; } + inline bool drivenFlag(int bit) { return m_flags[bit*FLAGS_PER_BIT + FLAG_DRIVEN]; } + string bitNames(int index) { + string bits=""; + bool prev = false; + int msb = 0; + // bit==-1 loops below; we do one extra iteration so end with prev=false + for (int bit=(m_flags.size()/FLAGS_PER_BIT)-1; bit >= -1; --bit) { + if (bit>=0 && !m_flags[bit*FLAGS_PER_BIT + index]) { + if (!prev) { prev=true; msb = bit; } + } else if (prev) { + AstBasicDType* bdtypep = m_varp->basicp(); + int lsb = bit+1; + if (bits != "") bits += ","; + if (lsb==msb) { + bits += cvtToStr(lsb+bdtypep->lsb()); + } else { + if (bdtypep->littleEndian()) { + bits += cvtToStr(lsb+bdtypep->lsb())+":"+cvtToStr(msb+bdtypep->lsb()); + } else { + bits += cvtToStr(msb+bdtypep->lsb())+":"+cvtToStr(lsb+bdtypep->lsb()); + } + } + prev = false; + } + } + return "["+bits+"]"; + } +public: + void usedWhole() { + UINFO(9, "set u[*] "<name()<name()<name()<name()<isParam()) { + bool allU=true; + bool allD=true; + bool anyU=m_usedWhole; + bool anyD=m_drivenWhole; + for (unsigned bit=0; bitv3warn(UNDRIVEN, "Signal is not driven, nor used: "<prettyName()); + } + else { + if (!m_usedWhole && !anyU) { + nodep->v3warn(UNUSED, "Signal is not used: "<prettyName()); + } else if (!m_usedWhole) { + nodep->v3warn(UNUSED, "Bits of signal are not used: "<prettyName() + <v3warn(UNDRIVEN, "Signal is not driven: "<prettyName()); + } else if (!m_drivenWhole) { + nodep->v3warn(UNDRIVEN, "Bits of signal are not driven: "<prettyName() + < UndrivenVar* for usage var, 0=not set yet + AstUser1InUse m_inuser1; + + // STATE + vector m_entryps; // Nodes to delete when we are finished + + // METHODS + static int debug() { + static int level = -1; + if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); + return level; + } + + UndrivenVarEntry* getEntryp(AstVar* nodep) { + if (!nodep->user1p()) { + UndrivenVarEntry* entryp = new UndrivenVarEntry (nodep); + m_entryps.push_back(entryp); + nodep->user1p(entryp); + return entryp; + } else { + UndrivenVarEntry* entryp = (UndrivenVarEntry*)(nodep->user1p()); + return entryp; + } + } + + // VISITORS + virtual void visit(AstVar* nodep, AstNUser*) { + UndrivenVarEntry* entryp = getEntryp (nodep); + if (nodep->isInput() + || nodep->isSigPublic() || nodep->isSigUserRWPublic()) { + entryp->drivenWhole(); + } + if (nodep->isOutput() + || nodep->isSigPublic() || nodep->isSigUserRWPublic() + || nodep->isSigUserRdPublic()) { + entryp->usedWhole(); + } + // Discover variables used in bit definitions, etc + nodep->iterateChildren(*this); + } + virtual void visit(AstArraySel* nodep, AstNUser*) { + // Arrays are rarely constant assigned, so for now we punt and do all entries + nodep->iterateChildren(*this); + } + virtual void visit(AstSel* nodep, AstNUser*) { + AstVarRef* varrefp = nodep->fromp()->castVarRef(); + AstConst* constp = nodep->lsbp()->castConst(); + if (varrefp && constp && !constp->num().isFourState()) { + UndrivenVarEntry* entryp = getEntryp (varrefp->varp()); + int lsb = constp->toUInt(); + if (varrefp->lvalue()) entryp->drivenBit(lsb, nodep->width()); + else entryp->usedBit(lsb, nodep->width()); + } else { + // else other varrefs handled as unknown mess in AstVarRef + nodep->iterateChildren(*this); + } + } + virtual void visit(AstVarRef* nodep, AstNUser*) { + // Any variable + UndrivenVarEntry* entryp = getEntryp (nodep->varp()); + if (nodep->lvalue()) entryp->drivenWhole(); + else entryp->usedWhole(); + } + + // Coverage artifacts etc shouldn't count as a sink + virtual void visit(AstCoverDecl* nodep, AstNUser*) {} + virtual void visit(AstCoverInc* nodep, AstNUser*) {} + virtual void visit(AstCoverToggle* nodep, AstNUser*) {} + virtual void visit(AstTraceDecl* nodep, AstNUser*) {} + virtual void visit(AstTraceInc* nodep, AstNUser*) {} + + // iterate + virtual void visit(AstConst* nodep, AstNUser*) {} + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + UndrivenVisitor(AstNetlist* nodep) { + AstNode::user1ClearTree(); // user1p() used on entire tree + nodep->accept(*this); + } + virtual ~UndrivenVisitor() { + for (vector::iterator it = m_entryps.begin(); it != m_entryps.end(); ++it) { + (*it)->reportViolations(); + delete (*it); + } + } +}; + +//###################################################################### +// Undriven class functions + +void V3Undriven::undrivenAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "<dumpTreeFile(v3Global.debugFilename("coverage.tree")); } + // Push constants, but only true constants preserving liveness + // so V3Undriven sees variables to be eliminated, ie "if (0 && foo) ..." + V3Const::constifyAllLive(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); + + // Signal based lint checks, no change to structures + // Must be before first constification pass drops dead code + V3Undriven::undrivenAll(v3Global.rootp()); + // Assertion insertion // After we've added block coverage, but before other nasty transforms V3AssertPre::assertPreAll(v3Global.rootp()); diff --git a/test_regress/t/t_lint_unused_bad.pl b/test_regress/t/t_lint_unused_bad.pl new file mode 100755 index 000000000..fb0e98238 --- /dev/null +++ b/test_regress/t/t_lint_unused_bad.pl @@ -0,0 +1,26 @@ +#!/usr/bin/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. + +compile ( + v_flags2 => ["--lint-only -Wall -Wno-DECLFILENAME"], + fails=>1, + verilator_make_gcc => 0, + make_top_shell => 0, + make_main => 0, + expect=> +'%Warning-UNUSED: t/t_lint_unused_bad.v:\d+: Bits of signal are not used: assunu1\[5:1\] +%Warning-UNUSED: Use .* to disable this message. +%Warning-UNDRIVEN: t/t_lint_unused_bad.v:\d+: Bits of signal are not driven: udrb2\[14:13,11\] +%Warning-UNDRIVEN: t/t_lint_unused_bad.v:\d+: Signal is not driven, nor used: unu3 +%Error: Exiting due to.*', + ) if $Self->{v3}; + +ok(1); +1; + diff --git a/test_regress/t/t_lint_unused_bad.v b/test_regress/t/t_lint_unused_bad.v new file mode 100644 index 000000000..4802adabf --- /dev/null +++ b/test_regress/t/t_lint_unused_bad.v @@ -0,0 +1,43 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2010 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Outputs + out, + // Inputs + in + ); + + input in; // inputs don't get flagged as undriven + output out; // outputs don't get flagged as unused + + wire out = in; + + sub sub (); + +endmodule + +module sub; + + wire pub /*verilator public*/; // Ignore publics + + wire [5:0] assunu1 = 0; // Assigned but unused + + wire [3:0] assunub2 = 0; // Assigned but bit 2 unused + + wire [15:10] udrb2; // [14:13,11] is undriven + assign udrb2[15] = 0; + assign udrb2[12] = 0; + assign udrb2[10] = 0; + + wire unu3; // Totally unused + + localparam THREE = 3; + + initial begin + if (0 && assunu1[0] != 0 && udrb2 != 0) begin end + if (0 && assunub2[THREE] && assunub2[1:0]!=0) begin end + end +endmodule