diff --git a/Changes b/Changes index fef21b51d..d76fa8b63 100644 --- a/Changes +++ b/Changes @@ -3,6 +3,10 @@ Revision history for Verilator The contributors that suggested a given feature are shown in []. [by ...] indicates the contributor was also the author of the fix; Thanks! +* Verilator 3.63* + +*** Add /*verilator isolate_asignments*/ attribute. [Mike Shinkarovsky] + * Verilator 3.631 1/2/2007 ** Support standard NAME[#] for cells created by arraying or generate for. diff --git a/bin/verilator b/bin/verilator index bb2d1042a..b74f43892 100755 --- a/bin/verilator +++ b/bin/verilator @@ -236,7 +236,7 @@ desired, but other assertions are, use --assert --nopsl.) =item --bin I Rarely needed. Override the default filename for Verilator itself. When a -dependancy (.d) file is created, this filename will become a source +dependency (.d) file is created, this filename will become a source dependency, such that a change in this binary will have make rebuild the output files. @@ -253,7 +253,7 @@ Enables all forms of coverage, alias for --coverage-line, --coverage-user Specifies basic block line coverage analysis code should be inserted. Coverage analysis adds statements at each code flow change point, which are -the branches of IF and CASE statements, a superset of normal Verilog Line +the branches of IF and CASE statements, a super-set of normal Verilog Line Coverage. At each such branch a unique counter is incremented. At the end of a test, the counters along with the filename and line number corresponding to each counter are written into logs/coverage.pl. @@ -857,7 +857,7 @@ order of magnitude. =head1 CROSS COMPILATION -Verilator supports cross-compling Verilated code. This is generally used +Verilator supports cross-compiling Verilated code. This is generally used to run Verilator on a Linux system and produce C++ code that is then compiled on Windows. @@ -1096,6 +1096,41 @@ be pure; they cannot reference any variables outside the task itself. Used after a input declaration to indicate the signal should be declared in SystemC as a sc_clock instead of a bool. +=item /*verilator isolate_asignments*/ + +Used after a signal declaration to indicate the assignments to this signal +in any blocks should be isolated into new blocks. When there is a large +combinatorial block that is resulting in a UNOPTFLAT warning, attaching +this to the signal causing a false loop may clear up the problem. + +IE, with the following + + reg splitme /* verilator isolate_asignments*/; + always @* begin + if (....) begin + splitme = ....; + other assignments + end + end + +Verilator will internally split the block that assigns to "splitme" into +two blocks: + +It would then internally break it into (sort of): + + // All assignments excluding those to splitme + always @* begin + if (....) begin + other assignments + end + end + // All assignments to splitme + always @* begin + if (....) begin + splitme = ....; + end + end + =item /*verilator tracing_off*/ Disable waveform tracing for all future signals that are declared in this @@ -1515,6 +1550,10 @@ The UNOPTFLAT warning may also be due to clock enables, identified from the reported path going through a clock gating cell. To fix these, use the clock_enable meta comment described above. +The UNOPTFLAT warning may also occur where outputs from a block of logic +are independent, but occur in the same always block. To fix this, use the +isolate_asignments meta comment described above. + =item UNSIGNED Warns that you are comparing a unsigned value in a way that implies it is diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index e13f0604f..200c4a0fc 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -156,6 +156,7 @@ RAW_OBJS = \ V3Scope.o \ V3Signed.o \ V3Split.o \ + V3SplitAs.o \ V3Stats.o \ V3StatsReport.o \ V3Subst.o \ diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 5435298df..59d0d0bcb 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -350,6 +350,7 @@ void AstVar::dump(ostream& str) { if (isUsedClock()) str<<" [C]"; if (isSigPublic()) str<<" [P]"; if (attrClockEn()) str<<" [aCLKEN]"; + if (attrIsolateAssign()) str<<" [aISO]"; if (attrFileDescr()) str<<" [aFD]"; if (isFuncReturn()) str<<" [FUNCRTN]"; else if (isFuncLocal()) str<<" [FUNC]"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index f8eda0f12..1f332489f 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -241,6 +241,7 @@ private: bool m_funcLocal:1; // Local variable for a function bool m_funcReturn:1; // Return variable for a function bool m_attrClockEn:1;// User clock enable attribute + bool m_attrIsolateAssign:1;// User isolate_asignments attribute bool m_fileDescr:1; // File descriptor bool m_isConst:1; // Table contains constant data bool m_isStatic:1; // Static variable @@ -252,7 +253,8 @@ private: m_sc=false; m_scClocked=false; m_scSensitive=false; m_usedClock=false; m_usedParam=false; m_sigPublic=false; m_funcLocal=false; m_funcReturn=false; - m_attrClockEn=false; m_fileDescr=false; m_isConst=false; m_isStatic=false; + m_attrClockEn=false; m_attrIsolateAssign=false; + m_fileDescr=false; m_isConst=false; m_isStatic=false; m_trace=false; } public: @@ -301,6 +303,7 @@ public: void attrClockEn(bool flag) { m_attrClockEn = flag; } void attrFileDescr(bool flag) { m_fileDescr = flag; } void attrScClocked(bool flag) { m_scClocked = flag; } + void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; } void usedClock(bool flag) { m_usedClock = flag; } void usedParam(bool flag) { m_usedParam = flag; } void sigPublic(bool flag) { m_sigPublic = flag; } @@ -348,6 +351,7 @@ public: bool attrClockEn() const { return m_attrClockEn; } bool attrFileDescr() const { return m_fileDescr; } bool attrScClocked() const { return m_scClocked; } + bool attrIsolateAssign() const { return m_attrIsolateAssign; } int widthAlignBytes() const; // Structure alignment 1,2,4 or 8 bytes (arrays affect this) int widthTotalBytes() const; // Width in bytes rounding up 1,2,4,8,12,... uint32_t msb() const { if (!rangep()) return 0; return rangep()->msbConst(); } @@ -359,6 +363,7 @@ public: // Note the method below too if (fromp->attrClockEn()) attrClockEn(true); if (fromp->attrFileDescr()) attrFileDescr(true); + if (fromp->attrIsolateAssign()) attrIsolateAssign(true); } bool gateMultiInputOptimizable() const { // Ok to gate optimize; must return false if propagateAttrFrom would do anything diff --git a/src/V3SplitAs.cpp b/src/V3SplitAs.cpp new file mode 100644 index 000000000..2021e82d1 --- /dev/null +++ b/src/V3SplitAs.cpp @@ -0,0 +1,204 @@ +// $Id$ +//************************************************************************* +// DESCRIPTION: Verilator: Break always into separate statements to reduce temps +// +// Code available from: http://www.veripool.com/verilator +// +// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli +// +//************************************************************************* +// +// Copyright 2003-2007 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// General Public License or the Perl Artistic License. +// +// 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. +// +//************************************************************************* +// V3SplitAs's Transformations: +// +// Search each ALWAYS for a VARREF lvalue with a /*isolate_asignments*/ attribute +// If found, color statements with both, assignment to that varref, or other assignments. +// Replicate the Always, and remove mis-colored duplicate code. +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" +#include +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3SplitAs.h" +#include "V3Stats.h" +#include "V3Ast.h" + +//###################################################################### + +class SplitAsBaseVisitor : public AstNVisitor { +public: + // METHODS + //int debug() { return 9; } +}; + +//###################################################################### +// Find all split variables in a block + +class SplitAsFindVisitor : public SplitAsBaseVisitor { +private: + // STATE + AstVarScope* m_splitVscp; // Variable we want to split + + // METHODS + virtual void visit(AstVarRef* nodep, AstNUser*) { + if (nodep->lvalue() && !m_splitVscp + && nodep->varp()->attrIsolateAssign()) { + m_splitVscp = nodep->varScopep(); + } + } + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + SplitAsFindVisitor(AstAlways* nodep) { + m_splitVscp = NULL; + nodep->accept(*this); + } + virtual ~SplitAsFindVisitor() {} + // METHODS + AstVarScope* splitVscp() const { return m_splitVscp; } +}; + +//###################################################################### +// Remove nodes not containing proper references + +class SplitAsCleanVisitor : public SplitAsBaseVisitor { +private: + // STATE + AstVarScope* m_splitVscp; // Variable we want to split + bool m_modeMatch; // Remove matching Vscp, else non-matching + bool m_keepStmt; // Current Statement must be preserved + bool m_matches; // Statement below has matching lvalue reference + + // METHODS + virtual void visit(AstVarRef* nodep, AstNUser*) { + if (nodep->lvalue()) { + if (nodep->varScopep()==m_splitVscp) { + UINFO(6," CL VAR "<iterateChildren(*this); + + if (m_keepStmt + || (m_modeMatch ? m_matches : !m_matches)) { + UINFO(6," Keep STMT "<unlinkFrBack(); pushDeletep(nodep); + } + } + // If something below matches, the upper statement remains too. + m_keepStmt = oldKeep || m_keepStmt; + UINFO(9," upKeep="<iterateChildren(*this); + } +public: + // CONSTUCTORS + SplitAsCleanVisitor(AstAlways* nodep, AstVarScope* vscp, bool modeMatch) { + m_splitVscp = vscp; + m_modeMatch = modeMatch; + nodep->accept(*this); + } + virtual ~SplitAsCleanVisitor() {} +}; + +//###################################################################### +// SplitAs class functions + +class SplitAsVisitor : public SplitAsBaseVisitor { +private: + // NODE STATE + // AstAlways::user() -> bool. True if already processed + + // STATE + V3Double0 m_statSplits; // Statistic tracking + AstVarScope* m_splitVscp; // Variable we want to split + + // METHODS + void splitAlways(AstAlways* nodep) { + UINFO(3,"Split "<=9) nodep->dumpTree(cout,"-in : "); + // Duplicate it and link in + AstAlways* newp = nodep->cloneTree(false)->castAlways(); + newp->user(true); // So we don't clone it again + nodep->addNextHere(newp); + { // Delete stuff we don't want in old + SplitAsCleanVisitor visitor (nodep, m_splitVscp, false); + if (debug()>=9) nodep->dumpTree(cout,"-out0: "); + } + { // Delete stuff we don't want in new + SplitAsCleanVisitor visitor (newp, m_splitVscp, true); + if (debug()>=9) newp->dumpTree(cout,"-out1: "); + } + } + + virtual void visit(AstAlways* nodep, AstNUser*) { + // Are there any lvalue references below this? + // There could be more then one. So, we process the first one found first. + if (!nodep->user()) { + SplitAsFindVisitor visitor (nodep); + m_splitVscp = visitor.splitVscp(); + if (m_splitVscp) { + splitAlways(nodep); + m_statSplits++; + } + } + } + + // Speedup; no always under math + virtual void visit(AstNodeMath* nodep, AstNUser*) {} + + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + SplitAsVisitor(AstNetlist* nodep) { + m_splitVscp = NULL; + AstNode::userClearTree(); // userp() used on entire tree + nodep->accept(*this); + } + virtual ~SplitAsVisitor() { + V3Stats::addStat("Optimizations, isolate_assignments blocks", m_statSplits); + } +}; + +//###################################################################### +// SplitAs class functions + +void V3SplitAs::splitAsAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "<dumpTreeFile(v3Global.debugFilename("split.tree")); + //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()) { diff --git a/src/verilog.l b/src/verilog.l index 1b3dffafa..2b55246a6 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -450,6 +450,7 @@ escid \\[^ \t\f\r\n]+ "/*verilator public*/" {yylval.fileline = CRELINE(); return yVL_PUBLIC;} "/*verilator public_module*/" {yylval.fileline = CRELINE(); return yVL_PUBLIC_MODULE;} "/*verilator sc_clock*/" {yylval.fileline = CRELINE(); return yVL_CLOCK;} +"/*verilator isolate_assignments*/" {yylval.fileline = CRELINE(); return yVL_ISOLATE_ASSIGNMENTS;} "/*verilator systemc_clock*/" {yylval.fileline = CRELINE(); return yVL_CLOCK;} "/*verilator tracing_off*/" {yylval.fileline = CRELINE(); return yVL_TRACING_OFF;} "/*verilator tracing_on*/" {yylval.fileline = CRELINE(); return yVL_TRACING_ON;} diff --git a/src/verilog.y b/src/verilog.y index d5f0958a3..ce6107da1 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -169,6 +169,7 @@ class AstSenTree; %token yVL_PARALLEL_CASE "/*verilator parallel_case*/" %token yVL_PUBLIC "/*verilator public*/" %token yVL_PUBLIC_MODULE "/*verilator public_module*/" +%token yVL_ISOLATE_ASSIGNMENTS "/*verilator isolate_assignments*/" %token yVL_TRACING_OFF "/*verilator tracing_off*/" %token yVL_TRACING_ON "/*verilator tracing_on*/" @@ -519,6 +520,7 @@ sigAttrList: sigAttr {} sigAttr: yVL_CLOCK { V3Parse::s_varAttrp->attrScClocked(true); } | yVL_CLOCK_ENABLE { V3Parse::s_varAttrp->attrClockEn(true); } | yVL_PUBLIC { V3Parse::s_varAttrp->sigPublic(true); } + | yVL_ISOLATE_ASSIGNMENTS { V3Parse::s_varAttrp->attrIsolateAssign(true); } ; sigList: onesig { $$ = $1; } diff --git a/test_regress/t/t_unopt_combo.v b/test_regress/t/t_unopt_combo.v index 9d59b542a..55d16547e 100644 --- a/test_regress/t/t_unopt_combo.v +++ b/test_regress/t/t_unopt_combo.v @@ -69,7 +69,12 @@ module file (/*AUTOARG*/ ); input [31:0] crc; +`ifdef ISOLATE + output reg [31:0] b /* verilator isolate_assignments*/; +`else output reg [31:0] b; +`endif + output reg [31:0] c; output reg [31:0] d; diff --git a/test_regress/t/t_unopt_combo_isolate.pl b/test_regress/t/t_unopt_combo_isolate.pl new file mode 100755 index 000000000..ab7217dd8 --- /dev/null +++ b/test_regress/t/t_unopt_combo_isolate.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# 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 +# General Public License or the Perl Artistic License. + +top_filename("t/t_unopt_combo.v"); + +compile ( + v_flags2 => ['-DISOLATE --stats'], + ); + +if ($Last_Self->{v3}) { + file_grep ($Last_Self->{stats}, qr/Optimizations, isolate_assignments blocks\s+1/i); +} + +execute ( + ); + +ok(1); +1;